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