Home | History | Annotate | Download | only in os
      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 
     44     private static native FileDescriptor native_open(String name, int length) throws IOException;
     45     // returns memory address for ashmem region
     46     private static native long native_mmap(FileDescriptor fd, int length, int mode)
     47             throws IOException;
     48     private static native void native_munmap(long addr, int length) throws IOException;
     49     private static native void native_close(FileDescriptor fd);
     50     private static native int native_read(FileDescriptor fd, long address, byte[] buffer,
     51             int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
     52     private static native void native_write(FileDescriptor fd, long 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 long 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, must be non-negative.
     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         if (length >= 0) {
     72             mFD = native_open(name, length);
     73         } else {
     74             throw new IOException("Invalid length: " + length);
     75         }
     76 
     77         if (length > 0) {
     78             mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
     79         } else {
     80             mAddress = 0;
     81         }
     82     }
     83 
     84     /**
     85      * Closes the memory file. If there are no other open references to the memory
     86      * file, it will be deleted.
     87      */
     88     public void close() {
     89         deactivate();
     90         if (!isClosed()) {
     91             native_close(mFD);
     92         }
     93     }
     94 
     95     /**
     96      * Unmaps the memory file from the process's memory space, but does not close it.
     97      * After this method has been called, read and write operations through this object
     98      * will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor.
     99      *
    100      * @hide
    101      */
    102     void deactivate() {
    103         if (!isDeactivated()) {
    104             try {
    105                 native_munmap(mAddress, mLength);
    106                 mAddress = 0;
    107             } catch (IOException ex) {
    108                 Log.e(TAG, ex.toString());
    109             }
    110         }
    111     }
    112 
    113     /**
    114      * Checks whether the memory file has been deactivated.
    115      */
    116     private boolean isDeactivated() {
    117         return mAddress == 0;
    118     }
    119 
    120     /**
    121      * Checks whether the memory file has been closed.
    122      */
    123     private boolean isClosed() {
    124         return !mFD.valid();
    125     }
    126 
    127     @Override
    128     protected void finalize() {
    129         if (!isClosed()) {
    130             Log.e(TAG, "MemoryFile.finalize() called while ashmem still open");
    131             close();
    132         }
    133     }
    134 
    135     /**
    136      * Returns the length of the memory file.
    137      *
    138      * @return file length.
    139      */
    140     public int length() {
    141         return mLength;
    142     }
    143 
    144     /**
    145      * Is memory file purging enabled?
    146      *
    147      * @return true if the file may be purged.
    148      */
    149     public boolean isPurgingAllowed() {
    150         return mAllowPurging;
    151     }
    152 
    153     /**
    154      * Enables or disables purging of the memory file.
    155      *
    156      * @param allowPurging true if the operating system can purge the contents
    157      * of the file in low memory situations
    158      * @return previous value of allowPurging
    159      */
    160     synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
    161         boolean oldValue = mAllowPurging;
    162         if (oldValue != allowPurging) {
    163             native_pin(mFD, !allowPurging);
    164             mAllowPurging = allowPurging;
    165         }
    166         return oldValue;
    167     }
    168 
    169     /**
    170      * Creates a new InputStream for reading from the memory file.
    171      *
    172      @return InputStream
    173      */
    174     public InputStream getInputStream() {
    175         return new MemoryInputStream();
    176     }
    177 
    178     /**
    179      * Creates a new OutputStream for writing to the memory file.
    180      *
    181      @return OutputStream
    182      */
    183      public OutputStream getOutputStream() {
    184         return new MemoryOutputStream();
    185     }
    186 
    187     /**
    188      * Reads bytes from the memory file.
    189      * Will throw an IOException if the file has been purged.
    190      *
    191      * @param buffer byte array to read bytes into.
    192      * @param srcOffset offset into the memory file to read from.
    193      * @param destOffset offset into the byte array buffer to read into.
    194      * @param count number of bytes to read.
    195      * @return number of bytes read.
    196      * @throws IOException if the memory file has been purged or deactivated.
    197      */
    198     public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
    199             throws IOException {
    200         if (isDeactivated()) {
    201             throw new IOException("Can't read from deactivated memory file.");
    202         }
    203         if (destOffset < 0 || destOffset > buffer.length || count < 0
    204                 || count > buffer.length - destOffset
    205                 || srcOffset < 0 || srcOffset > mLength
    206                 || count > mLength - srcOffset) {
    207             throw new IndexOutOfBoundsException();
    208         }
    209         return native_read(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
    210     }
    211 
    212     /**
    213      * Write bytes to the memory file.
    214      * Will throw an IOException if the file has been purged.
    215      *
    216      * @param buffer byte array to write bytes from.
    217      * @param srcOffset offset into the byte array buffer to write from.
    218      * @param destOffset offset  into the memory file to write to.
    219      * @param count number of bytes to write.
    220      * @throws IOException if the memory file has been purged or deactivated.
    221      */
    222     public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
    223             throws IOException {
    224         if (isDeactivated()) {
    225             throw new IOException("Can't write to deactivated memory file.");
    226         }
    227         if (srcOffset < 0 || srcOffset > buffer.length || count < 0
    228                 || count > buffer.length - srcOffset
    229                 || destOffset < 0 || destOffset > mLength
    230                 || count > mLength - destOffset) {
    231             throw new IndexOutOfBoundsException();
    232         }
    233         native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
    234     }
    235 
    236     /**
    237      * Gets a FileDescriptor for the memory file.
    238      *
    239      * The returned file descriptor is not duplicated.
    240      *
    241      * @throws IOException If the memory file has been closed.
    242      *
    243      * @hide
    244      */
    245     public FileDescriptor getFileDescriptor() throws IOException {
    246         return mFD;
    247     }
    248 
    249     /**
    250      * Returns the size of the memory file that the file descriptor refers to,
    251      * or -1 if the file descriptor does not refer to a memory file.
    252      *
    253      * @throws IOException If <code>fd</code> is not a valid file descriptor.
    254      *
    255      * @hide
    256      */
    257     public static int getSize(FileDescriptor fd) throws IOException {
    258         return native_get_size(fd);
    259     }
    260 
    261     private class MemoryInputStream extends InputStream {
    262 
    263         private int mMark = 0;
    264         private int mOffset = 0;
    265         private byte[] mSingleByte;
    266 
    267         @Override
    268         public int available() throws IOException {
    269             if (mOffset >= mLength) {
    270                 return 0;
    271             }
    272             return mLength - mOffset;
    273         }
    274 
    275         @Override
    276         public boolean markSupported() {
    277             return true;
    278         }
    279 
    280         @Override
    281         public void mark(int readlimit) {
    282             mMark = mOffset;
    283         }
    284 
    285         @Override
    286         public void reset() throws IOException {
    287             mOffset = mMark;
    288         }
    289 
    290         @Override
    291         public int read() throws IOException {
    292             if (mSingleByte == null) {
    293                 mSingleByte = new byte[1];
    294             }
    295             int result = read(mSingleByte, 0, 1);
    296             if (result != 1) {
    297                 return -1;
    298             }
    299             return mSingleByte[0];
    300         }
    301 
    302         @Override
    303         public int read(byte buffer[], int offset, int count) throws IOException {
    304             if (offset < 0 || count < 0 || offset + count > buffer.length) {
    305                 // readBytes() also does this check, but we need to do it before
    306                 // changing count.
    307                 throw new IndexOutOfBoundsException();
    308             }
    309             count = Math.min(count, available());
    310             if (count < 1) {
    311                 return -1;
    312             }
    313             int result = readBytes(buffer, mOffset, offset, count);
    314             if (result > 0) {
    315                 mOffset += result;
    316             }
    317             return result;
    318         }
    319 
    320         @Override
    321         public long skip(long n) throws IOException {
    322             if (mOffset + n > mLength) {
    323                 n = mLength - mOffset;
    324             }
    325             mOffset += n;
    326             return n;
    327         }
    328     }
    329 
    330     private class MemoryOutputStream extends OutputStream {
    331 
    332         private int mOffset = 0;
    333         private byte[] mSingleByte;
    334 
    335         @Override
    336         public void write(byte buffer[], int offset, int count) throws IOException {
    337             writeBytes(buffer, offset, mOffset, count);
    338             mOffset += count;
    339         }
    340 
    341         @Override
    342         public void write(int oneByte) throws IOException {
    343             if (mSingleByte == null) {
    344                 mSingleByte = new byte[1];
    345             }
    346             mSingleByte[0] = (byte)oneByte;
    347             write(mSingleByte, 0, 1);
    348         }
    349     }
    350 }
    351