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