1 /* 2 * Copyright (C) 2009 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.cts; 18 19 import static org.junit.Assert.assertArrayEquals; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNull; 23 import static org.junit.Assert.assertSame; 24 import static org.junit.Assert.assertThrows; 25 import static org.junit.Assert.assertTrue; 26 import static org.junit.Assert.fail; 27 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.os.MessageQueue; 31 import android.os.Parcel; 32 import android.os.ParcelFileDescriptor; 33 import android.os.ParcelFileDescriptor.AutoCloseInputStream; 34 import android.os.Parcelable; 35 import android.platform.test.annotations.AppModeFull; 36 import android.platform.test.annotations.AppModeSdkSandbox; 37 import android.platform.test.annotations.IgnoreUnderRavenwood; 38 import android.platform.test.ravenwood.RavenwoodRule; 39 import android.system.ErrnoException; 40 import android.system.Os; 41 import android.system.OsConstants; 42 43 import androidx.test.runner.AndroidJUnit4; 44 45 import com.google.common.util.concurrent.AbstractFuture; 46 47 import junit.framework.ComparisonFailure; 48 49 import org.junit.Rule; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 53 import java.io.File; 54 import java.io.FileDescriptor; 55 import java.io.FileInputStream; 56 import java.io.FileOutputStream; 57 import java.io.IOException; 58 import java.io.InputStream; 59 import java.io.OutputStream; 60 import java.net.InetAddress; 61 import java.net.ServerSocket; 62 import java.net.Socket; 63 import java.nio.file.Files; 64 import java.util.concurrent.ExecutionException; 65 import java.util.concurrent.TimeUnit; 66 import java.util.concurrent.TimeoutException; 67 68 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).") 69 @RunWith(AndroidJUnit4.class) 70 public class ParcelFileDescriptorTest { 71 @Rule 72 public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() 73 .setProvideMainThread(true).build(); 74 75 private static final long DURATION = 100l; 76 77 @Test testConstructorAndOpen()78 public void testConstructorAndOpen() throws Exception { 79 ParcelFileDescriptor tempFile = makeParcelFileDescriptor(); 80 81 ParcelFileDescriptor pfd = new ParcelFileDescriptor(tempFile); 82 AutoCloseInputStream in = new AutoCloseInputStream(pfd); 83 try { 84 // read the data that was wrote previously 85 assertEquals(0, in.read()); 86 assertEquals(1, in.read()); 87 assertEquals(2, in.read()); 88 assertEquals(3, in.read()); 89 } finally { 90 in.close(); 91 } 92 } 93 94 @Test testDetachAdopt()95 public void testDetachAdopt() throws Exception { 96 ParcelFileDescriptor tempFile = makeParcelFileDescriptor(); 97 98 ParcelFileDescriptor before = new ParcelFileDescriptor(tempFile); 99 ParcelFileDescriptor after = ParcelFileDescriptor.adoptFd(before.detachFd()); 100 101 // Verify reading from post-adopted FD works 102 try (AutoCloseInputStream in = new AutoCloseInputStream(after)) { 103 assertEquals(0, in.read()); 104 assertEquals(1, in.read()); 105 assertEquals(2, in.read()); 106 assertEquals(3, in.read()); 107 } 108 109 // Verify trying to detach a second time fails 110 assertThrows(IllegalStateException.class, () -> { 111 before.detachFd(); 112 }); 113 } 114 115 @Test testReadOnly()116 public void testReadOnly() throws Exception { 117 final File file = File.createTempFile("testReadOnly", "bin"); 118 file.createNewFile(); 119 try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, 120 ParcelFileDescriptor.MODE_READ_ONLY); 121 ParcelFileDescriptor.AutoCloseOutputStream out = 122 new ParcelFileDescriptor.AutoCloseOutputStream(pfd)) { 123 assertThrows(IOException.class, () -> { 124 out.write(42); 125 }); 126 } 127 } 128 129 @Test testNormal()130 public void testNormal() throws Exception { 131 final File file = File.createTempFile("testNormal", "bin"); 132 doMultiWrite(file, ParcelFileDescriptor.MODE_READ_WRITE 133 | ParcelFileDescriptor.MODE_CREATE); 134 assertArrayEquals(new byte[]{21, 42}, Files.readAllBytes(file.toPath())); 135 } 136 137 @Test testAppend()138 public void testAppend() throws Exception { 139 final File file = File.createTempFile("testAppend", "bin"); 140 doMultiWrite(file, ParcelFileDescriptor.MODE_READ_WRITE 141 | ParcelFileDescriptor.MODE_CREATE 142 | ParcelFileDescriptor.MODE_APPEND); 143 assertArrayEquals(new byte[]{42, 42, 21}, Files.readAllBytes(file.toPath())); 144 } 145 146 @Test testTruncate()147 public void testTruncate() throws Exception { 148 final File file = File.createTempFile("testTruncate", "bin"); 149 doMultiWrite(file, ParcelFileDescriptor.MODE_READ_WRITE 150 | ParcelFileDescriptor.MODE_CREATE 151 | ParcelFileDescriptor.MODE_TRUNCATE); 152 assertArrayEquals(new byte[]{21}, Files.readAllBytes(file.toPath())); 153 } 154 doMultiWrite(File file, int flags)155 private void doMultiWrite(File file, int flags) throws Exception { 156 try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, flags); 157 ParcelFileDescriptor.AutoCloseOutputStream out = 158 new ParcelFileDescriptor.AutoCloseOutputStream(pfd)) { 159 out.write(42); 160 out.write(42); 161 } 162 try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, flags); 163 ParcelFileDescriptor.AutoCloseOutputStream out = 164 new ParcelFileDescriptor.AutoCloseOutputStream(pfd)) { 165 out.write(21); 166 } 167 } 168 169 private static class DoneSignal extends AbstractFuture<Void> { set()170 public boolean set() { 171 return super.set(null); 172 } 173 174 @Override setException(Throwable t)175 public boolean setException(Throwable t) { 176 return super.setException(t); 177 } 178 } 179 180 @Test 181 @AppModeFull // opening a listening socket not permitted for instant apps 182 @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class) testFromSocket()183 public void testFromSocket() throws Throwable { 184 final int PORT = 12222; 185 final int DATA = 1; 186 187 final DoneSignal done = new DoneSignal(); 188 189 final Thread t = new Thread(new Runnable() { 190 @Override 191 public void run() { 192 try { 193 ServerSocket ss; 194 ss = new ServerSocket(PORT); 195 Socket sSocket = ss.accept(); 196 OutputStream out = sSocket.getOutputStream(); 197 out.write(DATA); 198 Thread.sleep(DURATION); 199 out.close(); 200 done.set(); 201 } catch (Exception e) { 202 done.setException(e); 203 } 204 } 205 }); 206 t.start(); 207 208 Thread.sleep(DURATION); 209 Socket socket; 210 socket = new Socket(InetAddress.getLocalHost(), PORT); 211 ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket); 212 AutoCloseInputStream in = new AutoCloseInputStream(pfd); 213 assertEquals(DATA, in.read()); 214 in.close(); 215 socket.close(); 216 pfd.close(); 217 218 done.get(5, TimeUnit.SECONDS); 219 } 220 assertFileDescriptorContent(byte[] expected, ParcelFileDescriptor fd)221 private static void assertFileDescriptorContent(byte[] expected, ParcelFileDescriptor fd) 222 throws IOException { 223 assertInputStreamContent(expected, new ParcelFileDescriptor.AutoCloseInputStream(fd)); 224 } 225 assertInputStreamContent(byte[] expected, InputStream is)226 private static void assertInputStreamContent(byte[] expected, InputStream is) 227 throws IOException { 228 try { 229 byte[] observed = new byte[expected.length]; 230 int count = is.read(observed); 231 assertEquals(expected.length, count); 232 assertEquals(-1, is.read()); 233 assertArrayEquals(expected, observed); 234 } finally { 235 is.close(); 236 } 237 } 238 239 @Test testToString()240 public void testToString() throws Exception { 241 ParcelFileDescriptor pfd = makeParcelFileDescriptor(); 242 assertNotNull(pfd.toString()); 243 } 244 245 @Test 246 @IgnoreUnderRavenwood(blockedBy = Parcel.class) testWriteToParcel()247 public void testWriteToParcel() throws Exception { 248 ParcelFileDescriptor pf = makeParcelFileDescriptor(); 249 250 Parcel pl = Parcel.obtain(); 251 pf.writeToParcel(pl, ParcelFileDescriptor.PARCELABLE_WRITE_RETURN_VALUE); 252 pl.setDataPosition(0); 253 ParcelFileDescriptor pfd = ParcelFileDescriptor.CREATOR.createFromParcel(pl); 254 AutoCloseInputStream in = new AutoCloseInputStream(pfd); 255 try { 256 // read the data that was wrote previously 257 assertEquals(0, in.read()); 258 assertEquals(1, in.read()); 259 assertEquals(2, in.read()); 260 assertEquals(3, in.read()); 261 } finally { 262 in.close(); 263 } 264 } 265 266 @Test testClose()267 public void testClose() throws Exception { 268 ParcelFileDescriptor pf = makeParcelFileDescriptor(); 269 AutoCloseInputStream in1 = new AutoCloseInputStream(pf); 270 try { 271 assertEquals(0, in1.read()); 272 } finally { 273 in1.close(); 274 } 275 276 pf.close(); 277 278 AutoCloseInputStream in2 = new AutoCloseInputStream(pf); 279 try { 280 assertEquals(0, in2.read()); 281 fail("Failed to throw exception."); 282 } catch (Exception e) { 283 // expected 284 } finally { 285 in2.close(); 286 } 287 } 288 289 @Test 290 @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class) testGetStatSize()291 public void testGetStatSize() throws Exception { 292 ParcelFileDescriptor pf = makeParcelFileDescriptor(); 293 assertTrue(pf.getStatSize() >= 0); 294 } 295 296 @Test testGetFileDescriptor()297 public void testGetFileDescriptor() throws Exception { 298 ParcelFileDescriptor pfd = makeParcelFileDescriptor(); 299 assertNotNull(pfd.getFileDescriptor()); 300 301 ParcelFileDescriptor p = new ParcelFileDescriptor(pfd); 302 assertSame(pfd.getFileDescriptor(), p.getFileDescriptor()); 303 } 304 305 @Test testDescribeContents()306 public void testDescribeContents() throws Exception{ 307 ParcelFileDescriptor pfd = makeParcelFileDescriptor(); 308 assertTrue((Parcelable.CONTENTS_FILE_DESCRIPTOR & pfd.describeContents()) != 0); 309 } 310 assertContains(String expected, String actual)311 private static void assertContains(String expected, String actual) { 312 if (actual.contains(expected)) return; 313 throw new ComparisonFailure("", expected, actual); 314 } 315 write(ParcelFileDescriptor pfd, int oneByte)316 private static void write(ParcelFileDescriptor pfd, int oneByte) throws IOException{ 317 new FileOutputStream(pfd.getFileDescriptor()).write(oneByte); 318 } 319 320 // This method is unlikely to be used by clients, as clients use ContentResolver, 321 // which builds AutoCloseInputStream under the hood rather than FileInputStream 322 // built from a raw FD. 323 // 324 // Using new FileInputStream(PFD.getFileDescriptor()) is discouraged, as error 325 // propagation is lost, and read() will never throw IOException in such case. read(ParcelFileDescriptor pfd)326 private static int read(ParcelFileDescriptor pfd) throws IOException { 327 return new FileInputStream(pfd.getFileDescriptor()).read(); 328 } 329 330 @Test testBasicPipeNormal()331 public void testBasicPipeNormal() throws Exception { 332 final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 333 final ParcelFileDescriptor red = pipe[0]; 334 final ParcelFileDescriptor blue = pipe[1]; 335 336 write(blue, 1); 337 assertEquals(1, read(red)); 338 339 blue.close(); 340 assertEquals(-1, read(red)); 341 red.checkError(); 342 } 343 344 @Test 345 @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class) testPipeNormal()346 public void testPipeNormal() throws Exception { 347 final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe(); 348 final ParcelFileDescriptor red = pipe[0]; 349 final ParcelFileDescriptor blue = pipe[1]; 350 351 write(blue, 1); 352 assertEquals(1, read(red)); 353 354 blue.close(); 355 assertEquals(-1, read(red)); 356 red.checkError(); 357 } 358 359 // Reading should be done via AutoCloseInputStream if possible, rather than 360 // recreating a FileInputStream from a raw FD, what's done in read(PFD). 361 @Test 362 @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class) testPipeError_Discouraged()363 public void testPipeError_Discouraged() throws Exception { 364 final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe(); 365 final ParcelFileDescriptor red = pipe[0]; 366 final ParcelFileDescriptor blue = pipe[1]; 367 368 write(blue, 2); 369 blue.closeWithError("OMG MUFFINS"); 370 371 // Even though closed we should still drain pipe. 372 assertEquals(2, read(red)); 373 assertEquals(-1, read(red)); 374 try { 375 red.checkError(); 376 fail("expected throw!"); 377 } catch (IOException e) { 378 assertContains("OMG MUFFINS", e.getMessage()); 379 } 380 } 381 382 @Test 383 @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class) testPipeError()384 public void testPipeError() throws Exception { 385 final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe(); 386 final ParcelFileDescriptor red = pipe[0]; 387 final ParcelFileDescriptor blue = pipe[1]; 388 389 write(blue, 2); 390 blue.closeWithError("OMG MUFFINS"); 391 392 try (AutoCloseInputStream is = new AutoCloseInputStream(red)) { 393 is.read(); 394 is.read(); // Checks errors on EOF. 395 fail("expected throw!"); 396 } catch (IOException e) { 397 assertContains("OMG MUFFINS", e.getMessage()); 398 } 399 } 400 401 @Test 402 @IgnoreUnderRavenwood(blockedBy = MessageQueue.class) testFileNormal()403 public void testFileNormal() throws Exception { 404 final Handler handler = new Handler(Looper.getMainLooper()); 405 final FutureCloseListener listener = new FutureCloseListener(); 406 final ParcelFileDescriptor file = ParcelFileDescriptor.open( 407 File.createTempFile("pfd", "bbq"), ParcelFileDescriptor.MODE_READ_WRITE, handler, 408 listener); 409 410 write(file, 7); 411 file.close(); 412 413 // make sure we were notified 414 assertEquals(null, listener.get()); 415 } 416 417 @Test 418 @IgnoreUnderRavenwood(blockedBy = MessageQueue.class) testFileError()419 public void testFileError() throws Exception { 420 final Handler handler = new Handler(Looper.getMainLooper()); 421 final FutureCloseListener listener = new FutureCloseListener(); 422 final ParcelFileDescriptor file = ParcelFileDescriptor.open( 423 File.createTempFile("pfd", "bbq"), ParcelFileDescriptor.MODE_READ_WRITE, handler, 424 listener); 425 426 write(file, 8); 427 file.closeWithError("OMG BANANAS"); 428 429 // make sure error came through 430 assertContains("OMG BANANAS", listener.get().getMessage()); 431 } 432 433 @Test 434 @IgnoreUnderRavenwood(blockedBy = MessageQueue.class) testFileDetach()435 public void testFileDetach() throws Exception { 436 final Handler handler = new Handler(Looper.getMainLooper()); 437 final FutureCloseListener listener = new FutureCloseListener(); 438 final ParcelFileDescriptor file = ParcelFileDescriptor.open( 439 File.createTempFile("pfd", "bbq"), ParcelFileDescriptor.MODE_READ_WRITE, handler, 440 listener); 441 442 file.detachFd(); 443 444 // make sure detach came through 445 assertContains("DETACHED", listener.get().getMessage()); 446 } 447 448 @Test 449 @IgnoreUnderRavenwood(blockedBy = MessageQueue.class) testFileWrapped()450 public void testFileWrapped() throws Exception { 451 final Handler handler1 = new Handler(Looper.getMainLooper()); 452 final Handler handler2 = new Handler(Looper.getMainLooper()); 453 final FutureCloseListener listener1 = new FutureCloseListener(); 454 final FutureCloseListener listener2 = new FutureCloseListener(); 455 final ParcelFileDescriptor file1 = ParcelFileDescriptor.open( 456 File.createTempFile("pfd", "bbq"), ParcelFileDescriptor.MODE_READ_WRITE, handler1, 457 listener1); 458 final ParcelFileDescriptor file2 = ParcelFileDescriptor.wrap(file1, handler2, listener2); 459 460 write(file2, 7); 461 file2.close(); 462 463 // make sure we were notified 464 assertEquals(null, listener2.get()); 465 } 466 467 @Test 468 @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class) testSocketErrorAfterClose()469 public void testSocketErrorAfterClose() throws Exception { 470 final ParcelFileDescriptor[] pair = ParcelFileDescriptor.createReliableSocketPair(); 471 final ParcelFileDescriptor red = pair[0]; 472 final ParcelFileDescriptor blue = pair[1]; 473 474 // both sides throw their hands in the air 475 blue.closeWithError("BLUE RAWR"); 476 red.closeWithError("RED RAWR"); 477 478 // red noticed the blue error, but after that the comm pipe was dead so 479 // blue had no way of seeing the red error. 480 try { 481 red.checkError(); 482 fail("expected throw!"); 483 } catch (IOException e) { 484 assertContains("BLUE RAWR", e.getMessage()); 485 } 486 487 // expected to not throw; no error 488 blue.checkError(); 489 } 490 491 @Test 492 @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class) testSocketMultipleCheck()493 public void testSocketMultipleCheck() throws Exception { 494 final ParcelFileDescriptor[] pair = ParcelFileDescriptor.createReliableSocketPair(); 495 final ParcelFileDescriptor red = pair[0]; 496 final ParcelFileDescriptor blue = pair[1]; 497 498 // allow checking before closed; they should all pass 499 blue.checkError(); 500 blue.checkError(); 501 blue.checkError(); 502 503 // and verify we actually see it 504 red.closeWithError("RAWR RED"); 505 try { 506 blue.checkError(); 507 fail("expected throw!"); 508 } catch (IOException e) { 509 assertContains("RAWR RED", e.getMessage()); 510 } 511 } 512 513 // http://b/21578056 514 @Test testFileNamesWithNonBmpChars()515 public void testFileNamesWithNonBmpChars() throws Exception { 516 final File file = File.createTempFile("treble_clef_\ud834\udd1e", ".tmp"); 517 final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, 518 ParcelFileDescriptor.MODE_READ_ONLY); 519 assertNotNull(pfd); 520 pfd.close(); 521 } 522 523 @Test 524 @IgnoreUnderRavenwood(blockedBy = Os.class) testCheckFinalizerBehavior()525 public void testCheckFinalizerBehavior() throws Exception { 526 final Runtime runtime = Runtime.getRuntime(); 527 ParcelFileDescriptor pfd = makeParcelFileDescriptor(); 528 assertTrue(checkIsValid(pfd.getFileDescriptor())); 529 530 ParcelFileDescriptor wrappedPfd = new ParcelFileDescriptor(pfd); 531 assertTrue(checkIsValid(wrappedPfd.getFileDescriptor())); 532 533 FileDescriptor fd = pfd.getFileDescriptor(); 534 int rawFd = pfd.getFd(); 535 pfd = null; 536 assertNull(pfd); // To keep tools happy - yes we are using the write to null 537 runtime.gc(); runtime.runFinalization(); 538 assertTrue("Wrapped PFD failed to hold reference", 539 checkIsValid(wrappedPfd.getFileDescriptor())); 540 assertTrue("FileDescriptor failed to hold reference", checkIsValid(fd)); 541 542 wrappedPfd = null; 543 assertNull(wrappedPfd); // To keep tools happy - yes we are using the write to null 544 runtime.gc(); runtime.runFinalization(); 545 // TODO: Enable this once b/65027998 is fixed 546 //assertTrue("FileDescriptor failed to hold reference", checkIsValid(fd)); 547 548 fd = null; 549 assertNull(fd); // To keep tools happy - yes we are using the write to null 550 runtime.gc(); runtime.runFinalization(); 551 552 try { 553 ParcelFileDescriptor.fromFd(rawFd); 554 fail("FD leaked"); 555 } catch (IOException ex) { 556 // Success 557 } 558 } 559 560 @Test testFromFd()561 public void testFromFd() throws Exception { 562 try (var pfd1 = makeParcelFileDescriptor()) { 563 try (var pfd2 = ParcelFileDescriptor.fromFd(pfd1.getFd())) { 564 checkSameFd(pfd1, pfd2); 565 } 566 } 567 } 568 569 @Test testDup()570 public void testDup() throws Exception { 571 try (var pfd1 = makeParcelFileDescriptor()) { 572 try (var pfd2 = pfd1.dup()) { 573 checkSameFd(pfd1, pfd2); 574 } 575 } 576 } 577 578 @Test testDupStatic()579 public void testDupStatic() throws Exception { 580 try (var pfd1 = makeParcelFileDescriptor()) { 581 try (var pfd2 = ParcelFileDescriptor.dup(pfd1.getFileDescriptor())) { 582 checkSameFd(pfd1, pfd2); 583 } 584 } 585 } 586 checkSameFd(ParcelFileDescriptor pfd1, ParcelFileDescriptor pfd2)587 void checkSameFd(ParcelFileDescriptor pfd1, ParcelFileDescriptor pfd2) throws Exception { 588 // Make sure dup'ed FDs share the same position. 589 seekTo(pfd1, 0); 590 seekTo(pfd2, 0); 591 592 assertEquals(0, tell(pfd1)); 593 assertEquals(0, tell(pfd2)); 594 595 seekTo(pfd1, 2); 596 597 assertEquals(2, tell(pfd1)); 598 assertEquals(2, tell(pfd2)); 599 } 600 601 /** Change PFD's current position. */ seekTo(ParcelFileDescriptor pfd, int pos)602 long seekTo(ParcelFileDescriptor pfd, int pos) throws Exception { 603 return Os.lseek(pfd.getFileDescriptor(), pos, OsConstants.SEEK_SET); 604 } 605 606 /** Returns the PFD's current position */ tell(ParcelFileDescriptor pfd)607 long tell(ParcelFileDescriptor pfd) throws Exception { 608 return Os.lseek(pfd.getFileDescriptor(), 0, OsConstants.SEEK_CUR); 609 } 610 checkIsValid(FileDescriptor fd)611 boolean checkIsValid(FileDescriptor fd) { 612 try { 613 Os.fstat(fd); 614 return true; 615 } catch (ErrnoException e) { 616 if (e.errno == OsConstants.EBADF) { 617 return false; 618 } else { 619 fail(e.getMessage()); 620 // not reached 621 return false; 622 } 623 } 624 } 625 makeParcelFileDescriptor()626 static ParcelFileDescriptor makeParcelFileDescriptor() throws Exception { 627 File file = File.createTempFile("testParcelFileDescriptor", "bin"); 628 try (FileOutputStream fout = new FileOutputStream(file)) { 629 fout.write(new byte[] { 0x0, 0x1, 0x2, 0x3 }); 630 } 631 632 ParcelFileDescriptor pf = null; 633 pf = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE); 634 return pf; 635 } 636 637 public static class FutureCloseListener extends AbstractFuture<IOException> 638 implements ParcelFileDescriptor.OnCloseListener { 639 @Override onClose(IOException e)640 public void onClose(IOException e) { 641 if (e instanceof ParcelFileDescriptor.FileDescriptorDetachedException) { 642 set(new IOException("DETACHED")); 643 } else { 644 set(e); 645 } 646 } 647 648 @Override get()649 public IOException get() throws InterruptedException, ExecutionException { 650 try { 651 return get(5, TimeUnit.SECONDS); 652 } catch (TimeoutException e) { 653 throw new RuntimeException(e); 654 } 655 } 656 } 657 } 658