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 dalvik.system.CloseGuard;
20
21import java.io.Closeable;
22import java.io.File;
23import java.io.FileDescriptor;
24import java.io.FileInputStream;
25import java.io.FileNotFoundException;
26import java.io.FileOutputStream;
27import java.io.IOException;
28import java.net.DatagramSocket;
29import java.net.Socket;
30
31/**
32 * The FileDescriptor returned by {@link Parcel#readFileDescriptor}, allowing
33 * you to close it when done with it.
34 */
35public class ParcelFileDescriptor implements Parcelable, Closeable {
36    private final FileDescriptor mFileDescriptor;
37
38    /**
39     * Wrapped {@link ParcelFileDescriptor}, if any. Used to avoid
40     * double-closing {@link #mFileDescriptor}.
41     */
42    private final ParcelFileDescriptor mWrapped;
43
44    private volatile boolean mClosed;
45
46    private final CloseGuard mGuard = CloseGuard.get();
47
48    /**
49     * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied
50     * and this file doesn't already exist, then create the file with
51     * permissions such that any application can read it.
52     */
53    public static final int MODE_WORLD_READABLE = 0x00000001;
54
55    /**
56     * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied
57     * and this file doesn't already exist, then create the file with
58     * permissions such that any application can write it.
59     */
60    public static final int MODE_WORLD_WRITEABLE = 0x00000002;
61
62    /**
63     * For use with {@link #open}: open the file with read-only access.
64     */
65    public static final int MODE_READ_ONLY = 0x10000000;
66
67    /**
68     * For use with {@link #open}: open the file with write-only access.
69     */
70    public static final int MODE_WRITE_ONLY = 0x20000000;
71
72    /**
73     * For use with {@link #open}: open the file with read and write access.
74     */
75    public static final int MODE_READ_WRITE = 0x30000000;
76
77    /**
78     * For use with {@link #open}: create the file if it doesn't already exist.
79     */
80    public static final int MODE_CREATE = 0x08000000;
81
82    /**
83     * For use with {@link #open}: erase contents of file when opening.
84     */
85    public static final int MODE_TRUNCATE = 0x04000000;
86
87    /**
88     * For use with {@link #open}: append to end of file while writing.
89     */
90    public static final int MODE_APPEND = 0x02000000;
91
92    /**
93     * Create a new ParcelFileDescriptor accessing a given file.
94     *
95     * @param file The file to be opened.
96     * @param mode The desired access mode, must be one of
97     * {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or
98     * {@link #MODE_READ_WRITE}; may also be any combination of
99     * {@link #MODE_CREATE}, {@link #MODE_TRUNCATE},
100     * {@link #MODE_WORLD_READABLE}, and {@link #MODE_WORLD_WRITEABLE}.
101     *
102     * @return Returns a new ParcelFileDescriptor pointing to the given
103     * file.
104     *
105     * @throws FileNotFoundException Throws FileNotFoundException if the given
106     * file does not exist or can not be opened with the requested mode.
107     */
108    public static ParcelFileDescriptor open(File file, int mode)
109            throws FileNotFoundException {
110        String path = file.getPath();
111        SecurityManager security = System.getSecurityManager();
112        if (security != null) {
113            security.checkRead(path);
114            if ((mode&MODE_WRITE_ONLY) != 0) {
115                security.checkWrite(path);
116            }
117        }
118
119        if ((mode&MODE_READ_WRITE) == 0) {
120            throw new IllegalArgumentException(
121                    "Must specify MODE_READ_ONLY, MODE_WRITE_ONLY, or MODE_READ_WRITE");
122        }
123
124        FileDescriptor fd = Parcel.openFileDescriptor(path, mode);
125        return fd != null ? new ParcelFileDescriptor(fd) : null;
126    }
127
128    /**
129     * Create a new ParcelFileDescriptor that is a dup of an existing
130     * FileDescriptor.  This obeys standard POSIX semantics, where the
131     * new file descriptor shared state such as file position with the
132     * original file descriptor.
133     */
134    public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
135        FileDescriptor fd = Parcel.dupFileDescriptor(orig);
136        return fd != null ? new ParcelFileDescriptor(fd) : null;
137    }
138
139    /**
140     * Create a new ParcelFileDescriptor that is a dup of the existing
141     * FileDescriptor.  This obeys standard POSIX semantics, where the
142     * new file descriptor shared state such as file position with the
143     * original file descriptor.
144     */
145    public ParcelFileDescriptor dup() throws IOException {
146        return dup(getFileDescriptor());
147    }
148
149    /**
150     * Create a new ParcelFileDescriptor from a raw native fd.  The new
151     * ParcelFileDescriptor holds a dup of the original fd passed in here,
152     * so you must still close that fd as well as the new ParcelFileDescriptor.
153     *
154     * @param fd The native fd that the ParcelFileDescriptor should dup.
155     *
156     * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
157     * for a dup of the given fd.
158     */
159    public static ParcelFileDescriptor fromFd(int fd) throws IOException {
160        FileDescriptor fdesc = getFileDescriptorFromFd(fd);
161        return new ParcelFileDescriptor(fdesc);
162    }
163
164    // Extracts the file descriptor from the specified socket and returns it untouched
165    private static native FileDescriptor getFileDescriptorFromFd(int fd) throws IOException;
166
167    /**
168     * Take ownership of a raw native fd in to a new ParcelFileDescriptor.
169     * The returned ParcelFileDescriptor now owns the given fd, and will be
170     * responsible for closing it.  You must not close the fd yourself.
171     *
172     * @param fd The native fd that the ParcelFileDescriptor should adopt.
173     *
174     * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
175     * for the given fd.
176     */
177    public static ParcelFileDescriptor adoptFd(int fd) {
178        FileDescriptor fdesc = getFileDescriptorFromFdNoDup(fd);
179        return new ParcelFileDescriptor(fdesc);
180    }
181
182    // Extracts the file descriptor from the specified socket and returns it untouched
183    private static native FileDescriptor getFileDescriptorFromFdNoDup(int fd);
184
185    /**
186     * Create a new ParcelFileDescriptor from the specified Socket.  The new
187     * ParcelFileDescriptor holds a dup of the original FileDescriptor in
188     * the Socket, so you must still close the Socket as well as the new
189     * ParcelFileDescriptor.
190     *
191     * @param socket The Socket whose FileDescriptor is used to create
192     *               a new ParcelFileDescriptor.
193     *
194     * @return A new ParcelFileDescriptor with the FileDescriptor of the
195     *         specified Socket.
196     */
197    public static ParcelFileDescriptor fromSocket(Socket socket) {
198        FileDescriptor fd = socket.getFileDescriptor$();
199        return fd != null ? new ParcelFileDescriptor(fd) : null;
200    }
201
202    /**
203     * Create a new ParcelFileDescriptor from the specified DatagramSocket.
204     *
205     * @param datagramSocket The DatagramSocket whose FileDescriptor is used
206     *               to create a new ParcelFileDescriptor.
207     *
208     * @return A new ParcelFileDescriptor with the FileDescriptor of the
209     *         specified DatagramSocket.
210     */
211    public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) {
212        FileDescriptor fd = datagramSocket.getFileDescriptor$();
213        return fd != null ? new ParcelFileDescriptor(fd) : null;
214    }
215
216    /**
217     * Create two ParcelFileDescriptors structured as a data pipe.  The first
218     * ParcelFileDescriptor in the returned array is the read side; the second
219     * is the write side.
220     */
221    public static ParcelFileDescriptor[] createPipe() throws IOException {
222        FileDescriptor[] fds = new FileDescriptor[2];
223        createPipeNative(fds);
224        ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2];
225        pfds[0] = new ParcelFileDescriptor(fds[0]);
226        pfds[1] = new ParcelFileDescriptor(fds[1]);
227        return pfds;
228    }
229
230    private static native void createPipeNative(FileDescriptor[] outFds) throws IOException;
231
232    /**
233     * @hide Please use createPipe() or ContentProvider.openPipeHelper().
234     * Gets a file descriptor for a read-only copy of the given data.
235     *
236     * @param data Data to copy.
237     * @param name Name for the shared memory area that may back the file descriptor.
238     *        This is purely informative and may be {@code null}.
239     * @return A ParcelFileDescriptor.
240     * @throws IOException if there is an error while creating the shared memory area.
241     */
242    @Deprecated
243    public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException {
244        if (data == null) return null;
245        MemoryFile file = new MemoryFile(name, data.length);
246        if (data.length > 0) {
247            file.writeBytes(data, 0, 0, data.length);
248        }
249        file.deactivate();
250        FileDescriptor fd = file.getFileDescriptor();
251        return fd != null ? new ParcelFileDescriptor(fd) : null;
252    }
253
254    /**
255     * Retrieve the actual FileDescriptor associated with this object.
256     *
257     * @return Returns the FileDescriptor associated with this object.
258     */
259    public FileDescriptor getFileDescriptor() {
260        return mFileDescriptor;
261    }
262
263    /**
264     * Return the total size of the file representing this fd, as determined
265     * by stat().  Returns -1 if the fd is not a file.
266     */
267    public native long getStatSize();
268
269    /**
270     * This is needed for implementing AssetFileDescriptor.AutoCloseOutputStream,
271     * and I really don't think we want it to be public.
272     * @hide
273     */
274    public native long seekTo(long pos);
275
276    /**
277     * Return the native fd int for this ParcelFileDescriptor.  The
278     * ParcelFileDescriptor still owns the fd, and it still must be closed
279     * through this API.
280     */
281    public int getFd() {
282        if (mClosed) {
283            throw new IllegalStateException("Already closed");
284        }
285        return getFdNative();
286    }
287
288    private native int getFdNative();
289
290    /**
291     * Return the native fd int for this ParcelFileDescriptor and detach it
292     * from the object here.  You are now responsible for closing the fd in
293     * native code.
294     */
295    public int detachFd() {
296        if (mClosed) {
297            throw new IllegalStateException("Already closed");
298        }
299        if (mWrapped != null) {
300            int fd = mWrapped.detachFd();
301            mClosed = true;
302            mGuard.close();
303            return fd;
304        }
305        int fd = getFd();
306        mClosed = true;
307        mGuard.close();
308        Parcel.clearFileDescriptor(mFileDescriptor);
309        return fd;
310    }
311
312    /**
313     * Close the ParcelFileDescriptor. This implementation closes the underlying
314     * OS resources allocated to represent this stream.
315     *
316     * @throws IOException
317     *             If an error occurs attempting to close this ParcelFileDescriptor.
318     */
319    @Override
320    public void close() throws IOException {
321        if (mClosed) return;
322        mClosed = true;
323        mGuard.close();
324
325        if (mWrapped != null) {
326            // If this is a proxy to another file descriptor, just call through to its
327            // close method.
328            mWrapped.close();
329        } else {
330            Parcel.closeFileDescriptor(mFileDescriptor);
331        }
332    }
333
334    /**
335     * An InputStream you can create on a ParcelFileDescriptor, which will
336     * take care of calling {@link ParcelFileDescriptor#close
337     * ParcelFileDescriptor.close()} for you when the stream is closed.
338     */
339    public static class AutoCloseInputStream extends FileInputStream {
340        private final ParcelFileDescriptor mFd;
341
342        public AutoCloseInputStream(ParcelFileDescriptor fd) {
343            super(fd.getFileDescriptor());
344            mFd = fd;
345        }
346
347        @Override
348        public void close() throws IOException {
349            try {
350                mFd.close();
351            } finally {
352                super.close();
353            }
354        }
355    }
356
357    /**
358     * An OutputStream you can create on a ParcelFileDescriptor, which will
359     * take care of calling {@link ParcelFileDescriptor#close
360     * ParcelFileDescriptor.close()} for you when the stream is closed.
361     */
362    public static class AutoCloseOutputStream extends FileOutputStream {
363        private final ParcelFileDescriptor mFd;
364
365        public AutoCloseOutputStream(ParcelFileDescriptor fd) {
366            super(fd.getFileDescriptor());
367            mFd = fd;
368        }
369
370        @Override
371        public void close() throws IOException {
372            try {
373                mFd.close();
374            } finally {
375                super.close();
376            }
377        }
378    }
379
380    @Override
381    public String toString() {
382        return "{ParcelFileDescriptor: " + mFileDescriptor + "}";
383    }
384
385    @Override
386    protected void finalize() throws Throwable {
387        if (mGuard != null) {
388            mGuard.warnIfOpen();
389        }
390        try {
391            if (!mClosed) {
392                close();
393            }
394        } finally {
395            super.finalize();
396        }
397    }
398
399    public ParcelFileDescriptor(ParcelFileDescriptor descriptor) {
400        mWrapped = descriptor;
401        mFileDescriptor = mWrapped.mFileDescriptor;
402        mGuard.open("close");
403    }
404
405    /** {@hide} */
406    public ParcelFileDescriptor(FileDescriptor descriptor) {
407        if (descriptor == null) {
408            throw new NullPointerException("descriptor must not be null");
409        }
410        mWrapped = null;
411        mFileDescriptor = descriptor;
412        mGuard.open("close");
413    }
414
415    @Override
416    public int describeContents() {
417        return Parcelable.CONTENTS_FILE_DESCRIPTOR;
418    }
419
420    /**
421     * {@inheritDoc}
422     * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags,
423     * the file descriptor will be closed after a copy is written to the Parcel.
424     */
425    @Override
426    public void writeToParcel(Parcel out, int flags) {
427        out.writeFileDescriptor(mFileDescriptor);
428        if ((flags&PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
429            try {
430                close();
431            } catch (IOException e) {
432                // Empty
433            }
434        }
435    }
436
437    public static final Parcelable.Creator<ParcelFileDescriptor> CREATOR
438            = new Parcelable.Creator<ParcelFileDescriptor>() {
439        @Override
440        public ParcelFileDescriptor createFromParcel(Parcel in) {
441            return in.readFileDescriptor();
442        }
443
444        @Override
445        public ParcelFileDescriptor[] newArray(int size) {
446            return new ParcelFileDescriptor[size];
447        }
448    };
449}
450