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