141e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills/*
241e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * Copyright (C) 2015 The Android Open Source Project
341e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills *
441e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * Licensed under the Apache License, Version 2.0 (the "License");
541e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * you may not use this file except in compliance with the License.
641e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * You may obtain a copy of the License at
741e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills *
841e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills *      http://www.apache.org/licenses/LICENSE-2.0
941e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills *
1041e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * Unless required by applicable law or agreed to in writing, software
1141e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * distributed under the License is distributed on an "AS IS" BASIS,
1241e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1341e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * See the License for the specific language governing permissions and
1441e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * limitations under the License.
1541e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills */
1641e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
1741e38d84f562e12198f7db0d45f633712cae6cbaMitchell Willspackage com.android.server.wifi;
1841e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
1972c639e8b97067e948eca8be50dfea3173121090Mitchell Willsimport static org.junit.Assert.assertTrue;
2041e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
2141e38d84f562e12198f7db0d45f633712cae6cbaMitchell Willsimport android.os.Looper;
2241e38d84f562e12198f7db0d45f633712cae6cbaMitchell Willsimport android.os.Message;
2341e38d84f562e12198f7db0d45f633712cae6cbaMitchell Willsimport android.os.MessageQueue;
24b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohenimport android.os.SystemClock;
25d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silbersteinimport android.util.Log;
2641e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
2741e38d84f562e12198f7db0d45f633712cae6cbaMitchell Willsimport java.lang.reflect.Constructor;
2841e38d84f562e12198f7db0d45f633712cae6cbaMitchell Willsimport java.lang.reflect.Field;
2941e38d84f562e12198f7db0d45f633712cae6cbaMitchell Willsimport java.lang.reflect.InvocationTargetException;
3041e38d84f562e12198f7db0d45f633712cae6cbaMitchell Willsimport java.lang.reflect.Method;
3141e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
3241e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills/**
3341e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * Creates a looper whose message queue can be manipulated
3441e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * This allows testing code that uses a looper to dispatch messages in a deterministic manner
3541e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills * Creating a MockLooper will also install it as the looper for the current thread
3641e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills */
3741e38d84f562e12198f7db0d45f633712cae6cbaMitchell Willspublic class MockLooper {
38e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen    protected final Looper mLooper;
3941e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
4072c639e8b97067e948eca8be50dfea3173121090Mitchell Wills    private static final Constructor<Looper> LOOPER_CONSTRUCTOR;
4172c639e8b97067e948eca8be50dfea3173121090Mitchell Wills    private static final Field THREAD_LOCAL_LOOPER_FIELD;
42b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen    private static final Field MESSAGE_QUEUE_MESSAGES_FIELD;
43b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen    private static final Field MESSAGE_NEXT_FIELD;
44b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen    private static final Field MESSAGE_WHEN_FIELD;
45b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen    private static final Method MESSAGE_MARK_IN_USE_METHOD;
46d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    private static final String TAG = "MockLooper";
47d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein
48d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    private AutoDispatchThread mAutoDispatchThread;
4941e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
5041e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    static {
5141e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        try {
5272c639e8b97067e948eca8be50dfea3173121090Mitchell Wills            LOOPER_CONSTRUCTOR = Looper.class.getDeclaredConstructor(Boolean.TYPE);
5372c639e8b97067e948eca8be50dfea3173121090Mitchell Wills            LOOPER_CONSTRUCTOR.setAccessible(true);
5472c639e8b97067e948eca8be50dfea3173121090Mitchell Wills            THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
5572c639e8b97067e948eca8be50dfea3173121090Mitchell Wills            THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
56b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
57b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
58b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
59b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            MESSAGE_NEXT_FIELD.setAccessible(true);
60b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
61b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            MESSAGE_WHEN_FIELD.setAccessible(true);
62b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            MESSAGE_MARK_IN_USE_METHOD = Message.class.getDeclaredMethod("markInUse");
63b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            MESSAGE_MARK_IN_USE_METHOD.setAccessible(true);
6472c639e8b97067e948eca8be50dfea3173121090Mitchell Wills        } catch (NoSuchFieldException | NoSuchMethodException e) {
6541e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills            throw new RuntimeException("Failed to initialize MockLooper", e);
6641e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        }
6741e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    }
6841e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
6941e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
70e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen    public MockLooper() {
71e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen        try {
72e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen            mLooper = LOOPER_CONSTRUCTOR.newInstance(false);
7341e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
74e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen            ThreadLocal<Looper> threadLocalLooper = (ThreadLocal<Looper>) THREAD_LOCAL_LOOPER_FIELD
75e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen                    .get(null);
76e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen            threadLocalLooper.set(mLooper);
77e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen        } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
78e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen            throw new RuntimeException("Reflection error constructing or accessing looper", e);
79e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen        }
8041e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    }
8141e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
8241e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    public Looper getLooper() {
8341e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        return mLooper;
8441e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    }
8541e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
86b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen    private Message getMessageLinkedList() {
8741e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        try {
88b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            MessageQueue queue = mLooper.getQueue();
89b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
90b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen        } catch (IllegalAccessException e) {
91b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            throw new RuntimeException("Access failed in MockLooper: get - MessageQueue.mMessages",
92b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    e);
93b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen        }
94b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen    }
95b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen
96b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen    public void moveTimeForward(long milliSeconds) {
97b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen        try {
98b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            Message msg = getMessageLinkedList();
99b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            while (msg != null) {
100b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                long updatedWhen = msg.getWhen() - milliSeconds;
101b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                if (updatedWhen < 0) {
102b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    updatedWhen = 0;
103b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                }
104b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
105b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
106b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            }
107b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen        } catch (IllegalAccessException e) {
108b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            throw new RuntimeException("Access failed in MockLooper: set - Message.when", e);
109b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen        }
110b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen    }
111b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen
112b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen    private Message messageQueueNext() {
113b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen        try {
114b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            long now = SystemClock.uptimeMillis();
115b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen
116b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            Message prevMsg = null;
117b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            Message msg = getMessageLinkedList();
118b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            if (msg != null && msg.getTarget() == null) {
119b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                // Stalled by a barrier. Find the next asynchronous message in
120b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                // the queue.
121b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                do {
122b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    prevMsg = msg;
123b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
124b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                } while (msg != null && !msg.isAsynchronous());
125b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            }
126b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            if (msg != null) {
127b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                if (now >= msg.getWhen()) {
128b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    // Got a message.
129b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    if (prevMsg != null) {
130b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                        MESSAGE_NEXT_FIELD.set(prevMsg, MESSAGE_NEXT_FIELD.get(msg));
131b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    } else {
132b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                        MESSAGE_QUEUE_MESSAGES_FIELD.set(mLooper.getQueue(),
133b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                                MESSAGE_NEXT_FIELD.get(msg));
134b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    }
135b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    MESSAGE_NEXT_FIELD.set(msg, null);
136b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    MESSAGE_MARK_IN_USE_METHOD.invoke(msg);
137b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                    return msg;
138b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen                }
139b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            }
14072c639e8b97067e948eca8be50dfea3173121090Mitchell Wills        } catch (IllegalAccessException | InvocationTargetException e) {
141b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen            throw new RuntimeException("Access failed in MockLooper", e);
14241e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        }
143b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen
144b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen        return null;
14541e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    }
14641e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
14741e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    /**
14841e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     * @return true if there are pending messages in the message queue
14941e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     */
150d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    public synchronized boolean isIdle() {
151b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen        Message messageList = getMessageLinkedList();
152b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen
153b35c2105231b61606db78d4bf8a75ec31039db61Etan Cohen        return messageList != null && SystemClock.uptimeMillis() >= messageList.getWhen();
15441e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    }
15541e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
15641e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    /**
15741e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     * @return the next message in the Looper's message queue or null if there is none
15841e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     */
159d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    public synchronized Message nextMessage() {
160e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen        if (isIdle()) {
16141e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills            return messageQueueNext();
16272c639e8b97067e948eca8be50dfea3173121090Mitchell Wills        } else {
16341e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills            return null;
16441e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        }
16541e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    }
16641e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
16741e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    /**
16841e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     * Dispatch the next message in the queue
16941e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     * Asserts that there is a message in the queue
17041e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     */
171d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    public synchronized void dispatchNext() {
172e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen        assertTrue(isIdle());
17341e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        Message msg = messageQueueNext();
17441e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        if (msg == null) {
17541e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills            return;
17641e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        }
17741e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        msg.getTarget().dispatchMessage(msg);
17841e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    }
17941e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills
18041e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    /**
18141e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     * Dispatch all messages currently in the queue
18241e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     * Will not fail if there are no messages pending
18341e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     * @return the number of messages dispatched
18441e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills     */
185d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    public synchronized int dispatchAll() {
18641e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        int count = 0;
187e1e1c7317e5e5d61daf90cd2f2861305f19890b7Etan Cohen        while (isIdle()) {
18841e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills            dispatchNext();
18941e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills            ++count;
19041e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        }
19141e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills        return count;
19241e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills    }
193d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein
194d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    /**
195d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein     * Thread used to dispatch messages when the main thread is blocked waiting for a response.
196d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein     */
197d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    private class AutoDispatchThread extends Thread {
198d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        private static final int MAX_LOOPS = 100;
199d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        private static final int LOOP_SLEEP_TIME_MS = 10;
200d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein
201d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        private RuntimeException mAutoDispatchException = null;
202d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein
203d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        /**
204d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein         * Run method for the auto dispatch thread.
205d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein         * The thread loops a maximum of MAX_LOOPS times with a 10ms sleep between loops.
206d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein         * The thread continues looping and attempting to dispatch all messages until at
207d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein         * least one message has been dispatched.
208d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein         */
209d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        @Override
210d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        public void run() {
211d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            int dispatchCount = 0;
212d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            for (int i = 0; i < MAX_LOOPS; i++) {
213d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                try {
214d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                    dispatchCount = dispatchAll();
215d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                } catch (RuntimeException e) {
216d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                    mAutoDispatchException = e;
217d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                }
218d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                Log.d(TAG, "dispatched " + dispatchCount + " messages");
219d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                if (dispatchCount > 0) {
220d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                    return;
221d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                }
222d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                try {
223d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                    Thread.sleep(LOOP_SLEEP_TIME_MS);
224d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                } catch (InterruptedException e) {
225d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                    mAutoDispatchException = new IllegalStateException(
226d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                            "stopAutoDispatch called before any messages were dispatched.");
227d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                    return;
228d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                }
229d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            }
230d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            Log.e(TAG, "AutoDispatchThread did not dispatch any messages.");
231d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            mAutoDispatchException = new IllegalStateException(
232d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                    "MockLooper did not dispatch any messages before exiting.");
233d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        }
234d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein
235d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        /**
236d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein         * Method allowing the MockLooper to pass any exceptions thrown by the thread to be passed
237d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein         * to the main thread.
238d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein         *
239d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein         * @return RuntimeException Exception created by stopping without dispatching a message
240d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein         */
241d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        public RuntimeException getException() {
242d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            return mAutoDispatchException;
243d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        }
244d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    }
245d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein
246d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    /**
247d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein     * Create and start a new AutoDispatchThread if one is not already running.
248d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein     */
249d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    public void startAutoDispatch() {
250d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        if (mAutoDispatchThread != null) {
251d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            throw new IllegalStateException(
252d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                    "startAutoDispatch called with the AutoDispatchThread already running.");
253d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        }
254d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        mAutoDispatchThread = new AutoDispatchThread();
255d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        mAutoDispatchThread.start();
256d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    }
257d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein
258d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    /**
259d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein     * If an AutoDispatchThread is currently running, stop and clean up.
260d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein     */
261d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    public void stopAutoDispatch() {
262d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        if (mAutoDispatchThread != null) {
263d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            if (mAutoDispatchThread.isAlive()) {
264d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                mAutoDispatchThread.interrupt();
265d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            }
266d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            try {
267d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                mAutoDispatchThread.join();
268d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            } catch (InterruptedException e) {
269d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                // Catch exception from join.
270d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            }
271d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein
272d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            RuntimeException e = mAutoDispatchThread.getException();
273d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            mAutoDispatchThread = null;
274d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            if (e != null) {
275d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                throw e;
276d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            }
277d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        } else {
278d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            // stopAutoDispatch was called when startAutoDispatch has not created a new thread.
279d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein            throw new IllegalStateException(
280d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein                    "stopAutoDispatch called without startAutoDispatch.");
281d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein        }
282d0846fa841b604ccb969c54b45367fa2bececf83Rebecca Silberstein    }
28341e38d84f562e12198f7db0d45f633712cae6cbaMitchell Wills}
284