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