1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.os; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.system.ErrnoException; 22 import android.system.Os; 23 import android.system.OsConstants; 24 25 import dalvik.system.VMRuntime; 26 27 import java.io.Closeable; 28 import java.io.FileDescriptor; 29 import java.nio.ByteBuffer; 30 import java.nio.DirectByteBuffer; 31 import java.nio.NioUtils; 32 33 import sun.misc.Cleaner; 34 35 /** 36 * SharedMemory enables the creation, mapping, and protection control over anonymous shared memory. 37 */ 38 public final class SharedMemory implements Parcelable, Closeable { 39 40 private final FileDescriptor mFileDescriptor; 41 private final int mSize; 42 private final MemoryRegistration mMemoryRegistration; 43 private Cleaner mCleaner; 44 SharedMemory(FileDescriptor fd)45 private SharedMemory(FileDescriptor fd) { 46 // This constructor is only used internally so it should be impossible to hit any of the 47 // exceptions unless something goes horribly wrong. 48 if (fd == null) { 49 throw new IllegalArgumentException( 50 "Unable to create SharedMemory from a null FileDescriptor"); 51 } 52 if (!fd.valid()) { 53 throw new IllegalArgumentException( 54 "Unable to create SharedMemory from closed FileDescriptor"); 55 } 56 mFileDescriptor = fd; 57 mSize = nGetSize(mFileDescriptor); 58 if (mSize <= 0) { 59 throw new IllegalArgumentException("FileDescriptor is not a valid ashmem fd"); 60 } 61 62 mMemoryRegistration = new MemoryRegistration(mSize); 63 mCleaner = Cleaner.create(mFileDescriptor, 64 new Closer(mFileDescriptor, mMemoryRegistration)); 65 } 66 67 /** 68 * Creates an anonymous SharedMemory instance with the provided debug name and size. The name 69 * is only used for debugging purposes and can help identify what the shared memory is used 70 * for when inspecting memory maps for the processes that have mapped this SharedMemory 71 * instance. 72 * 73 * @param name The debug name to use for this SharedMemory instance. This can be null, however 74 * a debug name is recommended to help identify memory usage when using tools 75 * such as lsof or examining /proc/[pid]/maps 76 * @param size The size of the shared memory to create. Must be greater than 0. 77 * @return A SharedMemory instance of the requested size 78 * @throws ErrnoException if the requested allocation fails. 79 */ create(@ullable String name, int size)80 public static @NonNull SharedMemory create(@Nullable String name, int size) 81 throws ErrnoException { 82 if (size <= 0) { 83 throw new IllegalArgumentException("Size must be greater than zero"); 84 } 85 return new SharedMemory(nCreate(name, size)); 86 } 87 checkOpen()88 private void checkOpen() { 89 if (!mFileDescriptor.valid()) { 90 throw new IllegalStateException("SharedMemory is closed"); 91 } 92 } 93 94 private static final int PROT_MASK = OsConstants.PROT_READ | OsConstants.PROT_WRITE 95 | OsConstants.PROT_EXEC | OsConstants.PROT_NONE; 96 validateProt(int prot)97 private static void validateProt(int prot) { 98 if ((prot & ~PROT_MASK) != 0) { 99 throw new IllegalArgumentException("Invalid prot value"); 100 } 101 } 102 103 /** 104 * Sets the protection on the shared memory to the combination specified in prot, which 105 * is either a bitwise-or'd combination of {@link android.system.OsConstants#PROT_READ}, 106 * {@link android.system.OsConstants#PROT_WRITE}, {@link android.system.OsConstants#PROT_EXEC} 107 * from {@link android.system.OsConstants}, or {@link android.system.OsConstants#PROT_NONE}, 108 * to remove all further access. 109 * 110 * Note that protection can only ever be removed, not added. By default shared memory 111 * is created with protection set to PROT_READ | PROT_WRITE | PROT_EXEC. The protection 112 * passed here also only applies to any mappings created after calling this method. Existing 113 * mmaps of the shared memory retain whatever protection they had when they were created. 114 * 115 * A common usage of this is to share a read-only copy of the data with something else. To do 116 * that first create the read/write mapping with PROT_READ | PROT_WRITE, 117 * then call setProtect(PROT_READ) to remove write capability, then send the SharedMemory 118 * to another process. That process will only be able to mmap with PROT_READ. 119 * 120 * @param prot Any bitwise-or'ed combination of 121 * {@link android.system.OsConstants#PROT_READ}, 122 * {@link android.system.OsConstants#PROT_WRITE}, and 123 * {@link android.system.OsConstants#PROT_EXEC}; or 124 * {@link android.system.OsConstants#PROT_NONE} 125 * @return Whether or not the requested protection was applied. Returns true on success, 126 * false if the requested protection was broader than the existing protection. 127 */ setProtect(int prot)128 public boolean setProtect(int prot) { 129 checkOpen(); 130 validateProt(prot); 131 int errno = nSetProt(mFileDescriptor, prot); 132 return errno == 0; 133 } 134 135 /** 136 * Returns the backing {@link FileDescriptor} for this SharedMemory object. The SharedMemory 137 * instance retains ownership of the FileDescriptor. 138 * 139 * This FileDescriptor is interoperable with the ASharedMemory NDK APIs. 140 * 141 * @return Returns the FileDescriptor associated with this object. 142 * 143 * @hide Exists only for MemoryFile interop 144 */ getFileDescriptor()145 public @NonNull FileDescriptor getFileDescriptor() { 146 return mFileDescriptor; 147 } 148 149 /** 150 * Returns the backing native fd int for this SharedMemory object. The SharedMemory 151 * instance retains ownership of the fd. 152 * 153 * This fd is interoperable with the ASharedMemory NDK APIs. 154 * 155 * @return Returns the native fd associated with this object, or -1 if it is already closed. 156 * 157 * @hide Exposed for native ASharedMemory_dupFromJava() 158 */ getFd()159 public int getFd() { 160 return mFileDescriptor.getInt$(); 161 } 162 163 /** 164 * @return The size of the SharedMemory region. 165 */ getSize()166 public int getSize() { 167 checkOpen(); 168 return mSize; 169 } 170 171 /** 172 * Creates a read/write mapping of the entire shared memory region. This requires the the 173 * protection level of the shared memory is at least PROT_READ|PROT_WRITE or the map will fail. 174 * 175 * Use {@link #map(int, int, int)} to have more control over the mapping if desired. 176 * This is equivalent to map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, getSize()) 177 * 178 * @return A ByteBuffer mapping 179 * @throws ErrnoException if the mmap call failed. 180 */ mapReadWrite()181 public @NonNull ByteBuffer mapReadWrite() throws ErrnoException { 182 return map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, mSize); 183 } 184 185 /** 186 * Creates a read-only mapping of the entire shared memory region. This requires the the 187 * protection level of the shared memory is at least PROT_READ or the map will fail. 188 * 189 * Use {@link #map(int, int, int)} to have more control over the mapping if desired. 190 * This is equivalent to map(OsConstants.PROT_READ, 0, getSize()) 191 * 192 * @return A ByteBuffer mapping 193 * @throws ErrnoException if the mmap call failed. 194 */ mapReadOnly()195 public @NonNull ByteBuffer mapReadOnly() throws ErrnoException { 196 return map(OsConstants.PROT_READ, 0, mSize); 197 } 198 199 /** 200 * Creates an mmap of the SharedMemory with the specified prot, offset, and length. This will 201 * always produce a new ByteBuffer window to the backing shared memory region. Every call 202 * to map() may be paired with a call to {@link #unmap(ByteBuffer)} when the ByteBuffer 203 * returned by map() is no longer needed. 204 * 205 * @param prot A bitwise-or'd combination of PROT_READ, PROT_WRITE, PROT_EXEC, or PROT_NONE. 206 * @param offset The offset into the shared memory to begin mapping. Must be >= 0 and less than 207 * getSize(). 208 * @param length The length of the region to map. Must be > 0 and offset + length must not 209 * exceed getSize(). 210 * @return A ByteBuffer mapping. 211 * @throws ErrnoException if the mmap call failed. 212 */ map(int prot, int offset, int length)213 public @NonNull ByteBuffer map(int prot, int offset, int length) throws ErrnoException { 214 checkOpen(); 215 validateProt(prot); 216 if (offset < 0) { 217 throw new IllegalArgumentException("Offset must be >= 0"); 218 } 219 if (length <= 0) { 220 throw new IllegalArgumentException("Length must be > 0"); 221 } 222 if (offset + length > mSize) { 223 throw new IllegalArgumentException("offset + length must not exceed getSize()"); 224 } 225 long address = Os.mmap(0, length, prot, OsConstants.MAP_SHARED, mFileDescriptor, offset); 226 boolean readOnly = (prot & OsConstants.PROT_WRITE) == 0; 227 Runnable unmapper = new Unmapper(address, length, mMemoryRegistration.acquire()); 228 return new DirectByteBuffer(length, address, mFileDescriptor, unmapper, readOnly); 229 } 230 231 /** 232 * Unmaps a buffer previously returned by {@link #map(int, int, int)}. This will immediately 233 * release the backing memory of the ByteBuffer, invalidating all references to it. Only 234 * call this method if there are no duplicates of the ByteBuffer in use and don't 235 * access the ByteBuffer after calling this method. 236 * 237 * @param buffer The buffer to unmap 238 */ unmap(@onNull ByteBuffer buffer)239 public static void unmap(@NonNull ByteBuffer buffer) { 240 if (buffer instanceof DirectByteBuffer) { 241 NioUtils.freeDirectBuffer(buffer); 242 } else { 243 throw new IllegalArgumentException( 244 "ByteBuffer wasn't created by #map(int, int, int); can't unmap"); 245 } 246 } 247 248 /** 249 * Close the backing {@link FileDescriptor} of this SharedMemory instance. Note that all 250 * open mappings of the shared memory will remain valid and may continue to be used. The 251 * shared memory will not be freed until all file descriptor handles are closed and all 252 * memory mappings are unmapped. 253 */ 254 @Override close()255 public void close() { 256 if (mCleaner != null) { 257 mCleaner.clean(); 258 mCleaner = null; 259 } 260 } 261 262 @Override describeContents()263 public int describeContents() { 264 return CONTENTS_FILE_DESCRIPTOR; 265 } 266 267 @Override writeToParcel(@onNull Parcel dest, int flags)268 public void writeToParcel(@NonNull Parcel dest, int flags) { 269 checkOpen(); 270 dest.writeFileDescriptor(mFileDescriptor); 271 } 272 273 public static final Parcelable.Creator<SharedMemory> CREATOR = 274 new Parcelable.Creator<SharedMemory>() { 275 @Override 276 public SharedMemory createFromParcel(Parcel source) { 277 FileDescriptor descriptor = source.readRawFileDescriptor(); 278 return new SharedMemory(descriptor); 279 } 280 281 @Override 282 public SharedMemory[] newArray(int size) { 283 return new SharedMemory[size]; 284 } 285 }; 286 287 /** 288 * Cleaner that closes the FD 289 */ 290 private static final class Closer implements Runnable { 291 private FileDescriptor mFd; 292 private MemoryRegistration mMemoryReference; 293 Closer(FileDescriptor fd, MemoryRegistration memoryReference)294 private Closer(FileDescriptor fd, MemoryRegistration memoryReference) { 295 mFd = fd; 296 mMemoryReference = memoryReference; 297 } 298 299 @Override run()300 public void run() { 301 try { 302 Os.close(mFd); 303 } catch (ErrnoException e) { /* swallow error */ } 304 mMemoryReference.release(); 305 mMemoryReference = null; 306 } 307 } 308 309 /** 310 * Cleaner that munmap regions 311 */ 312 private static final class Unmapper implements Runnable { 313 private long mAddress; 314 private int mSize; 315 private MemoryRegistration mMemoryReference; 316 Unmapper(long address, int size, MemoryRegistration memoryReference)317 private Unmapper(long address, int size, MemoryRegistration memoryReference) { 318 mAddress = address; 319 mSize = size; 320 mMemoryReference = memoryReference; 321 } 322 323 @Override run()324 public void run() { 325 try { 326 Os.munmap(mAddress, mSize); 327 } catch (ErrnoException e) { /* swallow exception */ } 328 mMemoryReference.release(); 329 mMemoryReference = null; 330 } 331 } 332 333 /** 334 * Helper class that ensures that the native allocation pressure against the VM heap stays 335 * active until the FD is closed as well as all mappings from that FD are closed. 336 */ 337 private static final class MemoryRegistration { 338 private int mSize; 339 private int mReferenceCount; 340 MemoryRegistration(int size)341 private MemoryRegistration(int size) { 342 mSize = size; 343 mReferenceCount = 1; 344 VMRuntime.getRuntime().registerNativeAllocation(mSize); 345 } 346 acquire()347 public synchronized MemoryRegistration acquire() { 348 mReferenceCount++; 349 return this; 350 } 351 release()352 public synchronized void release() { 353 mReferenceCount--; 354 if (mReferenceCount == 0) { 355 VMRuntime.getRuntime().registerNativeFree(mSize); 356 } 357 } 358 } 359 nCreate(String name, int size)360 private static native FileDescriptor nCreate(String name, int size) throws ErrnoException; nGetSize(FileDescriptor fd)361 private static native int nGetSize(FileDescriptor fd); nSetProt(FileDescriptor fd, int prot)362 private static native int nSetProt(FileDescriptor fd, int prot); 363 } 364