blob: 395485baa4e2566ba5f74f71bb85bbc712262fa4 [file] [log] [blame]
John Reckdea6a022017-05-25 17:09:33 -07001/*
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
17package android.os;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.system.ErrnoException;
22import android.system.Os;
23import android.system.OsConstants;
24
25import dalvik.system.VMRuntime;
26
27import java.io.Closeable;
28import java.io.FileDescriptor;
29import java.nio.ByteBuffer;
30import java.nio.DirectByteBuffer;
John Reckf995f9a2017-08-02 10:24:18 -070031import java.nio.NioUtils;
John Reckdea6a022017-05-25 17:09:33 -070032
33import sun.misc.Cleaner;
34
35/**
36 * SharedMemory enables the creation, mapping, and protection control over anonymous shared memory.
37 */
38public 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);
John Recke4f60cc2017-08-07 11:17:06 -070063 mCleaner = Cleaner.create(mFileDescriptor,
64 new Closer(mFileDescriptor, mMemoryRegistration));
John Reckdea6a022017-05-25 17:09:33 -070065 }
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.
John Recke4f60cc2017-08-07 11:17:06 -0700142 *
143 * @hide Exists only for MemoryFile interop
John Reckdea6a022017-05-25 17:09:33 -0700144 */
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.
John Recke4f60cc2017-08-07 11:17:06 -0700156 *
157 * @hide Exposed for native ASharedMemory_dupFromJava()
John Reckdea6a022017-05-25 17:09:33 -0700158 */
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 /**
John Reckf995f9a2017-08-02 10:24:18 -0700200 * 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.
John Reckdea6a022017-05-25 17:09:33 -0700204 *
205 * @param prot A bitwise-or'd combination of PROT_READ, PROT_WRITE, PROT_EXEC, or PROT_NONE.
John Reckf995f9a2017-08-02 10:24:18 -0700206 * @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().
John Reckdea6a022017-05-25 17:09:33 -0700210 * @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) {
John Reckf995f9a2017-08-02 10:24:18 -0700217 throw new IllegalArgumentException("Offset must be >= 0");
John Reckdea6a022017-05-25 17:09:33 -0700218 }
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 /**
John Reckf995f9a2017-08-02 10:24:18 -0700232 * 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 *
John Reckdea6a022017-05-25 17:09:33 -0700237 * @param buffer The buffer to unmap
238 */
239 public static void unmap(@NonNull ByteBuffer buffer) {
240 if (buffer instanceof DirectByteBuffer) {
John Reckf995f9a2017-08-02 10:24:18 -0700241 NioUtils.freeDirectBuffer(buffer);
John Reckdea6a022017-05-25 17:09:33 -0700242 } 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
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700273 public static final @android.annotation.NonNull Parcelable.Creator<SharedMemory> CREATOR =
John Reckdea6a022017-05-25 17:09:33 -0700274 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}