blob: 5273c20abf9d496403e36af1b634af49dae8c630 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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;
Jeff Sharkey7407c942012-11-12 13:42:49 -080018
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -070019import static libcore.io.OsConstants.AF_UNIX;
20import static libcore.io.OsConstants.SEEK_SET;
21import static libcore.io.OsConstants.SOCK_STREAM;
22import static libcore.io.OsConstants.S_ISLNK;
23import static libcore.io.OsConstants.S_ISREG;
24
25import android.content.BroadcastReceiver;
26import android.content.ContentProvider;
27import android.util.Log;
28
Jeff Sharkey7407c942012-11-12 13:42:49 -080029import dalvik.system.CloseGuard;
30
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -070031import libcore.io.ErrnoException;
32import libcore.io.IoUtils;
33import libcore.io.Libcore;
34import libcore.io.Memory;
35import libcore.io.OsConstants;
36import libcore.io.StructStat;
37
Jeff Sharkeye861b422012-03-01 20:59:22 -080038import java.io.Closeable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import java.io.File;
40import java.io.FileDescriptor;
41import java.io.FileInputStream;
42import java.io.FileNotFoundException;
43import java.io.FileOutputStream;
44import java.io.IOException;
Chia-chi Yeh47f8f0f2011-05-12 16:23:44 -070045import java.net.DatagramSocket;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import java.net.Socket;
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -070047import java.nio.ByteOrder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49/**
50 * The FileDescriptor returned by {@link Parcel#readFileDescriptor}, allowing
51 * you to close it when done with it.
52 */
Jeff Sharkeye861b422012-03-01 20:59:22 -080053public class ParcelFileDescriptor implements Parcelable, Closeable {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -070054 private static final String TAG = "ParcelFileDescriptor";
55
56 private final FileDescriptor mFd;
57
58 /**
59 * Optional socket used to communicate close events, status at close, and
60 * detect remote process crashes.
61 */
62 private FileDescriptor mCommFd;
Jeff Sharkey7407c942012-11-12 13:42:49 -080063
64 /**
65 * Wrapped {@link ParcelFileDescriptor}, if any. Used to avoid
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -070066 * double-closing {@link #mFd}.
Jeff Sharkey7407c942012-11-12 13:42:49 -080067 */
68 private final ParcelFileDescriptor mWrapped;
69
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -070070 /**
71 * Maximum {@link #mStatusBuf} size; longer status messages will be
72 * truncated.
73 */
74 private static final int MAX_STATUS = 1024;
75
76 /**
77 * Temporary buffer used by {@link #readCommStatus(FileDescriptor, byte[])},
78 * allocated on-demand.
79 */
80 private byte[] mStatusBuf;
81
82 /**
Amith Yamasanib433bb82013-09-18 15:10:16 -070083 * Status read by {@link #checkError()}, or null if not read yet.
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -070084 */
85 private Status mStatus;
86
Jeff Sharkey7407c942012-11-12 13:42:49 -080087 private volatile boolean mClosed;
88
89 private final CloseGuard mGuard = CloseGuard.get();
Elliott Hughes43907582011-04-12 14:25:23 -070090
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 /**
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -070092 * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and
93 * this file doesn't already exist, then create the file with permissions
94 * such that any application can read it.
95 *
96 * @deprecated Creating world-readable files is very dangerous, and likely
97 * to cause security holes in applications. It is strongly
98 * discouraged; instead, applications should use more formal
99 * mechanism for interactions such as {@link ContentProvider},
100 * {@link BroadcastReceiver}, and {@link android.app.Service}.
101 * There are no guarantees that this access mode will remain on
102 * a file, such as when it goes through a backup and restore.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 */
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700104 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 public static final int MODE_WORLD_READABLE = 0x00000001;
Elliott Hughes43907582011-04-12 14:25:23 -0700106
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 /**
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700108 * For use with {@link #open}: if {@link #MODE_CREATE} has been supplied and
109 * this file doesn't already exist, then create the file with permissions
110 * such that any application can write it.
111 *
112 * @deprecated Creating world-writable files is very dangerous, and likely
113 * to cause security holes in applications. It is strongly
114 * discouraged; instead, applications should use more formal
115 * mechanism for interactions such as {@link ContentProvider},
116 * {@link BroadcastReceiver}, and {@link android.app.Service}.
117 * There are no guarantees that this access mode will remain on
118 * a file, such as when it goes through a backup and restore.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 */
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700120 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 public static final int MODE_WORLD_WRITEABLE = 0x00000002;
Elliott Hughes43907582011-04-12 14:25:23 -0700122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 /**
124 * For use with {@link #open}: open the file with read-only access.
125 */
126 public static final int MODE_READ_ONLY = 0x10000000;
Elliott Hughes43907582011-04-12 14:25:23 -0700127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 /**
129 * For use with {@link #open}: open the file with write-only access.
130 */
131 public static final int MODE_WRITE_ONLY = 0x20000000;
Elliott Hughes43907582011-04-12 14:25:23 -0700132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 /**
134 * For use with {@link #open}: open the file with read and write access.
135 */
136 public static final int MODE_READ_WRITE = 0x30000000;
Elliott Hughes43907582011-04-12 14:25:23 -0700137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 /**
139 * For use with {@link #open}: create the file if it doesn't already exist.
140 */
141 public static final int MODE_CREATE = 0x08000000;
Elliott Hughes43907582011-04-12 14:25:23 -0700142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 /**
144 * For use with {@link #open}: erase contents of file when opening.
145 */
146 public static final int MODE_TRUNCATE = 0x04000000;
Elliott Hughes43907582011-04-12 14:25:23 -0700147
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 /**
149 * For use with {@link #open}: append to end of file while writing.
150 */
151 public static final int MODE_APPEND = 0x02000000;
Elliott Hughes43907582011-04-12 14:25:23 -0700152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 /**
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700154 * Create a new ParcelFileDescriptor wrapped around another descriptor. By
155 * default all method calls are delegated to the wrapped descriptor.
156 */
157 public ParcelFileDescriptor(ParcelFileDescriptor wrapped) {
158 // We keep a strong reference to the wrapped PFD, and rely on its
159 // finalizer to trigger CloseGuard. All calls are delegated to wrapper.
160 mWrapped = wrapped;
161 mFd = null;
162 mCommFd = null;
163 mClosed = true;
164 }
165
166 /** {@hide} */
167 public ParcelFileDescriptor(FileDescriptor fd) {
168 this(fd, null);
169 }
170
171 /** {@hide} */
172 public ParcelFileDescriptor(FileDescriptor fd, FileDescriptor commChannel) {
173 if (fd == null) {
174 throw new NullPointerException("FileDescriptor must not be null");
175 }
176 mWrapped = null;
177 mFd = fd;
178 mCommFd = commChannel;
179 mGuard.open("close");
180 }
181
182 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 * Create a new ParcelFileDescriptor accessing a given file.
Elliott Hughes43907582011-04-12 14:25:23 -0700184 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 * @param file The file to be opened.
186 * @param mode The desired access mode, must be one of
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700187 * {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or
188 * {@link #MODE_READ_WRITE}; may also be any combination of
189 * {@link #MODE_CREATE}, {@link #MODE_TRUNCATE},
190 * {@link #MODE_WORLD_READABLE}, and
191 * {@link #MODE_WORLD_WRITEABLE}.
192 * @return a new ParcelFileDescriptor pointing to the given file.
193 * @throws FileNotFoundException if the given file does not exist or can not
194 * be opened with the requested mode.
Jeff Sharkeye8c00d82013-10-15 15:46:10 -0700195 * @see #parseMode(String)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 */
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700197 public static ParcelFileDescriptor open(File file, int mode) throws FileNotFoundException {
198 final FileDescriptor fd = openInternal(file, mode);
199 if (fd == null) return null;
Elliott Hughes43907582011-04-12 14:25:23 -0700200
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700201 return new ParcelFileDescriptor(fd);
202 }
203
204 /**
205 * Create a new ParcelFileDescriptor accessing a given file.
206 *
207 * @param file The file to be opened.
208 * @param mode The desired access mode, must be one of
209 * {@link #MODE_READ_ONLY}, {@link #MODE_WRITE_ONLY}, or
210 * {@link #MODE_READ_WRITE}; may also be any combination of
211 * {@link #MODE_CREATE}, {@link #MODE_TRUNCATE},
212 * {@link #MODE_WORLD_READABLE}, and
213 * {@link #MODE_WORLD_WRITEABLE}.
214 * @param handler to call listener from; must not be null.
215 * @param listener to be invoked when the returned descriptor has been
216 * closed; must not be null.
217 * @return a new ParcelFileDescriptor pointing to the given file.
218 * @throws FileNotFoundException if the given file does not exist or can not
219 * be opened with the requested mode.
Jeff Sharkeye8c00d82013-10-15 15:46:10 -0700220 * @see #parseMode(String)
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700221 */
222 public static ParcelFileDescriptor open(
223 File file, int mode, Handler handler, OnCloseListener listener) throws IOException {
224 if (handler == null) {
225 throw new IllegalArgumentException("Handler must not be null");
226 }
227 if (listener == null) {
228 throw new IllegalArgumentException("Listener must not be null");
229 }
230
231 final FileDescriptor fd = openInternal(file, mode);
232 if (fd == null) return null;
233
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700234 final FileDescriptor[] comm = createCommSocketPair();
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700235 final ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd, comm[0]);
236
237 // Kick off thread to watch for status updates
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700238 IoUtils.setBlocking(comm[1], true);
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700239 final ListenerBridge bridge = new ListenerBridge(comm[1], handler.getLooper(), listener);
240 bridge.start();
241
242 return pfd;
243 }
244
245 private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
246 if ((mode & MODE_READ_WRITE) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 throw new IllegalArgumentException(
248 "Must specify MODE_READ_ONLY, MODE_WRITE_ONLY, or MODE_READ_WRITE");
249 }
Elliott Hughes43907582011-04-12 14:25:23 -0700250
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700251 final String path = file.getPath();
252 return Parcel.openFileDescriptor(path, mode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 }
254
255 /**
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700256 * Create a new ParcelFileDescriptor that is a dup of an existing
257 * FileDescriptor. This obeys standard POSIX semantics, where the
258 * new file descriptor shared state such as file position with the
259 * original file descriptor.
260 */
261 public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700262 try {
263 final FileDescriptor fd = Libcore.os.dup(orig);
264 return new ParcelFileDescriptor(fd);
265 } catch (ErrnoException e) {
266 throw e.rethrowAsIOException();
267 }
Dianne Hackborne17aeb32011-04-07 15:11:57 -0700268 }
269
270 /**
Dianne Hackborn62f20ec2011-08-15 17:40:28 -0700271 * Create a new ParcelFileDescriptor that is a dup of the existing
272 * FileDescriptor. This obeys standard POSIX semantics, where the
273 * new file descriptor shared state such as file position with the
274 * original file descriptor.
275 */
276 public ParcelFileDescriptor dup() throws IOException {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700277 if (mWrapped != null) {
278 return mWrapped.dup();
279 } else {
280 return dup(getFileDescriptor());
281 }
Dianne Hackborn62f20ec2011-08-15 17:40:28 -0700282 }
283
284 /**
Dianne Hackbornea2117bd2011-05-20 10:37:34 -0700285 * Create a new ParcelFileDescriptor from a raw native fd. The new
286 * ParcelFileDescriptor holds a dup of the original fd passed in here,
287 * so you must still close that fd as well as the new ParcelFileDescriptor.
288 *
289 * @param fd The native fd that the ParcelFileDescriptor should dup.
290 *
291 * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
292 * for a dup of the given fd.
293 */
294 public static ParcelFileDescriptor fromFd(int fd) throws IOException {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700295 final FileDescriptor original = new FileDescriptor();
296 original.setInt$(fd);
Dianne Hackbornea2117bd2011-05-20 10:37:34 -0700297
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700298 try {
299 final FileDescriptor dup = Libcore.os.dup(original);
300 return new ParcelFileDescriptor(dup);
301 } catch (ErrnoException e) {
302 throw e.rethrowAsIOException();
303 }
304 }
Dianne Hackbornea2117bd2011-05-20 10:37:34 -0700305
306 /**
307 * Take ownership of a raw native fd in to a new ParcelFileDescriptor.
308 * The returned ParcelFileDescriptor now owns the given fd, and will be
309 * responsible for closing it. You must not close the fd yourself.
310 *
311 * @param fd The native fd that the ParcelFileDescriptor should adopt.
312 *
313 * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
314 * for the given fd.
315 */
316 public static ParcelFileDescriptor adoptFd(int fd) {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700317 final FileDescriptor fdesc = new FileDescriptor();
318 fdesc.setInt$(fd);
319
Dianne Hackbornea2117bd2011-05-20 10:37:34 -0700320 return new ParcelFileDescriptor(fdesc);
321 }
322
Dianne Hackbornea2117bd2011-05-20 10:37:34 -0700323 /**
324 * Create a new ParcelFileDescriptor from the specified Socket. The new
325 * ParcelFileDescriptor holds a dup of the original FileDescriptor in
326 * the Socket, so you must still close the Socket as well as the new
327 * ParcelFileDescriptor.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 *
329 * @param socket The Socket whose FileDescriptor is used to create
330 * a new ParcelFileDescriptor.
331 *
332 * @return A new ParcelFileDescriptor with the FileDescriptor of the
333 * specified Socket.
334 */
335 public static ParcelFileDescriptor fromSocket(Socket socket) {
Elliott Hughes43907582011-04-12 14:25:23 -0700336 FileDescriptor fd = socket.getFileDescriptor$();
Dianne Hackborn18668392010-03-23 22:10:55 -0700337 return fd != null ? new ParcelFileDescriptor(fd) : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 }
339
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 /**
Chia-chi Yeh47f8f0f2011-05-12 16:23:44 -0700341 * Create a new ParcelFileDescriptor from the specified DatagramSocket.
342 *
343 * @param datagramSocket The DatagramSocket whose FileDescriptor is used
344 * to create a new ParcelFileDescriptor.
345 *
346 * @return A new ParcelFileDescriptor with the FileDescriptor of the
347 * specified DatagramSocket.
348 */
349 public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) {
350 FileDescriptor fd = datagramSocket.getFileDescriptor$();
351 return fd != null ? new ParcelFileDescriptor(fd) : null;
352 }
353
354 /**
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700355 * Create two ParcelFileDescriptors structured as a data pipe. The first
356 * ParcelFileDescriptor in the returned array is the read side; the second
357 * is the write side.
358 */
359 public static ParcelFileDescriptor[] createPipe() throws IOException {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700360 try {
361 final FileDescriptor[] fds = Libcore.os.pipe();
362 return new ParcelFileDescriptor[] {
363 new ParcelFileDescriptor(fds[0]),
364 new ParcelFileDescriptor(fds[1]) };
365 } catch (ErrnoException e) {
366 throw e.rethrowAsIOException();
367 }
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700368 }
369
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700370 /**
371 * Create two ParcelFileDescriptors structured as a data pipe. The first
372 * ParcelFileDescriptor in the returned array is the read side; the second
373 * is the write side.
374 * <p>
375 * The write end has the ability to deliver an error message through
376 * {@link #closeWithError(String)} which can be handled by the read end
Amith Yamasanib433bb82013-09-18 15:10:16 -0700377 * calling {@link #checkError()}, usually after detecting an EOF.
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700378 * This can also be used to detect remote crashes.
379 */
380 public static ParcelFileDescriptor[] createReliablePipe() throws IOException {
381 try {
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700382 final FileDescriptor[] comm = createCommSocketPair();
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700383 final FileDescriptor[] fds = Libcore.os.pipe();
384 return new ParcelFileDescriptor[] {
385 new ParcelFileDescriptor(fds[0], comm[0]),
386 new ParcelFileDescriptor(fds[1], comm[1]) };
387 } catch (ErrnoException e) {
388 throw e.rethrowAsIOException();
389 }
390 }
391
392 /**
393 * Create two ParcelFileDescriptors structured as a pair of sockets
394 * connected to each other. The two sockets are indistinguishable.
395 */
396 public static ParcelFileDescriptor[] createSocketPair() throws IOException {
397 try {
398 final FileDescriptor fd0 = new FileDescriptor();
399 final FileDescriptor fd1 = new FileDescriptor();
400 Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1);
401 return new ParcelFileDescriptor[] {
402 new ParcelFileDescriptor(fd0),
403 new ParcelFileDescriptor(fd1) };
404 } catch (ErrnoException e) {
405 throw e.rethrowAsIOException();
406 }
407 }
408
409 /**
410 * Create two ParcelFileDescriptors structured as a pair of sockets
411 * connected to each other. The two sockets are indistinguishable.
412 * <p>
413 * Both ends have the ability to deliver an error message through
414 * {@link #closeWithError(String)} which can be detected by the other end
Amith Yamasanib433bb82013-09-18 15:10:16 -0700415 * calling {@link #checkError()}, usually after detecting an EOF.
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700416 * This can also be used to detect remote crashes.
417 */
418 public static ParcelFileDescriptor[] createReliableSocketPair() throws IOException {
419 try {
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700420 final FileDescriptor[] comm = createCommSocketPair();
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700421 final FileDescriptor fd0 = new FileDescriptor();
422 final FileDescriptor fd1 = new FileDescriptor();
423 Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd0, fd1);
424 return new ParcelFileDescriptor[] {
425 new ParcelFileDescriptor(fd0, comm[0]),
426 new ParcelFileDescriptor(fd1, comm[1]) };
427 } catch (ErrnoException e) {
428 throw e.rethrowAsIOException();
429 }
430 }
431
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700432 private static FileDescriptor[] createCommSocketPair() throws IOException {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700433 try {
434 final FileDescriptor comm1 = new FileDescriptor();
435 final FileDescriptor comm2 = new FileDescriptor();
436 Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, comm1, comm2);
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700437 IoUtils.setBlocking(comm1, false);
438 IoUtils.setBlocking(comm2, false);
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700439 return new FileDescriptor[] { comm1, comm2 };
440 } catch (ErrnoException e) {
441 throw e.rethrowAsIOException();
442 }
443 }
Dianne Hackborn23fdaf62010-08-06 12:16:55 -0700444
445 /**
Dianne Hackborn540f86a2011-01-11 17:52:22 -0800446 * @hide Please use createPipe() or ContentProvider.openPipeHelper().
Bjorn Bringerta006b4722010-04-14 14:43:26 +0100447 * Gets a file descriptor for a read-only copy of the given data.
448 *
449 * @param data Data to copy.
450 * @param name Name for the shared memory area that may back the file descriptor.
451 * This is purely informative and may be {@code null}.
452 * @return A ParcelFileDescriptor.
453 * @throws IOException if there is an error while creating the shared memory area.
454 */
Dianne Hackborna2ea7472010-12-20 12:10:01 -0800455 @Deprecated
Bjorn Bringerta006b4722010-04-14 14:43:26 +0100456 public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException {
457 if (data == null) return null;
458 MemoryFile file = new MemoryFile(name, data.length);
459 if (data.length > 0) {
460 file.writeBytes(data, 0, 0, data.length);
461 }
462 file.deactivate();
463 FileDescriptor fd = file.getFileDescriptor();
464 return fd != null ? new ParcelFileDescriptor(fd) : null;
465 }
466
467 /**
Adam Lesinskieb8c3f92013-09-20 14:08:25 -0700468 * Converts a string representing a file mode, such as "rw", into a bitmask suitable for use
469 * with {@link #open}.
470 * <p>
471 * @param mode The string representation of the file mode.
472 * @return A bitmask representing the given file mode.
473 * @throws IllegalArgumentException if the given string does not match a known file mode.
474 */
475 public static int parseMode(String mode) {
476 final int modeBits;
477 if ("r".equals(mode)) {
478 modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
479 } else if ("w".equals(mode) || "wt".equals(mode)) {
480 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
481 | ParcelFileDescriptor.MODE_CREATE
482 | ParcelFileDescriptor.MODE_TRUNCATE;
483 } else if ("wa".equals(mode)) {
484 modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
485 | ParcelFileDescriptor.MODE_CREATE
486 | ParcelFileDescriptor.MODE_APPEND;
487 } else if ("rw".equals(mode)) {
488 modeBits = ParcelFileDescriptor.MODE_READ_WRITE
489 | ParcelFileDescriptor.MODE_CREATE;
490 } else if ("rwt".equals(mode)) {
491 modeBits = ParcelFileDescriptor.MODE_READ_WRITE
492 | ParcelFileDescriptor.MODE_CREATE
493 | ParcelFileDescriptor.MODE_TRUNCATE;
494 } else {
495 throw new IllegalArgumentException("Bad mode '" + mode + "'");
496 }
497 return modeBits;
498 }
499
500 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 * Retrieve the actual FileDescriptor associated with this object.
Elliott Hughes43907582011-04-12 14:25:23 -0700502 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 * @return Returns the FileDescriptor associated with this object.
504 */
505 public FileDescriptor getFileDescriptor() {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700506 if (mWrapped != null) {
507 return mWrapped.getFileDescriptor();
508 } else {
509 return mFd;
510 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 }
Elliott Hughes43907582011-04-12 14:25:23 -0700512
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 /**
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700514 * Return the total size of the file representing this fd, as determined by
515 * {@code stat()}. Returns -1 if the fd is not a file.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 */
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700517 public long getStatSize() {
518 if (mWrapped != null) {
519 return mWrapped.getStatSize();
520 } else {
521 try {
522 final StructStat st = Libcore.os.fstat(mFd);
523 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
524 return st.st_size;
525 } else {
526 return -1;
527 }
528 } catch (ErrnoException e) {
529 Log.w(TAG, "fstat() failed: " + e);
530 return -1;
531 }
532 }
533 }
Elliott Hughes43907582011-04-12 14:25:23 -0700534
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 /**
536 * This is needed for implementing AssetFileDescriptor.AutoCloseOutputStream,
537 * and I really don't think we want it to be public.
538 * @hide
539 */
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700540 public long seekTo(long pos) throws IOException {
541 if (mWrapped != null) {
542 return mWrapped.seekTo(pos);
543 } else {
544 try {
545 return Libcore.os.lseek(mFd, pos, SEEK_SET);
546 } catch (ErrnoException e) {
547 throw e.rethrowAsIOException();
548 }
549 }
550 }
Elliott Hughes43907582011-04-12 14:25:23 -0700551
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 /**
Dianne Hackbornc9119f52011-02-28 18:03:26 -0800553 * Return the native fd int for this ParcelFileDescriptor. The
554 * ParcelFileDescriptor still owns the fd, and it still must be closed
555 * through this API.
556 */
557 public int getFd() {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700558 if (mWrapped != null) {
559 return mWrapped.getFd();
560 } else {
561 if (mClosed) {
562 throw new IllegalStateException("Already closed");
563 }
564 return mFd.getInt$();
Dianne Hackbornc9119f52011-02-28 18:03:26 -0800565 }
Dianne Hackbornc9119f52011-02-28 18:03:26 -0800566 }
Elliott Hughes43907582011-04-12 14:25:23 -0700567
Dianne Hackbornc9119f52011-02-28 18:03:26 -0800568 /**
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700569 * Return the native fd int for this ParcelFileDescriptor and detach it from
570 * the object here. You are now responsible for closing the fd in native
571 * code.
572 * <p>
573 * You should not detach when the original creator of the descriptor is
574 * expecting a reliable signal through {@link #close()} or
575 * {@link #closeWithError(String)}.
576 *
577 * @see #canDetectErrors()
Dianne Hackbornc9119f52011-02-28 18:03:26 -0800578 */
579 public int detachFd() {
Jeff Sharkey7407c942012-11-12 13:42:49 -0800580 if (mWrapped != null) {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700581 return mWrapped.detachFd();
582 } else {
583 if (mClosed) {
584 throw new IllegalStateException("Already closed");
585 }
586 final int fd = getFd();
587 Parcel.clearFileDescriptor(mFd);
588 writeCommStatusAndClose(Status.DETACHED, null);
Dianne Hackbornc9119f52011-02-28 18:03:26 -0800589 return fd;
590 }
Dianne Hackbornc9119f52011-02-28 18:03:26 -0800591 }
Elliott Hughes43907582011-04-12 14:25:23 -0700592
Dianne Hackbornc9119f52011-02-28 18:03:26 -0800593 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 * Close the ParcelFileDescriptor. This implementation closes the underlying
595 * OS resources allocated to represent this stream.
Elliott Hughes43907582011-04-12 14:25:23 -0700596 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 * @throws IOException
598 * If an error occurs attempting to close this ParcelFileDescriptor.
599 */
Jeff Sharkey7407c942012-11-12 13:42:49 -0800600 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 public void close() throws IOException {
Jeff Sharkey7407c942012-11-12 13:42:49 -0800602 if (mWrapped != null) {
Amith Yamasani487c11a2013-09-18 09:16:15 -0700603 try {
604 mWrapped.close();
605 } finally {
606 releaseResources();
607 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 } else {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700609 closeWithStatus(Status.OK, null);
610 }
611 }
612
613 /**
614 * Close the ParcelFileDescriptor, informing any peer that an error occurred
615 * while processing. If the creator of this descriptor is not observing
616 * errors, it will close normally.
617 *
618 * @param msg describing the error; must not be null.
619 */
620 public void closeWithError(String msg) throws IOException {
621 if (mWrapped != null) {
Amith Yamasani487c11a2013-09-18 09:16:15 -0700622 try {
623 mWrapped.closeWithError(msg);
624 } finally {
625 releaseResources();
626 }
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700627 } else {
628 if (msg == null) {
629 throw new IllegalArgumentException("Message must not be null");
630 }
631 closeWithStatus(Status.ERROR, msg);
632 }
633 }
634
Amith Yamasani487c11a2013-09-18 09:16:15 -0700635 private void closeWithStatus(int status, String msg) {
636 if (mClosed) return;
637 mClosed = true;
638 mGuard.close();
639 // Status MUST be sent before closing actual descriptor
640 writeCommStatusAndClose(status, msg);
641 IoUtils.closeQuietly(mFd);
642 releaseResources();
643 }
644
645 /**
646 * Called when the fd is being closed, for subclasses to release any other resources
647 * associated with it, such as acquired providers.
648 * @hide
649 */
650 public void releaseResources() {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700651 }
652
653 private byte[] getOrCreateStatusBuffer() {
654 if (mStatusBuf == null) {
655 mStatusBuf = new byte[MAX_STATUS];
656 }
657 return mStatusBuf;
658 }
659
660 private void writeCommStatusAndClose(int status, String msg) {
661 if (mCommFd == null) {
662 // Not reliable, or someone already sent status
663 if (msg != null) {
664 Log.w(TAG, "Unable to inform peer: " + msg);
665 }
666 return;
667 }
668
669 if (status == Status.DETACHED) {
670 Log.w(TAG, "Peer expected signal when closed; unable to deliver after detach");
671 }
672
673 try {
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700674 if (status == Status.SILENCE) return;
675
676 // Since we're about to close, read off any remote status. It's
677 // okay to remember missing here.
678 mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer());
679
680 // Skip writing status when other end has already gone away.
681 if (mStatus != null) return;
682
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700683 try {
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700684 final byte[] buf = getOrCreateStatusBuffer();
685 int writePtr = 0;
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700686
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700687 Memory.pokeInt(buf, writePtr, status, ByteOrder.BIG_ENDIAN);
688 writePtr += 4;
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700689
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700690 if (msg != null) {
691 final byte[] rawMsg = msg.getBytes();
692 final int len = Math.min(rawMsg.length, buf.length - writePtr);
693 System.arraycopy(rawMsg, 0, buf, writePtr, len);
694 writePtr += len;
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700695 }
Jeff Sharkeyd99f9ca2013-10-25 10:22:18 -0700696
697 Libcore.os.write(mCommFd, buf, 0, writePtr);
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700698 } catch (ErrnoException e) {
699 // Reporting status is best-effort
700 Log.w(TAG, "Failed to report status: " + e);
701 }
702
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700703 } finally {
704 IoUtils.closeQuietly(mCommFd);
705 mCommFd = null;
706 }
707 }
708
709 private static Status readCommStatus(FileDescriptor comm, byte[] buf) {
710 try {
711 final int n = Libcore.os.read(comm, buf, 0, buf.length);
712 if (n == 0) {
713 // EOF means they're dead
714 return new Status(Status.DEAD);
715 } else {
716 final int status = Memory.peekInt(buf, 0, ByteOrder.BIG_ENDIAN);
717 if (status == Status.ERROR) {
718 final String msg = new String(buf, 4, n - 4);
719 return new Status(status, msg);
720 }
721 return new Status(status);
722 }
723 } catch (ErrnoException e) {
724 if (e.errno == OsConstants.EAGAIN) {
725 // Remote is still alive, but no status written yet
726 return null;
727 } else {
728 Log.d(TAG, "Failed to read status; assuming dead: " + e);
729 return new Status(Status.DEAD);
730 }
731 }
732 }
733
734 /**
735 * Indicates if this ParcelFileDescriptor can communicate and detect remote
736 * errors/crashes.
737 *
Amith Yamasanib433bb82013-09-18 15:10:16 -0700738 * @see #checkError()
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700739 */
740 public boolean canDetectErrors() {
741 if (mWrapped != null) {
742 return mWrapped.canDetectErrors();
743 } else {
744 return mCommFd != null;
745 }
746 }
747
748 /**
749 * Detect and throw if the other end of a pipe or socket pair encountered an
750 * error or crashed. This allows a reader to distinguish between a valid EOF
751 * and an error/crash.
752 * <p>
753 * If this ParcelFileDescriptor is unable to detect remote errors, it will
754 * return silently.
755 *
Amith Yamasanib433bb82013-09-18 15:10:16 -0700756 * @throws IOException for normal errors.
757 * @throws FileDescriptorDetachedException
758 * if the remote side called {@link #detachFd()}. Once detached, the remote
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700759 * side is unable to communicate any errors through
Amith Yamasanib433bb82013-09-18 15:10:16 -0700760 * {@link #closeWithError(String)}.
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700761 * @see #canDetectErrors()
762 */
Amith Yamasanib433bb82013-09-18 15:10:16 -0700763 public void checkError() throws IOException {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700764 if (mWrapped != null) {
Amith Yamasanib433bb82013-09-18 15:10:16 -0700765 mWrapped.checkError();
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700766 } else {
767 if (mStatus == null) {
768 if (mCommFd == null) {
769 Log.w(TAG, "Peer didn't provide a comm channel; unable to check for errors");
770 return;
771 }
772
773 // Try reading status; it might be null if nothing written yet.
774 // Either way, we keep comm open to write our status later.
775 mStatus = readCommStatus(mCommFd, getOrCreateStatusBuffer());
776 }
777
Amith Yamasanib433bb82013-09-18 15:10:16 -0700778 if (mStatus == null || mStatus.status == Status.OK) {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700779 // No status yet, or everything is peachy!
780 return;
781 } else {
782 throw mStatus.asIOException();
783 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 }
785 }
Elliott Hughes43907582011-04-12 14:25:23 -0700786
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 /**
788 * An InputStream you can create on a ParcelFileDescriptor, which will
789 * take care of calling {@link ParcelFileDescriptor#close
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700790 * ParcelFileDescriptor.close()} for you when the stream is closed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 */
792 public static class AutoCloseInputStream extends FileInputStream {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700793 private final ParcelFileDescriptor mPfd;
Elliott Hughes43907582011-04-12 14:25:23 -0700794
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700795 public AutoCloseInputStream(ParcelFileDescriptor pfd) {
796 super(pfd.getFileDescriptor());
797 mPfd = pfd;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 }
799
800 @Override
801 public void close() throws IOException {
Brian Carlstromfd9ddd12010-11-04 11:24:58 -0700802 try {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700803 mPfd.close();
Brian Carlstromfd9ddd12010-11-04 11:24:58 -0700804 } finally {
805 super.close();
806 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 }
808 }
Elliott Hughes43907582011-04-12 14:25:23 -0700809
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810 /**
811 * An OutputStream you can create on a ParcelFileDescriptor, which will
812 * take care of calling {@link ParcelFileDescriptor#close
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700813 * ParcelFileDescriptor.close()} for you when the stream is closed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 */
815 public static class AutoCloseOutputStream extends FileOutputStream {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700816 private final ParcelFileDescriptor mPfd;
Elliott Hughes43907582011-04-12 14:25:23 -0700817
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700818 public AutoCloseOutputStream(ParcelFileDescriptor pfd) {
819 super(pfd.getFileDescriptor());
820 mPfd = pfd;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 }
822
823 @Override
824 public void close() throws IOException {
Brian Carlstromfd9ddd12010-11-04 11:24:58 -0700825 try {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700826 mPfd.close();
Brian Carlstromfd9ddd12010-11-04 11:24:58 -0700827 } finally {
828 super.close();
829 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 }
831 }
Elliott Hughes43907582011-04-12 14:25:23 -0700832
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 @Override
834 public String toString() {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700835 if (mWrapped != null) {
836 return mWrapped.toString();
837 } else {
838 return "{ParcelFileDescriptor: " + mFd + "}";
839 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 }
Elliott Hughes43907582011-04-12 14:25:23 -0700841
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 @Override
843 protected void finalize() throws Throwable {
Amith Yamasani487c11a2013-09-18 09:16:15 -0700844 if (mWrapped != null) {
845 releaseResources();
846 }
Jeff Sharkey7407c942012-11-12 13:42:49 -0800847 if (mGuard != null) {
848 mGuard.warnIfOpen();
849 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 try {
851 if (!mClosed) {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700852 closeWithStatus(Status.LEAKED, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 }
854 } finally {
855 super.finalize();
856 }
857 }
Elliott Hughes43907582011-04-12 14:25:23 -0700858
Jeff Sharkey7407c942012-11-12 13:42:49 -0800859 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 public int describeContents() {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700861 if (mWrapped != null) {
862 return mWrapped.describeContents();
863 } else {
864 return Parcelable.CONTENTS_FILE_DESCRIPTOR;
865 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 }
867
Dan Egnorb3e4ef32010-07-20 09:03:35 -0700868 /**
869 * {@inheritDoc}
870 * If {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set in flags,
871 * the file descriptor will be closed after a copy is written to the Parcel.
872 */
Jeff Sharkey7407c942012-11-12 13:42:49 -0800873 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 public void writeToParcel(Parcel out, int flags) {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700875 if (mWrapped != null) {
Amith Yamasani487c11a2013-09-18 09:16:15 -0700876 try {
877 mWrapped.writeToParcel(out, flags);
878 } finally {
879 releaseResources();
880 }
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700881 } else {
882 out.writeFileDescriptor(mFd);
883 if (mCommFd != null) {
884 out.writeInt(1);
885 out.writeFileDescriptor(mCommFd);
886 } else {
887 out.writeInt(0);
888 }
889 if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0 && !mClosed) {
Amith Yamasani487c11a2013-09-18 09:16:15 -0700890 // Not a real close, so emit no status
891 closeWithStatus(Status.SILENCE, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 }
893 }
894 }
895
896 public static final Parcelable.Creator<ParcelFileDescriptor> CREATOR
897 = new Parcelable.Creator<ParcelFileDescriptor>() {
Jeff Sharkey7407c942012-11-12 13:42:49 -0800898 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 public ParcelFileDescriptor createFromParcel(Parcel in) {
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700900 final FileDescriptor fd = in.readRawFileDescriptor();
901 FileDescriptor commChannel = null;
902 if (in.readInt() != 0) {
903 commChannel = in.readRawFileDescriptor();
904 }
905 return new ParcelFileDescriptor(fd, commChannel);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 }
Jeff Sharkey7407c942012-11-12 13:42:49 -0800907
908 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 public ParcelFileDescriptor[] newArray(int size) {
910 return new ParcelFileDescriptor[size];
911 }
912 };
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700913
914 /**
915 * Callback indicating that a ParcelFileDescriptor has been closed.
916 */
917 public interface OnCloseListener {
918 /**
919 * Event indicating the ParcelFileDescriptor to which this listener was
920 * attached has been closed.
921 *
922 * @param e error state, or {@code null} if closed cleanly.
Amith Yamasanib433bb82013-09-18 15:10:16 -0700923 * If the close event was the result of
924 * {@link ParcelFileDescriptor#detachFd()}, this will be a
925 * {@link FileDescriptorDetachedException}. After detach the
926 * remote side may continue reading/writing to the underlying
927 * {@link FileDescriptor}, but they can no longer deliver
928 * reliable close/error events.
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700929 */
Amith Yamasanib433bb82013-09-18 15:10:16 -0700930 public void onClose(IOException e);
931 }
932
933 /**
934 * Exception that indicates that the file descriptor was detached.
935 */
936 public static class FileDescriptorDetachedException extends IOException {
937
938 private static final long serialVersionUID = 0xDe7ac4edFdL;
939
940 public FileDescriptorDetachedException() {
941 super("Remote side is detached");
942 }
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700943 }
944
945 /**
946 * Internal class representing a remote status read by
947 * {@link ParcelFileDescriptor#readCommStatus(FileDescriptor, byte[])}.
948 */
949 private static class Status {
950 /** Special value indicating remote side died. */
951 public static final int DEAD = -2;
952 /** Special value indicating no status should be written. */
953 public static final int SILENCE = -1;
954
955 /** Remote reported that everything went better than expected. */
956 public static final int OK = 0;
957 /** Remote reported error; length and message follow. */
958 public static final int ERROR = 1;
959 /** Remote reported {@link #detachFd()} and went rogue. */
960 public static final int DETACHED = 2;
961 /** Remote reported their object was finalized. */
962 public static final int LEAKED = 3;
963
964 public final int status;
965 public final String msg;
966
967 public Status(int status) {
968 this(status, null);
969 }
970
971 public Status(int status, String msg) {
972 this.status = status;
973 this.msg = msg;
974 }
975
976 public IOException asIOException() {
977 switch (status) {
978 case DEAD:
979 return new IOException("Remote side is dead");
980 case OK:
981 return null;
982 case ERROR:
983 return new IOException("Remote error: " + msg);
984 case DETACHED:
Amith Yamasanib433bb82013-09-18 15:10:16 -0700985 return new FileDescriptorDetachedException();
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -0700986 case LEAKED:
987 return new IOException("Remote side was leaked");
988 default:
989 return new IOException("Unknown status: " + status);
990 }
991 }
992 }
993
994 /**
995 * Bridge to watch for remote status, and deliver to listener. Currently
996 * requires that communication socket is <em>blocking</em>.
997 */
998 private static final class ListenerBridge extends Thread {
999 // TODO: switch to using Looper to avoid burning a thread
1000
1001 private FileDescriptor mCommFd;
1002 private final Handler mHandler;
1003
1004 public ListenerBridge(FileDescriptor comm, Looper looper, final OnCloseListener listener) {
1005 mCommFd = comm;
1006 mHandler = new Handler(looper) {
1007 @Override
1008 public void handleMessage(Message msg) {
1009 final Status s = (Status) msg.obj;
Amith Yamasanib433bb82013-09-18 15:10:16 -07001010 listener.onClose(s != null ? s.asIOException() : null);
Jeff Sharkeyda5a3e12013-08-11 12:54:42 -07001011 }
1012 };
1013 }
1014
1015 @Override
1016 public void run() {
1017 try {
1018 final byte[] buf = new byte[MAX_STATUS];
1019 final Status status = readCommStatus(mCommFd, buf);
1020 mHandler.obtainMessage(0, status).sendToTarget();
1021 } finally {
1022 IoUtils.closeQuietly(mCommFd);
1023 mCommFd = null;
1024 }
1025 }
1026 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027}