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.Parcel;
     20 import android.os.ParcelFileDescriptor;
     21 import android.os.Parcelable;
     22 
     23 import java.io.FileDescriptor;
     24 import java.io.FileInputStream;
     25 import java.io.FileOutputStream;
     26 import java.io.IOException;
     27 
     28 /**
     29  * File descriptor of an entry in the AssetManager.  This provides your own
     30  * opened FileDescriptor that can be used to read the data, as well as the
     31  * offset and length of that entry's data in the file.
     32  */
     33 public class AssetFileDescriptor implements Parcelable {
     34     /**
     35      * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)}
     36      * and {@link #getDeclaredLength} when a length has not been declared.  This means
     37      * the data extends to the end of the file.
     38      */
     39     public static final long UNKNOWN_LENGTH = -1;
     40 
     41     private final ParcelFileDescriptor mFd;
     42     private final long mStartOffset;
     43     private final long mLength;
     44 
     45     /**
     46      * Create a new AssetFileDescriptor from the given values.
     47      * @param fd The underlying file descriptor.
     48      * @param startOffset The location within the file that the asset starts.
     49      * This must be 0 if length is UNKNOWN_LENGTH.
     50      * @param length The number of bytes of the asset, or
     51      * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
     52      */
     53     public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
     54             long length) {
     55         if (fd == null) {
     56             throw new IllegalArgumentException("fd must not be null");
     57         }
     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      * Create and return a new auto-close input stream for this asset.  This
    131      * will either return a full asset {@link AutoCloseInputStream}, or
    132      * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream
    133      * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the
    134      * the object represents a complete file or sub-section of a file.  You
    135      * should only call this once for a particular asset.
    136      */
    137     public FileInputStream createInputStream() throws IOException {
    138         if (mLength < 0) {
    139             return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
    140         }
    141         return new AutoCloseInputStream(this);
    142     }
    143 
    144     /**
    145      * Create and return a new auto-close output stream for this asset.  This
    146      * will either return a full asset {@link AutoCloseOutputStream}, or
    147      * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream
    148      * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the
    149      * the object represents a complete file or sub-section of a file.  You
    150      * should only call this once for a particular asset.
    151      */
    152     public FileOutputStream createOutputStream() throws IOException {
    153         if (mLength < 0) {
    154             return new ParcelFileDescriptor.AutoCloseOutputStream(mFd);
    155         }
    156         return new AutoCloseOutputStream(this);
    157     }
    158 
    159     @Override
    160     public String toString() {
    161         return "{AssetFileDescriptor: " + mFd
    162                 + " start=" + mStartOffset + " len=" + mLength + "}";
    163     }
    164 
    165     /**
    166      * An InputStream you can create on a ParcelFileDescriptor, which will
    167      * take care of calling {@link ParcelFileDescriptor#close
    168      * ParcelFileDescritor.close()} for you when the stream is closed.
    169      */
    170     public static class AutoCloseInputStream
    171             extends ParcelFileDescriptor.AutoCloseInputStream {
    172         private long mRemaining;
    173 
    174         public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
    175             super(fd.getParcelFileDescriptor());
    176             super.skip(fd.getStartOffset());
    177             mRemaining = (int)fd.getLength();
    178         }
    179 
    180         @Override
    181         public int available() throws IOException {
    182             return mRemaining >= 0
    183                     ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
    184                     : super.available();
    185         }
    186 
    187         @Override
    188         public int read() throws IOException {
    189             byte[] buffer = new byte[1];
    190             int result = read(buffer, 0, 1);
    191             return result == -1 ? -1 : buffer[0] & 0xff;
    192         }
    193 
    194         @Override
    195         public int read(byte[] buffer, int offset, int count) throws IOException {
    196             if (mRemaining >= 0) {
    197                 if (mRemaining == 0) return -1;
    198                 if (count > mRemaining) count = (int)mRemaining;
    199                 int res = super.read(buffer, offset, count);
    200                 if (res >= 0) mRemaining -= res;
    201                 return res;
    202             }
    203 
    204             return super.read(buffer, offset, count);
    205         }
    206 
    207         @Override
    208         public int read(byte[] buffer) throws IOException {
    209             return read(buffer, 0, buffer.length);
    210         }
    211 
    212         @Override
    213         public long skip(long count) throws IOException {
    214             if (mRemaining >= 0) {
    215                 if (mRemaining == 0) return -1;
    216                 if (count > mRemaining) count = mRemaining;
    217                 long res = super.skip(count);
    218                 if (res >= 0) mRemaining -= res;
    219                 return res;
    220             }
    221 
    222             return super.skip(count);
    223         }
    224 
    225         @Override
    226         public void mark(int readlimit) {
    227             if (mRemaining >= 0) {
    228                 // Not supported.
    229                 return;
    230             }
    231             super.mark(readlimit);
    232         }
    233 
    234         @Override
    235         public boolean markSupported() {
    236             if (mRemaining >= 0) {
    237                 return false;
    238             }
    239             return super.markSupported();
    240         }
    241 
    242         @Override
    243         public synchronized void reset() throws IOException {
    244             if (mRemaining >= 0) {
    245                 // Not supported.
    246                 return;
    247             }
    248             super.reset();
    249         }
    250     }
    251 
    252     /**
    253      * An OutputStream you can create on a ParcelFileDescriptor, which will
    254      * take care of calling {@link ParcelFileDescriptor#close
    255      * ParcelFileDescritor.close()} for you when the stream is closed.
    256      */
    257     public static class AutoCloseOutputStream
    258             extends ParcelFileDescriptor.AutoCloseOutputStream {
    259         private long mRemaining;
    260 
    261         public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
    262             super(fd.getParcelFileDescriptor());
    263             if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
    264                 throw new IOException("Unable to seek");
    265             }
    266             mRemaining = (int)fd.getLength();
    267         }
    268 
    269         @Override
    270         public void write(byte[] buffer, int offset, int count) throws IOException {
    271             if (mRemaining >= 0) {
    272                 if (mRemaining == 0) return;
    273                 if (count > mRemaining) count = (int)mRemaining;
    274                 super.write(buffer, offset, count);
    275                 mRemaining -= count;
    276                 return;
    277             }
    278 
    279             super.write(buffer, offset, count);
    280         }
    281 
    282         @Override
    283         public void write(byte[] buffer) throws IOException {
    284             if (mRemaining >= 0) {
    285                 if (mRemaining == 0) return;
    286                 int count = buffer.length;
    287                 if (count > mRemaining) count = (int)mRemaining;
    288                 super.write(buffer);
    289                 mRemaining -= count;
    290                 return;
    291             }
    292 
    293             super.write(buffer);
    294         }
    295 
    296         @Override
    297         public void write(int oneByte) throws IOException {
    298             if (mRemaining >= 0) {
    299                 if (mRemaining == 0) return;
    300                 super.write(oneByte);
    301                 mRemaining--;
    302                 return;
    303             }
    304 
    305             super.write(oneByte);
    306         }
    307     }
    308 
    309 
    310     /* Parcelable interface */
    311     public int describeContents() {
    312         return mFd.describeContents();
    313     }
    314 
    315     public void writeToParcel(Parcel out, int flags) {
    316         mFd.writeToParcel(out, flags);
    317         out.writeLong(mStartOffset);
    318         out.writeLong(mLength);
    319     }
    320 
    321     AssetFileDescriptor(Parcel src) {
    322         mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
    323         mStartOffset = src.readLong();
    324         mLength = src.readLong();
    325     }
    326 
    327     public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
    328             = new Parcelable.Creator<AssetFileDescriptor>() {
    329         public AssetFileDescriptor createFromParcel(Parcel in) {
    330             return new AssetFileDescriptor(in);
    331         }
    332         public AssetFileDescriptor[] newArray(int size) {
    333             return new AssetFileDescriptor[size];
    334         }
    335     };
    336 
    337 }
    338