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