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