MemoryFile.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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
17package android.os;
18
19import android.util.Log;
20
21import java.io.IOException;
22import java.io.InputStream;
23import java.io.OutputStream;
24
25
26/**
27 * MemoryFile is a wrapper for the Linux ashmem driver.
28 * MemoryFiles are backed by shared memory, which can be optionally
29 * set to be purgeable.
30 * Purgeable files may have their contents reclaimed by the kernel
31 * in low memory conditions (only if allowPurging is set to true).
32 * After a file is purged, attempts to read or write the file will
33 * cause an IOException to be thrown.
34 */
35public class MemoryFile
36{
37    private static String TAG = "MemoryFile";
38
39    // returns fd
40    private native int native_open(String name, int length);
41    // returns memory address for ashmem region
42    private native int native_mmap(int fd, int length);
43    private native void native_close(int fd);
44    private native int native_read(int fd, int address, byte[] buffer,
45            int srcOffset, int destOffset, int count, boolean isUnpinned);
46    private native void native_write(int fd, int address, byte[] buffer,
47            int srcOffset, int destOffset, int count, boolean isUnpinned);
48    private native void native_pin(int fd, boolean pin);
49
50    private int mFD;        // ashmem file descriptor
51    private int mAddress;   // address of ashmem memory
52    private int mLength;    // total length of our ashmem region
53    private boolean mAllowPurging = false;  // true if our ashmem region is unpinned
54
55    /**
56     * MemoryFile constructor.
57     *
58     * @param name optional name for the file (can be null).
59     * @param length of the memory file in bytes.
60     */
61    public MemoryFile(String name, int length) {
62        mLength = length;
63        mFD = native_open(name, length);
64        mAddress = native_mmap(mFD, length);
65    }
66
67    /**
68     * Closes and releases all resources for the memory file.
69     */
70    public void close() {
71        if (mFD > 0) {
72            native_close(mFD);
73            mFD = 0;
74        }
75    }
76
77    @Override
78    protected void finalize() {
79        if (mFD > 0) {
80            Log.e(TAG, "MemoryFile.finalize() called while ashmem still open");
81            close();
82        }
83    }
84
85    /**
86     * Returns the length of the memory file.
87     *
88     * @return file length.
89     */
90    public int length() {
91        return mLength;
92    }
93
94    /**
95     * Is memory file purging enabled?
96     *
97     * @return true if the file may be purged.
98     */
99    public boolean isPurgingAllowed() {
100        return mAllowPurging;
101    }
102
103    /**
104     * Enables or disables purging of the memory file.
105     *
106     * @param allowPurging true if the operating system can purge the contents
107     * of the file in low memory situations
108     * @return previous value of allowPurging
109     */
110    synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
111        boolean oldValue = mAllowPurging;
112        if (oldValue != allowPurging) {
113            native_pin(mFD, !allowPurging);
114            mAllowPurging = allowPurging;
115        }
116        return oldValue;
117    }
118
119    /**
120     * Creates a new InputStream for reading from the memory file.
121     *
122     @return InputStream
123     */
124    public InputStream getInputStream() {
125        return new MemoryInputStream();
126    }
127
128    /**
129     * Creates a new OutputStream for writing to the memory file.
130     *
131     @return OutputStream
132     */
133     public OutputStream getOutputStream() {
134
135        return new MemoryOutputStream();
136    }
137
138    /**
139     * Reads bytes from the memory file.
140     * Will throw an IOException if the file has been purged.
141     *
142     * @param buffer byte array to read bytes into.
143     * @param srcOffset offset into the memory file to read from.
144     * @param destOffset offset into the byte array buffer to read into.
145     * @param count number of bytes to read.
146     * @return number of bytes read.
147     */
148    public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
149            throws IOException {
150        if (destOffset < 0 || destOffset > buffer.length || count < 0
151                || count > buffer.length - destOffset
152                || srcOffset < 0 || srcOffset > mLength
153                || count > mLength - srcOffset) {
154            throw new IndexOutOfBoundsException();
155        }
156        return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
157    }
158
159    /**
160     * Write bytes to the memory file.
161     * Will throw an IOException if the file has been purged.
162     *
163     * @param buffer byte array to write bytes from.
164     * @param srcOffset offset into the byte array buffer to write from.
165     * @param destOffset offset  into the memory file to write to.
166     * @param count number of bytes to write.
167     */
168    public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
169            throws IOException {
170        if (srcOffset < 0 || srcOffset > buffer.length || count < 0
171                || count > buffer.length - srcOffset
172                || destOffset < 0 || destOffset > mLength
173                || count > mLength - destOffset) {
174            throw new IndexOutOfBoundsException();
175        }
176        native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
177    }
178
179    private class MemoryInputStream extends InputStream {
180
181        private int mMark = 0;
182        private int mOffset = 0;
183        private byte[] mSingleByte;
184
185        @Override
186        public int available() throws IOException {
187            if (mOffset >= mLength) {
188                return 0;
189            }
190            return mLength - mOffset;
191        }
192
193        @Override
194        public boolean markSupported() {
195            return true;
196        }
197
198        @Override
199        public void mark(int readlimit) {
200            mMark = mOffset;
201        }
202
203        @Override
204        public void reset() throws IOException {
205            mOffset = mMark;
206        }
207
208        @Override
209        public int read() throws IOException {
210            if (mSingleByte == null) {
211                mSingleByte = new byte[1];
212            }
213            int result = read(mSingleByte, 0, 1);
214            if (result != 1) {
215                throw new IOException("read() failed");
216            }
217            return mSingleByte[0];
218        }
219
220        @Override
221        public int read(byte buffer[], int offset, int count) throws IOException {
222            int result = readBytes(buffer, mOffset, offset, count);
223            if (result > 0) {
224                mOffset += result;
225            }
226            return result;
227        }
228
229        @Override
230        public long skip(long n) throws IOException {
231            if (mOffset + n > mLength) {
232                n = mLength - mOffset;
233            }
234            mOffset += n;
235            return n;
236        }
237    }
238
239    private class MemoryOutputStream extends OutputStream {
240
241        private int mOffset = 0;
242        private byte[] mSingleByte;
243
244        @Override
245        public void write(byte buffer[], int offset, int count) throws IOException {
246            writeBytes(buffer, offset, mOffset, count);
247        }
248
249        @Override
250        public void write(int oneByte) throws IOException {
251            if (mSingleByte == null) {
252                mSingleByte = new byte[1];
253            }
254            mSingleByte[0] = (byte)oneByte;
255            write(mSingleByte, 0, 1);
256        }
257    }
258}
259