• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.os;
18 
19 import android.util.Log;
20 
21 import java.io.FileDescriptor;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import 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  */
36 public class MemoryFile
37 {
38     private static String TAG = "MemoryFile";
39 
40     // 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 
native_open(String name, int length)44     private static native FileDescriptor native_open(String name, int length) throws IOException;
45     // returns memory address for ashmem region
native_mmap(FileDescriptor fd, int length, int mode)46     private static native int native_mmap(FileDescriptor fd, int length, int mode)
47             throws IOException;
native_munmap(int addr, int length)48     private static native void native_munmap(int addr, int length) throws IOException;
native_close(FileDescriptor fd)49     private static native void native_close(FileDescriptor fd);
native_read(FileDescriptor fd, int address, byte[] buffer, int srcOffset, int destOffset, int count, boolean isUnpinned)50     private static native int native_read(FileDescriptor fd, int address, byte[] buffer,
51             int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
native_write(FileDescriptor fd, int address, byte[] buffer, int srcOffset, int destOffset, int count, boolean isUnpinned)52     private static native void native_write(FileDescriptor fd, int address, byte[] buffer,
53             int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
native_pin(FileDescriptor fd, boolean pin)54     private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException;
native_get_size(FileDescriptor fd)55     private static native int native_get_size(FileDescriptor fd) throws IOException;
56 
57     private FileDescriptor mFD;        // ashmem file descriptor
58     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     /**
63      * Allocates a new ashmem region. The region is initially not purgable.
64      *
65      * @param name optional name for the file (can be null).
66      * @param length of the memory file in bytes.
67      * @throws IOException if the memory file could not be created.
68      */
MemoryFile(String name, int length)69     public MemoryFile(String name, int length) throws IOException {
70         mLength = length;
71         mFD = native_open(name, length);
72         if (length > 0) {
73             mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
74         } else {
75             mAddress = 0;
76         }
77     }
78 
79     /**
80      * Closes the memory file. If there are no other open references to the memory
81      * file, it will be deleted.
82      */
close()83     public void close() {
84         deactivate();
85         if (!isClosed()) {
86             native_close(mFD);
87         }
88     }
89 
90     /**
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      */
deactivate()97     void deactivate() {
98         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      */
isDeactivated()111     private boolean isDeactivated() {
112         return mAddress == 0;
113     }
114 
115     /**
116      * Checks whether the memory file has been closed.
117      */
isClosed()118     private boolean isClosed() {
119         return !mFD.valid();
120     }
121 
122     @Override
finalize()123     protected void finalize() {
124         if (!isClosed()) {
125             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      */
length()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      */
isPurgingAllowed()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      */
allowPurging(boolean allowPurging)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      */
getInputStream()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      */
getOutputStream()178      public OutputStream getOutputStream() {
179         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.
191      * @throws IOException if the memory file has been purged or deactivated.
192      */
readBytes(byte[] buffer, int srcOffset, int destOffset, int count)193     public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
194             throws IOException {
195         if (isDeactivated()) {
196             throw new IOException("Can't read from deactivated memory file.");
197         }
198         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.
215      * @throws IOException if the memory file has been purged or deactivated.
216      */
writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)217     public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
218             throws IOException {
219         if (isDeactivated()) {
220             throw new IOException("Can't write to deactivated memory file.");
221         }
222         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 
231     /**
232      * Gets a FileDescriptor for the memory file.
233      *
234      * The returned file descriptor is not duplicated.
235      *
236      * @throws IOException If the memory file has been closed.
237      *
238      * @hide
239      */
getFileDescriptor()240     public FileDescriptor getFileDescriptor() throws IOException {
241         return mFD;
242     }
243 
244     /**
245      * 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.
247      *
248      * @throws IOException If <code>fd</code> is not a valid file descriptor.
249      *
250      * @hide
251      */
getSize(FileDescriptor fd)252     public static int getSize(FileDescriptor fd) throws IOException {
253         return native_get_size(fd);
254     }
255 
256     private class MemoryInputStream extends InputStream {
257 
258         private int mMark = 0;
259         private int mOffset = 0;
260         private byte[] mSingleByte;
261 
262         @Override
available()263         public int available() throws IOException {
264             if (mOffset >= mLength) {
265                 return 0;
266             }
267             return mLength - mOffset;
268         }
269 
270         @Override
markSupported()271         public boolean markSupported() {
272             return true;
273         }
274 
275         @Override
mark(int readlimit)276         public void mark(int readlimit) {
277             mMark = mOffset;
278         }
279 
280         @Override
reset()281         public void reset() throws IOException {
282             mOffset = mMark;
283         }
284 
285         @Override
read()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) {
292                 return -1;
293             }
294             return mSingleByte[0];
295         }
296 
297         @Override
read(byte buffer[], int offset, int count)298         public int read(byte buffer[], int offset, int count) throws IOException {
299             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             }
308             int result = readBytes(buffer, mOffset, offset, count);
309             if (result > 0) {
310                 mOffset += result;
311             }
312             return result;
313         }
314 
315         @Override
skip(long n)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
write(byte buffer[], int offset, int count)331         public void write(byte buffer[], int offset, int count) throws IOException {
332             writeBytes(buffer, offset, mOffset, count);
333             mOffset += count;
334         }
335 
336         @Override
write(int oneByte)337         public void write(int oneByte) throws IOException {
338             if (mSingleByte == null) {
339                 mSingleByte = new byte[1];
340             }
341             mSingleByte[0] = (byte)oneByte;
342             write(mSingleByte, 0, 1);
343         }
344     }
345 }
346