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