Home | History | Annotate | Download | only in cts
      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 
     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 
     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 
     86         TestLooperThread(Test testMode) {
     87             super("TestLooperThread");
     88             mTestMode = testMode;
     89         }
     90 
     91         @Override
     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
    112         public void run() {
    113             mIdleLatch.countDown();
    114             super.run();
    115         }
    116 
    117         public boolean hasIdleHandlerBeenCalled() throws InterruptedException {
    118             return mIdleLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
    119         }
    120 
    121         public long getTestProgress() {
    122             return mIdleLatch.getCount();
    123         }
    124     }
    125 
    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      */
    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      */
    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 
    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 
    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 
    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 
    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 
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    884             public LooperThread() {
    885                 super("MessengerLooperThread");
    886             }
    887 
    888             @Override
    889             public void onLooperPrepared() {
    890                 init();
    891                 mLooper = getLooper();
    892             }
    893 
    894             @Override
    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 
    907         public void success() {
    908             synchronized (this) {
    909                 mSuccess = true;
    910                 quit();
    911             }
    912         }
    913 
    914         public void failure(RuntimeException failure) {
    915             synchronized (this) {
    916                 mSuccess = false;
    917                 mFailure = failure;
    918                 quit();
    919             }
    920         }
    921 
    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 
    938         public AssertableHandlerThread() {
    939             super("AssertableHandlerThread");
    940         }
    941 
    942         @Override
    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 
    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