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