ParcelFileDescriptor.java revision b433bb8c96f98d280f4a8508ba500bd8f196a773
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.os; 18 19import static libcore.io.OsConstants.AF_UNIX; 20import static libcore.io.OsConstants.SEEK_SET; 21import static libcore.io.OsConstants.SOCK_STREAM; 22import static libcore.io.OsConstants.S_ISLNK; 23import static libcore.io.OsConstants.S_ISREG; 24 25import android.content.BroadcastReceiver; 26import android.content.ContentProvider; 27import android.util.Log; 28 29import dalvik.system.CloseGuard; 30 31import libcore.io.ErrnoException; 32import libcore.io.IoUtils; 33import libcore.io.Libcore; 34import libcore.io.Memory; 35import libcore.io.OsConstants; 36import libcore.io.StructStat; 37 38import java.io.Closeable; 39import java.io.File; 40import java.io.FileDescriptor; 41import java.io.FileInputStream; 42import java.io.FileNotFoundException; 43import java.io.FileOutputStream; 44import java.io.IOException; 45import java.net.DatagramSocket; 46import java.net.Socket; 47import java.nio.ByteOrder; 48 49/** 50 * The FileDescriptor returned by {@link Parcel#readFileDescriptor}, allowing 51 * you to close it when done with it. 52 */ 53public class ParcelFileDescriptor implements Parcelable, Closeable { 54 private static final String TAG = "ParcelFileDescriptor"; 55 56 private final FileDescriptor mFd; 57 58 /** 59 * Optional socket used to communicate close events, status at close, and 60 * detect remote process crashes. 61 */ 62 private FileDescriptor mCommFd; 63 64 /** 65 * Wrapped {@link ParcelFileDescriptor}, if any. Used to avoid 66 * double-closing {@link #mFd}. 67 */ 68 private final ParcelFileDescriptor mWrapped; 69 70 /** 71 * Maximum {@link #mStatusBuf} size; longer status messages will be 72 * truncated. 73 */ 74 private static final int MAX_STATUS = 1024; 75 76 /** 77 * Temporary buffer used by {@link #readCommStatus(FileDescriptor, byte[])}, 78 * allocated on-demand. 79 */ 80 private byte[] mStatusBuf; 81 82 /** 83 * Status read by {@link #checkError()}, or null if not read yet. 84 */ 85 private Status mStatus; 86 87 private volatile boolean mClosed; 88 89 private final CloseGuard mGuard = CloseGuard.get(); 90 91 /** 92 * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and 93 * this file doesn't already exist, then create the file with permissions 94 * such that any application can read it. 95 * 96 * @deprecated Creating world-readable files is very dangerous, and likely 97 * to cause security holes in applications. It is strongly 98 * discouraged; instead, applications should use more formal 99 * mechanism for interactions such as {@link ContentProvider}, 100 * {@link BroadcastReceiver}, and {@link android.app.Service}. 101 * There are no guarantees that this access mode will remain on 102 * a file, such as when it goes through a backup and restore. 103 */ 104 @Deprecated 105 public static final int MODE_WORLD_READABLE = 0x00000001; 106 107 /** 108 * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and 109 * this file doesn't already exist, then create the file with permissions 110 * such that any application can write it. 111 * 112 * @deprecated Creating world-writable files is very dangerous, and likely 113 * to cause security holes in applications. It is strongly 114 * discouraged; instead, applications should use more formal 115 * mechanism for interactions such as {@link ContentProvider}, 116 * {@link BroadcastReceiver}, and {@link android.app.Service}. 117 * There are no guarantees that this access mode will remain on 118 * a file, such as when it goes through a backup and restore. 119 */ 120 @Deprecated 121 public static final int MODE_WORLD_WRITEABLE = 0x00000002; 122 123 /** 124 * For use with {@link #open}: open the file with read-only access. 125 */ 126 public static final int MODE_READ_ONLY = 0x10000000; 127 128 /** 129 * For use with {@link #open}: open the file with write-only access. 130 */ 131 public static final int MODE_WRITE_ONLY = 0x20000000; 132 133 /** 134 * For use with {@link #open}: open the file with read and write access. 135 */ 136 public static final int MODE_READ_WRITE = 0x30000000; 137 138 /** 139 * For use with {@link #open}: create the file if it doesn't already exist. 140 */ 141 public static final int MODE_CREATE = 0x08000000; 142 143 /** 144 * For use with {@link #open}: erase contents of file when opening. 145 */ 146 public static final int MODE_TRUNCATE = 0x04000000; 147 148 /** 149 * For use with {@link #open}: append to end of file while writing. 150 */ 151 public static final int MODE_APPEND = 0x02000000; 152 153 /** 154 * Create a new ParcelFileDescriptor wrapped around another descriptor. By 155 * default all method calls are delegated to the wrapped descriptor. 156 */ 157 public ParcelFileDescriptor(ParcelFileDescriptor wrapped) { 158 // We keep a strong reference to the wrapped PFD, and rely on its 159 // finalizer to trigger CloseGuard. All calls are delegated to wrapper. 160 mWrapped = wrapped; 161 mFd = null; 162 mCommFd = null; 163 mClosed = true; 164 } 165 166 /** {@hide} */ 167 public ParcelFileDescriptor(FileDescriptor fd) { 168 this(fd, null); 169 } 170 171 /** {@hide} */ 172 public ParcelFileDescriptor(FileDescriptor fd, FileDescriptor commChannel) { 173 if (fd == null) { 174 throw new NullPointerException("FileDescriptor must not be null"); 175 } 176 mWrapped = null; 177 mFd = fd; 178 mCommFd = commChannel; 179 mGuard.open("close"); 180 } 181 182 /** 183 * Create a new ParcelFileDescriptor accessing a given file. 184 * 185 * @param file The file to be opened. 186 * @param mode The desired access mode, must be one of 187 * {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or 188 * {@link #MODE_READ_WRITE}; may also be any combination of 189 * {@link #MODE_CREATE}, {@link #MODE_TRUNCATE}, 190 * {@link #MODE_WORLD_READABLE}, and 191 * {@link #MODE_WORLD_WRITEABLE}. 192 * @return a new ParcelFileDescriptor pointing to the given file. 193 * @throws FileNotFoundException if the given file does not exist or can not 194 * be opened with the requested mode. 195 */ 196 public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException { 197 final FileDescriptor fd = openInternal(file, mode); 198 if (fd == null) return null; 199 200 return new ParcelFileDescriptor(fd); 201 } 202 203 /** 204 * Create a new ParcelFileDescriptor accessing a given file. 205 * 206 * @param file The file to be opened. 207 * @param mode The desired access mode, must be one of 208 * {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or 209 * {@link #MODE_READ_WRITE}; may also be any combination of 210 * {@link #MODE_CREATE}, {@link #MODE_TRUNCATE}, 211 * {@link #MODE_WORLD_READABLE}, and 212 * {@link #MODE_WORLD_WRITEABLE}. 213 * @param handler to call listener from; must not be null. 214 * @param listener to be invoked when the returned descriptor has been 215 * closed; must not be null. 216 * @return a new ParcelFileDescriptor pointing to the given file. 217 * @throws FileNotFoundException if the given file does not exist or can not 218 * be opened with the requested mode. 219 */ 220 public static ParcelFileDescriptor open( 221 File file, int mode, Handler handler, OnCloseListener listener) throws IOException { 222 if (handler == null) { 223 throw new IllegalArgumentException("Handler must not be null"); 224 } 225 if (listener == null) { 226 throw new IllegalArgumentException("Listener must not be null"); 227 } 228 229 final FileDescriptor fd = openInternal(file, mode); 230 if (fd == null) return null; 231 232 final FileDescriptor[] comm = createCommSocketPair(true); 233 final ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd, comm[0]); 234 235 // Kick off thread to watch for status updates 236 final ListenerBridge bridge = new ListenerBridge(comm[1], handler.getLooper(), listener); 237 bridge.start(); 238 239 return pfd; 240 } 241 242 private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException { 243 if ((mode & MODE_READ_WRITE) == 0) { 244 throw new IllegalArgumentException( 245 "Must specify MODE_READ_ONLY, MODE_WRITE_ONLY, or MODE_READ_WRITE"); 246 } 247 248 final String path = file.getPath(); 249 return Parcel.openFileDescriptor(path, mode); 250 } 251 252 /** 253 * Create a new ParcelFileDescriptor that is a dup of an existing 254 * FileDescriptor. This obeys standard POSIX semantics, where the 255 * new file descriptor shared state such as file position with the 256 * original file descriptor. 257 */ 258 public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException { 259 try { 260 final FileDescriptor fd = Libcore.os.dup(orig); 261 return new ParcelFileDescriptor(fd); 262 } catch (ErrnoException e) { 263 throw e.rethrowAsIOException(); 264 } 265 } 266 267 /** 268 * Create a new ParcelFileDescriptor that is a dup of the existing 269 * FileDescriptor. This obeys standard POSIX semantics, where the 270 * new file descriptor shared state such as file position with the 271 * original file descriptor. 272 */ 273 public ParcelFileDescriptor dup() throws IOException { 274 if (mWrapped != null) { 275 return mWrapped.dup(); 276 } else { 277 return dup(getFileDescriptor()); 278 } 279 } 280 281 /** 282 * Create a new ParcelFileDescriptor from a raw native fd. The new 283 * ParcelFileDescriptor holds a dup of the original fd passed in here, 284 * so you must still close that fd as well as the new ParcelFileDescriptor. 285 * 286 * @param fd The native fd that the ParcelFileDescriptor should dup. 287 * 288 * @return Returns a new ParcelFileDescriptor holding a FileDescriptor 289 * for a dup of the given fd. 290 */ 291 public static ParcelFileDescriptor fromFd(int fd) throws IOException { 292 final FileDescriptor original = new FileDescriptor(); 293 original.setInt$(fd); 294 295 try { 296 final FileDescriptor dup = Libcore.os.dup(original); 297 return new ParcelFileDescriptor(dup); 298 } catch (ErrnoException e) { 299 throw e.rethrowAsIOException(); 300 } 301 } 302 303 /** 304 * Take ownership of a raw native fd in to a new ParcelFileDescriptor. 305 * The returned ParcelFileDescriptor now owns the given fd, and will be 306 * responsible for closing it. You must not close the fd yourself. 307 * 308 * @param fd The native fd that the ParcelFileDescriptor should adopt. 309 * 310 * @return Returns a new ParcelFileDescriptor holding a FileDescriptor 311 * for the given fd. 312 */ 313 public static ParcelFileDescriptor adoptFd(int fd) { 314 final FileDescriptor fdesc = new FileDescriptor(); 315 fdesc.setInt$(fd); 316 317 return new ParcelFileDescriptor(fdesc); 318 } 319 320 /** 321 * Create a new ParcelFileDescriptor from the specified Socket. The new 322 * ParcelFileDescriptor holds a dup of the original FileDescriptor in 323 * the Socket, so you must still close the Socket as well as the new 324 * ParcelFileDescriptor. 325 * 326 * @param socket The Socket whose FileDescriptor is used to create 327 * a new ParcelFileDescriptor. 328 * 329 * @return A new ParcelFileDescriptor with the FileDescriptor of the 330 * specified Socket. 331 */ 332 public static ParcelFileDescriptor fromSocket(Socket socket) { 333 FileDescriptor fd = socket.getFileDescriptor$(); 334 return fd != null ? new ParcelFileDescriptor(fd) : null; 335 } 336 337 /** 338 * Create a new ParcelFileDescriptor from the specified DatagramSocket. 339 * 340 * @param datagramSocket The DatagramSocket whose FileDescriptor is used 341 * to create a new ParcelFileDescriptor. 342 * 343 * @return A new ParcelFileDescriptor with the FileDescriptor of the 344 * specified DatagramSocket. 345 */ 346 public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) { 347 FileDescriptor fd = datagramSocket.getFileDescriptor$(); 348 return fd != null ? new ParcelFileDescriptor(fd) : null; 349 } 350 351 /** 352 * Create two ParcelFileDescriptors structured as a data pipe. The first 353 * ParcelFileDescriptor in the returned array is the read side; the second 354 * is the write side. 355 */ 356 public static ParcelFileDescriptor[] createPipe() throws IOException { 357 try { 358 final FileDescriptor[] fds = Libcore.os.pipe(); 359 return new ParcelFileDescriptor[] { 360 new ParcelFileDescriptor(fds[0]), 361 new ParcelFileDescriptor(fds[1]) }; 362 } catch (ErrnoException e) { 363 throw e.rethrowAsIOException(); 364 } 365 } 366 367 /** 368 * Create two ParcelFileDescriptors structured as a data pipe. The first 369 * ParcelFileDescriptor in the returned array is the read side; the second 370 * is the write side. 371 * <p> 372 * The write end has the ability to deliver an error message through 373 * {@link #closeWithError(String)} which can be handled by the read end 374 * calling {@link #checkError()}, usually after detecting an EOF. 375 * This can also be used to detect remote crashes. 376 */ 377 public static ParcelFileDescriptor[] createReliablePipe() throws IOException { 378 try { 379 final FileDescriptor[] comm = createCommSocketPair(false); 380 final FileDescriptor[] fds = Libcore.os.pipe(); 381 return new ParcelFileDescriptor[] { 382 new ParcelFileDescriptor(fds[0], comm[0]), 383 new ParcelFileDescriptor(fds[1], comm[1]) }; 384 } catch (ErrnoException e) { 385 throw e.rethrowAsIOException(); 386 } 387 } 388 389 /** 390 * Create two ParcelFileDescriptors structured as a pair of sockets 391 * connected to each other. The two sockets are indistinguishable. 392 */ 393 public static ParcelFileDescriptor[] createSocketPair() throws IOException { 394 try { 395 final FileDescriptor fd0 = new FileDescriptor(); 396 final FileDescriptor fd1 = new FileDescriptor(); 397 Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); 398 return new ParcelFileDescriptor[] { 399 new ParcelFileDescriptor(fd0), 400 new ParcelFileDescriptor(fd1) }; 401 } catch (ErrnoException e) { 402 throw e.rethrowAsIOException(); 403 } 404 } 405 406 /** 407 * Create two ParcelFileDescriptors structured as a pair of sockets 408 * connected to each other. The two sockets are indistinguishable. 409 * <p> 410 * Both ends have the ability to deliver an error message through 411 * {@link #closeWithError(String)} which can be detected by the other end 412 * calling {@link #checkError()}, usually after detecting an EOF. 413 * This can also be used to detect remote crashes. 414 */ 415 public static ParcelFileDescriptor[] createReliableSocketPair() throws IOException { 416 try { 417 final FileDescriptor[] comm = createCommSocketPair(false); 418 final FileDescriptor fd0 = new FileDescriptor(); 419 final FileDescriptor fd1 = new FileDescriptor(); 420 Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); 421 return new ParcelFileDescriptor[] { 422 new ParcelFileDescriptor(fd0, comm[0]), 423 new ParcelFileDescriptor(fd1, comm[1]) }; 424 } catch (ErrnoException e) { 425 throw e.rethrowAsIOException(); 426 } 427 } 428 429 private static FileDescriptor[] createCommSocketPair(boolean blocking) throws IOException { 430 try { 431 final FileDescriptor comm1 = new FileDescriptor(); 432 final FileDescriptor comm2 = new FileDescriptor(); 433 Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2); 434 IoUtils.setBlocking(comm1, blocking); 435 IoUtils.setBlocking(comm2, blocking); 436 return new FileDescriptor[] { comm1, comm2 }; 437 } catch (ErrnoException e) { 438 throw e.rethrowAsIOException(); 439 } 440 } 441 442 /** 443 * @hide Please use createPipe() or ContentProvider.openPipeHelper(). 444 * Gets a file descriptor for a read-only copy of the given data. 445 * 446 * @param data Data to copy. 447 * @param name Name for the shared memory area that may back the file descriptor. 448 * This is purely informative and may be {@code null}. 449 * @return A ParcelFileDescriptor. 450 * @throws IOException if there is an error while creating the shared memory area. 451 */ 452 @Deprecated 453 public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException { 454 if (data == null) return null; 455 MemoryFile file = new MemoryFile(name, data.length); 456 if (data.length > 0) { 457 file.writeBytes(data, 0, 0, data.length); 458 } 459 file.deactivate(); 460 FileDescriptor fd = file.getFileDescriptor(); 461 return fd != null ? new ParcelFileDescriptor(fd) : null; 462 } 463 464 /** 465 * Retrieve the actual FileDescriptor associated with this object. 466 * 467 * @return Returns the FileDescriptor associated with this object. 468 */ 469 public FileDescriptor getFileDescriptor() { 470 if (mWrapped != null) { 471 return mWrapped.getFileDescriptor(); 472 } else { 473 return mFd; 474 } 475 } 476 477 /** 478 * Return the total size of the file representing this fd, as determined by 479 * {@code stat()}. Returns -1 if the fd is not a file. 480 */ 481 public long getStatSize() { 482 if (mWrapped != null) { 483 return mWrapped.getStatSize(); 484 } else { 485 try { 486 final StructStat st = Libcore.os.fstat(mFd); 487 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { 488 return st.st_size; 489 } else { 490 return -1; 491 } 492 } catch (ErrnoException e) { 493 Log.w(TAG, "fstat() failed: " + e); 494 return -1; 495 } 496 } 497 } 498 499 /** 500 * This is needed for implementing AssetFileDescriptor.AutoCloseOutputStream, 501 * and I really don't think we want it to be public. 502 * @hide 503 */ 504 public long seekTo(long pos) throws IOException { 505 if (mWrapped != null) { 506 return mWrapped.seekTo(pos); 507 } else { 508 try { 509 return Libcore.os.lseek(mFd, pos, SEEK_SET); 510 } catch (ErrnoException e) { 511 throw e.rethrowAsIOException(); 512 } 513 } 514 } 515 516 /** 517 * Return the native fd int for this ParcelFileDescriptor. The 518 * ParcelFileDescriptor still owns the fd, and it still must be closed 519 * through this API. 520 */ 521 public int getFd() { 522 if (mWrapped != null) { 523 return mWrapped.getFd(); 524 } else { 525 if (mClosed) { 526 throw new IllegalStateException("Already closed"); 527 } 528 return mFd.getInt$(); 529 } 530 } 531 532 /** 533 * Return the native fd int for this ParcelFileDescriptor and detach it from 534 * the object here. You are now responsible for closing the fd in native 535 * code. 536 * <p> 537 * You should not detach when the original creator of the descriptor is 538 * expecting a reliable signal through {@link #close()} or 539 * {@link #closeWithError(String)}. 540 * 541 * @see #canDetectErrors() 542 */ 543 public int detachFd() { 544 if (mWrapped != null) { 545 return mWrapped.detachFd(); 546 } else { 547 if (mClosed) { 548 throw new IllegalStateException("Already closed"); 549 } 550 final int fd = getFd(); 551 Parcel.clearFileDescriptor(mFd); 552 writeCommStatusAndClose(Status.DETACHED, null); 553 return fd; 554 } 555 } 556 557 /** 558 * Close the ParcelFileDescriptor. This implementation closes the underlying 559 * OS resources allocated to represent this stream. 560 * 561 * @throws IOException 562 * If an error occurs attempting to close this ParcelFileDescriptor. 563 */ 564 @Override 565 public void close() throws IOException { 566 if (mWrapped != null) { 567 mWrapped.close(); 568 } else { 569 closeWithStatus(Status.OK, null); 570 } 571 } 572 573 /** 574 * Close the ParcelFileDescriptor, informing any peer that an error occurred 575 * while processing. If the creator of this descriptor is not observing 576 * errors, it will close normally. 577 * 578 * @param msg describing the error; must not be null. 579 */ 580 public void closeWithError(String msg) throws IOException { 581 if (mWrapped != null) { 582 mWrapped.closeWithError(msg); 583 } else { 584 if (msg == null) { 585 throw new IllegalArgumentException("Message must not be null"); 586 } 587 closeWithStatus(Status.ERROR, msg); 588 } 589 } 590 591 private void closeWithStatus(int status, String msg) throws IOException { 592 if (mWrapped != null) { 593 mWrapped.closeWithStatus(status, msg); 594 } else { 595 if (mClosed) return; 596 mClosed = true; 597 mGuard.close(); 598 // Status MUST be sent before closing actual descriptor 599 writeCommStatusAndClose(status, msg); 600 IoUtils.closeQuietly(mFd); 601 } 602 } 603 604 private byte[] getOrCreateStatusBuffer() { 605 if (mStatusBuf == null) { 606 mStatusBuf = new byte[MAX_STATUS]; 607 } 608 return mStatusBuf; 609 } 610 611 private void writeCommStatusAndClose(int status, String msg) { 612 if (mCommFd == null) { 613 // Not reliable, or someone already sent status 614 if (msg != null) { 615 Log.w(TAG, "Unable to inform peer: " + msg); 616 } 617 return; 618 } 619 620 if (status == Status.DETACHED) { 621 Log.w(TAG, "Peer expected signal when closed; unable to deliver after detach"); 622 } 623 624 try { 625 try { 626 if (status != Status.SILENCE) { 627 final byte[] buf = getOrCreateStatusBuffer(); 628 int writePtr = 0; 629 630 Memory.pokeInt(buf, writePtr, status, ByteOrder.BIG_ENDIAN); 631 writePtr += 4; 632 633 if (msg != null) { 634 final byte[] rawMsg = msg.getBytes(); 635 final int len = Math.min(rawMsg.length, buf.length - writePtr); 636 System.arraycopy(rawMsg, 0, buf, writePtr, len); 637 writePtr += len; 638 } 639 640 Libcore.os.write(mCommFd, buf, 0, writePtr); 641 } 642 } catch (ErrnoException e) { 643 // Reporting status is best-effort 644 Log.w(TAG, "Failed to report status: " + e); 645 } 646 647 if (status != Status.SILENCE) { 648 // Since we're about to close, read off any remote status. It's 649 // okay to remember missing here. 650 mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer()); 651 } 652 653 } finally { 654 IoUtils.closeQuietly(mCommFd); 655 mCommFd = null; 656 } 657 } 658 659 private static Status readCommStatus(FileDescriptor comm, byte[] buf) { 660 try { 661 final int n = Libcore.os.read(comm, buf, 0, buf.length); 662 if (n == 0) { 663 // EOF means they're dead 664 return new Status(Status.DEAD); 665 } else { 666 final int status = Memory.peekInt(buf, 0, ByteOrder.BIG_ENDIAN); 667 if (status == Status.ERROR) { 668 final String msg = new String(buf, 4, n - 4); 669 return new Status(status, msg); 670 } 671 return new Status(status); 672 } 673 } catch (ErrnoException e) { 674 if (e.errno == OsConstants.EAGAIN) { 675 // Remote is still alive, but no status written yet 676 return null; 677 } else { 678 Log.d(TAG, "Failed to read status; assuming dead: " + e); 679 return new Status(Status.DEAD); 680 } 681 } 682 } 683 684 /** 685 * Indicates if this ParcelFileDescriptor can communicate and detect remote 686 * errors/crashes. 687 * 688 * @see #checkError() 689 */ 690 public boolean canDetectErrors() { 691 if (mWrapped != null) { 692 return mWrapped.canDetectErrors(); 693 } else { 694 return mCommFd != null; 695 } 696 } 697 698 /** 699 * Detect and throw if the other end of a pipe or socket pair encountered an 700 * error or crashed. This allows a reader to distinguish between a valid EOF 701 * and an error/crash. 702 * <p> 703 * If this ParcelFileDescriptor is unable to detect remote errors, it will 704 * return silently. 705 * 706 * @throws IOException for normal errors. 707 * @throws FileDescriptorDetachedException 708 * if the remote side called {@link #detachFd()}. Once detached, the remote 709 * side is unable to communicate any errors through 710 * {@link #closeWithError(String)}. 711 * @see #canDetectErrors() 712 */ 713 public void checkError() throws IOException { 714 if (mWrapped != null) { 715 mWrapped.checkError(); 716 } else { 717 if (mStatus == null) { 718 if (mCommFd == null) { 719 Log.w(TAG, "Peer didn't provide a comm channel; unable to check for errors"); 720 return; 721 } 722 723 // Try reading status; it might be null if nothing written yet. 724 // Either way, we keep comm open to write our status later. 725 mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer()); 726 } 727 728 if (mStatus == null || mStatus.status == Status.OK) { 729 // No status yet, or everything is peachy! 730 return; 731 } else { 732 throw mStatus.asIOException(); 733 } 734 } 735 } 736 737 /** 738 * An InputStream you can create on a ParcelFileDescriptor, which will 739 * take care of calling {@link ParcelFileDescriptor#close 740 * ParcelFileDescriptor.close()} for you when the stream is closed. 741 */ 742 public static class AutoCloseInputStream extends FileInputStream { 743 private final ParcelFileDescriptor mPfd; 744 745 public AutoCloseInputStream(ParcelFileDescriptor pfd) { 746 super(pfd.getFileDescriptor()); 747 mPfd = pfd; 748 } 749 750 @Override 751 public void close() throws IOException { 752 try { 753 mPfd.close(); 754 } finally { 755 super.close(); 756 } 757 } 758 } 759 760 /** 761 * An OutputStream you can create on a ParcelFileDescriptor, which will 762 * take care of calling {@link ParcelFileDescriptor#close 763 * ParcelFileDescriptor.close()} for you when the stream is closed. 764 */ 765 public static class AutoCloseOutputStream extends FileOutputStream { 766 private final ParcelFileDescriptor mPfd; 767 768 public AutoCloseOutputStream(ParcelFileDescriptor pfd) { 769 super(pfd.getFileDescriptor()); 770 mPfd = pfd; 771 } 772 773 @Override 774 public void close() throws IOException { 775 try { 776 mPfd.close(); 777 } finally { 778 super.close(); 779 } 780 } 781 } 782 783 @Override 784 public String toString() { 785 if (mWrapped != null) { 786 return mWrapped.toString(); 787 } else { 788 return "{ParcelFileDescriptor: " + mFd + "}"; 789 } 790 } 791 792 @Override 793 protected void finalize() throws Throwable { 794 if (mGuard != null) { 795 mGuard.warnIfOpen(); 796 } 797 try { 798 if (!mClosed) { 799 closeWithStatus(Status.LEAKED, null); 800 } 801 } finally { 802 super.finalize(); 803 } 804 } 805 806 @Override 807 public int describeContents() { 808 if (mWrapped != null) { 809 return mWrapped.describeContents(); 810 } else { 811 return Parcelable.CONTENTS_FILE_DESCRIPTOR; 812 } 813 } 814 815 /** 816 * {@inheritDoc} 817 * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags, 818 * the file descriptor will be closed after a copy is written to the Parcel. 819 */ 820 @Override 821 public void writeToParcel(Parcel out, int flags) { 822 if (mWrapped != null) { 823 mWrapped.writeToParcel(out, flags); 824 } else { 825 out.writeFileDescriptor(mFd); 826 if (mCommFd != null) { 827 out.writeInt(1); 828 out.writeFileDescriptor(mCommFd); 829 } else { 830 out.writeInt(0); 831 } 832 if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) { 833 try { 834 // Not a real close, so emit no status 835 closeWithStatus(Status.SILENCE, null); 836 } catch (IOException e) { 837 } 838 } 839 } 840 } 841 842 public static final Parcelable.Creator<ParcelFileDescriptor> CREATOR 843 = new Parcelable.Creator<ParcelFileDescriptor>() { 844 @Override 845 public ParcelFileDescriptor createFromParcel(Parcel in) { 846 final FileDescriptor fd = in.readRawFileDescriptor(); 847 FileDescriptor commChannel = null; 848 if (in.readInt() != 0) { 849 commChannel = in.readRawFileDescriptor(); 850 } 851 return new ParcelFileDescriptor(fd, commChannel); 852 } 853 854 @Override 855 public ParcelFileDescriptor[] newArray(int size) { 856 return new ParcelFileDescriptor[size]; 857 } 858 }; 859 860 /** 861 * Callback indicating that a ParcelFileDescriptor has been closed. 862 */ 863 public interface OnCloseListener { 864 /** 865 * Event indicating the ParcelFileDescriptor to which this listener was 866 * attached has been closed. 867 * 868 * @param e error state, or {@code null} if closed cleanly. 869 * If the close event was the result of 870 * {@link ParcelFileDescriptor#detachFd()}, this will be a 871 * {@link FileDescriptorDetachedException}. After detach the 872 * remote side may continue reading/writing to the underlying 873 * {@link FileDescriptor}, but they can no longer deliver 874 * reliable close/error events. 875 */ 876 public void onClose(IOException e); 877 } 878 879 /** 880 * Exception that indicates that the file descriptor was detached. 881 */ 882 public static class FileDescriptorDetachedException extends IOException { 883 884 private static final long serialVersionUID = 0xDe7ac4edFdL; 885 886 public FileDescriptorDetachedException() { 887 super("Remote side is detached"); 888 } 889 } 890 891 /** 892 * Internal class representing a remote status read by 893 * {@link ParcelFileDescriptor#readCommStatus(FileDescriptor, byte[])}. 894 */ 895 private static class Status { 896 /** Special value indicating remote side died. */ 897 public static final int DEAD = -2; 898 /** Special value indicating no status should be written. */ 899 public static final int SILENCE = -1; 900 901 /** Remote reported that everything went better than expected. */ 902 public static final int OK = 0; 903 /** Remote reported error; length and message follow. */ 904 public static final int ERROR = 1; 905 /** Remote reported {@link #detachFd()} and went rogue. */ 906 public static final int DETACHED = 2; 907 /** Remote reported their object was finalized. */ 908 public static final int LEAKED = 3; 909 910 public final int status; 911 public final String msg; 912 913 public Status(int status) { 914 this(status, null); 915 } 916 917 public Status(int status, String msg) { 918 this.status = status; 919 this.msg = msg; 920 } 921 922 public IOException asIOException() { 923 switch (status) { 924 case DEAD: 925 return new IOException("Remote side is dead"); 926 case OK: 927 return null; 928 case ERROR: 929 return new IOException("Remote error: " + msg); 930 case DETACHED: 931 return new FileDescriptorDetachedException(); 932 case LEAKED: 933 return new IOException("Remote side was leaked"); 934 default: 935 return new IOException("Unknown status: " + status); 936 } 937 } 938 } 939 940 /** 941 * Bridge to watch for remote status, and deliver to listener. Currently 942 * requires that communication socket is <em>blocking</em>. 943 */ 944 private static final class ListenerBridge extends Thread { 945 // TODO: switch to using Looper to avoid burning a thread 946 947 private FileDescriptor mCommFd; 948 private final Handler mHandler; 949 950 public ListenerBridge(FileDescriptor comm, Looper looper, final OnCloseListener listener) { 951 mCommFd = comm; 952 mHandler = new Handler(looper) { 953 @Override 954 public void handleMessage(Message msg) { 955 final Status s = (Status) msg.obj; 956 listener.onClose(s != null ? s.asIOException() : null); 957 } 958 }; 959 } 960 961 @Override 962 public void run() { 963 try { 964 final byte[] buf = new byte[MAX_STATUS]; 965 final Status status = readCommStatus(mCommFd, buf); 966 mHandler.obtainMessage(0, status).sendToTarget(); 967 } finally { 968 IoUtils.closeQuietly(mCommFd); 969 mCommFd = null; 970 } 971 } 972 } 973} 974