Home | History | Annotate | Download | only in os
      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 
     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      */
     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 
     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 
     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      */
    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      */
    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      */
    159     public int getFd() {
    160         return mFileDescriptor.getInt$();
    161     }
    162 
    163     /**
    164      * @return The size of the SharedMemory region.
    165      */
    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      */
    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      */
    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      */
    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      */
    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
    255     public void close() {
    256         if (mCleaner != null) {
    257             mCleaner.clean();
    258             mCleaner = null;
    259         }
    260     }
    261 
    262     @Override
    263     public int describeContents() {
    264         return CONTENTS_FILE_DESCRIPTOR;
    265     }
    266 
    267     @Override
    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 
    294         private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
    295             mFd = fd;
    296             mMemoryReference = memoryReference;
    297         }
    298 
    299         @Override
    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 
    317         private Unmapper(long address, int size, MemoryRegistration memoryReference) {
    318             mAddress = address;
    319             mSize = size;
    320             mMemoryReference = memoryReference;
    321         }
    322 
    323         @Override
    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 
    341         private MemoryRegistration(int size) {
    342             mSize = size;
    343             mReferenceCount = 1;
    344             VMRuntime.getRuntime().registerNativeAllocation(mSize);
    345         }
    346 
    347         public synchronized MemoryRegistration acquire() {
    348             mReferenceCount++;
    349             return this;
    350         }
    351 
    352         public synchronized void release() {
    353             mReferenceCount--;
    354             if (mReferenceCount == 0) {
    355                 VMRuntime.getRuntime().registerNativeFree(mSize);
    356             }
    357         }
    358     }
    359 
    360     private static native FileDescriptor nCreate(String name, int size) throws ErrnoException;
    361     private static native int nGetSize(FileDescriptor fd);
    362     private static native int nSetProt(FileDescriptor fd, int prot);
    363 }
    364