• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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