AssetFileDescriptor.java revision 32559e191fd2580393d77161a32bcaeaa49fbe5d
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.Parcel;
20import android.os.ParcelFileDescriptor;
21import android.os.Parcelable;
22
23import java.io.Closeable;
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, Closeable {
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 (fd == null) {
57            throw new IllegalArgumentException("fd must not be null");
58        }
59        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     */
126    @Override
127    public void close() throws IOException {
128        mFd.close();
129    }
130
131    /**
132     * 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 {
191            byte[] buffer = new byte[1];
192            int result = read(buffer, 0, 1);
193            return result == -1 ? -1 : buffer[0] & 0xff;
194        }
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 {
211            return read(buffer, 0, buffer.length);
212        }
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
224            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    }
253
254    /**
255     * 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    };
338
339}
340