1 /* 2 * Copyright (C) 2008 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 android.os.Handler; 20 import android.os.HandlerThread; 21 import android.os.Looper; 22 import android.os.Message; 23 import android.os.MessageQueue; 24 import android.os.MessageQueue.OnFileDescriptorEventListener; 25 import android.os.ParcelFileDescriptor; 26 import android.os.ParcelFileDescriptor.AutoCloseInputStream; 27 import android.os.ParcelFileDescriptor.AutoCloseOutputStream; 28 import android.system.ErrnoException; 29 import android.system.Os; 30 import android.os.SystemClock; 31 import android.os.MessageQueue.IdleHandler; 32 import android.test.AndroidTestCase; 33 34 import java.io.FileDescriptor; 35 import java.io.FileInputStream; 36 import java.io.FileOutputStream; 37 import java.io.IOException; 38 import java.util.concurrent.CountDownLatch; 39 import java.util.concurrent.TimeUnit; 40 41 public class MessageQueueTest extends AndroidTestCase { 42 43 private static final long TIMEOUT = 1000; 44 testAddIdleHandler()45 public void testAddIdleHandler() throws InterruptedException { 46 TestLooperThread looperThread = new TestLooperThread(Test.ADD_IDLE_HANDLER); 47 looperThread.start(); 48 49 try { 50 if (!looperThread.hasIdleHandlerBeenCalled()) { 51 fail("IdleHandler#queueIdle was NOT called: " + looperThread.getTestProgress()); 52 } 53 } finally { 54 assertTrue("The looper should have been running.", looperThread.quit()); 55 } 56 } 57 testRemoveIdleHandler()58 public void testRemoveIdleHandler() throws InterruptedException { 59 TestLooperThread looperThread = new TestLooperThread(Test.REMOVE_IDLE_HANDLER); 60 looperThread.start(); 61 62 try { 63 if (looperThread.hasIdleHandlerBeenCalled()) { 64 fail("IdleHandler#queueIdle was called: " + looperThread.getTestProgress()); 65 } 66 } finally { 67 assertTrue("The looper should have been running.", looperThread.quit()); 68 } 69 } 70 71 private enum Test {ADD_IDLE_HANDLER, REMOVE_IDLE_HANDLER} 72 73 /** 74 * {@link HandlerThread} that adds or removes an idle handler depending on the {@link Test} 75 * given. It uses a {@link CountDownLatch} with an initial count of 2. The first count down 76 * occurs right before the looper's run thread had started running. The final count down 77 * occurs when the idle handler was executed. Tests can call {@link #hasIdleHandlerBeenCalled()} 78 * to see if the countdown reached to 0 or not. 79 */ 80 private static class TestLooperThread extends HandlerThread { 81 82 private final Test mTestMode; 83 84 private final CountDownLatch mIdleLatch = new CountDownLatch(2); 85 TestLooperThread(Test testMode)86 TestLooperThread(Test testMode) { 87 super("TestLooperThread"); 88 mTestMode = testMode; 89 } 90 91 @Override onLooperPrepared()92 protected void onLooperPrepared() { 93 super.onLooperPrepared(); 94 95 IdleHandler idleHandler = new IdleHandler() { 96 @Override 97 public boolean queueIdle() { 98 mIdleLatch.countDown(); 99 return false; 100 } 101 }; 102 103 if (mTestMode == Test.ADD_IDLE_HANDLER) { 104 Looper.myQueue().addIdleHandler(idleHandler); 105 } else { 106 Looper.myQueue().addIdleHandler(idleHandler); 107 Looper.myQueue().removeIdleHandler(idleHandler); 108 } 109 } 110 111 @Override run()112 public void run() { 113 mIdleLatch.countDown(); 114 super.run(); 115 } 116 hasIdleHandlerBeenCalled()117 public boolean hasIdleHandlerBeenCalled() throws InterruptedException { 118 return mIdleLatch.await(TIMEOUT, TimeUnit.MILLISECONDS); 119 } 120 getTestProgress()121 public long getTestProgress() { 122 return mIdleLatch.getCount(); 123 } 124 } 125 testIsIdle()126 public void testIsIdle() throws Exception { 127 HandlerThread thread = new HandlerThread("testIsIdle"); 128 thread.start(); 129 try { 130 // Queue should initially be idle. 131 assertTrue(thread.getLooper().getQueue().isIdle()); 132 133 // Post two messages. Block in the first one leaving the second one pending. 134 final CountDownLatch latch1 = new CountDownLatch(1); 135 final CountDownLatch latch2 = new CountDownLatch(1); 136 Handler handler = new Handler(thread.getLooper()); 137 handler.post(new Runnable() { 138 @Override 139 public void run() { 140 // Wait for latch1 released before returning. 141 try { 142 latch1.await(TIMEOUT, TimeUnit.MILLISECONDS); 143 } catch (InterruptedException ex) { } 144 } 145 }); 146 handler.post(new Runnable() { 147 @Override 148 public void run() { 149 // Release latch2 when finished. 150 latch2.countDown(); 151 } 152 }); 153 154 // The first message is blocked so the second should still be in the queue. 155 // At this point the queue will not be idle because there is a pending message. 156 assertFalse(thread.getLooper().getQueue().isIdle()); 157 158 // Let the first message complete and wait for the second to leave the queue. 159 // At this point the queue will be idle because it is empty. 160 latch1.countDown(); 161 latch2.await(TIMEOUT, TimeUnit.MILLISECONDS); 162 assertTrue(thread.getLooper().getQueue().isIdle()); 163 } finally { 164 thread.quitSafely(); 165 } 166 } 167 168 /** 169 * Use MessageQueue, send message by order 170 */ testMessageOrder()171 public void testMessageOrder() throws Exception { 172 173 OrderTestHelper tester = new OrderTestHelper() { 174 @Override 175 public void init() { 176 super.init(); 177 long now = SystemClock.uptimeMillis() + 200; 178 mLastMessage = 4; 179 180 mHandler.sendMessageAtTime(mHandler.obtainMessage(2), now + 1); 181 mHandler.sendMessageAtTime(mHandler.obtainMessage(3), now + 2); 182 mHandler.sendMessageAtTime(mHandler.obtainMessage(4), now + 2); 183 mHandler.sendMessageAtTime(mHandler.obtainMessage(0), now + 0); 184 mHandler.sendMessageAtTime(mHandler.obtainMessage(1), now + 0); 185 } 186 187 }; 188 tester.doTest(1000, 50); 189 } 190 191 /** 192 * Use MessageQueue, send message at front of queue. 193 */ testAtFrontOfQueue()194 public void testAtFrontOfQueue() throws Exception { 195 196 OrderTestHelper tester = new OrderTestHelper() { 197 198 @Override 199 public void init() { 200 super.init(); 201 long now = SystemClock.uptimeMillis() + 200; 202 mLastMessage = 3; 203 mHandler.sendMessageAtTime(mHandler.obtainMessage(3), now); 204 mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(2)); 205 mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(0)); 206 } 207 208 @Override 209 public void handleMessage(Message msg) { 210 super.handleMessage(msg); 211 if (msg.what == 0) { 212 mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(1)); 213 } 214 } 215 }; 216 217 tester.doTest(1000, 50); 218 } 219 testRegisterFileDescriptorCallbackThrowsWhenFdIsNull()220 public void testRegisterFileDescriptorCallbackThrowsWhenFdIsNull() { 221 MessageQueue queue = Looper.getMainLooper().getQueue(); 222 try { 223 queue.addOnFileDescriptorEventListener(null, 0, 224 new OnFileDescriptorEventListener() { 225 @Override 226 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 227 return 0; 228 } 229 }); 230 fail("Expected IllegalArgumentException"); 231 } catch (IllegalArgumentException ex) { 232 // expected 233 } 234 } 235 testRegisterFileDescriptorCallbackThrowsWhenCallbackIsNull()236 public void testRegisterFileDescriptorCallbackThrowsWhenCallbackIsNull() throws Exception { 237 MessageQueue queue = Looper.getMainLooper().getQueue(); 238 ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 239 try (ParcelFileDescriptor reader = pipe[0]; 240 ParcelFileDescriptor writer = pipe[1]) { 241 try { 242 queue.addOnFileDescriptorEventListener(reader.getFileDescriptor(), 0, null); 243 fail("Expected IllegalArgumentException"); 244 } catch (IllegalArgumentException ex) { 245 // expected 246 } 247 } 248 } 249 testUnregisterFileDescriptorCallbackThrowsWhenFdIsNull()250 public void testUnregisterFileDescriptorCallbackThrowsWhenFdIsNull() throws Exception { 251 MessageQueue queue = Looper.getMainLooper().getQueue(); 252 try { 253 queue.removeOnFileDescriptorEventListener(null); 254 fail("Expected IllegalArgumentException"); 255 } catch (IllegalArgumentException ex) { 256 // expected 257 } 258 } 259 testUnregisterFileDescriptorCallbackDoesNothingWhenFdNotRegistered()260 public void testUnregisterFileDescriptorCallbackDoesNothingWhenFdNotRegistered() 261 throws Exception { 262 MessageQueue queue = Looper.getMainLooper().getQueue(); 263 ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 264 try (ParcelFileDescriptor reader = pipe[0]; 265 ParcelFileDescriptor writer = pipe[1]) { 266 queue.removeOnFileDescriptorEventListener(reader.getFileDescriptor()); 267 } 268 } 269 testFileDescriptorCallbacks()270 public void testFileDescriptorCallbacks() throws Throwable { 271 // Prepare a special looper that we can catch exceptions from. 272 AssertableHandlerThread thread = new AssertableHandlerThread(); 273 thread.start(); 274 try { 275 final CountDownLatch writerSawError = new CountDownLatch(1); 276 final CountDownLatch readerDone = new CountDownLatch(1); 277 final MessageQueue queue = thread.getLooper().getQueue(); 278 final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 279 try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]); 280 final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) { 281 final int size = 256 * 1024; 282 283 // Prepare to write a lot of data to the pipe asynchronously. 284 // We don't actually care about the content (assume pipes work correctly) 285 // so we just write lots of zeros. 286 OnFileDescriptorEventListener writerCallback = new OnFileDescriptorEventListener() { 287 private byte[] mBuffer = new byte[4096]; 288 private int mRemaining = size; 289 private boolean mDone; 290 291 @Override 292 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 293 assertEquals(pipe[1].getFileDescriptor(), fd); 294 if (!mDone) { 295 // When an error happens because the reader closed its end, 296 // signal the test, and remove the callback. 297 if ((events & OnFileDescriptorEventListener.EVENT_ERROR) != 0) { 298 writerSawError.countDown(); 299 mDone = true; 300 return 0; 301 } 302 303 // Write all output until an error is observed. 304 if ((events & OnFileDescriptorEventListener.EVENT_OUTPUT) != 0) { 305 int count = Math.min(mBuffer.length, mRemaining); 306 try { 307 writer.write(mBuffer, 0, count); 308 } catch (IOException ex) { 309 throw new RuntimeException(ex); 310 } 311 mRemaining -= count; 312 return mRemaining != 0 ? EVENT_OUTPUT : EVENT_ERROR; 313 } 314 } 315 316 // Should never see anything else. 317 fail("Saw unexpected events: " + events + ", mDone=" + mDone); 318 return 0; 319 } 320 }; 321 322 // Prepare to read all of that data. 323 OnFileDescriptorEventListener readerCallback = new OnFileDescriptorEventListener() { 324 private byte[] mBuffer = new byte[4096]; 325 private int mRemaining = size; 326 private boolean mDone; 327 328 @Override 329 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 330 assertEquals(pipe[0].getFileDescriptor(), fd); 331 if (!mDone) { 332 // Errors should not happen. 333 if ((events & OnFileDescriptorEventListener.EVENT_ERROR) != 0) { 334 fail("Saw unexpected error."); 335 return 0; 336 } 337 338 // Read until everything is read, signal the test, 339 // and remove the callback. 340 if ((events & OnFileDescriptorEventListener.EVENT_INPUT) != 0) { 341 try { 342 int count = reader.read(mBuffer, 0, mBuffer.length); 343 mRemaining -= count; 344 } catch (IOException ex) { 345 throw new RuntimeException(ex); 346 } 347 if (mRemaining != 0) { 348 return EVENT_INPUT; 349 } 350 readerDone.countDown(); 351 mDone = true; 352 return 0; 353 } 354 } 355 356 // Should never see anything else. 357 fail("Saw unexpected events: " + events + ", mDone=" + mDone); 358 return 0; 359 } 360 }; 361 362 // Register the callbacks. 363 queue.addOnFileDescriptorEventListener(reader.getFD(), 364 OnFileDescriptorEventListener.EVENT_INPUT, readerCallback); 365 queue.addOnFileDescriptorEventListener(writer.getFD(), 366 OnFileDescriptorEventListener.EVENT_OUTPUT, writerCallback); 367 368 // Wait for the reader to see all of the data that the writer 369 // is prepared to send. 370 readerDone.await(TIMEOUT, TimeUnit.MILLISECONDS); 371 372 // At this point the reader's callback should be unregistered. 373 // Close the reader's file descriptor (pretend it crashed or something). 374 reader.close(); 375 376 // Because the reader is gone, the writer should observe an error (EPIPE). 377 // Wait for this to happen. 378 writerSawError.await(TIMEOUT, TimeUnit.MILLISECONDS); 379 380 // The reader and writer should already be unregistered. 381 // Try to unregistered them again to ensure nothing bad happens. 382 queue.removeOnFileDescriptorEventListener(reader.getFD()); 383 queue.removeOnFileDescriptorEventListener(writer.getFD()); 384 } 385 } finally { 386 thread.quitAndRethrow(); 387 } 388 } 389 390 /** 391 * Since file descriptor numbers may be reused, there are some interesting 392 * edge cases around closing file descriptors within the callback and adding 393 * new ones with the same number. 394 * 395 * Register a file descriptor, close it from within the callback, then return. 396 * Later, create a new file descriptor register it. Ensure that we start getting 397 * events for the new file descriptor. 398 * 399 * This test exercises special logic in Looper.cpp for EPOLL_CTL_DEL handling EBADF. 400 */ testPathologicalFileDescriptorReuseCallbacks1()401 public void testPathologicalFileDescriptorReuseCallbacks1() throws Throwable { 402 // Prepare a special looper that we can catch exceptions from. 403 AssertableHandlerThread thread = new AssertableHandlerThread(); 404 thread.start(); 405 try { 406 final MessageQueue queue = thread.getLooper().getQueue(); 407 final Handler handler = new Handler(thread.getLooper()); 408 409 final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 410 try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]); 411 final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) { 412 // Register the callback. 413 final CountDownLatch awoke = new CountDownLatch(1); 414 queue.addOnFileDescriptorEventListener(reader.getFD(), 415 OnFileDescriptorEventListener.EVENT_ERROR, 416 new OnFileDescriptorEventListener() { 417 @Override 418 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 419 awoke.countDown(); 420 421 // Close the reader before we return. 422 closeQuietly(reader); 423 424 // Return 0 to unregister the callback. 425 return 0; 426 } 427 }); 428 429 // Close the writer to wake up the callback (due to hangup). 430 writer.close(); 431 432 // Wait for the looper to catch up and run the callback. 433 assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS)); 434 syncWait(handler); 435 } 436 437 // At this point, the reader and writer are both closed. 438 // Make a new pipe and ensure that things still work as expected. 439 final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe(); 440 try (final FileInputStream reader2 = new AutoCloseInputStream(pipe2[0]); 441 final FileOutputStream writer2 = new AutoCloseOutputStream(pipe2[1])) { 442 // Register the callback. 443 final CountDownLatch awoke = new CountDownLatch(1); 444 queue.addOnFileDescriptorEventListener(reader2.getFD(), 445 OnFileDescriptorEventListener.EVENT_INPUT, 446 new OnFileDescriptorEventListener() { 447 @Override 448 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 449 awoke.countDown(); 450 451 // Return 0 to unregister the callback. 452 return 0; 453 } 454 }); 455 456 // Close the writer to wake up the callback (due to hangup). 457 writer2.close(); 458 459 // Wait for the looper to catch up and run the callback. 460 assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS)); 461 syncWait(handler); 462 } 463 } finally { 464 thread.quitAndRethrow(); 465 } 466 } 467 468 /** 469 * Since file descriptor numbers may be reused, there are some interesting 470 * edge cases around closing file descriptors within the callback and adding 471 * new ones with the same number. 472 * 473 * Register a file descriptor, close it from within the callback, reassign its 474 * number to a different pipe, then return. Later, register the same file descriptor 475 * again (now referring to a new pipe). Ensure that we start getting 476 * events for the new file descriptor. 477 * 478 * This test exercises special logic in Looper.cpp for EPOLL_CTL_DEL handling ENOENT. 479 */ testPathologicalFileDescriptorReuseCallbacks2()480 public void testPathologicalFileDescriptorReuseCallbacks2() throws Throwable { 481 // Prepare a special looper that we can catch exceptions from. 482 AssertableHandlerThread thread = new AssertableHandlerThread(); 483 thread.start(); 484 try { 485 final MessageQueue queue = thread.getLooper().getQueue(); 486 final Handler handler = new Handler(thread.getLooper()); 487 488 final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 489 final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe(); 490 try { 491 final int oldReaderFd = pipe[0].getFd(); 492 try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]); 493 final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) { 494 // Register the callback. 495 final CountDownLatch awoke = new CountDownLatch(1); 496 queue.addOnFileDescriptorEventListener(reader.getFD(), 497 OnFileDescriptorEventListener.EVENT_ERROR, 498 new OnFileDescriptorEventListener() { 499 @Override 500 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 501 awoke.countDown(); 502 503 // Close the reader before we return and hijack its fd. 504 hijackFd(pipe2, pipe); 505 506 // Return 0 to unregister the callback. 507 return 0; 508 } 509 }); 510 511 // Close the writer to wake up the callback (due to hangup). 512 writer.close(); 513 514 // Wait for the looper to catch up and run the callback. 515 assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS)); 516 syncWait(handler); 517 } 518 519 // Now we have a new pipe with the same file descriptor, make sure we can 520 // register it successfully. 521 assertEquals(oldReaderFd, pipe2[0].getFd()); 522 try (final FileInputStream reader2 = new AutoCloseInputStream(pipe2[0]); 523 final FileOutputStream writer2 = new AutoCloseOutputStream(pipe2[1])) { 524 // Register the callback. 525 final CountDownLatch awoke = new CountDownLatch(1); 526 queue.addOnFileDescriptorEventListener(reader2.getFD(), 527 OnFileDescriptorEventListener.EVENT_INPUT, 528 new OnFileDescriptorEventListener() { 529 @Override 530 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 531 awoke.countDown(); 532 533 // Return 0 to unregister the callback. 534 return 0; 535 } 536 }); 537 538 // Close the writer to wake up the callback (due to hangup). 539 writer2.close(); 540 541 // Wait for the looper to catch up and run the callback. 542 assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS)); 543 syncWait(handler); 544 } 545 } finally { 546 closeQuietly(pipe[0]); 547 closeQuietly(pipe[1]); 548 closeQuietly(pipe2[0]); 549 closeQuietly(pipe2[1]); 550 } 551 } finally { 552 thread.quitAndRethrow(); 553 } 554 } 555 556 /** 557 * Since file descriptor numbers may be reused, there are some interesting 558 * edge cases around closing file descriptors within the callback and adding 559 * new ones with the same number. 560 * 561 * Register a file descriptor, close it from within the callback, reassign its 562 * number to a different pipe, register it, then return. 563 * Ensure that we start getting events for the new file descriptor. 564 * 565 * This test exercises special logic in Looper.cpp for EPOLL_CTL_MOD handling 566 * ENOENT and fallback to EPOLL_CTL_ADD as well as sequence number checks when removing 567 * the fd after the callback returns. 568 */ testPathologicalFileDescriptorReuseCallbacks3()569 public void testPathologicalFileDescriptorReuseCallbacks3() throws Throwable { 570 // Prepare a special looper that we can catch exceptions from. 571 AssertableHandlerThread thread = new AssertableHandlerThread(); 572 thread.start(); 573 try { 574 final MessageQueue queue = thread.getLooper().getQueue(); 575 final Handler handler = new Handler(thread.getLooper()); 576 577 final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 578 final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe(); 579 try { 580 final CountDownLatch awoke2 = new CountDownLatch(1); 581 final int oldReaderFd = pipe[0].getFd(); 582 try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]); 583 final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) { 584 // Register the callback. 585 final CountDownLatch awoke = new CountDownLatch(1); 586 queue.addOnFileDescriptorEventListener(reader.getFD(), 587 OnFileDescriptorEventListener.EVENT_ERROR, 588 new OnFileDescriptorEventListener() { 589 @Override 590 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 591 awoke.countDown(); 592 593 // Close the reader before we return and hijack its fd. 594 hijackFd(pipe2, pipe); 595 596 // Now we have a new pipe, make sure we can register it successfully. 597 queue.addOnFileDescriptorEventListener(pipe2[0].getFileDescriptor(), 598 OnFileDescriptorEventListener.EVENT_INPUT, 599 new OnFileDescriptorEventListener() { 600 @Override 601 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 602 awoke2.countDown(); 603 604 // Return 0 to unregister the callback. 605 return 0; 606 } 607 }); 608 609 // Return 0 to unregister the callback. 610 return 0; 611 } 612 }); 613 614 // Close the writer to wake up the callback (due to hangup). 615 writer.close(); 616 617 // Wait for the looper to catch up and run the callback. 618 assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS)); 619 syncWait(handler); 620 } 621 622 // Close the second writer to wake up the second callback (due to hangup). 623 pipe2[1].close(); 624 625 // Wait for the looper to catch up and run the callback. 626 assertTrue("awoke2", awoke2.await(TIMEOUT, TimeUnit.MILLISECONDS)); 627 syncWait(handler); 628 629 // Close the second reader now that we're done with the test. 630 assertEquals(oldReaderFd, pipe2[0].getFd()); 631 pipe2[0].close(); 632 } finally { 633 closeQuietly(pipe[0]); 634 closeQuietly(pipe[1]); 635 closeQuietly(pipe2[0]); 636 closeQuietly(pipe2[1]); 637 } 638 } finally { 639 thread.quitAndRethrow(); 640 } 641 } 642 643 /** 644 * Since file descriptor numbers may be reused, there are some interesting 645 * edge cases around closing file descriptors within the callback and adding 646 * new ones with the same number. 647 * 648 * Register a file descriptor, make a duplicate of it, close it from within the 649 * callback, then return. Look for signs that the Looper is spinning 650 * and never getting a chance to block. 651 * 652 * This test exercises special logic in Looper.cpp for rebuilding the epoll set 653 * in case it contains a file descriptor which has been closed and cannot be removed. 654 */ testPathologicalFileDescriptorReuseCallbacks4()655 public void testPathologicalFileDescriptorReuseCallbacks4() throws Throwable { 656 // Prepare a special looper that we can catch exceptions from. 657 ParcelFileDescriptor dup = null; 658 AssertableHandlerThread thread = new AssertableHandlerThread(); 659 thread.start(); 660 try { 661 try { 662 final MessageQueue queue = thread.getLooper().getQueue(); 663 final Handler handler = new Handler(thread.getLooper()); 664 665 final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); 666 dup = pipe[0].dup(); 667 try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]); 668 final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) { 669 // Register the callback. 670 final CountDownLatch awoke = new CountDownLatch(1); 671 queue.addOnFileDescriptorEventListener(reader.getFD(), 672 OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() { 673 @Override 674 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 675 awoke.countDown(); 676 677 // Close the file descriptor before we return. 678 try { 679 reader.close(); 680 } catch (IOException ex) { 681 throw new RuntimeException(ex); 682 } 683 684 // Return 0 to unregister the callback. 685 return 0; 686 } 687 }); 688 689 // Close the writer to wake up the callback (due to hangup). 690 writer.close(); 691 692 // Wait for the looper to catch up and run the callback. 693 assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS)); 694 syncWait(handler); 695 } 696 697 // Wait a little bit before we stop the thread. 698 Thread.sleep(2000); 699 } finally { 700 // Check for how long the thread was running. 701 // If the Looper behaved correctly, then it should have blocked for most of 702 // the duration of the test (including that sleep above) since not much else 703 // was happening. If we failed to actually rebuild the epoll set then the 704 // Looper may have been spinning continuously due to an FD that was never 705 // properly removed from the epoll set so the thread runtime will be very high. 706 long runtime = thread.quitAndRethrow(); 707 assertFalse("Looper thread spent most of its time spinning instead of blocked.", 708 runtime > 1000); 709 } 710 } finally { 711 // Close the duplicate now that we are done with it. 712 if (dup != null) { 713 dup.close(); 714 } 715 } 716 } 717 testSyncBarriers()718 public void testSyncBarriers() throws Exception { 719 OrderTestHelper tester = new OrderTestHelper() { 720 private int mBarrierToken1; 721 private int mBarrierToken2; 722 723 @Override 724 public void init() { 725 super.init(); 726 mLastMessage = 10; 727 mHandler.sendEmptyMessage(0); 728 mBarrierToken1 = Looper.myQueue().postSyncBarrier(); 729 mHandler.sendEmptyMessage(5); 730 sendAsyncMessage(1); 731 sendAsyncMessage(2); 732 sendAsyncMessage(3); 733 mHandler.sendEmptyMessage(6); 734 } 735 736 @Override 737 public void handleMessage(Message msg) { 738 super.handleMessage(msg); 739 if (msg.what == 3) { 740 mHandler.sendEmptyMessage(7); 741 mBarrierToken2 = Looper.myQueue().postSyncBarrier(); 742 sendAsyncMessage(4); 743 sendAsyncMessage(8); 744 } else if (msg.what == 4) { 745 Looper.myQueue().removeSyncBarrier(mBarrierToken1); 746 sendAsyncMessage(9); 747 mHandler.sendEmptyMessage(10); 748 } else if (msg.what == 8) { 749 Looper.myQueue().removeSyncBarrier(mBarrierToken2); 750 } 751 } 752 753 private void sendAsyncMessage(int what) { 754 Message msg = mHandler.obtainMessage(what); 755 msg.setAsynchronous(true); 756 mHandler.sendMessage(msg); 757 } 758 }; 759 760 tester.doTest(1000, 50); 761 } 762 testReleaseSyncBarrierThrowsIfTokenNotValid()763 public void testReleaseSyncBarrierThrowsIfTokenNotValid() throws Exception { 764 MessageQueue queue = Looper.getMainLooper().getQueue(); 765 766 // Invalid token 767 try { 768 queue.removeSyncBarrier(-1); 769 fail("Should have thrown IllegalStateException"); 770 } catch (IllegalStateException ex) { 771 // expected 772 } 773 774 // Token already removed. 775 int barrierToken = queue.postSyncBarrier(); 776 queue.removeSyncBarrier(barrierToken); 777 try { 778 queue.removeSyncBarrier(barrierToken); 779 fail("Should have thrown IllegalStateException"); 780 } catch (IllegalStateException ex) { 781 // expected 782 } 783 } 784 syncWait(Handler handler)785 private void syncWait(Handler handler) throws InterruptedException { 786 final CountDownLatch latch = new CountDownLatch(1); 787 handler.post(new Runnable() { 788 @Override 789 public void run() { 790 latch.countDown(); 791 } 792 }); 793 assertTrue("Handler got stuck.", latch.await(TIMEOUT, TimeUnit.MILLISECONDS)); 794 } 795 closeQuietly(AutoCloseable c)796 private static void closeQuietly(AutoCloseable c) { 797 if (c != null) { 798 try { 799 c.close(); 800 } catch (RuntimeException rethrown) { 801 throw rethrown; 802 } catch (Exception ex) { 803 } 804 } 805 } 806 hijackFd(ParcelFileDescriptor[] newPipe, ParcelFileDescriptor[] oldPipe)807 private static void hijackFd(ParcelFileDescriptor[] newPipe, ParcelFileDescriptor[] oldPipe) { 808 // Detach the old pipe's first fd and get its number. 809 int fd = oldPipe[0].detachFd(); 810 811 // Assign the new pipe's first fd to the same number as the old pipe's first fd. 812 // This causes the old pipe's first fd to be closed and reassigned. 813 try { 814 Os.dup2(newPipe[0].getFileDescriptor(), fd); 815 } catch (ErrnoException ex) { 816 throw new RuntimeException(ex); 817 } 818 819 // Fix up the new pipe's first fd object. 820 closeQuietly(newPipe[0]); 821 newPipe[0] = ParcelFileDescriptor.adoptFd(fd); 822 } 823 824 /** 825 * Helper class used to test sending message to message queue. 826 */ 827 private class OrderTestHelper { 828 Handler mHandler; 829 int mLastMessage; 830 int mCount; 831 private boolean mSuccess; 832 private RuntimeException mFailure; 833 private boolean mDone; 834 private Looper mLooper; 835 init()836 public void init() { 837 mHandler = new Handler() { 838 @Override 839 public void handleMessage(Message msg) { 840 OrderTestHelper.this.handleMessage(msg); 841 } 842 }; 843 } 844 handleMessage(Message msg)845 public void handleMessage(Message msg) { 846 if (mCount <= mLastMessage) { 847 if (msg.what != mCount) { 848 failure(new RuntimeException("Expected message #" + mCount + ", received #" 849 + msg.what)); 850 } else if (mCount == mLastMessage) { 851 success(); 852 } 853 854 mCount++; 855 } else { 856 failure(new RuntimeException("Message received after done, #" + msg.what)); 857 } 858 } 859 doTest(long timeout, long interval)860 public void doTest(long timeout, long interval) throws InterruptedException { 861 (new LooperThread()).start(); 862 863 synchronized (this) { 864 long now = System.currentTimeMillis(); 865 long endTime = now + timeout; 866 while (!mDone && now < endTime) { 867 wait(interval); 868 now = System.currentTimeMillis(); 869 } 870 } 871 872 mLooper.quit(); 873 874 if (!mDone) { 875 throw new RuntimeException("test timed out"); 876 } 877 if (!mSuccess) { 878 throw mFailure; 879 } 880 } 881 882 class LooperThread extends HandlerThread { 883 LooperThread()884 public LooperThread() { 885 super("MessengerLooperThread"); 886 } 887 888 @Override onLooperPrepared()889 public void onLooperPrepared() { 890 init(); 891 mLooper = getLooper(); 892 } 893 894 @Override run()895 public void run() { 896 super.run(); 897 synchronized (OrderTestHelper.this) { 898 mDone = true; 899 if (!mSuccess && mFailure == null) { 900 mFailure = new RuntimeException("no failure exception set"); 901 } 902 OrderTestHelper.this.notifyAll(); 903 } 904 } 905 } 906 success()907 public void success() { 908 synchronized (this) { 909 mSuccess = true; 910 quit(); 911 } 912 } 913 failure(RuntimeException failure)914 public void failure(RuntimeException failure) { 915 synchronized (this) { 916 mSuccess = false; 917 mFailure = failure; 918 quit(); 919 } 920 } 921 quit()922 private void quit() { 923 synchronized (this) { 924 mDone = true; 925 notifyAll(); 926 } 927 } 928 } 929 930 /** 931 * A HandlerThread that propagates exceptions out of the event loop 932 * instead of crashing the process. 933 */ 934 private static class AssertableHandlerThread extends HandlerThread { 935 private Throwable mThrowable; 936 private long mRuntime; 937 AssertableHandlerThread()938 public AssertableHandlerThread() { 939 super("AssertableHandlerThread"); 940 } 941 942 @Override run()943 public void run() { 944 final long startTime = SystemClock.currentThreadTimeMillis(); 945 try { 946 super.run(); 947 } catch (Throwable t) { 948 mThrowable = t; 949 } finally { 950 mRuntime = SystemClock.currentThreadTimeMillis() - startTime; 951 } 952 } 953 quitAndRethrow()954 public long quitAndRethrow() throws Throwable { 955 quitSafely(); 956 join(TIMEOUT); 957 if (mThrowable != null) { 958 throw mThrowable; 959 } 960 return mRuntime; 961 } 962 } 963 } 964