AssetFileDescriptor.java revision 3dec7d563a2f3e1eb967ce2054a00b6620e3558c
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.FileDescriptor;
24import java.io.FileInputStream;
25import java.io.FileOutputStream;
26import 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 */
33public 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     */
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     */
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     */
77    public FileDescriptor getFileDescriptor() {
78        return mFd.getFileDescriptor();
79    }
80
81    /**
82     * Returns the byte offset where this asset entry's data starts.
83     */
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     */
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     */
115    public long getDeclaredLength() {
116        return mLength;
117    }
118
119    /**
120     * Convenience for calling <code>getParcelFileDescriptor().close()</code>.
121     */
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     */
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     */
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
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
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
178        public int available() throws IOException {
179            return mRemaining >= 0
180                    ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
181                    : super.available();
182        }
183
184        @Override
185        public int read() throws IOException {
186            if (mRemaining >= 0) {
187                if (mRemaining == 0) return -1;
188                int res = super.read();
189                if (res >= 0) mRemaining--;
190                return res;
191            }
192
193            return super.read();
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            if (mRemaining >= 0) {
212                if (mRemaining == 0) return -1;
213                int count = buffer.length;
214                if (count > mRemaining) count = (int)mRemaining;
215                int res = super.read(buffer, 0, count);
216                if (res >= 0) mRemaining -= res;
217                return res;
218            }
219
220            return super.read(buffer);
221        }
222
223        @Override
224        public long skip(long count) throws IOException {
225            if (mRemaining >= 0) {
226                if (mRemaining == 0) return -1;
227                if (count > mRemaining) count = mRemaining;
228                long res = super.skip(count);
229                if (res >= 0) mRemaining -= res;
230                return res;
231            }
232
233            // TODO Auto-generated method stub
234            return super.skip(count);
235        }
236
237        @Override
238        public void mark(int readlimit) {
239            if (mRemaining >= 0) {
240                // Not supported.
241                return;
242            }
243            super.mark(readlimit);
244        }
245
246        @Override
247        public boolean markSupported() {
248            if (mRemaining >= 0) {
249                return false;
250            }
251            return super.markSupported();
252        }
253
254        @Override
255        public synchronized void reset() throws IOException {
256            if (mRemaining >= 0) {
257                // Not supported.
258                return;
259            }
260            super.reset();
261        }
262    }
263
264    /**
265     * An OutputStream you can create on a ParcelFileDescriptor, which will
266     * take care of calling {@link ParcelFileDescriptor#close
267     * ParcelFileDescritor.close()} for you when the stream is closed.
268     */
269    public static class AutoCloseOutputStream
270            extends ParcelFileDescriptor.AutoCloseOutputStream {
271        private long mRemaining;
272
273        public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
274            super(fd.getParcelFileDescriptor());
275            if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
276                throw new IOException("Unable to seek");
277            }
278            mRemaining = (int)fd.getLength();
279        }
280
281        @Override
282        public void write(byte[] buffer, int offset, int count) throws IOException {
283            if (mRemaining >= 0) {
284                if (mRemaining == 0) return;
285                if (count > mRemaining) count = (int)mRemaining;
286                super.write(buffer, offset, count);
287                mRemaining -= count;
288                return;
289            }
290
291            super.write(buffer, offset, count);
292        }
293
294        @Override
295        public void write(byte[] buffer) throws IOException {
296            if (mRemaining >= 0) {
297                if (mRemaining == 0) return;
298                int count = buffer.length;
299                if (count > mRemaining) count = (int)mRemaining;
300                super.write(buffer);
301                mRemaining -= count;
302                return;
303            }
304
305            super.write(buffer);
306        }
307
308        @Override
309        public void write(int oneByte) throws IOException {
310            if (mRemaining >= 0) {
311                if (mRemaining == 0) return;
312                super.write(oneByte);
313                mRemaining--;
314                return;
315            }
316
317            super.write(oneByte);
318        }
319    }
320
321
322    /* Parcelable interface */
323    public int describeContents() {
324        return mFd.describeContents();
325    }
326
327    public void writeToParcel(Parcel out, int flags) {
328        mFd.writeToParcel(out, flags);
329        out.writeLong(mStartOffset);
330        out.writeLong(mLength);
331    }
332
333    AssetFileDescriptor(Parcel src) {
334        mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
335        mStartOffset = src.readLong();
336        mLength = src.readLong();
337    }
338
339    public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
340            = new Parcelable.Creator<AssetFileDescriptor>() {
341        public AssetFileDescriptor createFromParcel(Parcel in) {
342            return new AssetFileDescriptor(in);
343        }
344        public AssetFileDescriptor[] newArray(int size) {
345            return new AssetFileDescriptor[size];
346        }
347    };
348}
349