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