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