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