blob: 6cec55a4a09ebad6f48484d69c91ac1fe01124b8 [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
19import android.util.Log;
20
Bjorn Bringert761e0912009-05-29 11:46:12 +010021import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import java.io.IOException;
23import java.io.InputStream;
24import java.io.OutputStream;
25
26
27/**
28 * MemoryFile is a wrapper for the Linux ashmem driver.
29 * MemoryFiles are backed by shared memory, which can be optionally
30 * set to be purgeable.
Jesse Wilson112d3392011-03-24 17:57:55 -070031 * Purgeable files may have their contents reclaimed by the kernel
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032 * in low memory conditions (only if allowPurging is set to true).
33 * After a file is purged, attempts to read or write the file will
34 * cause an IOException to be thrown.
35 */
36public class MemoryFile
37{
38 private static String TAG = "MemoryFile";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
Bjorn Bringert963cd0062009-05-29 14:05:12 +010040 // mmap(2) protection flags from <sys/mman.h>
41 private static final int PROT_READ = 0x1;
42 private static final int PROT_WRITE = 0x2;
43
Bjorn Bringert761e0912009-05-29 11:46:12 +010044 private static native FileDescriptor native_open(String name, int length) throws IOException;
45 // returns memory address for ashmem region
Ashok Bhatc20cadb2014-01-10 13:40:47 +000046 private static native long native_mmap(FileDescriptor fd, int length, int mode)
Bjorn Bringert963cd0062009-05-29 14:05:12 +010047 throws IOException;
Ashok Bhatc20cadb2014-01-10 13:40:47 +000048 private static native void native_munmap(long addr, int length) throws IOException;
Bjorn Bringert761e0912009-05-29 11:46:12 +010049 private static native void native_close(FileDescriptor fd);
Ashok Bhatc20cadb2014-01-10 13:40:47 +000050 private static native int native_read(FileDescriptor fd, long address, byte[] buffer,
Bjorn Bringert761e0912009-05-29 11:46:12 +010051 int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
Ashok Bhatc20cadb2014-01-10 13:40:47 +000052 private static native void native_write(FileDescriptor fd, long address, byte[] buffer,
Bjorn Bringert761e0912009-05-29 11:46:12 +010053 int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
54 private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException;
Marco Nelissen7bcbd512009-06-23 10:34:55 -070055 private static native int native_get_size(FileDescriptor fd) throws IOException;
Bjorn Bringert761e0912009-05-29 11:46:12 +010056
57 private FileDescriptor mFD; // ashmem file descriptor
Ashok Bhatc20cadb2014-01-10 13:40:47 +000058 private long mAddress; // address of ashmem memory
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 private int mLength; // total length of our ashmem region
60 private boolean mAllowPurging = false; // true if our ashmem region is unpinned
61
62 /**
Bjorn Bringert963cd0062009-05-29 14:05:12 +010063 * Allocates a new ashmem region. The region is initially not purgable.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 *
65 * @param name optional name for the file (can be null).
Narayan Kamathf626ca22014-04-08 16:10:52 +010066 * @param length of the memory file in bytes, must be non-negative.
Bjorn Bringert9fc2e9c2009-05-28 14:48:32 +010067 * @throws IOException if the memory file could not be created.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 */
Bjorn Bringert9fc2e9c2009-05-28 14:48:32 +010069 public MemoryFile(String name, int length) throws IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 mLength = length;
Narayan Kamathf626ca22014-04-08 16:10:52 +010071 if (length >= 0) {
72 mFD = native_open(name, length);
73 } else {
74 throw new IOException("Invalid length: " + length);
75 }
76
Bjorn Bringerta006b4722010-04-14 14:43:26 +010077 if (length > 0) {
78 mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
79 } else {
80 mAddress = 0;
Bjorn Bringert963cd0062009-05-29 14:05:12 +010081 }
Bjorn Bringert963cd0062009-05-29 14:05:12 +010082 }
83
84 /**
85 * Closes the memory file. If there are no other open references to the memory
86 * file, it will be deleted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 */
88 public void close() {
Bjorn Bringert761e0912009-05-29 11:46:12 +010089 deactivate();
90 if (!isClosed()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 native_close(mFD);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 }
93 }
94
Bjorn Bringert963cd0062009-05-29 14:05:12 +010095 /**
96 * Unmaps the memory file from the process's memory space, but does not close it.
97 * After this method has been called, read and write operations through this object
98 * will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor.
99 *
100 * @hide
101 */
Bjorn Bringerta006b4722010-04-14 14:43:26 +0100102 void deactivate() {
Bjorn Bringert761e0912009-05-29 11:46:12 +0100103 if (!isDeactivated()) {
104 try {
105 native_munmap(mAddress, mLength);
106 mAddress = 0;
107 } catch (IOException ex) {
108 Log.e(TAG, ex.toString());
109 }
110 }
111 }
112
113 /**
114 * Checks whether the memory file has been deactivated.
115 */
116 private boolean isDeactivated() {
117 return mAddress == 0;
118 }
119
120 /**
121 * Checks whether the memory file has been closed.
122 */
123 private boolean isClosed() {
124 return !mFD.valid();
125 }
126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 @Override
128 protected void finalize() {
Bjorn Bringert761e0912009-05-29 11:46:12 +0100129 if (!isClosed()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 Log.e(TAG, "MemoryFile.finalize() called while ashmem still open");
131 close();
132 }
133 }
Jesse Wilson112d3392011-03-24 17:57:55 -0700134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 /**
136 * Returns the length of the memory file.
137 *
138 * @return file length.
139 */
140 public int length() {
141 return mLength;
142 }
143
144 /**
145 * Is memory file purging enabled?
146 *
147 * @return true if the file may be purged.
148 */
149 public boolean isPurgingAllowed() {
150 return mAllowPurging;
151 }
152
153 /**
154 * Enables or disables purging of the memory file.
155 *
156 * @param allowPurging true if the operating system can purge the contents
157 * of the file in low memory situations
158 * @return previous value of allowPurging
159 */
160 synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
161 boolean oldValue = mAllowPurging;
162 if (oldValue != allowPurging) {
163 native_pin(mFD, !allowPurging);
164 mAllowPurging = allowPurging;
165 }
166 return oldValue;
167 }
168
169 /**
170 * Creates a new InputStream for reading from the memory file.
171 *
172 @return InputStream
173 */
174 public InputStream getInputStream() {
175 return new MemoryInputStream();
176 }
177
178 /**
179 * Creates a new OutputStream for writing to the memory file.
180 *
181 @return OutputStream
182 */
183 public OutputStream getOutputStream() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 return new MemoryOutputStream();
185 }
186
187 /**
188 * Reads bytes from the memory file.
189 * Will throw an IOException if the file has been purged.
190 *
191 * @param buffer byte array to read bytes into.
192 * @param srcOffset offset into the memory file to read from.
193 * @param destOffset offset into the byte array buffer to read into.
194 * @param count number of bytes to read.
195 * @return number of bytes read.
Bjorn Bringert761e0912009-05-29 11:46:12 +0100196 * @throws IOException if the memory file has been purged or deactivated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 */
Jesse Wilson112d3392011-03-24 17:57:55 -0700198 public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 throws IOException {
Bjorn Bringert761e0912009-05-29 11:46:12 +0100200 if (isDeactivated()) {
201 throw new IOException("Can't read from deactivated memory file.");
202 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 if (destOffset < 0 || destOffset > buffer.length || count < 0
204 || count > buffer.length - destOffset
205 || srcOffset < 0 || srcOffset > mLength
206 || count > mLength - srcOffset) {
207 throw new IndexOutOfBoundsException();
208 }
209 return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
210 }
211
212 /**
213 * Write bytes to the memory file.
214 * Will throw an IOException if the file has been purged.
215 *
216 * @param buffer byte array to write bytes from.
217 * @param srcOffset offset into the byte array buffer to write from.
218 * @param destOffset offset into the memory file to write to.
219 * @param count number of bytes to write.
Bjorn Bringert761e0912009-05-29 11:46:12 +0100220 * @throws IOException if the memory file has been purged or deactivated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 */
222 public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
223 throws IOException {
Bjorn Bringert761e0912009-05-29 11:46:12 +0100224 if (isDeactivated()) {
225 throw new IOException("Can't write to deactivated memory file.");
226 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 if (srcOffset < 0 || srcOffset > buffer.length || count < 0
228 || count > buffer.length - srcOffset
229 || destOffset < 0 || destOffset > mLength
230 || count > mLength - destOffset) {
231 throw new IndexOutOfBoundsException();
232 }
233 native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
234 }
235
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100236 /**
Bjorn Bringerta006b4722010-04-14 14:43:26 +0100237 * Gets a FileDescriptor for the memory file.
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100238 *
239 * The returned file descriptor is not duplicated.
240 *
241 * @throws IOException If the memory file has been closed.
242 *
243 * @hide
244 */
245 public FileDescriptor getFileDescriptor() throws IOException {
246 return mFD;
247 }
248
249 /**
Marco Nelissen7bcbd512009-06-23 10:34:55 -0700250 * Returns the size of the memory file that the file descriptor refers to,
251 * or -1 if the file descriptor does not refer to a memory file.
Marco Nelissenec100902009-06-17 08:56:59 -0700252 *
253 * @throws IOException If <code>fd</code> is not a valid file descriptor.
254 *
255 * @hide
256 */
Marco Nelissen7bcbd512009-06-23 10:34:55 -0700257 public static int getSize(FileDescriptor fd) throws IOException {
258 return native_get_size(fd);
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100259 }
260
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 private class MemoryInputStream extends InputStream {
262
263 private int mMark = 0;
264 private int mOffset = 0;
265 private byte[] mSingleByte;
266
267 @Override
268 public int available() throws IOException {
269 if (mOffset >= mLength) {
270 return 0;
271 }
272 return mLength - mOffset;
273 }
274
275 @Override
276 public boolean markSupported() {
277 return true;
278 }
279
280 @Override
281 public void mark(int readlimit) {
282 mMark = mOffset;
283 }
284
285 @Override
286 public void reset() throws IOException {
287 mOffset = mMark;
288 }
289
290 @Override
291 public int read() throws IOException {
292 if (mSingleByte == null) {
293 mSingleByte = new byte[1];
294 }
295 int result = read(mSingleByte, 0, 1);
296 if (result != 1) {
Bjorn Bringertc1823702009-06-01 10:53:06 +0100297 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 }
299 return mSingleByte[0];
300 }
301
302 @Override
303 public int read(byte buffer[], int offset, int count) throws IOException {
Bjorn Bringertc1823702009-06-01 10:53:06 +0100304 if (offset < 0 || count < 0 || offset + count > buffer.length) {
305 // readBytes() also does this check, but we need to do it before
306 // changing count.
307 throw new IndexOutOfBoundsException();
308 }
309 count = Math.min(count, available());
310 if (count < 1) {
311 return -1;
312 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 int result = readBytes(buffer, mOffset, offset, count);
314 if (result > 0) {
315 mOffset += result;
316 }
317 return result;
318 }
319
320 @Override
321 public long skip(long n) throws IOException {
322 if (mOffset + n > mLength) {
323 n = mLength - mOffset;
324 }
325 mOffset += n;
326 return n;
327 }
328 }
329
330 private class MemoryOutputStream extends OutputStream {
331
332 private int mOffset = 0;
333 private byte[] mSingleByte;
334
335 @Override
336 public void write(byte buffer[], int offset, int count) throws IOException {
337 writeBytes(buffer, offset, mOffset, count);
Jesse Wilson112d3392011-03-24 17:57:55 -0700338 mOffset += count;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 }
340
341 @Override
342 public void write(int oneByte) throws IOException {
343 if (mSingleByte == null) {
344 mSingleByte = new byte[1];
345 }
346 mSingleByte[0] = (byte)oneByte;
347 write(mSingleByte, 0, 1);
348 }
349 }
350}