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      */
AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length)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      */
AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length, Bundle extras)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      */
getParcelFileDescriptor()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      */
getFileDescriptor()101     public FileDescriptor getFileDescriptor() {
102         return mFd.getFileDescriptor();
103     }
104 
105     /**
106      * Returns the byte offset where this asset entry's data starts.
107      */
getStartOffset()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      */
getExtras()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      */
getLength()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      */
getDeclaredLength()147     public long getDeclaredLength() {
148         return mLength;
149     }
150 
151     /**
152      * Convenience for calling <code>getParcelFileDescriptor().close()</code>.
153      */
154     @Override
close()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      */
createInputStream()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      */
createOutputStream()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
toString()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 
AutoCloseInputStream(AssetFileDescriptor fd)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
available()211         public int available() throws IOException {
212             return mRemaining >= 0
213                     ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
214                     : super.available();
215         }
216 
217         @Override
read()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
read(byte[] buffer, int offset, int count)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
read(byte[] buffer)238         public int read(byte[] buffer) throws IOException {
239             return read(buffer, 0, buffer.length);
240         }
241 
242         @Override
skip(long count)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
mark(int readlimit)256         public void mark(int readlimit) {
257             if (mRemaining >= 0) {
258                 // Not supported.
259                 return;
260             }
261             super.mark(readlimit);
262         }
263 
264         @Override
markSupported()265         public boolean markSupported() {
266             if (mRemaining >= 0) {
267                 return false;
268             }
269             return super.markSupported();
270         }
271 
272         @Override
reset()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 
AutoCloseOutputStream(AssetFileDescriptor fd)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
write(byte[] buffer, int offset, int count)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
write(byte[] buffer)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
write(int oneByte)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
describeContents()341     public int describeContents() {
342         return mFd.describeContents();
343     }
344 
345     @Override
writeToParcel(Parcel out, int flags)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 
AssetFileDescriptor(Parcel src)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