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.system.ErrnoException;
     20 
     21 import java.io.FileDescriptor;
     22 import java.io.IOException;
     23 import java.io.InputStream;
     24 import java.io.OutputStream;
     25 import java.nio.ByteBuffer;
     26 
     27 
     28 /**
     29  * MemoryFile is a wrapper for {@link SharedMemory} which can optionally be set to purgeable.
     30  *
     31  * Applications should generally prefer to use {@link SharedMemory} which offers more flexible
     32  * access & control over the shared memory region than MemoryFile does.
     33  *
     34  * Purgeable files may have their contents reclaimed by the kernel
     35  * in low memory conditions (only if allowPurging is set to true).
     36  * After a file is purged, attempts to read or write the file will
     37  * cause an IOException to be thrown.
     38  */
     39 public class MemoryFile {
     40     private static String TAG = "MemoryFile";
     41 
     42     // Returns 'true' if purged, 'false' otherwise
     43     private static native boolean native_pin(FileDescriptor fd, boolean pin) throws IOException;
     44     private static native int native_get_size(FileDescriptor fd) throws IOException;
     45 
     46     private SharedMemory mSharedMemory;
     47     private ByteBuffer mMapping;
     48     private boolean mAllowPurging = false;  // true if our ashmem region is unpinned
     49 
     50     /**
     51      * Allocates a new ashmem region. The region is initially not purgable.
     52      *
     53      * @param name optional name for the file (can be null).
     54      * @param length of the memory file in bytes, must be positive.
     55      * @throws IOException if the memory file could not be created.
     56      */
     57     public MemoryFile(String name, int length) throws IOException {
     58         try {
     59             mSharedMemory = SharedMemory.create(name, length);
     60             mMapping = mSharedMemory.mapReadWrite();
     61         } catch (ErrnoException ex) {
     62             ex.rethrowAsIOException();
     63         }
     64     }
     65 
     66     /**
     67      * Closes the memory file. If there are no other open references to the memory
     68      * file, it will be deleted.
     69      */
     70     public void close() {
     71         deactivate();
     72         mSharedMemory.close();
     73     }
     74 
     75     /**
     76      * Unmaps the memory file from the process's memory space, but does not close it.
     77      * After this method has been called, read and write operations through this object
     78      * will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor.
     79      *
     80      * @hide
     81      */
     82     void deactivate() {
     83         if (mMapping != null) {
     84             SharedMemory.unmap(mMapping);
     85             mMapping = null;
     86         }
     87     }
     88 
     89     private void checkActive() throws IOException {
     90         if (mMapping == null) {
     91             throw new IOException("MemoryFile has been deactivated");
     92         }
     93     }
     94 
     95     private void beginAccess() throws IOException {
     96         checkActive();
     97         if (mAllowPurging) {
     98             if (native_pin(mSharedMemory.getFileDescriptor(), true)) {
     99                 throw new IOException("MemoryFile has been purged");
    100             }
    101         }
    102     }
    103 
    104     private void endAccess() throws IOException {
    105         if (mAllowPurging) {
    106             native_pin(mSharedMemory.getFileDescriptor(), false);
    107         }
    108     }
    109 
    110     /**
    111      * Returns the length of the memory file.
    112      *
    113      * @return file length.
    114      */
    115     public int length() {
    116         return mSharedMemory.getSize();
    117     }
    118 
    119     /**
    120      * Is memory file purging enabled?
    121      *
    122      * @return true if the file may be purged.
    123      *
    124      * @deprecated Purgable is considered generally fragile and hard to use safely. Applications
    125      * are recommend to instead use {@link android.content.ComponentCallbacks2#onTrimMemory(int)}
    126      * to react to memory events and release shared memory regions as appropriate.
    127      */
    128     @Deprecated
    129     public boolean isPurgingAllowed() {
    130         return mAllowPurging;
    131     }
    132 
    133     /**
    134      * Enables or disables purging of the memory file.
    135      *
    136      * @param allowPurging true if the operating system can purge the contents
    137      * of the file in low memory situations
    138      * @return previous value of allowPurging
    139      *
    140      * @deprecated Purgable is considered generally fragile and hard to use safely. Applications
    141      * are recommend to instead use {@link android.content.ComponentCallbacks2#onTrimMemory(int)}
    142      * to react to memory events and release shared memory regions as appropriate.
    143      */
    144     @Deprecated
    145     synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
    146         boolean oldValue = mAllowPurging;
    147         if (oldValue != allowPurging) {
    148             native_pin(mSharedMemory.getFileDescriptor(), !allowPurging);
    149             mAllowPurging = allowPurging;
    150         }
    151         return oldValue;
    152     }
    153 
    154     /**
    155      * Creates a new InputStream for reading from the memory file.
    156      *
    157      @return InputStream
    158      */
    159     public InputStream getInputStream() {
    160         return new MemoryInputStream();
    161     }
    162 
    163     /**
    164      * Creates a new OutputStream for writing to the memory file.
    165      *
    166      @return OutputStream
    167      */
    168      public OutputStream getOutputStream() {
    169         return new MemoryOutputStream();
    170     }
    171 
    172     /**
    173      * Reads bytes from the memory file.
    174      * Will throw an IOException if the file has been purged.
    175      *
    176      * @param buffer byte array to read bytes into.
    177      * @param srcOffset offset into the memory file to read from.
    178      * @param destOffset offset into the byte array buffer to read into.
    179      * @param count number of bytes to read.
    180      * @return number of bytes read.
    181      * @throws IOException if the memory file has been purged or deactivated.
    182      */
    183     public int readBytes(byte[] buffer, int srcOffset, int destOffset, int count)
    184             throws IOException {
    185         beginAccess();
    186         try {
    187             mMapping.position(srcOffset);
    188             mMapping.get(buffer, destOffset, count);
    189         } finally {
    190             endAccess();
    191         }
    192         return count;
    193     }
    194 
    195     /**
    196      * Write bytes to the memory file.
    197      * Will throw an IOException if the file has been purged.
    198      *
    199      * @param buffer byte array to write bytes from.
    200      * @param srcOffset offset into the byte array buffer to write from.
    201      * @param destOffset offset  into the memory file to write to.
    202      * @param count number of bytes to write.
    203      * @throws IOException if the memory file has been purged or deactivated.
    204      */
    205     public void writeBytes(byte[] buffer, int srcOffset, int destOffset, int count)
    206             throws IOException {
    207         beginAccess();
    208         try {
    209             mMapping.position(destOffset);
    210             mMapping.put(buffer, srcOffset, count);
    211         } finally {
    212             endAccess();
    213         }
    214     }
    215 
    216     /**
    217      * Gets a FileDescriptor for the memory file.
    218      *
    219      * The returned file descriptor is not duplicated.
    220      *
    221      * @throws IOException If the memory file has been closed.
    222      *
    223      * @hide
    224      */
    225     public FileDescriptor getFileDescriptor() throws IOException {
    226         return mSharedMemory.getFileDescriptor();
    227     }
    228 
    229     /**
    230      * Returns the size of the memory file that the file descriptor refers to,
    231      * or -1 if the file descriptor does not refer to a memory file.
    232      *
    233      * @throws IOException If <code>fd</code> is not a valid file descriptor.
    234      *
    235      * @hide
    236      */
    237     public static int getSize(FileDescriptor fd) throws IOException {
    238         return native_get_size(fd);
    239     }
    240 
    241     private class MemoryInputStream extends InputStream {
    242 
    243         private int mMark = 0;
    244         private int mOffset = 0;
    245         private byte[] mSingleByte;
    246 
    247         @Override
    248         public int available() throws IOException {
    249             if (mOffset >= mSharedMemory.getSize()) {
    250                 return 0;
    251             }
    252             return mSharedMemory.getSize() - mOffset;
    253         }
    254 
    255         @Override
    256         public boolean markSupported() {
    257             return true;
    258         }
    259 
    260         @Override
    261         public void mark(int readlimit) {
    262             mMark = mOffset;
    263         }
    264 
    265         @Override
    266         public void reset() throws IOException {
    267             mOffset = mMark;
    268         }
    269 
    270         @Override
    271         public int read() throws IOException {
    272             if (mSingleByte == null) {
    273                 mSingleByte = new byte[1];
    274             }
    275             int result = read(mSingleByte, 0, 1);
    276             if (result != 1) {
    277                 return -1;
    278             }
    279             return mSingleByte[0];
    280         }
    281 
    282         @Override
    283         public int read(byte buffer[], int offset, int count) throws IOException {
    284             if (offset < 0 || count < 0 || offset + count > buffer.length) {
    285                 // readBytes() also does this check, but we need to do it before
    286                 // changing count.
    287                 throw new IndexOutOfBoundsException();
    288             }
    289             count = Math.min(count, available());
    290             if (count < 1) {
    291                 return -1;
    292             }
    293             int result = readBytes(buffer, mOffset, offset, count);
    294             if (result > 0) {
    295                 mOffset += result;
    296             }
    297             return result;
    298         }
    299 
    300         @Override
    301         public long skip(long n) throws IOException {
    302             if (mOffset + n > mSharedMemory.getSize()) {
    303                 n = mSharedMemory.getSize() - mOffset;
    304             }
    305             mOffset += n;
    306             return n;
    307         }
    308     }
    309 
    310     private class MemoryOutputStream extends OutputStream {
    311 
    312         private int mOffset = 0;
    313         private byte[] mSingleByte;
    314 
    315         @Override
    316         public void write(byte buffer[], int offset, int count) throws IOException {
    317             writeBytes(buffer, offset, mOffset, count);
    318             mOffset += count;
    319         }
    320 
    321         @Override
    322         public void write(int oneByte) throws IOException {
    323             if (mSingleByte == null) {
    324                 mSingleByte = new byte[1];
    325             }
    326             mSingleByte[0] = (byte)oneByte;
    327             write(mSingleByte, 0, 1);
    328         }
    329     }
    330 }
    331