ParcelFileDescriptor.java revision e8c00d8ed477e199b7f8d1b1e2f37e9cf8593372
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 * @see #parseMode(String) 196 */ 197 public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException { 198 final FileDescriptor fd = openInternal(file, mode); 199 if (fd == null) return null; 200 201 return new ParcelFileDescriptor(fd); 202 } 203 204 /** 205 * Create a new ParcelFileDescriptor accessing a given file. 206 * 207 * @param file The file to be opened. 208 * @param mode The desired access mode, must be one of 209 * {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or 210 * {@link #MODE_READ_WRITE}; may also be any combination of 211 * {@link #MODE_CREATE}, {@link #MODE_TRUNCATE}, 212 * {@link #MODE_WORLD_READABLE}, and 213 * {@link #MODE_WORLD_WRITEABLE}. 214 * @param handler to call listener from; must not be null. 215 * @param listener to be invoked when the returned descriptor has been 216 * closed; must not be null. 217 * @return a new ParcelFileDescriptor pointing to the given file. 218 * @throws FileNotFoundException if the given file does not exist or can not 219 * be opened with the requested mode. 220 * @see #parseMode(String) 221 */ 222 public static ParcelFileDescriptor open( 223 File file, int mode, Handler handler, OnCloseListener listener) throws IOException { 224 if (handler == null) { 225 throw new IllegalArgumentException("Handler must not be null"); 226 } 227 if (listener == null) { 228 throw new IllegalArgumentException("Listener must not be null"); 229 } 230 231 final FileDescriptor fd = openInternal(file, mode); 232 if (fd == null) return null; 233 234 final FileDescriptor[] comm = createCommSocketPair(true); 235 final ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd, comm[0]); 236 237 // Kick off thread to watch for status updates 238 final ListenerBridge bridge = new ListenerBridge(comm[1], handler.getLooper(), listener); 239 bridge.start(); 240 241 return pfd; 242 } 243 244 private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException { 245 if ((mode & MODE_READ_WRITE) == 0) { 246 throw new IllegalArgumentException( 247 "Must specify MODE_READ_ONLY, MODE_WRITE_ONLY, or MODE_READ_WRITE"); 248 } 249 250 final String path = file.getPath(); 251 return Parcel.openFileDescriptor(path, mode); 252 } 253 254 /** 255 * Create a new ParcelFileDescriptor that is a dup of an existing 256 * FileDescriptor. This obeys standard POSIX semantics, where the 257 * new file descriptor shared state such as file position with the 258 * original file descriptor. 259 */ 260 public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException { 261 try { 262 final FileDescriptor fd = Libcore.os.dup(orig); 263 return new ParcelFileDescriptor(fd); 264 } catch (ErrnoException e) { 265 throw e.rethrowAsIOException(); 266 } 267 } 268 269 /** 270 * Create a new ParcelFileDescriptor that is a dup of the existing 271 * FileDescriptor. This obeys standard POSIX semantics, where the 272 * new file descriptor shared state such as file position with the 273 * original file descriptor. 274 */ 275 public ParcelFileDescriptor dup() throws IOException { 276 if (mWrapped != null) { 277 return mWrapped.dup(); 278 } else { 279 return dup(getFileDescriptor()); 280 } 281 } 282 283 /** 284 * Create a new ParcelFileDescriptor from a raw native fd. The new 285 * ParcelFileDescriptor holds a dup of the original fd passed in here, 286 * so you must still close that fd as well as the new ParcelFileDescriptor. 287 * 288 * @param fd The native fd that the ParcelFileDescriptor should dup. 289 * 290 * @return Returns a new ParcelFileDescriptor holding a FileDescriptor 291 * for a dup of the given fd. 292 */ 293 public static ParcelFileDescriptor fromFd(int fd) throws IOException { 294 final FileDescriptor original = new FileDescriptor(); 295 original.setInt$(fd); 296 297 try { 298 final FileDescriptor dup = Libcore.os.dup(original); 299 return new ParcelFileDescriptor(dup); 300 } catch (ErrnoException e) { 301 throw e.rethrowAsIOException(); 302 } 303 } 304 305 /** 306 * Take ownership of a raw native fd in to a new ParcelFileDescriptor. 307 * The returned ParcelFileDescriptor now owns the given fd, and will be 308 * responsible for closing it. You must not close the fd yourself. 309 * 310 * @param fd The native fd that the ParcelFileDescriptor should adopt. 311 * 312 * @return Returns a new ParcelFileDescriptor holding a FileDescriptor 313 * for the given fd. 314 */ 315 public static ParcelFileDescriptor adoptFd(int fd) { 316 final FileDescriptor fdesc = new FileDescriptor(); 317 fdesc.setInt$(fd); 318 319 return new ParcelFileDescriptor(fdesc); 320 } 321 322 /** 323 * Create a new ParcelFileDescriptor from the specified Socket. The new 324 * ParcelFileDescriptor holds a dup of the original FileDescriptor in 325 * the Socket, so you must still close the Socket as well as the new 326 * ParcelFileDescriptor. 327 * 328 * @param socket The Socket whose FileDescriptor is used to create 329 * a new ParcelFileDescriptor. 330 * 331 * @return A new ParcelFileDescriptor with the FileDescriptor of the 332 * specified Socket. 333 */ 334 public static ParcelFileDescriptor fromSocket(Socket socket) { 335 FileDescriptor fd = socket.getFileDescriptor$(); 336 return fd != null ? new ParcelFileDescriptor(fd) : null; 337 } 338 339 /** 340 * Create a new ParcelFileDescriptor from the specified DatagramSocket. 341 * 342 * @param datagramSocket The DatagramSocket whose FileDescriptor is used 343 * to create a new ParcelFileDescriptor. 344 * 345 * @return A new ParcelFileDescriptor with the FileDescriptor of the 346 * specified DatagramSocket. 347 */ 348 public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) { 349 FileDescriptor fd = datagramSocket.getFileDescriptor$(); 350 return fd != null ? new ParcelFileDescriptor(fd) : null; 351 } 352 353 /** 354 * Create two ParcelFileDescriptors structured as a data pipe. The first 355 * ParcelFileDescriptor in the returned array is the read side; the second 356 * is the write side. 357 */ 358 public static ParcelFileDescriptor[] createPipe() throws IOException { 359 try { 360 final FileDescriptor[] fds = Libcore.os.pipe(); 361 return new ParcelFileDescriptor[] { 362 new ParcelFileDescriptor(fds[0]), 363 new ParcelFileDescriptor(fds[1]) }; 364 } catch (ErrnoException e) { 365 throw e.rethrowAsIOException(); 366 } 367 } 368 369 /** 370 * Create two ParcelFileDescriptors structured as a data pipe. The first 371 * ParcelFileDescriptor in the returned array is the read side; the second 372 * is the write side. 373 * <p> 374 * The write end has the ability to deliver an error message through 375 * {@link #closeWithError(String)} which can be handled by the read end 376 * calling {@link #checkError()}, usually after detecting an EOF. 377 * This can also be used to detect remote crashes. 378 */ 379 public static ParcelFileDescriptor[] createReliablePipe() throws IOException { 380 try { 381 final FileDescriptor[] comm = createCommSocketPair(false); 382 final FileDescriptor[] fds = Libcore.os.pipe(); 383 return new ParcelFileDescriptor[] { 384 new ParcelFileDescriptor(fds[0], comm[0]), 385 new ParcelFileDescriptor(fds[1], comm[1]) }; 386 } catch (ErrnoException e) { 387 throw e.rethrowAsIOException(); 388 } 389 } 390 391 /** 392 * Create two ParcelFileDescriptors structured as a pair of sockets 393 * connected to each other. The two sockets are indistinguishable. 394 */ 395 public static ParcelFileDescriptor[] createSocketPair() throws IOException { 396 try { 397 final FileDescriptor fd0 = new FileDescriptor(); 398 final FileDescriptor fd1 = new FileDescriptor(); 399 Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); 400 return new ParcelFileDescriptor[] { 401 new ParcelFileDescriptor(fd0), 402 new ParcelFileDescriptor(fd1) }; 403 } catch (ErrnoException e) { 404 throw e.rethrowAsIOException(); 405 } 406 } 407 408 /** 409 * Create two ParcelFileDescriptors structured as a pair of sockets 410 * connected to each other. The two sockets are indistinguishable. 411 * <p> 412 * Both ends have the ability to deliver an error message through 413 * {@link #closeWithError(String)} which can be detected by the other end 414 * calling {@link #checkError()}, usually after detecting an EOF. 415 * This can also be used to detect remote crashes. 416 */ 417 public static ParcelFileDescriptor[] createReliableSocketPair() throws IOException { 418 try { 419 final FileDescriptor[] comm = createCommSocketPair(false); 420 final FileDescriptor fd0 = new FileDescriptor(); 421 final FileDescriptor fd1 = new FileDescriptor(); 422 Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1); 423 return new ParcelFileDescriptor[] { 424 new ParcelFileDescriptor(fd0, comm[0]), 425 new ParcelFileDescriptor(fd1, comm[1]) }; 426 } catch (ErrnoException e) { 427 throw e.rethrowAsIOException(); 428 } 429 } 430 431 private static FileDescriptor[] createCommSocketPair(boolean blocking) throws IOException { 432 try { 433 final FileDescriptor comm1 = new FileDescriptor(); 434 final FileDescriptor comm2 = new FileDescriptor(); 435 Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2); 436 IoUtils.setBlocking(comm1, blocking); 437 IoUtils.setBlocking(comm2, blocking); 438 return new FileDescriptor[] { comm1, comm2 }; 439 } catch (ErrnoException e) { 440 throw e.rethrowAsIOException(); 441 } 442 } 443 444 /** 445 * @hide Please use createPipe() or ContentProvider.openPipeHelper(). 446 * Gets a file descriptor for a read-only copy of the given data. 447 * 448 * @param data Data to copy. 449 * @param name Name for the shared memory area that may back the file descriptor. 450 * This is purely informative and may be {@code null}. 451 * @return A ParcelFileDescriptor. 452 * @throws IOException if there is an error while creating the shared memory area. 453 */ 454 @Deprecated 455 public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException { 456 if (data == null) return null; 457 MemoryFile file = new MemoryFile(name, data.length); 458 if (data.length > 0) { 459 file.writeBytes(data, 0, 0, data.length); 460 } 461 file.deactivate(); 462 FileDescriptor fd = file.getFileDescriptor(); 463 return fd != null ? new ParcelFileDescriptor(fd) : null; 464 } 465 466 /** 467 * Converts a string representing a file mode, such as "rw", into a bitmask suitable for use 468 * with {@link #open}. 469 * <p> 470 * @param mode The string representation of the file mode. 471 * @return A bitmask representing the given file mode. 472 * @throws IllegalArgumentException if the given string does not match a known file mode. 473 */ 474 public static int parseMode(String mode) { 475 final int modeBits; 476 if ("r".equals(mode)) { 477 modeBits = ParcelFileDescriptor.MODE_READ_ONLY; 478 } else if ("w".equals(mode) || "wt".equals(mode)) { 479 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY 480 | ParcelFileDescriptor.MODE_CREATE 481 | ParcelFileDescriptor.MODE_TRUNCATE; 482 } else if ("wa".equals(mode)) { 483 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY 484 | ParcelFileDescriptor.MODE_CREATE 485 | ParcelFileDescriptor.MODE_APPEND; 486 } else if ("rw".equals(mode)) { 487 modeBits = ParcelFileDescriptor.MODE_READ_WRITE 488 | ParcelFileDescriptor.MODE_CREATE; 489 } else if ("rwt".equals(mode)) { 490 modeBits = ParcelFileDescriptor.MODE_READ_WRITE 491 | ParcelFileDescriptor.MODE_CREATE 492 | ParcelFileDescriptor.MODE_TRUNCATE; 493 } else { 494 throw new IllegalArgumentException("Bad mode '" + mode + "'"); 495 } 496 return modeBits; 497 } 498 499 /** 500 * Retrieve the actual FileDescriptor associated with this object. 501 * 502 * @return Returns the FileDescriptor associated with this object. 503 */ 504 public FileDescriptor getFileDescriptor() { 505 if (mWrapped != null) { 506 return mWrapped.getFileDescriptor(); 507 } else { 508 return mFd; 509 } 510 } 511 512 /** 513 * Return the total size of the file representing this fd, as determined by 514 * {@code stat()}. Returns -1 if the fd is not a file. 515 */ 516 public long getStatSize() { 517 if (mWrapped != null) { 518 return mWrapped.getStatSize(); 519 } else { 520 try { 521 final StructStat st = Libcore.os.fstat(mFd); 522 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { 523 return st.st_size; 524 } else { 525 return -1; 526 } 527 } catch (ErrnoException e) { 528 Log.w(TAG, "fstat() failed: " + e); 529 return -1; 530 } 531 } 532 } 533 534 /** 535 * This is needed for implementing AssetFileDescriptor.AutoCloseOutputStream, 536 * and I really don't think we want it to be public. 537 * @hide 538 */ 539 public long seekTo(long pos) throws IOException { 540 if (mWrapped != null) { 541 return mWrapped.seekTo(pos); 542 } else { 543 try { 544 return Libcore.os.lseek(mFd, pos, SEEK_SET); 545 } catch (ErrnoException e) { 546 throw e.rethrowAsIOException(); 547 } 548 } 549 } 550 551 /** 552 * Return the native fd int for this ParcelFileDescriptor. The 553 * ParcelFileDescriptor still owns the fd, and it still must be closed 554 * through this API. 555 */ 556 public int getFd() { 557 if (mWrapped != null) { 558 return mWrapped.getFd(); 559 } else { 560 if (mClosed) { 561 throw new IllegalStateException("Already closed"); 562 } 563 return mFd.getInt$(); 564 } 565 } 566 567 /** 568 * Return the native fd int for this ParcelFileDescriptor and detach it from 569 * the object here. You are now responsible for closing the fd in native 570 * code. 571 * <p> 572 * You should not detach when the original creator of the descriptor is 573 * expecting a reliable signal through {@link #close()} or 574 * {@link #closeWithError(String)}. 575 * 576 * @see #canDetectErrors() 577 */ 578 public int detachFd() { 579 if (mWrapped != null) { 580 return mWrapped.detachFd(); 581 } else { 582 if (mClosed) { 583 throw new IllegalStateException("Already closed"); 584 } 585 final int fd = getFd(); 586 Parcel.clearFileDescriptor(mFd); 587 writeCommStatusAndClose(Status.DETACHED, null); 588 return fd; 589 } 590 } 591 592 /** 593 * Close the ParcelFileDescriptor. This implementation closes the underlying 594 * OS resources allocated to represent this stream. 595 * 596 * @throws IOException 597 * If an error occurs attempting to close this ParcelFileDescriptor. 598 */ 599 @Override 600 public void close() throws IOException { 601 if (mWrapped != null) { 602 try { 603 mWrapped.close(); 604 } finally { 605 releaseResources(); 606 } 607 } else { 608 closeWithStatus(Status.OK, null); 609 } 610 } 611 612 /** 613 * Close the ParcelFileDescriptor, informing any peer that an error occurred 614 * while processing. If the creator of this descriptor is not observing 615 * errors, it will close normally. 616 * 617 * @param msg describing the error; must not be null. 618 */ 619 public void closeWithError(String msg) throws IOException { 620 if (mWrapped != null) { 621 try { 622 mWrapped.closeWithError(msg); 623 } finally { 624 releaseResources(); 625 } 626 } else { 627 if (msg == null) { 628 throw new IllegalArgumentException("Message must not be null"); 629 } 630 closeWithStatus(Status.ERROR, msg); 631 } 632 } 633 634 private void closeWithStatus(int status, String msg) { 635 if (mClosed) return; 636 mClosed = true; 637 mGuard.close(); 638 // Status MUST be sent before closing actual descriptor 639 writeCommStatusAndClose(status, msg); 640 IoUtils.closeQuietly(mFd); 641 releaseResources(); 642 } 643 644 /** 645 * Called when the fd is being closed, for subclasses to release any other resources 646 * associated with it, such as acquired providers. 647 * @hide 648 */ 649 public void releaseResources() { 650 } 651 652 private byte[] getOrCreateStatusBuffer() { 653 if (mStatusBuf == null) { 654 mStatusBuf = new byte[MAX_STATUS]; 655 } 656 return mStatusBuf; 657 } 658 659 private void writeCommStatusAndClose(int status, String msg) { 660 if (mCommFd == null) { 661 // Not reliable, or someone already sent status 662 if (msg != null) { 663 Log.w(TAG, "Unable to inform peer: " + msg); 664 } 665 return; 666 } 667 668 if (status == Status.DETACHED) { 669 Log.w(TAG, "Peer expected signal when closed; unable to deliver after detach"); 670 } 671 672 try { 673 try { 674 if (status != Status.SILENCE) { 675 final byte[] buf = getOrCreateStatusBuffer(); 676 int writePtr = 0; 677 678 Memory.pokeInt(buf, writePtr, status, ByteOrder.BIG_ENDIAN); 679 writePtr += 4; 680 681 if (msg != null) { 682 final byte[] rawMsg = msg.getBytes(); 683 final int len = Math.min(rawMsg.length, buf.length - writePtr); 684 System.arraycopy(rawMsg, 0, buf, writePtr, len); 685 writePtr += len; 686 } 687 688 Libcore.os.write(mCommFd, buf, 0, writePtr); 689 } 690 } catch (ErrnoException e) { 691 // Reporting status is best-effort 692 Log.w(TAG, "Failed to report status: " + e); 693 } 694 695 if (status != Status.SILENCE) { 696 // Since we're about to close, read off any remote status. It's 697 // okay to remember missing here. 698 mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer()); 699 } 700 701 } finally { 702 IoUtils.closeQuietly(mCommFd); 703 mCommFd = null; 704 } 705 } 706 707 private static Status readCommStatus(FileDescriptor comm, byte[] buf) { 708 try { 709 final int n = Libcore.os.read(comm, buf, 0, buf.length); 710 if (n == 0) { 711 // EOF means they're dead 712 return new Status(Status.DEAD); 713 } else { 714 final int status = Memory.peekInt(buf, 0, ByteOrder.BIG_ENDIAN); 715 if (status == Status.ERROR) { 716 final String msg = new String(buf, 4, n - 4); 717 return new Status(status, msg); 718 } 719 return new Status(status); 720 } 721 } catch (ErrnoException e) { 722 if (e.errno == OsConstants.EAGAIN) { 723 // Remote is still alive, but no status written yet 724 return null; 725 } else { 726 Log.d(TAG, "Failed to read status; assuming dead: " + e); 727 return new Status(Status.DEAD); 728 } 729 } 730 } 731 732 /** 733 * Indicates if this ParcelFileDescriptor can communicate and detect remote 734 * errors/crashes. 735 * 736 * @see #checkError() 737 */ 738 public boolean canDetectErrors() { 739 if (mWrapped != null) { 740 return mWrapped.canDetectErrors(); 741 } else { 742 return mCommFd != null; 743 } 744 } 745 746 /** 747 * Detect and throw if the other end of a pipe or socket pair encountered an 748 * error or crashed. This allows a reader to distinguish between a valid EOF 749 * and an error/crash. 750 * <p> 751 * If this ParcelFileDescriptor is unable to detect remote errors, it will 752 * return silently. 753 * 754 * @throws IOException for normal errors. 755 * @throws FileDescriptorDetachedException 756 * if the remote side called {@link #detachFd()}. Once detached, the remote 757 * side is unable to communicate any errors through 758 * {@link #closeWithError(String)}. 759 * @see #canDetectErrors() 760 */ 761 public void checkError() throws IOException { 762 if (mWrapped != null) { 763 mWrapped.checkError(); 764 } else { 765 if (mStatus == null) { 766 if (mCommFd == null) { 767 Log.w(TAG, "Peer didn't provide a comm channel; unable to check for errors"); 768 return; 769 } 770 771 // Try reading status; it might be null if nothing written yet. 772 // Either way, we keep comm open to write our status later. 773 mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer()); 774 } 775 776 if (mStatus == null || mStatus.status == Status.OK) { 777 // No status yet, or everything is peachy! 778 return; 779 } else { 780 throw mStatus.asIOException(); 781 } 782 } 783 } 784 785 /** 786 * An InputStream you can create on a ParcelFileDescriptor, which will 787 * take care of calling {@link ParcelFileDescriptor#close 788 * ParcelFileDescriptor.close()} for you when the stream is closed. 789 */ 790 public static class AutoCloseInputStream extends FileInputStream { 791 private final ParcelFileDescriptor mPfd; 792 793 public AutoCloseInputStream(ParcelFileDescriptor pfd) { 794 super(pfd.getFileDescriptor()); 795 mPfd = pfd; 796 } 797 798 @Override 799 public void close() throws IOException { 800 try { 801 mPfd.close(); 802 } finally { 803 super.close(); 804 } 805 } 806 } 807 808 /** 809 * An OutputStream you can create on a ParcelFileDescriptor, which will 810 * take care of calling {@link ParcelFileDescriptor#close 811 * ParcelFileDescriptor.close()} for you when the stream is closed. 812 */ 813 public static class AutoCloseOutputStream extends FileOutputStream { 814 private final ParcelFileDescriptor mPfd; 815 816 public AutoCloseOutputStream(ParcelFileDescriptor pfd) { 817 super(pfd.getFileDescriptor()); 818 mPfd = pfd; 819 } 820 821 @Override 822 public void close() throws IOException { 823 try { 824 mPfd.close(); 825 } finally { 826 super.close(); 827 } 828 } 829 } 830 831 @Override 832 public String toString() { 833 if (mWrapped != null) { 834 return mWrapped.toString(); 835 } else { 836 return "{ParcelFileDescriptor: " + mFd + "}"; 837 } 838 } 839 840 @Override 841 protected void finalize() throws Throwable { 842 if (mWrapped != null) { 843 releaseResources(); 844 } 845 if (mGuard != null) { 846 mGuard.warnIfOpen(); 847 } 848 try { 849 if (!mClosed) { 850 closeWithStatus(Status.LEAKED, null); 851 } 852 } finally { 853 super.finalize(); 854 } 855 } 856 857 @Override 858 public int describeContents() { 859 if (mWrapped != null) { 860 return mWrapped.describeContents(); 861 } else { 862 return Parcelable.CONTENTS_FILE_DESCRIPTOR; 863 } 864 } 865 866 /** 867 * {@inheritDoc} 868 * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags, 869 * the file descriptor will be closed after a copy is written to the Parcel. 870 */ 871 @Override 872 public void writeToParcel(Parcel out, int flags) { 873 if (mWrapped != null) { 874 try { 875 mWrapped.writeToParcel(out, flags); 876 } finally { 877 releaseResources(); 878 } 879 } else { 880 out.writeFileDescriptor(mFd); 881 if (mCommFd != null) { 882 out.writeInt(1); 883 out.writeFileDescriptor(mCommFd); 884 } else { 885 out.writeInt(0); 886 } 887 if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) { 888 // Not a real close, so emit no status 889 closeWithStatus(Status.SILENCE, null); 890 } 891 } 892 } 893 894 public static final Parcelable.Creator<ParcelFileDescriptor> CREATOR 895 = new Parcelable.Creator<ParcelFileDescriptor>() { 896 @Override 897 public ParcelFileDescriptor createFromParcel(Parcel in) { 898 final FileDescriptor fd = in.readRawFileDescriptor(); 899 FileDescriptor commChannel = null; 900 if (in.readInt() != 0) { 901 commChannel = in.readRawFileDescriptor(); 902 } 903 return new ParcelFileDescriptor(fd, commChannel); 904 } 905 906 @Override 907 public ParcelFileDescriptor[] newArray(int size) { 908 return new ParcelFileDescriptor[size]; 909 } 910 }; 911 912 /** 913 * Callback indicating that a ParcelFileDescriptor has been closed. 914 */ 915 public interface OnCloseListener { 916 /** 917 * Event indicating the ParcelFileDescriptor to which this listener was 918 * attached has been closed. 919 * 920 * @param e error state, or {@code null} if closed cleanly. 921 * If the close event was the result of 922 * {@link ParcelFileDescriptor#detachFd()}, this will be a 923 * {@link FileDescriptorDetachedException}. After detach the 924 * remote side may continue reading/writing to the underlying 925 * {@link FileDescriptor}, but they can no longer deliver 926 * reliable close/error events. 927 */ 928 public void onClose(IOException e); 929 } 930 931 /** 932 * Exception that indicates that the file descriptor was detached. 933 */ 934 public static class FileDescriptorDetachedException extends IOException { 935 936 private static final long serialVersionUID = 0xDe7ac4edFdL; 937 938 public FileDescriptorDetachedException() { 939 super("Remote side is detached"); 940 } 941 } 942 943 /** 944 * Internal class representing a remote status read by 945 * {@link ParcelFileDescriptor#readCommStatus(FileDescriptor, byte[])}. 946 */ 947 private static class Status { 948 /** Special value indicating remote side died. */ 949 public static final int DEAD = -2; 950 /** Special value indicating no status should be written. */ 951 public static final int SILENCE = -1; 952 953 /** Remote reported that everything went better than expected. */ 954 public static final int OK = 0; 955 /** Remote reported error; length and message follow. */ 956 public static final int ERROR = 1; 957 /** Remote reported {@link #detachFd()} and went rogue. */ 958 public static final int DETACHED = 2; 959 /** Remote reported their object was finalized. */ 960 public static final int LEAKED = 3; 961 962 public final int status; 963 public final String msg; 964 965 public Status(int status) { 966 this(status, null); 967 } 968 969 public Status(int status, String msg) { 970 this.status = status; 971 this.msg = msg; 972 } 973 974 public IOException asIOException() { 975 switch (status) { 976 case DEAD: 977 return new IOException("Remote side is dead"); 978 case OK: 979 return null; 980 case ERROR: 981 return new IOException("Remote error: " + msg); 982 case DETACHED: 983 return new FileDescriptorDetachedException(); 984 case LEAKED: 985 return new IOException("Remote side was leaked"); 986 default: 987 return new IOException("Unknown status: " + status); 988 } 989 } 990 } 991 992 /** 993 * Bridge to watch for remote status, and deliver to listener. Currently 994 * requires that communication socket is <em>blocking</em>. 995 */ 996 private static final class ListenerBridge extends Thread { 997 // TODO: switch to using Looper to avoid burning a thread 998 999 private FileDescriptor mCommFd; 1000 private final Handler mHandler; 1001 1002 public ListenerBridge(FileDescriptor comm, Looper looper, final OnCloseListener listener) { 1003 mCommFd = comm; 1004 mHandler = new Handler(looper) { 1005 @Override 1006 public void handleMessage(Message msg) { 1007 final Status s = (Status) msg.obj; 1008 listener.onClose(s != null ? s.asIOException() : null); 1009 } 1010 }; 1011 } 1012 1013 @Override 1014 public void run() { 1015 try { 1016 final byte[] buf = new byte[MAX_STATUS]; 1017 final Status status = readCommStatus(mCommFd, buf); 1018 mHandler.obtainMessage(0, status).sendToTarget(); 1019 } finally { 1020 IoUtils.closeQuietly(mCommFd); 1021 mCommFd = null; 1022 } 1023 } 1024 } 1025} 1026