Home | History | Annotate | Download | only in res
      1 /*
      2  * Copyright (C) 2006 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.content.res;
     18 
     19 import android.os.MemoryFile;
     20 import android.os.Parcel;
     21 import android.os.ParcelFileDescriptor;
     22 import android.os.Parcelable;
     23 
     24 import java.io.FileDescriptor;
     25 import java.io.FileInputStream;
     26 import java.io.FileOutputStream;
     27 import java.io.IOException;
     28 import java.io.InputStream;
     29 import java.nio.channels.FileChannel;
     30 
     31 /**
     32  * File descriptor of an entry in the AssetManager.  This provides your own
     33  * opened FileDescriptor that can be used to read the data, as well as the
     34  * offset and length of that entry's data in the file.
     35  */
     36 public class AssetFileDescriptor implements Parcelable {
     37     /**
     38      * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)}
     39      * and {@link #getDeclaredLength} when a length has not been declared.  This means
     40      * the data extends to the end of the file.
     41      */
     42     public static final long UNKNOWN_LENGTH = -1;
     43 
     44     private final ParcelFileDescriptor mFd;
     45     private final long mStartOffset;
     46     private final long mLength;
     47 
     48     /**
     49      * Create a new AssetFileDescriptor from the given values.
     50      * @param fd The underlying file descriptor.
     51      * @param startOffset The location within the file that the asset starts.
     52      * This must be 0 if length is UNKNOWN_LENGTH.
     53      * @param length The number of bytes of the asset, or
     54      * {@link #UNKNOWN_LENGTH if it extends to the end of the file.
     55      */
     56     public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
     57             long length) {
     58         if (length < 0 && startOffset != 0) {
     59             throw new IllegalArgumentException(
     60                     "startOffset must be 0 when using UNKNOWN_LENGTH");
     61         }
     62         mFd = fd;
     63         mStartOffset = startOffset;
     64         mLength = length;
     65     }
     66 
     67     /**
     68      * The AssetFileDescriptor contains its own ParcelFileDescriptor, which
     69      * in addition to the normal FileDescriptor object also allows you to close
     70      * the descriptor when you are done with it.
     71      */
     72     public ParcelFileDescriptor getParcelFileDescriptor() {
     73         return mFd;
     74     }
     75 
     76     /**
     77      * Returns the FileDescriptor that can be used to read the data in the
     78      * file.
     79      */
     80     public FileDescriptor getFileDescriptor() {
     81         return mFd.getFileDescriptor();
     82     }
     83 
     84     /**
     85      * Returns the byte offset where this asset entry's data starts.
     86      */
     87     public long getStartOffset() {
     88         return mStartOffset;
     89     }
     90 
     91     /**
     92      * Returns the total number of bytes of this asset entry's data.  May be
     93      * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file.
     94      * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH},
     95      * this will use {@link ParcelFileDescriptor#getStatSize()
     96      * ParcelFileDescriptor.getStatSize()} to find the total size of the file,
     97      * returning that number if found or {@link #UNKNOWN_LENGTH} if it could
     98      * not be determined.
     99      *
    100      * @see #getDeclaredLength()
    101      */
    102     public long getLength() {
    103         if (mLength >= 0) {
    104             return mLength;
    105         }
    106         long len = mFd.getStatSize();
    107         return len >= 0 ? len : UNKNOWN_LENGTH;
    108     }
    109 
    110     /**
    111      * Return the actual number of bytes that were declared when the
    112      * AssetFileDescriptor was constructed.  Will be
    113      * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data
    114      * should be read to the end of the file.
    115      *
    116      * @see #getDeclaredLength()
    117      */
    118     public long getDeclaredLength() {
    119         return mLength;
    120     }
    121 
    122     /**
    123      * Convenience for calling <code>getParcelFileDescriptor().close()</code>.
    124      */
    125     public void close() throws IOException {
    126         mFd.close();
    127     }
    128 
    129     /**
    130      * Checks whether this file descriptor is for a memory file.
    131      */
    132     private boolean isMemoryFile() throws IOException {
    133         try {
    134             return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
    135         } catch (IOException e) {
    136             return false;
    137         }
    138     }
    139 
    140     /**
    141      * Create and return a new auto-close input stream for this asset.  This
    142      * will either return a full asset {@link AutoCloseInputStream}, or
    143      * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream
    144      * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the
    145      * the object represents a complete file or sub-section of a file.  You
    146      * should only call this once for a particular asset.
    147      */
    148     public FileInputStream createInputStream() throws IOException {
    149         if (isMemoryFile()) {
    150             if (mLength > Integer.MAX_VALUE) {
    151                 throw new IOException("File length too large for a memory file: " + mLength);
    152             }
    153             return new AutoCloseMemoryFileInputStream(mFd, (int)mLength);
    154         }
    155         if (mLength < 0) {
    156             return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
    157         }
    158         return new AutoCloseInputStream(this);
    159     }
    160 
    161     /**
    162      * Create and return a new auto-close output stream for this asset.  This
    163      * will either return a full asset {@link AutoCloseOutputStream}, or
    164      * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream
    165      * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the
    166      * the object represents a complete file or sub-section of a file.  You
    167      * should only call this once for a particular asset.
    168      */
    169     public FileOutputStream createOutputStream() throws IOException {
    170         if (mLength < 0) {
    171             return new ParcelFileDescriptor.AutoCloseOutputStream(mFd);
    172         }
    173         return new AutoCloseOutputStream(this);
    174     }
    175 
    176     @Override
    177     public String toString() {
    178         return "{AssetFileDescriptor: " + mFd
    179                 + " start=" + mStartOffset + " len=" + mLength + "}";
    180     }
    181 
    182     /**
    183      * An InputStream you can create on a ParcelFileDescriptor, which will
    184      * take care of calling {@link ParcelFileDescriptor#close
    185      * ParcelFileDescritor.close()} for you when the stream is closed.
    186      */
    187     public static class AutoCloseInputStream
    188             extends ParcelFileDescriptor.AutoCloseInputStream {
    189         private long mRemaining;
    190 
    191         public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
    192             super(fd.getParcelFileDescriptor());
    193             super.skip(fd.getStartOffset());
    194             mRemaining = (int)fd.getLength();
    195         }
    196 
    197         @Override
    198         public int available() throws IOException {
    199             return mRemaining >= 0
    200                     ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
    201                     : super.available();
    202         }
    203 
    204         @Override
    205         public int read() throws IOException {
    206             if (mRemaining >= 0) {
    207                 if (mRemaining == 0) return -1;
    208                 int res = super.read();
    209                 if (res >= 0) mRemaining--;
    210                 return res;
    211             }
    212 
    213             return super.read();
    214         }
    215 
    216         @Override
    217         public int read(byte[] buffer, int offset, int count) throws IOException {
    218             if (mRemaining >= 0) {
    219                 if (mRemaining == 0) return -1;
    220                 if (count > mRemaining) count = (int)mRemaining;
    221                 int res = super.read(buffer, offset, count);
    222                 if (res >= 0) mRemaining -= res;
    223                 return res;
    224             }
    225 
    226             return super.read(buffer, offset, count);
    227         }
    228 
    229         @Override
    230         public int read(byte[] buffer) throws IOException {
    231             if (mRemaining >= 0) {
    232                 if (mRemaining == 0) return -1;
    233                 int count = buffer.length;
    234                 if (count > mRemaining) count = (int)mRemaining;
    235                 int res = super.read(buffer, 0, count);
    236                 if (res >= 0) mRemaining -= res;
    237                 return res;
    238             }
    239 
    240             return super.read(buffer);
    241         }
    242 
    243         @Override
    244         public long skip(long count) throws IOException {
    245             if (mRemaining >= 0) {
    246                 if (mRemaining == 0) return -1;
    247                 if (count > mRemaining) count = mRemaining;
    248                 long res = super.skip(count);
    249                 if (res >= 0) mRemaining -= res;
    250                 return res;
    251             }
    252 
    253             // TODO Auto-generated method stub
    254             return super.skip(count);
    255         }
    256 
    257         @Override
    258         public void mark(int readlimit) {
    259             if (mRemaining >= 0) {
    260                 // Not supported.
    261                 return;
    262             }
    263             super.mark(readlimit);
    264         }
    265 
    266         @Override
    267         public boolean markSupported() {
    268             if (mRemaining >= 0) {
    269                 return false;
    270             }
    271             return super.markSupported();
    272         }
    273 
    274         @Override
    275         public synchronized void reset() throws IOException {
    276             if (mRemaining >= 0) {
    277                 // Not supported.
    278                 return;
    279             }
    280             super.reset();
    281         }
    282     }
    283 
    284     /**
    285      * An input stream that reads from a MemoryFile and closes it when the stream is closed.
    286      * This extends FileInputStream just because {@link #createInputStream} returns
    287      * a FileInputStream. All the FileInputStream methods are
    288      * overridden to use the MemoryFile instead.
    289      */
    290     private static class AutoCloseMemoryFileInputStream extends FileInputStream {
    291         private ParcelFileDescriptor mParcelFd;
    292         private MemoryFile mFile;
    293         private InputStream mStream;
    294 
    295         public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length)
    296                 throws IOException {
    297             super(fd.getFileDescriptor());
    298             mParcelFd = fd;
    299             mFile = new MemoryFile(fd.getFileDescriptor(), length, "r");
    300             mStream = mFile.getInputStream();
    301         }
    302 
    303         @Override
    304         public int available() throws IOException {
    305             return mStream.available();
    306         }
    307 
    308         @Override
    309         public void close() throws IOException {
    310             mParcelFd.close();  // must close ParcelFileDescriptor, not just the file descriptor,
    311                                 // since it could be a subclass of ParcelFileDescriptor.
    312                                 // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases
    313                                 // a content provider
    314             mFile.close();      // to unmap the memory file from the address space.
    315             mStream.close();    // doesn't actually do anything
    316         }
    317 
    318         @Override
    319         public FileChannel getChannel() {
    320             return null;
    321         }
    322 
    323         @Override
    324         public int read() throws IOException {
    325             return mStream.read();
    326         }
    327 
    328         @Override
    329         public int read(byte[] buffer, int offset, int count) throws IOException {
    330             return mStream.read(buffer, offset, count);
    331         }
    332 
    333         @Override
    334         public int read(byte[] buffer) throws IOException {
    335             return mStream.read(buffer);
    336         }
    337 
    338         @Override
    339         public long skip(long count) throws IOException {
    340             return mStream.skip(count);
    341         }
    342     }
    343 
    344     /**
    345      * An OutputStream you can create on a ParcelFileDescriptor, which will
    346      * take care of calling {@link ParcelFileDescriptor#close
    347      * ParcelFileDescritor.close()} for you when the stream is closed.
    348      */
    349     public static class AutoCloseOutputStream
    350             extends ParcelFileDescriptor.AutoCloseOutputStream {
    351         private long mRemaining;
    352 
    353         public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
    354             super(fd.getParcelFileDescriptor());
    355             if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
    356                 throw new IOException("Unable to seek");
    357             }
    358             mRemaining = (int)fd.getLength();
    359         }
    360 
    361         @Override
    362         public void write(byte[] buffer, int offset, int count) throws IOException {
    363             if (mRemaining >= 0) {
    364                 if (mRemaining == 0) return;
    365                 if (count > mRemaining) count = (int)mRemaining;
    366                 super.write(buffer, offset, count);
    367                 mRemaining -= count;
    368                 return;
    369             }
    370 
    371             super.write(buffer, offset, count);
    372         }
    373 
    374         @Override
    375         public void write(byte[] buffer) throws IOException {
    376             if (mRemaining >= 0) {
    377                 if (mRemaining == 0) return;
    378                 int count = buffer.length;
    379                 if (count > mRemaining) count = (int)mRemaining;
    380                 super.write(buffer);
    381                 mRemaining -= count;
    382                 return;
    383             }
    384 
    385             super.write(buffer);
    386         }
    387 
    388         @Override
    389         public void write(int oneByte) throws IOException {
    390             if (mRemaining >= 0) {
    391                 if (mRemaining == 0) return;
    392                 super.write(oneByte);
    393                 mRemaining--;
    394                 return;
    395             }
    396 
    397             super.write(oneByte);
    398         }
    399     }
    400 
    401 
    402     /* Parcelable interface */
    403     public int describeContents() {
    404         return mFd.describeContents();
    405     }
    406 
    407     public void writeToParcel(Parcel out, int flags) {
    408         mFd.writeToParcel(out, flags);
    409         out.writeLong(mStartOffset);
    410         out.writeLong(mLength);
    411     }
    412 
    413     AssetFileDescriptor(Parcel src) {
    414         mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
    415         mStartOffset = src.readLong();
    416         mLength = src.readLong();
    417     }
    418 
    419     public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
    420             = new Parcelable.Creator<AssetFileDescriptor>() {
    421         public AssetFileDescriptor createFromParcel(Parcel in) {
    422             return new AssetFileDescriptor(in);
    423         }
    424         public AssetFileDescriptor[] newArray(int size) {
    425             return new AssetFileDescriptor[size];
    426         }
    427     };
    428 
    429     /**
    430      * Creates an AssetFileDescriptor from a memory file.
    431      *
    432      * @hide
    433      */
    434     public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile)
    435             throws IOException {
    436         ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor();
    437         return new AssetFileDescriptor(fd, 0, memoryFile.length());
    438     }
    439 
    440 }
    441