1e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk/* 2e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * Copyright (C) 2017 The Android Open Source Project 3e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * 4e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * except in compliance with the License. You may obtain a copy of the License at 6e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * 7e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * http://www.apache.org/licenses/LICENSE-2.0 8e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * 9e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * Unless required by applicable law or agreed to in writing, software distributed under the 10e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * KIND, either express or implied. See the License for the specific language governing 12e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * permissions and limitations under the License. 13e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk */ 14e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 15340b0e5216b4fcc435e0459b1ca46155a572100dJason Monkpackage android.testing; 16e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 17e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkimport android.os.Handler; 18745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monkimport android.os.HandlerThread; 19e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkimport android.os.Looper; 20e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkimport android.os.Message; 21e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkimport android.os.MessageQueue; 22745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monkimport android.os.TestLooperManager; 23745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monkimport android.support.test.InstrumentationRegistry; 24e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkimport android.util.ArrayMap; 25e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 26745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monkimport org.junit.runners.model.FrameworkMethod; 27e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 28e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkimport java.lang.annotation.ElementType; 29e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkimport java.lang.annotation.Retention; 30e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkimport java.lang.annotation.RetentionPolicy; 31e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkimport java.lang.annotation.Target; 32e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkimport java.util.Map; 33e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 34e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk/** 350c408008f1061f9421872e4407a539688b21b79eJason Monk * This is a wrapper around {@link TestLooperManager} to make it easier to manage 360c408008f1061f9421872e4407a539688b21b79eJason Monk * and provide an easy annotation for use with tests. 370c408008f1061f9421872e4407a539688b21b79eJason Monk * 380c408008f1061f9421872e4407a539688b21b79eJason Monk * @see TestableLooperTest TestableLooperTest for examples. 39e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk */ 40e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monkpublic class TestableLooper { 41e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 42e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk private Looper mLooper; 43e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk private MessageQueue mQueue; 44e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk private MessageHandler mMessageHandler; 45e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 46e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk private Handler mHandler; 47745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk private Runnable mEmptyMessage; 48745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk private TestLooperManager mQueueWrapper; 49e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 50745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk public TestableLooper(Looper l) throws Exception { 51c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk this(acquireLooperManager(l), l); 52e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 53e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 541e352f4c34534864306a79f20a1af9d73b7cbd5fJason Monk private TestableLooper(TestLooperManager wrapper, Looper l) { 55745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mQueueWrapper = wrapper; 56745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk setupQueue(l); 57e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 58e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 591e352f4c34534864306a79f20a1af9d73b7cbd5fJason Monk private TestableLooper(Looper looper, boolean b) { 60745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk setupQueue(looper); 61fd8f615953631863c94d7080441f8dd4a0a74f56Jason Monk } 62fd8f615953631863c94d7080441f8dd4a0a74f56Jason Monk 63745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk public Looper getLooper() { 64745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk return mLooper; 65fd8f615953631863c94d7080441f8dd4a0a74f56Jason Monk } 66fd8f615953631863c94d7080441f8dd4a0a74f56Jason Monk 671e352f4c34534864306a79f20a1af9d73b7cbd5fJason Monk private void setupQueue(Looper l) { 68745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mLooper = l; 69e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk mQueue = mLooper.getQueue(); 70e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk mHandler = new Handler(mLooper); 71e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 72e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 73e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk /** 740c408008f1061f9421872e4407a539688b21b79eJason Monk * Must be called to release the looper when the test is complete, otherwise 750c408008f1061f9421872e4407a539688b21b79eJason Monk * the looper will not be available for any subsequent tests. This is 760c408008f1061f9421872e4407a539688b21b79eJason Monk * automatically handled for tests using {@link RunWithLooper}. 77e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk */ 781e352f4c34534864306a79f20a1af9d73b7cbd5fJason Monk public void destroy() { 79745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mQueueWrapper.release(); 80c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk if (mLooper == Looper.getMainLooper()) { 81c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk TestableInstrumentation.releaseMain(); 82c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk } 83e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 84e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 850c408008f1061f9421872e4407a539688b21b79eJason Monk /** 860c408008f1061f9421872e4407a539688b21b79eJason Monk * Sets a callback for all messages processed on this TestableLooper. 870c408008f1061f9421872e4407a539688b21b79eJason Monk * 880c408008f1061f9421872e4407a539688b21b79eJason Monk * @see {@link MessageHandler} 890c408008f1061f9421872e4407a539688b21b79eJason Monk */ 90e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk public void setMessageHandler(MessageHandler handler) { 91e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk mMessageHandler = handler; 92e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 93e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 94e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk /** 95e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * Parse num messages from the message queue. 96e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * 97e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * @param num Number of messages to parse 98e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk */ 99e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk public int processMessages(int num) { 100e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk for (int i = 0; i < num; i++) { 101e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk if (!parseMessageInt()) { 102e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk return i + 1; 103e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 104e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 105e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk return num; 106e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 107e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 1080c408008f1061f9421872e4407a539688b21b79eJason Monk /** 1090c408008f1061f9421872e4407a539688b21b79eJason Monk * Process messages in the queue until no more are found. 1100c408008f1061f9421872e4407a539688b21b79eJason Monk */ 111e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk public void processAllMessages() { 112e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk while (processQueuedMessages() != 0) ; 113e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 114e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 115e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk private int processQueuedMessages() { 116e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk int count = 0; 117745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mEmptyMessage = () -> { }; 118745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mHandler.post(mEmptyMessage); 119745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk waitForMessage(mQueueWrapper, mHandler, mEmptyMessage); 120e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk while (parseMessageInt()) count++; 121e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk return count; 122e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 123e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 124e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk private boolean parseMessageInt() { 125e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk try { 126745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk Message result = mQueueWrapper.next(); 127e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk if (result != null) { 128e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk // This is a break message. 129745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk if (result.getCallback() == mEmptyMessage) { 130745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mQueueWrapper.recycle(result); 131e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk return false; 132e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 133e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 134e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk if (mMessageHandler != null) { 135e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk if (mMessageHandler.onMessageHandled(result)) { 1361e352f4c34534864306a79f20a1af9d73b7cbd5fJason Monk mQueueWrapper.execute(result); 137745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mQueueWrapper.recycle(result); 138e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } else { 139745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mQueueWrapper.recycle(result); 140e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk // Message handler indicated it doesn't want us to continue. 141e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk return false; 142e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 143e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } else { 1441e352f4c34534864306a79f20a1af9d73b7cbd5fJason Monk mQueueWrapper.execute(result); 145745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mQueueWrapper.recycle(result); 146e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 147e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } else { 148e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk // No messages, don't continue parsing 149e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk return false; 150e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 151e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } catch (Exception e) { 152e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk throw new RuntimeException(e); 153e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 154e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk return true; 155e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 156e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 157e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk /** 158e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * Runs an executable with myLooper set and processes all messages added. 159e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk */ 160e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk public void runWithLooper(RunnableWithException runnable) throws Exception { 161745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk new Handler(getLooper()).post(() -> { 162745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk try { 163745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk runnable.run(); 164745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } catch (Exception e) { 165745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk throw new RuntimeException(e); 166745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 167745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk }); 168e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk processAllMessages(); 169e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 170e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 171e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk public interface RunnableWithException { 172e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk void run() throws Exception; 173e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 174e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 1750c408008f1061f9421872e4407a539688b21b79eJason Monk /** 1760c408008f1061f9421872e4407a539688b21b79eJason Monk * Annotation that tells the {@link AndroidTestingRunner} to create a TestableLooper and 1770c408008f1061f9421872e4407a539688b21b79eJason Monk * run this test/class on that thread. The {@link TestableLooper} can be acquired using 1780c408008f1061f9421872e4407a539688b21b79eJason Monk * {@link #get(Object)}. 1790c408008f1061f9421872e4407a539688b21b79eJason Monk */ 180e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk @Retention(RetentionPolicy.RUNTIME) 181e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk @Target({ElementType.METHOD, ElementType.TYPE}) 182e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk public @interface RunWithLooper { 183e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk boolean setAsMainLooper() default false; 184e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 185e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 186745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk private static void waitForMessage(TestLooperManager queueWrapper, Handler handler, 187745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk Runnable execute) { 188745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk for (int i = 0; i < 10; i++) { 189745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk if (!queueWrapper.hasMessages(handler, null, execute)) { 190745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk try { 191745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk Thread.sleep(1); 192745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } catch (InterruptedException e) { 193745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 194745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 195745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 196745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk if (!queueWrapper.hasMessages(handler, null, execute)) { 197745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk throw new RuntimeException("Message didn't queue..."); 198745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 199745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 200745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk 201c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk private static TestLooperManager acquireLooperManager(Looper l) { 202c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk if (l == Looper.getMainLooper()) { 203c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk TestableInstrumentation.acquireMain(); 204c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk } 205c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk return InstrumentationRegistry.getInstrumentation().acquireLooperManager(l); 206c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk } 207c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk 208e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk private static final Map<Object, TestableLooper> sLoopers = new ArrayMap<>(); 209e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 2100c408008f1061f9421872e4407a539688b21b79eJason Monk /** 2110c408008f1061f9421872e4407a539688b21b79eJason Monk * For use with {@link RunWithLooper}, used to get the TestableLooper that was 2120c408008f1061f9421872e4407a539688b21b79eJason Monk * automatically created for this test. 2130c408008f1061f9421872e4407a539688b21b79eJason Monk */ 214e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk public static TestableLooper get(Object test) { 215e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk return sLoopers.get(test); 216e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 217e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 2180c408008f1061f9421872e4407a539688b21b79eJason Monk static class LooperFrameworkMethod extends FrameworkMethod { 219745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk private HandlerThread mHandlerThread; 220745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk 221745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk private final TestableLooper mTestableLooper; 222745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk private final Looper mLooper; 223745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk private final Handler mHandler; 224e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 225745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk public LooperFrameworkMethod(FrameworkMethod base, boolean setAsMain, Object test) { 226745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk super(base.getMethod()); 227e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk try { 228745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mLooper = setAsMain ? Looper.getMainLooper() : createLooper(); 229745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mTestableLooper = new TestableLooper(mLooper, false); 230e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } catch (Exception e) { 231e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk throw new RuntimeException(e); 232e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 233745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk sLoopers.put(test, mTestableLooper); 234745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mHandler = new Handler(mLooper); 235f715f417d23d0605abcac309135340ac6ce311ceJason Monk } 236e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 237745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk public LooperFrameworkMethod(TestableLooper other, FrameworkMethod base) { 238745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk super(base.getMethod()); 239745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mLooper = other.mLooper; 240745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mTestableLooper = other; 2411e352f4c34534864306a79f20a1af9d73b7cbd5fJason Monk mHandler = Handler.createAsync(mLooper); 242745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 243745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk 244745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk public static FrameworkMethod get(FrameworkMethod base, boolean setAsMain, Object test) { 245745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk if (sLoopers.containsKey(test)) { 246745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk return new LooperFrameworkMethod(sLoopers.get(test), base); 247f715f417d23d0605abcac309135340ac6ce311ceJason Monk } 248745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk return new LooperFrameworkMethod(base, setAsMain, test); 249745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 250fd8f615953631863c94d7080441f8dd4a0a74f56Jason Monk 251745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk @Override 252745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk public Object invokeExplosively(Object target, Object... params) throws Throwable { 253745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk if (Looper.myLooper() == mLooper) { 254745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk // Already on the right thread from another statement, just execute then. 255745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk return super.invokeExplosively(target, params); 256745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 257745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk boolean set = mTestableLooper.mQueueWrapper == null; 258745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk if (set) { 259c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk mTestableLooper.mQueueWrapper = acquireLooperManager(mLooper); 260745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 2619ec0f1eca44190167c8d8d858cd13c27fe9ce7d2Jason Monk try { 262745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk Object[] ret = new Object[1]; 263745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk // Run the execution on the looper thread. 264745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk Runnable execute = () -> { 265745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk try { 266745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk ret[0] = super.invokeExplosively(target, params); 267745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } catch (Throwable throwable) { 268745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk throw new LooperException(throwable); 269745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 270745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk }; 271745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk Message m = Message.obtain(mHandler, execute); 272745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk 273745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk // Dispatch our message. 274745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk try { 275745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mTestableLooper.mQueueWrapper.execute(m); 276745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } catch (LooperException e) { 277745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk throw e.getSource(); 278745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } catch (RuntimeException re) { 279745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk // If the TestLooperManager has to post, it will wrap what it throws in a 280745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk // RuntimeException, make sure we grab the actual source. 281745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk if (re.getCause() instanceof LooperException) { 282745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk throw ((LooperException) re.getCause()).getSource(); 283745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } else { 284745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk throw re.getCause(); 285745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 286745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } finally { 287745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk m.recycle(); 288745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 289745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk return ret[0]; 2909ec0f1eca44190167c8d8d858cd13c27fe9ce7d2Jason Monk } finally { 291745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk if (set) { 292745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mTestableLooper.mQueueWrapper.release(); 293745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mTestableLooper.mQueueWrapper = null; 294c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk if (mLooper == Looper.getMainLooper()) { 295c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk TestableInstrumentation.releaseMain(); 296c429f690e54db1bdc4631aab7c0191e30ccf1d51Jason Monk } 297745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 298745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 299745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 300745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk 301745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk private Looper createLooper() { 302745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk // TODO: Find way to share these. 303745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mHandlerThread = new HandlerThread(TestableLooper.class.getSimpleName()); 304745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mHandlerThread.start(); 305745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk return mHandlerThread.getLooper(); 306745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 307745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk 308745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk @Override 309745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk protected void finalize() throws Throwable { 310745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk super.finalize(); 311745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk if (mHandlerThread != null) { 312745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mHandlerThread.quit(); 313745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 314745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 315745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk 316745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk private static class LooperException extends RuntimeException { 317745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk private final Throwable mSource; 318745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk 319745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk public LooperException(Throwable t) { 320745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk mSource = t; 321745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk } 322745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk 323745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk public Throwable getSource() { 324745d0a8b77ad3c7c8a220b6e05dbceff54154d57Jason Monk return mSource; 3259ec0f1eca44190167c8d8d858cd13c27fe9ce7d2Jason Monk } 326e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 327e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 328e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk 3290c408008f1061f9421872e4407a539688b21b79eJason Monk /** 3300c408008f1061f9421872e4407a539688b21b79eJason Monk * Callback to control the execution of messages on the looper, when set with 3310c408008f1061f9421872e4407a539688b21b79eJason Monk * {@link #setMessageHandler(MessageHandler)} then {@link #onMessageHandled(Message)} 3320c408008f1061f9421872e4407a539688b21b79eJason Monk * will get called back for every message processed on the {@link TestableLooper}. 3330c408008f1061f9421872e4407a539688b21b79eJason Monk */ 334e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk public interface MessageHandler { 335e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk /** 336e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * Return true to have the message executed and delivered to target. 337e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk * Return false to not execute the message and stop executing messages. 338e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk */ 339e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk boolean onMessageHandled(Message m); 340e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk } 341e750748404c25a3b1128f6bfeaa65e2bc5550f81Jason Monk} 342