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