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