ParcelFileDescriptor.java revision 487c11a3101c6cd9fc18758b3032383666f55e46
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(boolean)}, 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(boolean)}, 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(boolean)}, 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 try { 568 mWrapped.close(); 569 } finally { 570 releaseResources(); 571 } 572 } else { 573 closeWithStatus(Status.OK, null); 574 } 575 } 576 577 /** 578 * Close the ParcelFileDescriptor, informing any peer that an error occurred 579 * while processing. If the creator of this descriptor is not observing 580 * errors, it will close normally. 581 * 582 * @param msg describing the error; must not be null. 583 */ 584 public void closeWithError(String msg) throws IOException { 585 if (mWrapped != null) { 586 try { 587 mWrapped.closeWithError(msg); 588 } finally { 589 releaseResources(); 590 } 591 } else { 592 if (msg == null) { 593 throw new IllegalArgumentException("Message must not be null"); 594 } 595 closeWithStatus(Status.ERROR, msg); 596 } 597 } 598 599 private void closeWithStatus(int status, String msg) { 600 if (mClosed) return; 601 mClosed = true; 602 mGuard.close(); 603 // Status MUST be sent before closing actual descriptor 604 writeCommStatusAndClose(status, msg); 605 IoUtils.closeQuietly(mFd); 606 releaseResources(); 607 } 608 609 /** 610 * Called when the fd is being closed, for subclasses to release any other resources 611 * associated with it, such as acquired providers. 612 * @hide 613 */ 614 public void releaseResources() { 615 } 616 617 private byte[] getOrCreateStatusBuffer() { 618 if (mStatusBuf == null) { 619 mStatusBuf = new byte[MAX_STATUS]; 620 } 621 return mStatusBuf; 622 } 623 624 private void writeCommStatusAndClose(int status, String msg) { 625 if (mCommFd == null) { 626 // Not reliable, or someone already sent status 627 if (msg != null) { 628 Log.w(TAG, "Unable to inform peer: " + msg); 629 } 630 return; 631 } 632 633 if (status == Status.DETACHED) { 634 Log.w(TAG, "Peer expected signal when closed; unable to deliver after detach"); 635 } 636 637 try { 638 try { 639 if (status != Status.SILENCE) { 640 final byte[] buf = getOrCreateStatusBuffer(); 641 int writePtr = 0; 642 643 Memory.pokeInt(buf, writePtr, status, ByteOrder.BIG_ENDIAN); 644 writePtr += 4; 645 646 if (msg != null) { 647 final byte[] rawMsg = msg.getBytes(); 648 final int len = Math.min(rawMsg.length, buf.length - writePtr); 649 System.arraycopy(rawMsg, 0, buf, writePtr, len); 650 writePtr += len; 651 } 652 653 Libcore.os.write(mCommFd, buf, 0, writePtr); 654 } 655 } catch (ErrnoException e) { 656 // Reporting status is best-effort 657 Log.w(TAG, "Failed to report status: " + e); 658 } 659 660 if (status != Status.SILENCE) { 661 // Since we're about to close, read off any remote status. It's 662 // okay to remember missing here. 663 mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer()); 664 } 665 666 } finally { 667 IoUtils.closeQuietly(mCommFd); 668 mCommFd = null; 669 } 670 } 671 672 private static Status readCommStatus(FileDescriptor comm, byte[] buf) { 673 try { 674 final int n = Libcore.os.read(comm, buf, 0, buf.length); 675 if (n == 0) { 676 // EOF means they're dead 677 return new Status(Status.DEAD); 678 } else { 679 final int status = Memory.peekInt(buf, 0, ByteOrder.BIG_ENDIAN); 680 if (status == Status.ERROR) { 681 final String msg = new String(buf, 4, n - 4); 682 return new Status(status, msg); 683 } 684 return new Status(status); 685 } 686 } catch (ErrnoException e) { 687 if (e.errno == OsConstants.EAGAIN) { 688 // Remote is still alive, but no status written yet 689 return null; 690 } else { 691 Log.d(TAG, "Failed to read status; assuming dead: " + e); 692 return new Status(Status.DEAD); 693 } 694 } 695 } 696 697 /** 698 * Indicates if this ParcelFileDescriptor can communicate and detect remote 699 * errors/crashes. 700 * 701 * @see #checkError(boolean) 702 */ 703 public boolean canDetectErrors() { 704 if (mWrapped != null) { 705 return mWrapped.canDetectErrors(); 706 } else { 707 return mCommFd != null; 708 } 709 } 710 711 /** 712 * Detect and throw if the other end of a pipe or socket pair encountered an 713 * error or crashed. This allows a reader to distinguish between a valid EOF 714 * and an error/crash. 715 * <p> 716 * If this ParcelFileDescriptor is unable to detect remote errors, it will 717 * return silently. 718 * 719 * @param throwIfDetached requests that an exception be thrown if the remote 720 * side called {@link #detachFd()}. Once detached, the remote 721 * side is unable to communicate any errors through 722 * {@link #closeWithError(String)}. An application may pass true 723 * if it needs a stronger guarantee that the stream was closed 724 * normally and was not merely detached. 725 * @see #canDetectErrors() 726 */ 727 public void checkError(boolean throwIfDetached) throws IOException { 728 if (mWrapped != null) { 729 mWrapped.checkError(throwIfDetached); 730 } else { 731 if (mStatus == null) { 732 if (mCommFd == null) { 733 Log.w(TAG, "Peer didn't provide a comm channel; unable to check for errors"); 734 return; 735 } 736 737 // Try reading status; it might be null if nothing written yet. 738 // Either way, we keep comm open to write our status later. 739 mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer()); 740 } 741 742 if (mStatus == null || mStatus.status == Status.OK 743 || (mStatus.status == Status.DETACHED && !throwIfDetached)) { 744 // No status yet, or everything is peachy! 745 return; 746 } else { 747 throw mStatus.asIOException(); 748 } 749 } 750 } 751 752 /** 753 * An InputStream you can create on a ParcelFileDescriptor, which will 754 * take care of calling {@link ParcelFileDescriptor#close 755 * ParcelFileDescriptor.close()} for you when the stream is closed. 756 */ 757 public static class AutoCloseInputStream extends FileInputStream { 758 private final ParcelFileDescriptor mPfd; 759 760 public AutoCloseInputStream(ParcelFileDescriptor pfd) { 761 super(pfd.getFileDescriptor()); 762 mPfd = pfd; 763 } 764 765 @Override 766 public void close() throws IOException { 767 try { 768 mPfd.close(); 769 } finally { 770 super.close(); 771 } 772 } 773 } 774 775 /** 776 * An OutputStream you can create on a ParcelFileDescriptor, which will 777 * take care of calling {@link ParcelFileDescriptor#close 778 * ParcelFileDescriptor.close()} for you when the stream is closed. 779 */ 780 public static class AutoCloseOutputStream extends FileOutputStream { 781 private final ParcelFileDescriptor mPfd; 782 783 public AutoCloseOutputStream(ParcelFileDescriptor pfd) { 784 super(pfd.getFileDescriptor()); 785 mPfd = pfd; 786 } 787 788 @Override 789 public void close() throws IOException { 790 try { 791 mPfd.close(); 792 } finally { 793 super.close(); 794 } 795 } 796 } 797 798 @Override 799 public String toString() { 800 if (mWrapped != null) { 801 return mWrapped.toString(); 802 } else { 803 return "{ParcelFileDescriptor: " + mFd + "}"; 804 } 805 } 806 807 @Override 808 protected void finalize() throws Throwable { 809 if (mWrapped != null) { 810 releaseResources(); 811 } 812 if (mGuard != null) { 813 mGuard.warnIfOpen(); 814 } 815 try { 816 if (!mClosed) { 817 closeWithStatus(Status.LEAKED, null); 818 } 819 } finally { 820 super.finalize(); 821 } 822 } 823 824 @Override 825 public int describeContents() { 826 if (mWrapped != null) { 827 return mWrapped.describeContents(); 828 } else { 829 return Parcelable.CONTENTS_FILE_DESCRIPTOR; 830 } 831 } 832 833 /** 834 * {@inheritDoc} 835 * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags, 836 * the file descriptor will be closed after a copy is written to the Parcel. 837 */ 838 @Override 839 public void writeToParcel(Parcel out, int flags) { 840 if (mWrapped != null) { 841 try { 842 mWrapped.writeToParcel(out, flags); 843 } finally { 844 releaseResources(); 845 } 846 } else { 847 out.writeFileDescriptor(mFd); 848 if (mCommFd != null) { 849 out.writeInt(1); 850 out.writeFileDescriptor(mCommFd); 851 } else { 852 out.writeInt(0); 853 } 854 if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) { 855 // Not a real close, so emit no status 856 closeWithStatus(Status.SILENCE, null); 857 } 858 } 859 } 860 861 public static final Parcelable.Creator<ParcelFileDescriptor> CREATOR 862 = new Parcelable.Creator<ParcelFileDescriptor>() { 863 @Override 864 public ParcelFileDescriptor createFromParcel(Parcel in) { 865 final FileDescriptor fd = in.readRawFileDescriptor(); 866 FileDescriptor commChannel = null; 867 if (in.readInt() != 0) { 868 commChannel = in.readRawFileDescriptor(); 869 } 870 return new ParcelFileDescriptor(fd, commChannel); 871 } 872 873 @Override 874 public ParcelFileDescriptor[] newArray(int size) { 875 return new ParcelFileDescriptor[size]; 876 } 877 }; 878 879 /** 880 * Callback indicating that a ParcelFileDescriptor has been closed. 881 */ 882 public interface OnCloseListener { 883 /** 884 * Event indicating the ParcelFileDescriptor to which this listener was 885 * attached has been closed. 886 * 887 * @param e error state, or {@code null} if closed cleanly. 888 * @param fromDetach indicates if close event was result of 889 * {@link ParcelFileDescriptor#detachFd()}. After detach the 890 * remote side may continue reading/writing to the underlying 891 * {@link FileDescriptor}, but they can no longer deliver 892 * reliable close/error events. 893 */ 894 public void onClose(IOException e, boolean fromDetach); 895 } 896 897 /** 898 * Internal class representing a remote status read by 899 * {@link ParcelFileDescriptor#readCommStatus(FileDescriptor, byte[])}. 900 */ 901 private static class Status { 902 /** Special value indicating remote side died. */ 903 public static final int DEAD = -2; 904 /** Special value indicating no status should be written. */ 905 public static final int SILENCE = -1; 906 907 /** Remote reported that everything went better than expected. */ 908 public static final int OK = 0; 909 /** Remote reported error; length and message follow. */ 910 public static final int ERROR = 1; 911 /** Remote reported {@link #detachFd()} and went rogue. */ 912 public static final int DETACHED = 2; 913 /** Remote reported their object was finalized. */ 914 public static final int LEAKED = 3; 915 916 public final int status; 917 public final String msg; 918 919 public Status(int status) { 920 this(status, null); 921 } 922 923 public Status(int status, String msg) { 924 this.status = status; 925 this.msg = msg; 926 } 927 928 public IOException asIOException() { 929 switch (status) { 930 case DEAD: 931 return new IOException("Remote side is dead"); 932 case OK: 933 return null; 934 case ERROR: 935 return new IOException("Remote error: " + msg); 936 case DETACHED: 937 return new IOException("Remote side is detached"); 938 case LEAKED: 939 return new IOException("Remote side was leaked"); 940 default: 941 return new IOException("Unknown status: " + status); 942 } 943 } 944 } 945 946 /** 947 * Bridge to watch for remote status, and deliver to listener. Currently 948 * requires that communication socket is <em>blocking</em>. 949 */ 950 private static final class ListenerBridge extends Thread { 951 // TODO: switch to using Looper to avoid burning a thread 952 953 private FileDescriptor mCommFd; 954 private final Handler mHandler; 955 956 public ListenerBridge(FileDescriptor comm, Looper looper, final OnCloseListener listener) { 957 mCommFd = comm; 958 mHandler = new Handler(looper) { 959 @Override 960 public void handleMessage(Message msg) { 961 final Status s = (Status) msg.obj; 962 if (s.status == Status.DETACHED) { 963 listener.onClose(null, true); 964 } else if (s.status == Status.OK) { 965 listener.onClose(null, false); 966 } else { 967 listener.onClose(s.asIOException(), false); 968 } 969 } 970 }; 971 } 972 973 @Override 974 public void run() { 975 try { 976 final byte[] buf = new byte[MAX_STATUS]; 977 final Status status = readCommStatus(mCommFd, buf); 978 mHandler.obtainMessage(0, status).sendToTarget(); 979 } finally { 980 IoUtils.closeQuietly(mCommFd); 981 mCommFd = null; 982 } 983 } 984 } 985} 986