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