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, 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 private static final int PROT_MASK = OsConstants.PROT_READ | OsConstants.PROT_WRITE 97 | OsConstants.PROT_EXEC | OsConstants.PROT_NONE; 98 validateProt(int prot)99 private static void validateProt(int prot) { 100 if ((prot & ~PROT_MASK) != 0) { 101 throw new IllegalArgumentException("Invalid prot value"); 102 } 103 } 104 105 /** 106 * Sets the protection on the shared memory to the combination specified in prot, which 107 * is either a bitwise-or'd combination of {@link android.system.OsConstants#PROT_READ}, 108 * {@link android.system.OsConstants#PROT_WRITE}, {@link android.system.OsConstants#PROT_EXEC} 109 * from {@link android.system.OsConstants}, or {@link android.system.OsConstants#PROT_NONE}, 110 * to remove all further access. 111 * 112 * Note that protection can only ever be removed, not added. By default shared memory 113 * is created with protection set to PROT_READ | PROT_WRITE | PROT_EXEC. The protection 114 * passed here also only applies to any mappings created after calling this method. Existing 115 * mmaps of the shared memory retain whatever protection they had when they were created. 116 * 117 * A common usage of this is to share a read-only copy of the data with something else. To do 118 * that first create the read/write mapping with PROT_READ | PROT_WRITE, 119 * then call setProtect(PROT_READ) to remove write capability, then send the SharedMemory 120 * to another process. That process will only be able to mmap with PROT_READ. 121 * 122 * @param prot Any bitwise-or'ed combination of 123 * {@link android.system.OsConstants#PROT_READ}, 124 * {@link android.system.OsConstants#PROT_WRITE}, and 125 * {@link android.system.OsConstants#PROT_EXEC}; or 126 * {@link android.system.OsConstants#PROT_NONE} 127 * @return Whether or not the requested protection was applied. Returns true on success, 128 * false if the requested protection was broader than the existing protection. 129 */ setProtect(int prot)130 public boolean setProtect(int prot) { 131 checkOpen(); 132 validateProt(prot); 133 int errno = nSetProt(mFileDescriptor, prot); 134 return errno == 0; 135 } 136 137 /** 138 * Returns the backing {@link FileDescriptor} for this SharedMemory object. The SharedMemory 139 * instance retains ownership of the FileDescriptor. 140 * 141 * This FileDescriptor is interoperable with the ASharedMemory NDK APIs. 142 * 143 * @return Returns the FileDescriptor associated with this object. 144 * 145 * @hide Exists only for MemoryFile interop 146 */ getFileDescriptor()147 public @NonNull FileDescriptor getFileDescriptor() { 148 return mFileDescriptor; 149 } 150 151 /** 152 * Returns the backing native fd int for this SharedMemory object. The SharedMemory 153 * instance retains ownership of the fd. 154 * 155 * This fd is interoperable with the ASharedMemory NDK APIs. 156 * 157 * @return Returns the native fd associated with this object, or -1 if it is already closed. 158 * 159 * @hide Exposed for native ASharedMemory_dupFromJava() 160 */ 161 @UnsupportedAppUsage(trackingBug = 171971817) getFd()162 public int getFd() { 163 return mFileDescriptor.getInt$(); 164 } 165 166 /** 167 * @return The size of the SharedMemory region. 168 */ getSize()169 public int getSize() { 170 checkOpen(); 171 return mSize; 172 } 173 174 /** 175 * Creates a read/write mapping of the entire shared memory region. This requires the the 176 * protection level of the shared memory is at least PROT_READ|PROT_WRITE or the map will fail. 177 * 178 * Use {@link #map(int, int, int)} to have more control over the mapping if desired. 179 * This is equivalent to map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, getSize()) 180 * 181 * @return A ByteBuffer mapping 182 * @throws ErrnoException if the mmap call failed. 183 */ mapReadWrite()184 public @NonNull ByteBuffer mapReadWrite() throws ErrnoException { 185 return map(OsConstants.PROT_READ | OsConstants.PROT_WRITE, 0, mSize); 186 } 187 188 /** 189 * Creates a read-only mapping of the entire shared memory region. This requires the the 190 * protection level of the shared memory is at least PROT_READ or the map will fail. 191 * 192 * Use {@link #map(int, int, int)} to have more control over the mapping if desired. 193 * This is equivalent to map(OsConstants.PROT_READ, 0, getSize()) 194 * 195 * @return A ByteBuffer mapping 196 * @throws ErrnoException if the mmap call failed. 197 */ mapReadOnly()198 public @NonNull ByteBuffer mapReadOnly() throws ErrnoException { 199 return map(OsConstants.PROT_READ, 0, mSize); 200 } 201 202 /** 203 * Creates an mmap of the SharedMemory with the specified prot, offset, and length. This will 204 * always produce a new ByteBuffer window to the backing shared memory region. Every call 205 * to map() may be paired with a call to {@link #unmap(ByteBuffer)} when the ByteBuffer 206 * returned by map() is no longer needed. 207 * 208 * @param prot A bitwise-or'd combination of PROT_READ, PROT_WRITE, PROT_EXEC, or PROT_NONE. 209 * @param offset The offset into the shared memory to begin mapping. Must be >= 0 and less than 210 * getSize(). 211 * @param length The length of the region to map. Must be > 0 and offset + length must not 212 * exceed getSize(). 213 * @return A ByteBuffer mapping. 214 * @throws ErrnoException if the mmap call failed. 215 */ map(int prot, int offset, int length)216 public @NonNull ByteBuffer map(int prot, int offset, int length) throws ErrnoException { 217 checkOpen(); 218 validateProt(prot); 219 if (offset < 0) { 220 throw new IllegalArgumentException("Offset must be >= 0"); 221 } 222 if (length <= 0) { 223 throw new IllegalArgumentException("Length must be > 0"); 224 } 225 if (offset + length > mSize) { 226 throw new IllegalArgumentException("offset + length must not exceed getSize()"); 227 } 228 long address = Os.mmap(0, length, prot, OsConstants.MAP_SHARED, mFileDescriptor, offset); 229 boolean readOnly = (prot & OsConstants.PROT_WRITE) == 0; 230 Runnable unmapper = new Unmapper(address, length, mMemoryRegistration.acquire()); 231 return new DirectByteBuffer(length, address, mFileDescriptor, unmapper, readOnly); 232 } 233 234 /** 235 * Unmaps a buffer previously returned by {@link #map(int, int, int)}. This will immediately 236 * release the backing memory of the ByteBuffer, invalidating all references to it. Only 237 * call this method if there are no duplicates of the ByteBuffer in use and don't 238 * access the ByteBuffer after calling this method. 239 * 240 * @param buffer The buffer to unmap 241 */ unmap(@onNull ByteBuffer buffer)242 public static void unmap(@NonNull ByteBuffer buffer) { 243 if (buffer instanceof DirectByteBuffer) { 244 NioUtils.freeDirectBuffer(buffer); 245 } else { 246 throw new IllegalArgumentException( 247 "ByteBuffer wasn't created by #map(int, int, int); can't unmap"); 248 } 249 } 250 251 /** 252 * Close the backing {@link FileDescriptor} of this SharedMemory instance. Note that all 253 * open mappings of the shared memory will remain valid and may continue to be used. The 254 * shared memory will not be freed until all file descriptor handles are closed and all 255 * memory mappings are unmapped. 256 */ 257 @Override close()258 public void close() { 259 if (mCleaner != null) { 260 mCleaner.clean(); 261 mCleaner = null; 262 } 263 } 264 265 @Override describeContents()266 public int describeContents() { 267 return CONTENTS_FILE_DESCRIPTOR; 268 } 269 270 @Override writeToParcel(@onNull Parcel dest, int flags)271 public void writeToParcel(@NonNull Parcel dest, int flags) { 272 checkOpen(); 273 dest.writeFileDescriptor(mFileDescriptor); 274 } 275 276 /** 277 * Returns a dup'd ParcelFileDescriptor from the SharedMemory FileDescriptor. 278 * This obeys standard POSIX semantics, where the 279 * new file descriptor shared state such as file position with the 280 * original file descriptor. 281 * TODO: propose this method as a public or system API for next release to achieve parity with 282 * NDK ASharedMemory_dupFromJava. 283 * 284 * @hide 285 */ getFdDup()286 public ParcelFileDescriptor getFdDup() throws IOException { 287 return ParcelFileDescriptor.dup(mFileDescriptor); 288 } 289 290 public static final @android.annotation.NonNull Parcelable.Creator<SharedMemory> CREATOR = 291 new Parcelable.Creator<SharedMemory>() { 292 @Override 293 public SharedMemory createFromParcel(Parcel source) { 294 FileDescriptor descriptor = source.readRawFileDescriptor(); 295 return new SharedMemory(descriptor); 296 } 297 298 @Override 299 public SharedMemory[] newArray(int size) { 300 return new SharedMemory[size]; 301 } 302 }; 303 304 /** 305 * Cleaner that closes the FD 306 */ 307 private static final class Closer implements Runnable { 308 private FileDescriptor mFd; 309 private MemoryRegistration mMemoryReference; 310 Closer(FileDescriptor fd, MemoryRegistration memoryReference)311 private Closer(FileDescriptor fd, MemoryRegistration memoryReference) { 312 mFd = fd; 313 mMemoryReference = memoryReference; 314 } 315 316 @Override run()317 public void run() { 318 try { 319 Os.close(mFd); 320 } catch (ErrnoException e) { /* swallow error */ } 321 mMemoryReference.release(); 322 mMemoryReference = null; 323 } 324 } 325 326 /** 327 * Cleaner that munmap regions 328 */ 329 private static final class Unmapper implements Runnable { 330 private long mAddress; 331 private int mSize; 332 private MemoryRegistration mMemoryReference; 333 Unmapper(long address, int size, MemoryRegistration memoryReference)334 private Unmapper(long address, int size, MemoryRegistration memoryReference) { 335 mAddress = address; 336 mSize = size; 337 mMemoryReference = memoryReference; 338 } 339 340 @Override run()341 public void run() { 342 try { 343 Os.munmap(mAddress, mSize); 344 } catch (ErrnoException e) { /* swallow exception */ } 345 mMemoryReference.release(); 346 mMemoryReference = null; 347 } 348 } 349 350 /** 351 * Helper class that ensures that the native allocation pressure against the VM heap stays 352 * active until the FD is closed as well as all mappings from that FD are closed. 353 */ 354 private static final class MemoryRegistration { 355 private int mSize; 356 private int mReferenceCount; 357 MemoryRegistration(int size)358 private MemoryRegistration(int size) { 359 mSize = size; 360 mReferenceCount = 1; 361 VMRuntime.getRuntime().registerNativeAllocation(mSize); 362 } 363 acquire()364 public synchronized MemoryRegistration acquire() { 365 mReferenceCount++; 366 return this; 367 } 368 release()369 public synchronized void release() { 370 mReferenceCount--; 371 if (mReferenceCount == 0) { 372 VMRuntime.getRuntime().registerNativeFree(mSize); 373 } 374 } 375 } 376 nCreate(String name, int size)377 private static native FileDescriptor nCreate(String name, int size) throws ErrnoException; nGetSize(FileDescriptor fd)378 private static native int nGetSize(FileDescriptor fd); nSetProt(FileDescriptor fd, int prot)379 private static native int nSetProt(FileDescriptor fd, int prot); 380 } 381