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