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.FileDescriptor; 22import 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"; 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 44 private static native FileDescriptor native_open(String name, int length) throws IOException; 45 // returns memory address for ashmem region 46 private static native int native_mmap(FileDescriptor fd, int length, int mode) 47 throws IOException; 48 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; 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 */ 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 */ 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 */ 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 */ 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 122 @Override 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 */ 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() { 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 */ 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 */ 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 */ 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 */ 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 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) { 292 return -1; 293 } 294 return mSingleByte[0]; 295 } 296 297 @Override 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 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 mOffset += count; 334 } 335 336 @Override 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