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