blob: e4cc77fe88cdf9c154f55f2d882ffbc806647c42 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.content.res;
18
19import android.os.Parcel;
20import android.os.ParcelFileDescriptor;
21import android.os.Parcelable;
22
Jeff Sharkey32559e12013-04-29 11:19:18 -070023import java.io.Closeable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import java.io.FileDescriptor;
25import java.io.FileInputStream;
26import java.io.FileOutputStream;
27import java.io.IOException;
28
29/**
30 * File descriptor of an entry in the AssetManager. This provides your own
31 * opened FileDescriptor that can be used to read the data, as well as the
32 * offset and length of that entry's data in the file.
33 */
Jeff Sharkey32559e12013-04-29 11:19:18 -070034public class AssetFileDescriptor implements Parcelable, Closeable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035 /**
36 * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)}
37 * and {@link #getDeclaredLength} when a length has not been declared. This means
38 * the data extends to the end of the file.
39 */
40 public static final long UNKNOWN_LENGTH = -1;
41
42 private final ParcelFileDescriptor mFd;
43 private final long mStartOffset;
44 private final long mLength;
45
46 /**
47 * Create a new AssetFileDescriptor from the given values.
48 * @param fd The underlying file descriptor.
49 * @param startOffset The location within the file that the asset starts.
50 * This must be 0 if length is UNKNOWN_LENGTH.
51 * @param length The number of bytes of the asset, or
Bjorn Bringerta006b4722010-04-14 14:43:26 +010052 * {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 */
54 public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
55 long length) {
Jeff Brownaf67fc62012-05-08 14:29:09 -070056 if (fd == null) {
57 throw new IllegalArgumentException("fd must not be null");
58 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 if (length < 0 && startOffset != 0) {
60 throw new IllegalArgumentException(
61 "startOffset must be 0 when using UNKNOWN_LENGTH");
62 }
63 mFd = fd;
64 mStartOffset = startOffset;
65 mLength = length;
66 }
67
68 /**
69 * The AssetFileDescriptor contains its own ParcelFileDescriptor, which
70 * in addition to the normal FileDescriptor object also allows you to close
71 * the descriptor when you are done with it.
72 */
73 public ParcelFileDescriptor getParcelFileDescriptor() {
74 return mFd;
75 }
76
77 /**
78 * Returns the FileDescriptor that can be used to read the data in the
79 * file.
80 */
81 public FileDescriptor getFileDescriptor() {
82 return mFd.getFileDescriptor();
83 }
84
85 /**
86 * Returns the byte offset where this asset entry's data starts.
87 */
88 public long getStartOffset() {
89 return mStartOffset;
90 }
91
92 /**
93 * Returns the total number of bytes of this asset entry's data. May be
94 * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file.
95 * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH},
96 * this will use {@link ParcelFileDescriptor#getStatSize()
97 * ParcelFileDescriptor.getStatSize()} to find the total size of the file,
98 * returning that number if found or {@link #UNKNOWN_LENGTH} if it could
99 * not be determined.
100 *
101 * @see #getDeclaredLength()
102 */
103 public long getLength() {
104 if (mLength >= 0) {
105 return mLength;
106 }
107 long len = mFd.getStatSize();
108 return len >= 0 ? len : UNKNOWN_LENGTH;
109 }
110
111 /**
112 * Return the actual number of bytes that were declared when the
113 * AssetFileDescriptor was constructed. Will be
114 * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data
115 * should be read to the end of the file.
116 *
117 * @see #getDeclaredLength()
118 */
119 public long getDeclaredLength() {
120 return mLength;
121 }
122
123 /**
124 * Convenience for calling <code>getParcelFileDescriptor().close()</code>.
125 */
Jeff Sharkey32559e12013-04-29 11:19:18 -0700126 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 public void close() throws IOException {
128 mFd.close();
129 }
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100130
131 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 * Create and return a new auto-close input stream for this asset. This
133 * will either return a full asset {@link AutoCloseInputStream}, or
134 * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream
135 * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the
136 * the object represents a complete file or sub-section of a file. You
137 * should only call this once for a particular asset.
138 */
139 public FileInputStream createInputStream() throws IOException {
140 if (mLength < 0) {
141 return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
142 }
143 return new AutoCloseInputStream(this);
144 }
145
146 /**
147 * Create and return a new auto-close output stream for this asset. This
148 * will either return a full asset {@link AutoCloseOutputStream}, or
149 * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream
150 * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the
151 * the object represents a complete file or sub-section of a file. You
152 * should only call this once for a particular asset.
153 */
154 public FileOutputStream createOutputStream() throws IOException {
155 if (mLength < 0) {
156 return new ParcelFileDescriptor.AutoCloseOutputStream(mFd);
157 }
158 return new AutoCloseOutputStream(this);
159 }
160
161 @Override
162 public String toString() {
163 return "{AssetFileDescriptor: " + mFd
164 + " start=" + mStartOffset + " len=" + mLength + "}";
165 }
166
167 /**
168 * An InputStream you can create on a ParcelFileDescriptor, which will
169 * take care of calling {@link ParcelFileDescriptor#close
170 * ParcelFileDescritor.close()} for you when the stream is closed.
171 */
172 public static class AutoCloseInputStream
173 extends ParcelFileDescriptor.AutoCloseInputStream {
174 private long mRemaining;
175
176 public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
177 super(fd.getParcelFileDescriptor());
178 super.skip(fd.getStartOffset());
179 mRemaining = (int)fd.getLength();
180 }
181
182 @Override
183 public int available() throws IOException {
184 return mRemaining >= 0
185 ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
186 : super.available();
187 }
188
189 @Override
190 public int read() throws IOException {
Bjorn Bringert21279932010-12-02 11:34:28 +0000191 byte[] buffer = new byte[1];
192 int result = read(buffer, 0, 1);
193 return result == -1 ? -1 : buffer[0] & 0xff;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 }
195
196 @Override
197 public int read(byte[] buffer, int offset, int count) throws IOException {
198 if (mRemaining >= 0) {
199 if (mRemaining == 0) return -1;
200 if (count > mRemaining) count = (int)mRemaining;
201 int res = super.read(buffer, offset, count);
202 if (res >= 0) mRemaining -= res;
203 return res;
204 }
205
206 return super.read(buffer, offset, count);
207 }
208
209 @Override
210 public int read(byte[] buffer) throws IOException {
Bjorn Bringert21279932010-12-02 11:34:28 +0000211 return read(buffer, 0, buffer.length);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 }
213
214 @Override
215 public long skip(long count) throws IOException {
216 if (mRemaining >= 0) {
217 if (mRemaining == 0) return -1;
218 if (count > mRemaining) count = mRemaining;
219 long res = super.skip(count);
220 if (res >= 0) mRemaining -= res;
221 return res;
222 }
223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 return super.skip(count);
225 }
226
227 @Override
228 public void mark(int readlimit) {
229 if (mRemaining >= 0) {
230 // Not supported.
231 return;
232 }
233 super.mark(readlimit);
234 }
235
236 @Override
237 public boolean markSupported() {
238 if (mRemaining >= 0) {
239 return false;
240 }
241 return super.markSupported();
242 }
243
244 @Override
245 public synchronized void reset() throws IOException {
246 if (mRemaining >= 0) {
247 // Not supported.
248 return;
249 }
250 super.reset();
251 }
252 }
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100253
254 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 * An OutputStream you can create on a ParcelFileDescriptor, which will
256 * take care of calling {@link ParcelFileDescriptor#close
257 * ParcelFileDescritor.close()} for you when the stream is closed.
258 */
259 public static class AutoCloseOutputStream
260 extends ParcelFileDescriptor.AutoCloseOutputStream {
261 private long mRemaining;
262
263 public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
264 super(fd.getParcelFileDescriptor());
265 if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
266 throw new IOException("Unable to seek");
267 }
268 mRemaining = (int)fd.getLength();
269 }
270
271 @Override
272 public void write(byte[] buffer, int offset, int count) throws IOException {
273 if (mRemaining >= 0) {
274 if (mRemaining == 0) return;
275 if (count > mRemaining) count = (int)mRemaining;
276 super.write(buffer, offset, count);
277 mRemaining -= count;
278 return;
279 }
280
281 super.write(buffer, offset, count);
282 }
283
284 @Override
285 public void write(byte[] buffer) throws IOException {
286 if (mRemaining >= 0) {
287 if (mRemaining == 0) return;
288 int count = buffer.length;
289 if (count > mRemaining) count = (int)mRemaining;
290 super.write(buffer);
291 mRemaining -= count;
292 return;
293 }
294
295 super.write(buffer);
296 }
297
298 @Override
299 public void write(int oneByte) throws IOException {
300 if (mRemaining >= 0) {
301 if (mRemaining == 0) return;
302 super.write(oneByte);
303 mRemaining--;
304 return;
305 }
306
307 super.write(oneByte);
308 }
309 }
310
311
312 /* Parcelable interface */
313 public int describeContents() {
314 return mFd.describeContents();
315 }
316
317 public void writeToParcel(Parcel out, int flags) {
318 mFd.writeToParcel(out, flags);
319 out.writeLong(mStartOffset);
320 out.writeLong(mLength);
321 }
322
323 AssetFileDescriptor(Parcel src) {
324 mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
325 mStartOffset = src.readLong();
326 mLength = src.readLong();
327 }
328
329 public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
330 = new Parcelable.Creator<AssetFileDescriptor>() {
331 public AssetFileDescriptor createFromParcel(Parcel in) {
332 return new AssetFileDescriptor(in);
333 }
334 public AssetFileDescriptor[] newArray(int size) {
335 return new AssetFileDescriptor[size];
336 }
337 };
Bjorn Bringert963cd0062009-05-29 14:05:12 +0100338
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339}