blob: f82702a5acebcaaff031fbe431dfa2fb6ed3b58a [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.
31 * Purgeable files may have their contents reclaimed by the kernel
32 * 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
Bjorn Bringert963cd0062009-05-29 14:05:12 +010046 private static native int native_mmap(FileDescriptor fd, int length, int mode)
47 throws IOException;
Bjorn Bringert761e0912009-05-29 11:46:12 +010048 private static native void native_munmap(int addr, int length) throws IOException;
49 private static native void native_close(FileDescriptor fd);
50 private static native int native_read(FileDescriptor fd, int address, byte[] buffer,
51 int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
52 private static native void native_write(FileDescriptor fd, int address, byte[] buffer,
53 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 private int mAddress; // address of ashmem memory
59 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).
66 * @param length of the memory file in bytes.
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;
71 mFD = native_open(name, length);
Bjorn Bringerta006b4722010-04-14 14:43:26 +010072 if (length > 0) {
73 mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
74 } else {
75 mAddress = 0;
Bjorn Bringert963cd0062009-05-29 14:05:12 +010076 }
Bjorn Bringert963cd0062009-05-29 14:05:12 +010077 }
78
79 /**
80 * Closes the memory file. If there are no other open references to the memory
81 * file, it will be deleted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 */
83 public void close() {
Bjorn Bringert761e0912009-05-29 11:46:12 +010084 deactivate();
85 if (!isClosed()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 native_close(mFD);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 }
88 }
89
Bjorn Bringert963cd0062009-05-29 14:05:12 +010090 /**
91 * Unmaps the memory file from the process's memory space, but does not close it.
92 * After this method has been called, read and write operations through this object
93 * will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor.
94 *
95 * @hide
96 */
Bjorn Bringerta006b4722010-04-14 14:43:26 +010097 void deactivate() {
Bjorn Bringert761e0912009-05-29 11:46:12 +010098 if (!isDeactivated()) {
99 try {
100 native_munmap(mAddress, mLength);
101 mAddress = 0;
102 } catch (IOException ex) {
103 Log.e(TAG, ex.toString());
104 }
105 }
106 }
107
108 /**
109 * Checks whether the memory file has been deactivated.
110 */
111 private boolean isDeactivated() {
112 return mAddress == 0;
113 }
114
115 /**
116 * Checks whether the memory file has been closed.
117 */
118 private boolean isClosed() {
119 return !mFD.valid();
120 }
121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 @Override
123 protected void finalize() {
Bjorn Bringert761e0912009-05-29 11:46:12 +0100124 if (!isClosed()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 Log.e(TAG, "MemoryFile.finalize() called while ashmem still open");
126 close();
127 }
128 }
129
130 /**
131 * Returns the length of the memory file.
132 *
133 * @return file length.
134 */
135 public int length() {
136 return mLength;
137 }
138
139 /**
140 * Is memory file purging enabled?
141 *
142 * @return true if the file may be purged.
143 */
144 public boolean isPurgingAllowed() {
145 return mAllowPurging;
146 }
147
148 /**
149 * Enables or disables purging of the memory file.
150 *
151 * @param allowPurging true if the operating system can purge the contents
152 * of the file in low memory situations
153 * @return previous value of allowPurging
154 */
155 synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
156 boolean oldValue = mAllowPurging;
157 if (oldValue != allowPurging) {
158 native_pin(mFD, !allowPurging);
159 mAllowPurging = allowPurging;
160 }
161 return oldValue;
162 }
163
164 /**
165 * Creates a new InputStream for reading from the memory file.
166 *
167 @return InputStream
168 */
169 public InputStream getInputStream() {
170 return new MemoryInputStream();
171 }
172
173 /**
174 * Creates a new OutputStream for writing to the memory file.
175 *
176 @return OutputStream
177 */
178 public OutputStream getOutputStream() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 return new MemoryOutputStream();
180 }
181
182 /**
183 * Reads bytes from the memory file.
184 * Will throw an IOException if the file has been purged.
185 *
186 * @param buffer byte array to read bytes into.
187 * @param srcOffset offset into the memory file to read from.
188 * @param destOffset offset into the byte array buffer to read into.
189 * @param count number of bytes to read.
190 * @return number of bytes read.
Bjorn Bringert761e0912009-05-29 11:46:12 +0100191 * @throws IOException if the memory file has been purged or deactivated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 */
193 public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
194 throws IOException {
Bjorn Bringert761e0912009-05-29 11:46:12 +0100195 if (isDeactivated()) {
196 throw new IOException("Can't read from deactivated memory file.");
197 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 if (destOffset < 0 || destOffset > buffer.length || count < 0
199 || count > buffer.length - destOffset
200 || srcOffset < 0 || srcOffset > mLength
201 || count > mLength - srcOffset) {
202 throw new IndexOutOfBoundsException();
203 }
204 return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
205 }
206
207 /**
208 * Write bytes to the memory file.
209 * Will throw an IOException if the file has been purged.
210 *
211 * @param buffer byte array to write bytes from.
212 * @param srcOffset offset into the byte array buffer to write from.
213 * @param destOffset offset into the memory file to write to.
214 * @param count number of bytes to write.
Bjorn Bringert761e0912009-05-29 11:46:12 +0100215 * @throws IOException if the memory file has been purged or deactivated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 */
217 public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
218 throws IOException {
Bjorn Bringert761e0912009-05-29 11:46:12 +0100219 if (isDeactivated()) {
220 throw new IOException("Can't write to deactivated memory file.");
221 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 if (srcOffset < 0 || srcOffset > buffer.length || count < 0
223 || count > buffer.length - srcOffset
224 || destOffset < 0 || destOffset > mLength
225 || count > mLength - destOffset) {
226 throw new IndexOutOfBoundsException();
227 }
228 native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
229 }
230
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100231 /**
Bjorn Bringerta006b4722010-04-14 14:43:26 +0100232 * Gets a FileDescriptor for the memory file.
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100233 *
234 * The returned file descriptor is not duplicated.
235 *
236 * @throws IOException If the memory file has been closed.
237 *
238 * @hide
239 */
240 public FileDescriptor getFileDescriptor() throws IOException {
241 return mFD;
242 }
243
244 /**
Marco Nelissen7bcbd512009-06-23 10:34:55 -0700245 * Returns the size of the memory file that the file descriptor refers to,
246 * or -1 if the file descriptor does not refer to a memory file.
Marco Nelissenec100902009-06-17 08:56:59 -0700247 *
248 * @throws IOException If <code>fd</code> is not a valid file descriptor.
249 *
250 * @hide
251 */
Marco Nelissen7bcbd512009-06-23 10:34:55 -0700252 public static int getSize(FileDescriptor fd) throws IOException {
253 return native_get_size(fd);
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100254 }
255
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 private class MemoryInputStream extends InputStream {
257
258 private int mMark = 0;
259 private int mOffset = 0;
260 private byte[] mSingleByte;
261
262 @Override
263 public int available() throws IOException {
264 if (mOffset >= mLength) {
265 return 0;
266 }
267 return mLength - mOffset;
268 }
269
270 @Override
271 public boolean markSupported() {
272 return true;
273 }
274
275 @Override
276 public void mark(int readlimit) {
277 mMark = mOffset;
278 }
279
280 @Override
281 public void reset() throws IOException {
282 mOffset = mMark;
283 }
284
285 @Override
286 public int read() throws IOException {
287 if (mSingleByte == null) {
288 mSingleByte = new byte[1];
289 }
290 int result = read(mSingleByte, 0, 1);
291 if (result != 1) {
Bjorn Bringertc1823702009-06-01 10:53:06 +0100292 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 }
294 return mSingleByte[0];
295 }
296
297 @Override
298 public int read(byte buffer[], int offset, int count) throws IOException {
Bjorn Bringertc1823702009-06-01 10:53:06 +0100299 if (offset < 0 || count < 0 || offset + count > buffer.length) {
300 // readBytes() also does this check, but we need to do it before
301 // changing count.
302 throw new IndexOutOfBoundsException();
303 }
304 count = Math.min(count, available());
305 if (count < 1) {
306 return -1;
307 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 int result = readBytes(buffer, mOffset, offset, count);
309 if (result > 0) {
310 mOffset += result;
311 }
312 return result;
313 }
314
315 @Override
316 public long skip(long n) throws IOException {
317 if (mOffset + n > mLength) {
318 n = mLength - mOffset;
319 }
320 mOffset += n;
321 return n;
322 }
323 }
324
325 private class MemoryOutputStream extends OutputStream {
326
327 private int mOffset = 0;
328 private byte[] mSingleByte;
329
330 @Override
331 public void write(byte buffer[], int offset, int count) throws IOException {
332 writeBytes(buffer, offset, mOffset, count);
333 }
334
335 @Override
336 public void write(int oneByte) throws IOException {
337 if (mSingleByte == null) {
338 mSingleByte = new byte[1];
339 }
340 mSingleByte[0] = (byte)oneByte;
341 write(mSingleByte, 0, 1);
342 }
343 }
344}