• 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 static android.system.OsConstants.S_ISFIFO;
20 import static android.system.OsConstants.S_ISSOCK;
21 
22 import android.compat.annotation.UnsupportedAppUsage;
23 import android.os.Bundle;
24 import android.os.Parcel;
25 import android.os.ParcelFileDescriptor;
26 import android.os.Parcelable;
27 import android.system.ErrnoException;
28 import android.system.Os;
29 import android.system.StructStat;
30 
31 import java.io.Closeable;
32 import java.io.FileDescriptor;
33 import java.io.FileInputStream;
34 import java.io.FileOutputStream;
35 import java.io.IOException;
36 import java.nio.ByteBuffer;
37 import java.nio.MappedByteBuffer;
38 import java.nio.channels.FileChannel;
39 import java.nio.channels.FileLock;
40 import java.nio.channels.ReadableByteChannel;
41 import java.nio.channels.WritableByteChannel;
42 
43 /**
44  * File descriptor of an entry in the AssetManager.  This provides your own
45  * opened FileDescriptor that can be used to read the data, as well as the
46  * offset and length of that entry's data in the file.
47  */
48 public class AssetFileDescriptor implements Parcelable, Closeable {
49     /**
50      * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)}
51      * and {@link #getDeclaredLength} when a length has not been declared.  This means
52      * the data extends to the end of the file.
53      */
54     public static final long UNKNOWN_LENGTH = -1;
55 
56     @UnsupportedAppUsage
57     private final ParcelFileDescriptor mFd;
58     @UnsupportedAppUsage
59     private final long mStartOffset;
60     @UnsupportedAppUsage
61     private final long mLength;
62     private final Bundle mExtras;
63 
64     /**
65      * Create a new AssetFileDescriptor from the given values.
66      *
67      * @param fd The underlying file descriptor.
68      * @param startOffset The location within the file that the asset starts.
69      *            This must be 0 if length is UNKNOWN_LENGTH.
70      * @param length The number of bytes of the asset, or
71      *            {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
72      */
AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length)73     public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
74             long length) {
75         this(fd, startOffset, length, null);
76     }
77 
78     /**
79      * Create a new AssetFileDescriptor from the given values.
80      *
81      * @param fd The underlying file descriptor.
82      * @param startOffset The location within the file that the asset starts.
83      *            This must be 0 if length is UNKNOWN_LENGTH.
84      * @param length The number of bytes of the asset, or
85      *            {@link #UNKNOWN_LENGTH} if it extends to the end of the file.
86      * @param extras additional details that can be used to interpret the
87      *            underlying file descriptor. May be null.
88      */
AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset, long length, Bundle extras)89     public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
90             long length, Bundle extras) {
91         if (fd == null) {
92             throw new IllegalArgumentException("fd must not be null");
93         }
94         if (length < 0 && startOffset != 0) {
95             throw new IllegalArgumentException(
96                     "startOffset must be 0 when using UNKNOWN_LENGTH");
97         }
98         mFd = fd;
99         mStartOffset = startOffset;
100         mLength = length;
101         mExtras = extras;
102     }
103 
104     /**
105      * The AssetFileDescriptor contains its own ParcelFileDescriptor, which
106      * in addition to the normal FileDescriptor object also allows you to close
107      * the descriptor when you are done with it.
108      */
getParcelFileDescriptor()109     public ParcelFileDescriptor getParcelFileDescriptor() {
110         return mFd;
111     }
112 
113     /**
114      * Returns the FileDescriptor that can be used to read the data in the
115      * file.
116      */
getFileDescriptor()117     public FileDescriptor getFileDescriptor() {
118         return mFd.getFileDescriptor();
119     }
120 
121     /**
122      * Returns the byte offset where this asset entry's data starts.
123      */
getStartOffset()124     public long getStartOffset() {
125         return mStartOffset;
126     }
127 
128     /**
129      * Returns any additional details that can be used to interpret the
130      * underlying file descriptor. May be null.
131      */
getExtras()132     public Bundle getExtras() {
133         return mExtras;
134     }
135 
136     /**
137      * Returns the total number of bytes of this asset entry's data.  May be
138      * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file.
139      * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH},
140      * this will use {@link ParcelFileDescriptor#getStatSize()
141      * ParcelFileDescriptor.getStatSize()} to find the total size of the file,
142      * returning that number if found or {@link #UNKNOWN_LENGTH} if it could
143      * not be determined.
144      *
145      * @see #getDeclaredLength()
146      */
getLength()147     public long getLength() {
148         if (mLength >= 0) {
149             return mLength;
150         }
151         long len = mFd.getStatSize();
152         return len >= 0 ? len : UNKNOWN_LENGTH;
153     }
154 
155     /**
156      * Return the actual number of bytes that were declared when the
157      * AssetFileDescriptor was constructed.  Will be
158      * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data
159      * should be read to the end of the file.
160      *
161      * @see #getDeclaredLength()
162      */
getDeclaredLength()163     public long getDeclaredLength() {
164         return mLength;
165     }
166 
167     /**
168      * Convenience for calling <code>getParcelFileDescriptor().close()</code>.
169      */
170     @Override
close()171     public void close() throws IOException {
172         mFd.close();
173     }
174 
175     /**
176      * Create and return a new auto-close input stream for this asset.  This
177      * will either return a full asset {@link AutoCloseInputStream}, or
178      * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream
179      * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the
180      * the object represents a complete file or sub-section of a file.  You
181      * should only call this once for a particular asset.
182      */
createInputStream()183     public FileInputStream createInputStream() throws IOException {
184         if (mLength < 0) {
185             return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
186         }
187         return new AutoCloseInputStream(this);
188     }
189 
190     /**
191      * Create and return a new auto-close output stream for this asset.  This
192      * will either return a full asset {@link AutoCloseOutputStream}, or
193      * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream
194      * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the
195      * the object represents a complete file or sub-section of a file.  You
196      * should only call this once for a particular asset.
197      */
createOutputStream()198     public FileOutputStream createOutputStream() throws IOException {
199         if (mLength < 0) {
200             return new ParcelFileDescriptor.AutoCloseOutputStream(mFd);
201         }
202         return new AutoCloseOutputStream(this);
203     }
204 
205     @Override
toString()206     public String toString() {
207         return "{AssetFileDescriptor: " + mFd
208                 + " start=" + mStartOffset + " len=" + mLength + "}";
209     }
210 
211     /**
212      * An InputStream you can create on a ParcelFileDescriptor, which will
213      * take care of calling {@link ParcelFileDescriptor#close
214      * ParcelFileDescriptor.close()} for you when the stream is closed.
215      * It has a ParcelFileDescriptor.AutoCloseInputStream member to make delegate calls
216      * and during definition it will create seekable or non seekable child object
217      * AssetFileDescriptor.AutoCloseInputStream depends on the type of file descriptor
218      * to provide different solution.
219      */
220     public static class AutoCloseInputStream
221             extends ParcelFileDescriptor.AutoCloseInputStream {
222         private ParcelFileDescriptor.AutoCloseInputStream mDelegateInputStream;
223 
AutoCloseInputStream(AssetFileDescriptor fd)224         public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
225             super(fd.getParcelFileDescriptor());
226             StructStat ss;
227             try {
228                 ss = Os.fstat(fd.getParcelFileDescriptor().getFileDescriptor());
229             } catch (ErrnoException e) {
230                 throw new IOException(e);
231             }
232             if (S_ISSOCK(ss.st_mode) || S_ISFIFO(ss.st_mode)) {
233                 mDelegateInputStream = new NonSeekableAutoCloseInputStream(fd);
234             } else {
235                 mDelegateInputStream = new SeekableAutoCloseInputStream(fd);
236             }
237         }
238 
239         @Override
available()240         public int available() throws IOException {
241             return mDelegateInputStream.available();
242         }
243 
244         @Override
read()245         public int read() throws IOException {
246             return mDelegateInputStream.read();
247         }
248 
249         @Override
read(byte[] buffer, int offset, int count)250         public int read(byte[] buffer, int offset, int count) throws IOException {
251             return mDelegateInputStream.read(buffer, offset, count);
252         }
253 
254         @Override
read(byte[] buffer)255         public int read(byte[] buffer) throws IOException {
256             return mDelegateInputStream.read(buffer);
257         }
258 
259         @Override
skip(long count)260         public long skip(long count) throws IOException {
261             return mDelegateInputStream.skip(count);
262         }
263 
264         @Override
mark(int readlimit)265         public void mark(int readlimit) {
266             mDelegateInputStream.mark(readlimit);
267         }
268 
269         @Override
markSupported()270         public boolean markSupported() {
271             return mDelegateInputStream.markSupported();
272         }
273 
274         @Override
reset()275         public synchronized void reset() throws IOException {
276             mDelegateInputStream.reset();
277         }
278 
279         @Override
getChannel()280         public FileChannel getChannel() {
281             return mDelegateInputStream.getChannel();
282         }
283         @Override
close()284         public void close() throws IOException {
285             // Make the mDelegateInputStream own file descriptor and super.close()
286             // is not needed here to avoid double close the file descriptor.
287             mDelegateInputStream.close();
288         }
289     }
290 
291     /**
292      * An InputStream you can create on a non seekable file descriptor,
293      * like PIPE, SOCKET and FIFO, which will take care of calling
294      * {@link ParcelFileDescriptor#close ParcelFileDescriptor.close()}
295      * for you when the stream is closed.
296      */
297     private static class NonSeekableAutoCloseInputStream
298             extends ParcelFileDescriptor.AutoCloseInputStream {
299         private long mRemaining;
300 
NonSeekableAutoCloseInputStream(AssetFileDescriptor fd)301         NonSeekableAutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
302             super(fd.getParcelFileDescriptor());
303             super.skip(fd.getStartOffset());
304             mRemaining = (int) fd.getLength();
305         }
306 
307         @Override
available()308         public int available() throws IOException {
309             return mRemaining >= 0
310                     ? (mRemaining < 0x7fffffff ? (int) mRemaining : 0x7fffffff)
311                     : super.available();
312         }
313 
314         @Override
read()315         public int read() throws IOException {
316             byte[] buffer = new byte[1];
317             int result = read(buffer, 0, 1);
318             return result == -1 ? -1 : buffer[0] & 0xff;
319         }
320 
321         @Override
read(byte[] buffer, int offset, int count)322         public int read(byte[] buffer, int offset, int count) throws IOException {
323             if (mRemaining >= 0) {
324                 if (mRemaining == 0) return -1;
325                 if (count > mRemaining) count = (int) mRemaining;
326                 int res = super.read(buffer, offset, count);
327                 if (res >= 0) mRemaining -= res;
328                 return res;
329             }
330 
331             return super.read(buffer, offset, count);
332         }
333 
334         @Override
read(byte[] buffer)335         public int read(byte[] buffer) throws IOException {
336             return read(buffer, 0, buffer.length);
337         }
338 
339         @Override
skip(long count)340         public long skip(long count) throws IOException {
341             if (mRemaining >= 0) {
342                 if (mRemaining == 0) return -1;
343                 if (count > mRemaining) count = mRemaining;
344                 long res = super.skip(count);
345                 if (res >= 0) mRemaining -= res;
346                 return res;
347             }
348 
349             return super.skip(count);
350         }
351 
352         @Override
mark(int readlimit)353         public void mark(int readlimit) {
354             if (mRemaining >= 0) {
355                 // Not supported.
356                 return;
357             }
358             super.mark(readlimit);
359         }
360 
361         @Override
markSupported()362         public boolean markSupported() {
363             if (mRemaining >= 0) {
364                 return false;
365             }
366             return super.markSupported();
367         }
368 
369         @Override
reset()370         public synchronized void reset() throws IOException {
371             if (mRemaining >= 0) {
372                 // Not supported.
373                 return;
374             }
375             super.reset();
376         }
377     }
378 
379     /**
380      * An InputStream you can create on a seekable file descriptor, which means
381      * you can use pread to read from a specific offset, this will take care of
382      * calling {@link ParcelFileDescriptor#close ParcelFileDescriptor.close()}
383      * for you when the stream is closed.
384      */
385     private static class SeekableAutoCloseInputStream
386             extends ParcelFileDescriptor.AutoCloseInputStream {
387         /** Size of current file. */
388         private long mTotalSize;
389         /** The absolute position of current file start point. */
390         private final long mFileOffset;
391         /** The relative position where input stream is against mFileOffset. */
392         private long mOffset;
393         private OffsetCorrectFileChannel mOffsetCorrectFileChannel;
394 
SeekableAutoCloseInputStream(AssetFileDescriptor fd)395         SeekableAutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
396             super(fd.getParcelFileDescriptor());
397             mTotalSize = fd.getLength();
398             mFileOffset = fd.getStartOffset();
399         }
400 
401         @Override
available()402         public int available() throws IOException {
403             long available = mTotalSize - mOffset;
404             return available >= 0
405                     ? (available < 0x7fffffff ? (int) available : 0x7fffffff)
406                     : 0;
407         }
408 
409         @Override
read()410         public int read() throws IOException {
411             byte[] buffer = new byte[1];
412             int result = read(buffer, 0, 1);
413             return result == -1 ? -1 : buffer[0] & 0xff;
414         }
415 
416         @Override
read(byte[] buffer, int offset, int count)417         public int read(byte[] buffer, int offset, int count) throws IOException {
418             int available = available();
419             if (available <= 0) {
420                 return -1;
421             }
422             if (count == 0) {
423                 // Java's InputStream explicitly specifies that this returns zero.
424                 return 0;
425             }
426 
427             if (count > available) count = available;
428             try {
429                 int res = Os.pread(getFD(), buffer, offset, count, mFileOffset + mOffset);
430                 // pread returns 0 at end of file, while java's InputStream interface requires -1
431                 if (res == 0) res = -1;
432                 if (res > 0) {
433                     mOffset += res;
434                     updateChannelPosition(mOffset + mFileOffset);
435                 }
436                 return res;
437             } catch (ErrnoException e) {
438                 throw new IOException(e);
439             }
440         }
441 
442         @Override
read(byte[] buffer)443         public int read(byte[] buffer) throws IOException {
444             return read(buffer, 0, buffer.length);
445         }
446 
447         @Override
skip(long count)448         public long skip(long count) throws IOException {
449             int available = available();
450             if (available <= 0) {
451                 return -1;
452             }
453 
454             if (count > available) count = available;
455             mOffset += count;
456             updateChannelPosition(mOffset + mFileOffset);
457             return count;
458         }
459 
460         @Override
mark(int readlimit)461         public void mark(int readlimit) {
462             // Not supported.
463             return;
464         }
465 
466         @Override
markSupported()467         public boolean markSupported() {
468             return false;
469         }
470 
471         @Override
reset()472         public synchronized void reset() throws IOException {
473             // Not supported.
474             return;
475         }
476 
477         @Override
getChannel()478         public FileChannel getChannel() {
479             if (mOffsetCorrectFileChannel == null) {
480                 mOffsetCorrectFileChannel = new OffsetCorrectFileChannel(super.getChannel());
481             }
482             try {
483                 updateChannelPosition(mOffset + mFileOffset);
484             } catch (IOException e) {
485                 throw new RuntimeException(e);
486             }
487             return mOffsetCorrectFileChannel;
488         }
489 
490         /**
491          * Update the position of mOffsetCorrectFileChannel only after it is constructed.
492          *
493          * @param newPosition The absolute position mOffsetCorrectFileChannel needs to be moved to.
494          */
updateChannelPosition(long newPosition)495         private void updateChannelPosition(long newPosition) throws IOException {
496             if (mOffsetCorrectFileChannel != null) {
497                 mOffsetCorrectFileChannel.position(newPosition);
498             }
499         }
500 
501         /**
502          * A FileChannel wrapper that will update mOffset of the AutoCloseInputStream
503          * to correct position when using FileChannel to read. All occurrence of position
504          * should be using absolute solution and each override method just do Delegation
505          * besides additional check. All methods related to write mode have been disabled
506          * and will throw UnsupportedOperationException with customized message.
507          */
508         private class OffsetCorrectFileChannel extends FileChannel {
509             private final FileChannel mDelegate;
510             private static final String METHOD_NOT_SUPPORTED_MESSAGE =
511                     "This Method is not supported in AutoCloseInputStream FileChannel.";
512 
OffsetCorrectFileChannel(FileChannel fc)513             OffsetCorrectFileChannel(FileChannel fc) {
514                 mDelegate = fc;
515             }
516 
517             @Override
read(ByteBuffer dst)518             public int read(ByteBuffer dst) throws IOException {
519                 if (available() <= 0) return -1;
520                 int bytesRead = mDelegate.read(dst);
521                 if (bytesRead != -1) mOffset += bytesRead;
522                 return bytesRead;
523             }
524 
525             @Override
read(ByteBuffer[] dsts, int offset, int length)526             public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
527                 if (available() <= 0) return -1;
528                 if (mOffset + length > mTotalSize) {
529                     length = (int) (mTotalSize - mOffset);
530                 }
531                 long bytesRead = mDelegate.read(dsts, offset, length);
532                 if (bytesRead != -1) mOffset += bytesRead;
533                 return bytesRead;
534             }
535 
536             @Override
537             /**The only read method that does not move channel position*/
read(ByteBuffer dst, long position)538             public int read(ByteBuffer dst, long position) throws IOException {
539                 if (position - mFileOffset > mTotalSize) return -1;
540                 return mDelegate.read(dst, position);
541             }
542 
543             @Override
position()544             public long position() throws IOException {
545                 return mDelegate.position();
546             }
547 
548             @Override
position(long newPosition)549             public FileChannel position(long newPosition) throws IOException {
550                 mOffset = newPosition - mFileOffset;
551                 return mDelegate.position(newPosition);
552             }
553 
554             @Override
size()555             public long size() throws IOException {
556                 return mTotalSize;
557             }
558 
559             @Override
transferTo(long position, long count, WritableByteChannel target)560             public long transferTo(long position, long count, WritableByteChannel target)
561                     throws IOException {
562                 if (position - mFileOffset > mTotalSize) {
563                     return 0;
564                 }
565                 if (position - mFileOffset + count > mTotalSize) {
566                     count = mTotalSize - (position - mFileOffset);
567                 }
568                 return mDelegate.transferTo(position, count, target);
569             }
570 
571             @Override
map(MapMode mode, long position, long size)572             public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
573                 if (position - mFileOffset > mTotalSize) {
574                     throw new IOException(
575                             "Cannot map to buffer because position exceed current file size.");
576                 }
577                 if (position - mFileOffset + size > mTotalSize) {
578                     size = mTotalSize - (position - mFileOffset);
579                 }
580                 return mDelegate.map(mode, position, size);
581             }
582 
583             @Override
implCloseChannel()584             protected void implCloseChannel() throws IOException {
585                 mDelegate.close();
586             }
587 
588             @Override
write(ByteBuffer src)589             public int write(ByteBuffer src) throws IOException {
590                 throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
591             }
592 
593             @Override
write(ByteBuffer[] srcs, int offset, int length)594             public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
595                 throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
596             }
597 
598             @Override
write(ByteBuffer src, long position)599             public int write(ByteBuffer src, long position) throws IOException {
600                 throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
601             }
602 
603             @Override
transferFrom(ReadableByteChannel src, long position, long count)604             public long transferFrom(ReadableByteChannel src, long position, long count)
605                     throws IOException {
606                 throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
607             }
608 
609             @Override
truncate(long size)610             public FileChannel truncate(long size) throws IOException {
611                 throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
612             }
613 
614             @Override
force(boolean metaData)615             public void force(boolean metaData) throws IOException {
616                 throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
617             }
618 
619             @Override
lock(long position, long size, boolean shared)620             public FileLock lock(long position, long size, boolean shared) throws IOException {
621                 throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
622             }
623 
624             @Override
tryLock(long position, long size, boolean shared)625             public FileLock tryLock(long position, long size, boolean shared) throws IOException {
626                 throw new UnsupportedOperationException(METHOD_NOT_SUPPORTED_MESSAGE);
627             }
628         }
629     }
630 
631     /**
632      * An OutputStream you can create on a ParcelFileDescriptor, which will
633      * take care of calling {@link ParcelFileDescriptor#close
634      * ParcelFileDescriptor.close()} for you when the stream is closed.
635      */
636     public static class AutoCloseOutputStream
637             extends ParcelFileDescriptor.AutoCloseOutputStream {
638         private long mRemaining;
639 
AutoCloseOutputStream(AssetFileDescriptor fd)640         public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
641             super(fd.getParcelFileDescriptor());
642             if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
643                 throw new IOException("Unable to seek");
644             }
645             mRemaining = (int) fd.getLength();
646         }
647 
648         @Override
write(byte[] buffer, int offset, int count)649         public void write(byte[] buffer, int offset, int count) throws IOException {
650             if (mRemaining >= 0) {
651                 if (mRemaining == 0) return;
652                 if (count > mRemaining) count = (int) mRemaining;
653                 super.write(buffer, offset, count);
654                 mRemaining -= count;
655                 return;
656             }
657 
658             super.write(buffer, offset, count);
659         }
660 
661         @Override
write(byte[] buffer)662         public void write(byte[] buffer) throws IOException {
663             if (mRemaining >= 0) {
664                 if (mRemaining == 0) return;
665                 int count = buffer.length;
666                 if (count > mRemaining) count = (int) mRemaining;
667                 super.write(buffer);
668                 mRemaining -= count;
669                 return;
670             }
671 
672             super.write(buffer);
673         }
674 
675         @Override
write(int oneByte)676         public void write(int oneByte) throws IOException {
677             if (mRemaining >= 0) {
678                 if (mRemaining == 0) return;
679                 super.write(oneByte);
680                 mRemaining--;
681                 return;
682             }
683 
684             super.write(oneByte);
685         }
686     }
687 
688     /* Parcelable interface */
689     @Override
describeContents()690     public int describeContents() {
691         return mFd.describeContents();
692     }
693 
694     @Override
writeToParcel(Parcel out, int flags)695     public void writeToParcel(Parcel out, int flags) {
696         mFd.writeToParcel(out, flags);
697         out.writeLong(mStartOffset);
698         out.writeLong(mLength);
699         if (mExtras != null) {
700             out.writeInt(1);
701             out.writeBundle(mExtras);
702         } else {
703             out.writeInt(0);
704         }
705     }
706 
AssetFileDescriptor(Parcel src)707     AssetFileDescriptor(Parcel src) {
708         mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
709         mStartOffset = src.readLong();
710         mLength = src.readLong();
711         if (src.readInt() != 0) {
712             mExtras = src.readBundle();
713         } else {
714             mExtras = null;
715         }
716     }
717 
718     public static final @android.annotation.NonNull Parcelable.Creator<AssetFileDescriptor> CREATOR
719             = new Parcelable.Creator<AssetFileDescriptor>() {
720         public AssetFileDescriptor createFromParcel(Parcel in) {
721             return new AssetFileDescriptor(in);
722         }
723         public AssetFileDescriptor[] newArray(int size) {
724             return new AssetFileDescriptor[size];
725         }
726     };
727 
728 }
729