blob: 57a88012a31a0e1613b14989e285e3fa48897787 [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;
Andrei Onea24ec3212019-03-15 17:35:05 +000021import android.annotation.UnsupportedAppUsage;
John Reckdea6a022017-05-25 17:09:33 -070022import android.system.ErrnoException;
23import android.system.Os;
24import android.system.OsConstants;
25
26import dalvik.system.VMRuntime;
27
28import java.io.Closeable;
29import java.io.FileDescriptor;
30import java.nio.ByteBuffer;
31import java.nio.DirectByteBuffer;
John Reckf995f9a2017-08-02 10:24:18 -070032import java.nio.NioUtils;
John Reckdea6a022017-05-25 17:09:33 -070033
34import sun.misc.Cleaner;
35
36/**
37 * SharedMemory enables the creation, mapping, and protection control over anonymous shared memory.
38 */
39public 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
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);
John Recke4f60cc2017-08-07 11:17:06 -070064 mCleaner = Cleaner.create(mFileDescriptor,
Adam Seatonb16411f2019-07-26 17:13:18 +000065 new Closer(mFileDescriptor, mMemoryRegistration));
John Reckdea6a022017-05-25 17:09:33 -070066 }
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 */
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
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
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 */
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.
John Recke4f60cc2017-08-07 11:17:06 -0700143 *
144 * @hide Exists only for MemoryFile interop
John Reckdea6a022017-05-25 17:09:33 -0700145 */
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.
John Recke4f60cc2017-08-07 11:17:06 -0700157 *
158 * @hide Exposed for native ASharedMemory_dupFromJava()
John Reckdea6a022017-05-25 17:09:33 -0700159 */
Andrei Onea24ec3212019-03-15 17:35:05 +0000160 @UnsupportedAppUsage
John Reckdea6a022017-05-25 17:09:33 -0700161 public int getFd() {
162 return mFileDescriptor.getInt$();
163 }
164
165 /**
166 * @return The size of the SharedMemory region.
167 */
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 */
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 */
197 public @NonNull ByteBuffer mapReadOnly() throws ErrnoException {
198 return map(OsConstants.PROT_READ, 0, mSize);
199 }
200
201 /**
John Reckf995f9a2017-08-02 10:24:18 -0700202 * 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.
John Reckdea6a022017-05-25 17:09:33 -0700206 *
207 * @param prot A bitwise-or'd combination of PROT_READ, PROT_WRITE, PROT_EXEC, or PROT_NONE.
John Reckf995f9a2017-08-02 10:24:18 -0700208 * @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().
John Reckdea6a022017-05-25 17:09:33 -0700212 * @return A ByteBuffer mapping.
213 * @throws ErrnoException if the mmap call failed.
214 */
215 public @NonNull ByteBuffer map(int prot, int offset, int length) throws ErrnoException {
216 checkOpen();
217 validateProt(prot);
218 if (offset < 0) {
John Reckf995f9a2017-08-02 10:24:18 -0700219 throw new IllegalArgumentException("Offset must be >= 0");
John Reckdea6a022017-05-25 17:09:33 -0700220 }
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 /**
John Reckf995f9a2017-08-02 10:24:18 -0700234 * 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 *
John Reckdea6a022017-05-25 17:09:33 -0700239 * @param buffer The buffer to unmap
240 */
241 public static void unmap(@NonNull ByteBuffer buffer) {
242 if (buffer instanceof DirectByteBuffer) {
John Reckf995f9a2017-08-02 10:24:18 -0700243 NioUtils.freeDirectBuffer(buffer);
John Reckdea6a022017-05-25 17:09:33 -0700244 } 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
257 public void close() {
258 if (mCleaner != null) {
259 mCleaner.clean();
260 mCleaner = null;
261 }
262 }
263
264 @Override
265 public int describeContents() {
266 return CONTENTS_FILE_DESCRIPTOR;
267 }
268
269 @Override
270 public void writeToParcel(@NonNull Parcel dest, int flags) {
271 checkOpen();
272 dest.writeFileDescriptor(mFileDescriptor);
273 }
274
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700275 public static final @android.annotation.NonNull Parcelable.Creator<SharedMemory> CREATOR =
John Reckdea6a022017-05-25 17:09:33 -0700276 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 {
Adam Seatonb16411f2019-07-26 17:13:18 +0000293 private FileDescriptor mFd;
John Reckdea6a022017-05-25 17:09:33 -0700294 private MemoryRegistration mMemoryReference;
295
Adam Seatonb16411f2019-07-26 17:13:18 +0000296 private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
John Reckdea6a022017-05-25 17:09:33 -0700297 mFd = fd;
298 mMemoryReference = memoryReference;
299 }
300
301 @Override
302 public void run() {
303 try {
Adam Seatonb16411f2019-07-26 17:13:18 +0000304 Os.close(mFd);
John Reckdea6a022017-05-25 17:09:33 -0700305 } 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
319 private Unmapper(long address, int size, MemoryRegistration memoryReference) {
320 mAddress = address;
321 mSize = size;
322 mMemoryReference = memoryReference;
323 }
324
325 @Override
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
343 private MemoryRegistration(int size) {
344 mSize = size;
345 mReferenceCount = 1;
346 VMRuntime.getRuntime().registerNativeAllocation(mSize);
347 }
348
349 public synchronized MemoryRegistration acquire() {
350 mReferenceCount++;
351 return this;
352 }
353
354 public synchronized void release() {
355 mReferenceCount--;
356 if (mReferenceCount == 0) {
357 VMRuntime.getRuntime().registerNativeFree(mSize);
358 }
359 }
360 }
361
362 private static native FileDescriptor nCreate(String name, int size) throws ErrnoException;
363 private static native int nGetSize(FileDescriptor fd);
364 private static native int nSetProt(FileDescriptor fd, int prot);
365}