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