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