• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length)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      */
getParcelFileDescriptor()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      */
getFileDescriptor()80     public FileDescriptor getFileDescriptor() {
81         return mFd.getFileDescriptor();
82     }
83 
84     /**
85      * Returns the byte offset where this asset entry's data starts.
86      */
getStartOffset()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      */
getLength()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      */
getDeclaredLength()118     public long getDeclaredLength() {
119         return mLength;
120     }
121 
122     /**
123      * Convenience for calling <code>getParcelFileDescriptor().close()</code>.
124      */
close()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      */
createInputStream()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      */
createOutputStream()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
toString()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 
AutoCloseInputStream(AssetFileDescriptor fd)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
available()181         public int available() throws IOException {
182             return mRemaining >= 0
183                     ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
184                     : super.available();
185         }
186 
187         @Override
read()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
read(byte[] buffer, int offset, int count)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
read(byte[] buffer)208         public int read(byte[] buffer) throws IOException {
209             return read(buffer, 0, buffer.length);
210         }
211 
212         @Override
skip(long count)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
mark(int readlimit)226         public void mark(int readlimit) {
227             if (mRemaining >= 0) {
228                 // Not supported.
229                 return;
230             }
231             super.mark(readlimit);
232         }
233 
234         @Override
markSupported()235         public boolean markSupported() {
236             if (mRemaining >= 0) {
237                 return false;
238             }
239             return super.markSupported();
240         }
241 
242         @Override
reset()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 
AutoCloseOutputStream(AssetFileDescriptor fd)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
write(byte[] buffer, int offset, int count)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
write(byte[] buffer)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
write(int oneByte)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 */
describeContents()311     public int describeContents() {
312         return mFd.describeContents();
313     }
314 
writeToParcel(Parcel out, int flags)315     public void writeToParcel(Parcel out, int flags) {
316         mFd.writeToParcel(out, flags);
317         out.writeLong(mStartOffset);
318         out.writeLong(mLength);
319     }
320 
AssetFileDescriptor(Parcel src)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