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