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