blob: f84f9f05b13e7e06773a2c29481f01870a5c7670 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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
Artur Satayevafdb23a2019-12-10 17:47:53 +000019import android.compat.annotation.UnsupportedAppUsage;
John Reckdea6a022017-05-25 17:09:33 -070020import android.system.ErrnoException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021
Bjorn Bringert761e0912009-05-29 11:46:12 +010022import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import java.io.IOException;
24import java.io.InputStream;
25import java.io.OutputStream;
John Reckdea6a022017-05-25 17:09:33 -070026import java.nio.ByteBuffer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027
28
29/**
John Reckdea6a022017-05-25 17:09:33 -070030 * MemoryFile is a wrapper for {@link SharedMemory} which can optionally be set to purgeable.
31 *
32 * Applications should generally prefer to use {@link SharedMemory} which offers more flexible
33 * access & control over the shared memory region than MemoryFile does.
34 *
Jesse Wilson112d3392011-03-24 17:57:55 -070035 * Purgeable files may have their contents reclaimed by the kernel
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036 * in low memory conditions (only if allowPurging is set to true).
37 * After a file is purged, attempts to read or write the file will
38 * cause an IOException to be thrown.
39 */
John Reckdea6a022017-05-25 17:09:33 -070040public class MemoryFile {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 private static String TAG = "MemoryFile";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042
John Reckdea6a022017-05-25 17:09:33 -070043 // Returns 'true' if purged, 'false' otherwise
Andrei Onea24ec3212019-03-15 17:35:05 +000044 @UnsupportedAppUsage
John Reckdea6a022017-05-25 17:09:33 -070045 private static native boolean native_pin(FileDescriptor fd, boolean pin) throws IOException;
Andrei Onea24ec3212019-03-15 17:35:05 +000046 @UnsupportedAppUsage
Marco Nelissen7bcbd512009-06-23 10:34:55 -070047 private static native int native_get_size(FileDescriptor fd) throws IOException;
Bjorn Bringert761e0912009-05-29 11:46:12 +010048
John Reckdea6a022017-05-25 17:09:33 -070049 private SharedMemory mSharedMemory;
50 private ByteBuffer mMapping;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 private boolean mAllowPurging = false; // true if our ashmem region is unpinned
52
53 /**
Bjorn Bringert963cd0062009-05-29 14:05:12 +010054 * Allocates a new ashmem region. The region is initially not purgable.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 *
56 * @param name optional name for the file (can be null).
John Reckdea6a022017-05-25 17:09:33 -070057 * @param length of the memory file in bytes, must be positive.
Bjorn Bringert9fc2e9c2009-05-28 14:48:32 +010058 * @throws IOException if the memory file could not be created.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 */
Bjorn Bringert9fc2e9c2009-05-28 14:48:32 +010060 public MemoryFile(String name, int length) throws IOException {
John Reckdea6a022017-05-25 17:09:33 -070061 try {
John Reckdea6a022017-05-25 17:09:33 -070062 mSharedMemory = SharedMemory.create(name, length);
John Reck707108b2017-06-29 15:07:12 -070063 mMapping = mSharedMemory.mapReadWrite();
John Reckdea6a022017-05-25 17:09:33 -070064 } catch (ErrnoException ex) {
65 ex.rethrowAsIOException();
Bjorn Bringert963cd0062009-05-29 14:05:12 +010066 }
Bjorn Bringert963cd0062009-05-29 14:05:12 +010067 }
68
69 /**
70 * Closes the memory file. If there are no other open references to the memory
71 * file, it will be deleted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 */
73 public void close() {
Bjorn Bringert761e0912009-05-29 11:46:12 +010074 deactivate();
John Reckdea6a022017-05-25 17:09:33 -070075 mSharedMemory.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 }
77
Bjorn Bringert963cd0062009-05-29 14:05:12 +010078 /**
79 * Unmaps the memory file from the process's memory space, but does not close it.
80 * After this method has been called, read and write operations through this object
81 * will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor.
82 *
83 * @hide
84 */
Andrei Onea24ec3212019-03-15 17:35:05 +000085 @UnsupportedAppUsage
Bjorn Bringerta006b4722010-04-14 14:43:26 +010086 void deactivate() {
John Reckdea6a022017-05-25 17:09:33 -070087 if (mMapping != null) {
88 SharedMemory.unmap(mMapping);
89 mMapping = null;
90 }
91 }
92
93 private void checkActive() throws IOException {
94 if (mMapping == null) {
95 throw new IOException("MemoryFile has been deactivated");
96 }
97 }
98
99 private void beginAccess() throws IOException {
100 checkActive();
101 if (mAllowPurging) {
102 if (native_pin(mSharedMemory.getFileDescriptor(), true)) {
103 throw new IOException("MemoryFile has been purged");
Bjorn Bringert761e0912009-05-29 11:46:12 +0100104 }
105 }
106 }
107
John Reckdea6a022017-05-25 17:09:33 -0700108 private void endAccess() throws IOException {
109 if (mAllowPurging) {
110 native_pin(mSharedMemory.getFileDescriptor(), false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 }
112 }
Jesse Wilson112d3392011-03-24 17:57:55 -0700113
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 /**
115 * Returns the length of the memory file.
116 *
117 * @return file length.
118 */
119 public int length() {
John Reckdea6a022017-05-25 17:09:33 -0700120 return mSharedMemory.getSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 }
122
123 /**
124 * Is memory file purging enabled?
125 *
126 * @return true if the file may be purged.
John Reckdea6a022017-05-25 17:09:33 -0700127 *
128 * @deprecated Purgable is considered generally fragile and hard to use safely. Applications
129 * are recommend to instead use {@link android.content.ComponentCallbacks2#onTrimMemory(int)}
130 * to react to memory events and release shared memory regions as appropriate.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 */
John Reckdea6a022017-05-25 17:09:33 -0700132 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 public boolean isPurgingAllowed() {
134 return mAllowPurging;
135 }
136
137 /**
138 * Enables or disables purging of the memory file.
139 *
140 * @param allowPurging true if the operating system can purge the contents
141 * of the file in low memory situations
142 * @return previous value of allowPurging
John Reckdea6a022017-05-25 17:09:33 -0700143 *
144 * @deprecated Purgable is considered generally fragile and hard to use safely. Applications
145 * are recommend to instead use {@link android.content.ComponentCallbacks2#onTrimMemory(int)}
146 * to react to memory events and release shared memory regions as appropriate.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 */
John Reckdea6a022017-05-25 17:09:33 -0700148 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
150 boolean oldValue = mAllowPurging;
151 if (oldValue != allowPurging) {
John Reckdea6a022017-05-25 17:09:33 -0700152 native_pin(mSharedMemory.getFileDescriptor(), !allowPurging);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 mAllowPurging = allowPurging;
154 }
155 return oldValue;
156 }
157
158 /**
159 * Creates a new InputStream for reading from the memory file.
160 *
161 @return InputStream
162 */
163 public InputStream getInputStream() {
164 return new MemoryInputStream();
165 }
166
167 /**
168 * Creates a new OutputStream for writing to the memory file.
169 *
170 @return OutputStream
171 */
172 public OutputStream getOutputStream() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 return new MemoryOutputStream();
174 }
175
176 /**
177 * Reads bytes from the memory file.
178 * Will throw an IOException if the file has been purged.
179 *
180 * @param buffer byte array to read bytes into.
181 * @param srcOffset offset into the memory file to read from.
182 * @param destOffset offset into the byte array buffer to read into.
183 * @param count number of bytes to read.
184 * @return number of bytes read.
Bjorn Bringert761e0912009-05-29 11:46:12 +0100185 * @throws IOException if the memory file has been purged or deactivated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 */
Jesse Wilson112d3392011-03-24 17:57:55 -0700187 public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 throws IOException {
John Reckdea6a022017-05-25 17:09:33 -0700189 beginAccess();
190 try {
191 mMapping.position(srcOffset);
192 mMapping.get(buffer, destOffset, count);
193 } finally {
194 endAccess();
Bjorn Bringert761e0912009-05-29 11:46:12 +0100195 }
John Reckdea6a022017-05-25 17:09:33 -0700196 return count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 }
198
199 /**
200 * Write bytes to the memory file.
201 * Will throw an IOException if the file has been purged.
202 *
203 * @param buffer byte array to write bytes from.
204 * @param srcOffset offset into the byte array buffer to write from.
205 * @param destOffset offset into the memory file to write to.
206 * @param count number of bytes to write.
Bjorn Bringert761e0912009-05-29 11:46:12 +0100207 * @throws IOException if the memory file has been purged or deactivated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 */
209 public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
210 throws IOException {
John Reckdea6a022017-05-25 17:09:33 -0700211 beginAccess();
212 try {
213 mMapping.position(destOffset);
214 mMapping.put(buffer, srcOffset, count);
215 } finally {
216 endAccess();
Bjorn Bringert761e0912009-05-29 11:46:12 +0100217 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 }
219
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100220 /**
Bjorn Bringerta006b4722010-04-14 14:43:26 +0100221 * Gets a FileDescriptor for the memory file.
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100222 *
223 * The returned file descriptor is not duplicated.
224 *
225 * @throws IOException If the memory file has been closed.
John Recke4f60cc2017-08-07 11:17:06 -0700226 *
227 * @hide
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100228 */
Andrei Onea24ec3212019-03-15 17:35:05 +0000229 @UnsupportedAppUsage
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100230 public FileDescriptor getFileDescriptor() throws IOException {
John Reckdea6a022017-05-25 17:09:33 -0700231 return mSharedMemory.getFileDescriptor();
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100232 }
233
234 /**
Marco Nelissen7bcbd512009-06-23 10:34:55 -0700235 * Returns the size of the memory file that the file descriptor refers to,
236 * or -1 if the file descriptor does not refer to a memory file.
Marco Nelissenec100902009-06-17 08:56:59 -0700237 *
238 * @throws IOException If <code>fd</code> is not a valid file descriptor.
239 *
240 * @hide
241 */
Andrei Onea24ec3212019-03-15 17:35:05 +0000242 @UnsupportedAppUsage
Marco Nelissen7bcbd512009-06-23 10:34:55 -0700243 public static int getSize(FileDescriptor fd) throws IOException {
244 return native_get_size(fd);
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100245 }
246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 private class MemoryInputStream extends InputStream {
248
249 private int mMark = 0;
250 private int mOffset = 0;
251 private byte[] mSingleByte;
252
253 @Override
254 public int available() throws IOException {
John Reckdea6a022017-05-25 17:09:33 -0700255 if (mOffset >= mSharedMemory.getSize()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 return 0;
257 }
John Reckdea6a022017-05-25 17:09:33 -0700258 return mSharedMemory.getSize() - mOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 }
260
261 @Override
262 public boolean markSupported() {
263 return true;
264 }
265
266 @Override
267 public void mark(int readlimit) {
268 mMark = mOffset;
269 }
270
271 @Override
272 public void reset() throws IOException {
273 mOffset = mMark;
274 }
275
276 @Override
277 public int read() throws IOException {
278 if (mSingleByte == null) {
279 mSingleByte = new byte[1];
280 }
281 int result = read(mSingleByte, 0, 1);
282 if (result != 1) {
Bjorn Bringertc1823702009-06-01 10:53:06 +0100283 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 }
285 return mSingleByte[0];
286 }
287
288 @Override
289 public int read(byte buffer[], int offset, int count) throws IOException {
Bjorn Bringertc1823702009-06-01 10:53:06 +0100290 if (offset < 0 || count < 0 || offset + count > buffer.length) {
291 // readBytes() also does this check, but we need to do it before
292 // changing count.
293 throw new IndexOutOfBoundsException();
294 }
295 count = Math.min(count, available());
296 if (count < 1) {
297 return -1;
298 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 int result = readBytes(buffer, mOffset, offset, count);
300 if (result > 0) {
301 mOffset += result;
302 }
303 return result;
304 }
305
306 @Override
307 public long skip(long n) throws IOException {
John Reckdea6a022017-05-25 17:09:33 -0700308 if (mOffset + n > mSharedMemory.getSize()) {
309 n = mSharedMemory.getSize() - mOffset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 }
311 mOffset += n;
312 return n;
313 }
314 }
315
316 private class MemoryOutputStream extends OutputStream {
317
318 private int mOffset = 0;
319 private byte[] mSingleByte;
320
321 @Override
322 public void write(byte buffer[], int offset, int count) throws IOException {
323 writeBytes(buffer, offset, mOffset, count);
Jesse Wilson112d3392011-03-24 17:57:55 -0700324 mOffset += count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 }
326
327 @Override
328 public void write(int oneByte) throws IOException {
329 if (mSingleByte == null) {
330 mSingleByte = new byte[1];
331 }
332 mSingleByte[0] = (byte)oneByte;
333 write(mSingleByte, 0, 1);
334 }
335 }
336}