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