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