• 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.MemoryFile;
20 import android.os.Parcel;
21 import android.os.ParcelFileDescriptor;
22 import android.os.Parcelable;
23 
24 import java.io.FileDescriptor;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.nio.channels.FileChannel;
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 {
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     private final ParcelFileDescriptor mFd;
45     private final long mStartOffset;
46     private final long mLength;
47 
48     /**
49      * Create a new AssetFileDescriptor from the given values.
50      * @param fd The underlying file descriptor.
51      * @param startOffset The location within the file that the asset starts.
52      * This must be 0 if length is UNKNOWN_LENGTH.
53      * @param length The number of bytes of the asset, or
54      * {@link #UNKNOWN_LENGTH if it extends to the end of the file.
55      */
AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length)56     public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
57             long length) {
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      * Checks whether this file descriptor is for a memory file.
131      */
isMemoryFile()132     private boolean isMemoryFile() throws IOException {
133         try {
134             return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
135         } catch (IOException e) {
136             return false;
137         }
138     }
139 
140     /**
141      * Create and return a new auto-close input stream for this asset.  This
142      * will either return a full asset {@link AutoCloseInputStream}, or
143      * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream
144      * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the
145      * the object represents a complete file or sub-section of a file.  You
146      * should only call this once for a particular asset.
147      */
createInputStream()148     public FileInputStream createInputStream() throws IOException {
149         if (isMemoryFile()) {
150             if (mLength > Integer.MAX_VALUE) {
151                 throw new IOException("File length too large for a memory file: " + mLength);
152             }
153             return new AutoCloseMemoryFileInputStream(mFd, (int)mLength);
154         }
155         if (mLength < 0) {
156             return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
157         }
158         return new AutoCloseInputStream(this);
159     }
160 
161     /**
162      * Create and return a new auto-close output stream for this asset.  This
163      * will either return a full asset {@link AutoCloseOutputStream}, or
164      * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream
165      * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the
166      * the object represents a complete file or sub-section of a file.  You
167      * should only call this once for a particular asset.
168      */
createOutputStream()169     public FileOutputStream createOutputStream() throws IOException {
170         if (mLength < 0) {
171             return new ParcelFileDescriptor.AutoCloseOutputStream(mFd);
172         }
173         return new AutoCloseOutputStream(this);
174     }
175 
176     @Override
toString()177     public String toString() {
178         return "{AssetFileDescriptor: " + mFd
179                 + " start=" + mStartOffset + " len=" + mLength + "}";
180     }
181 
182     /**
183      * An InputStream you can create on a ParcelFileDescriptor, which will
184      * take care of calling {@link ParcelFileDescriptor#close
185      * ParcelFileDescritor.close()} for you when the stream is closed.
186      */
187     public static class AutoCloseInputStream
188             extends ParcelFileDescriptor.AutoCloseInputStream {
189         private long mRemaining;
190 
AutoCloseInputStream(AssetFileDescriptor fd)191         public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
192             super(fd.getParcelFileDescriptor());
193             super.skip(fd.getStartOffset());
194             mRemaining = (int)fd.getLength();
195         }
196 
197         @Override
available()198         public int available() throws IOException {
199             return mRemaining >= 0
200                     ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
201                     : super.available();
202         }
203 
204         @Override
read()205         public int read() throws IOException {
206             if (mRemaining >= 0) {
207                 if (mRemaining == 0) return -1;
208                 int res = super.read();
209                 if (res >= 0) mRemaining--;
210                 return res;
211             }
212 
213             return super.read();
214         }
215 
216         @Override
read(byte[] buffer, int offset, int count)217         public int read(byte[] buffer, int offset, int count) throws IOException {
218             if (mRemaining >= 0) {
219                 if (mRemaining == 0) return -1;
220                 if (count > mRemaining) count = (int)mRemaining;
221                 int res = super.read(buffer, offset, count);
222                 if (res >= 0) mRemaining -= res;
223                 return res;
224             }
225 
226             return super.read(buffer, offset, count);
227         }
228 
229         @Override
read(byte[] buffer)230         public int read(byte[] buffer) throws IOException {
231             if (mRemaining >= 0) {
232                 if (mRemaining == 0) return -1;
233                 int count = buffer.length;
234                 if (count > mRemaining) count = (int)mRemaining;
235                 int res = super.read(buffer, 0, count);
236                 if (res >= 0) mRemaining -= res;
237                 return res;
238             }
239 
240             return super.read(buffer);
241         }
242 
243         @Override
skip(long count)244         public long skip(long count) throws IOException {
245             if (mRemaining >= 0) {
246                 if (mRemaining == 0) return -1;
247                 if (count > mRemaining) count = mRemaining;
248                 long res = super.skip(count);
249                 if (res >= 0) mRemaining -= res;
250                 return res;
251             }
252 
253             // TODO Auto-generated method stub
254             return super.skip(count);
255         }
256 
257         @Override
mark(int readlimit)258         public void mark(int readlimit) {
259             if (mRemaining >= 0) {
260                 // Not supported.
261                 return;
262             }
263             super.mark(readlimit);
264         }
265 
266         @Override
markSupported()267         public boolean markSupported() {
268             if (mRemaining >= 0) {
269                 return false;
270             }
271             return super.markSupported();
272         }
273 
274         @Override
reset()275         public synchronized void reset() throws IOException {
276             if (mRemaining >= 0) {
277                 // Not supported.
278                 return;
279             }
280             super.reset();
281         }
282     }
283 
284     /**
285      * An input stream that reads from a MemoryFile and closes it when the stream is closed.
286      * This extends FileInputStream just because {@link #createInputStream} returns
287      * a FileInputStream. All the FileInputStream methods are
288      * overridden to use the MemoryFile instead.
289      */
290     private static class AutoCloseMemoryFileInputStream extends FileInputStream {
291         private ParcelFileDescriptor mParcelFd;
292         private MemoryFile mFile;
293         private InputStream mStream;
294 
AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length)295         public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length)
296                 throws IOException {
297             super(fd.getFileDescriptor());
298             mParcelFd = fd;
299             mFile = new MemoryFile(fd.getFileDescriptor(), length, "r");
300             mStream = mFile.getInputStream();
301         }
302 
303         @Override
available()304         public int available() throws IOException {
305             return mStream.available();
306         }
307 
308         @Override
close()309         public void close() throws IOException {
310             mParcelFd.close();  // must close ParcelFileDescriptor, not just the file descriptor,
311                                 // since it could be a subclass of ParcelFileDescriptor.
312                                 // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases
313                                 // a content provider
314             mFile.close();      // to unmap the memory file from the address space.
315             mStream.close();    // doesn't actually do anything
316         }
317 
318         @Override
getChannel()319         public FileChannel getChannel() {
320             return null;
321         }
322 
323         @Override
read()324         public int read() throws IOException {
325             return mStream.read();
326         }
327 
328         @Override
read(byte[] buffer, int offset, int count)329         public int read(byte[] buffer, int offset, int count) throws IOException {
330             return mStream.read(buffer, offset, count);
331         }
332 
333         @Override
read(byte[] buffer)334         public int read(byte[] buffer) throws IOException {
335             return mStream.read(buffer);
336         }
337 
338         @Override
skip(long count)339         public long skip(long count) throws IOException {
340             return mStream.skip(count);
341         }
342     }
343 
344     /**
345      * An OutputStream you can create on a ParcelFileDescriptor, which will
346      * take care of calling {@link ParcelFileDescriptor#close
347      * ParcelFileDescritor.close()} for you when the stream is closed.
348      */
349     public static class AutoCloseOutputStream
350             extends ParcelFileDescriptor.AutoCloseOutputStream {
351         private long mRemaining;
352 
AutoCloseOutputStream(AssetFileDescriptor fd)353         public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
354             super(fd.getParcelFileDescriptor());
355             if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
356                 throw new IOException("Unable to seek");
357             }
358             mRemaining = (int)fd.getLength();
359         }
360 
361         @Override
write(byte[] buffer, int offset, int count)362         public void write(byte[] buffer, int offset, int count) throws IOException {
363             if (mRemaining >= 0) {
364                 if (mRemaining == 0) return;
365                 if (count > mRemaining) count = (int)mRemaining;
366                 super.write(buffer, offset, count);
367                 mRemaining -= count;
368                 return;
369             }
370 
371             super.write(buffer, offset, count);
372         }
373 
374         @Override
write(byte[] buffer)375         public void write(byte[] buffer) throws IOException {
376             if (mRemaining >= 0) {
377                 if (mRemaining == 0) return;
378                 int count = buffer.length;
379                 if (count > mRemaining) count = (int)mRemaining;
380                 super.write(buffer);
381                 mRemaining -= count;
382                 return;
383             }
384 
385             super.write(buffer);
386         }
387 
388         @Override
write(int oneByte)389         public void write(int oneByte) throws IOException {
390             if (mRemaining >= 0) {
391                 if (mRemaining == 0) return;
392                 super.write(oneByte);
393                 mRemaining--;
394                 return;
395             }
396 
397             super.write(oneByte);
398         }
399     }
400 
401 
402     /* Parcelable interface */
describeContents()403     public int describeContents() {
404         return mFd.describeContents();
405     }
406 
writeToParcel(Parcel out, int flags)407     public void writeToParcel(Parcel out, int flags) {
408         mFd.writeToParcel(out, flags);
409         out.writeLong(mStartOffset);
410         out.writeLong(mLength);
411     }
412 
AssetFileDescriptor(Parcel src)413     AssetFileDescriptor(Parcel src) {
414         mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
415         mStartOffset = src.readLong();
416         mLength = src.readLong();
417     }
418 
419     public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
420             = new Parcelable.Creator<AssetFileDescriptor>() {
421         public AssetFileDescriptor createFromParcel(Parcel in) {
422             return new AssetFileDescriptor(in);
423         }
424         public AssetFileDescriptor[] newArray(int size) {
425             return new AssetFileDescriptor[size];
426         }
427     };
428 
429     /**
430      * Creates an AssetFileDescriptor from a memory file.
431      *
432      * @hide
433      */
fromMemoryFile(MemoryFile memoryFile)434     public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile)
435             throws IOException {
436         ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor();
437         return new AssetFileDescriptor(fd, 0, memoryFile.length());
438     }
439 
440 }
441