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