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