1bd90909c4ef180602ac088758ffdc13d37d24629Jack He/*
2bd90909c4ef180602ac088758ffdc13d37d24629Jack He * Copyright 2018 The Android Open Source Project
3bd90909c4ef180602ac088758ffdc13d37d24629Jack He *
4bd90909c4ef180602ac088758ffdc13d37d24629Jack He * Licensed under the Apache License, Version 2.0 (the "License");
5bd90909c4ef180602ac088758ffdc13d37d24629Jack He * you may not use this file except in compliance with the License.
6bd90909c4ef180602ac088758ffdc13d37d24629Jack He * You may obtain a copy of the License at
7bd90909c4ef180602ac088758ffdc13d37d24629Jack He *
8bd90909c4ef180602ac088758ffdc13d37d24629Jack He *      http://www.apache.org/licenses/LICENSE-2.0
9bd90909c4ef180602ac088758ffdc13d37d24629Jack He *
10bd90909c4ef180602ac088758ffdc13d37d24629Jack He * Unless required by applicable law or agreed to in writing, software
11bd90909c4ef180602ac088758ffdc13d37d24629Jack He * distributed under the License is distributed on an "AS IS" BASIS,
12bd90909c4ef180602ac088758ffdc13d37d24629Jack He * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bd90909c4ef180602ac088758ffdc13d37d24629Jack He * See the License for the specific language governing permissions and
14bd90909c4ef180602ac088758ffdc13d37d24629Jack He * limitations under the License.
15bd90909c4ef180602ac088758ffdc13d37d24629Jack He */
16bd90909c4ef180602ac088758ffdc13d37d24629Jack Hepackage com.android.bluetooth;
17bd90909c4ef180602ac088758ffdc13d37d24629Jack He
18bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport static org.mockito.ArgumentMatchers.eq;
19bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport static org.mockito.Mockito.*;
20bd90909c4ef180602ac088758ffdc13d37d24629Jack He
21bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport android.bluetooth.BluetoothAdapter;
227b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavovimport android.bluetooth.BluetoothDevice;
23bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport android.content.Intent;
243162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavovimport android.os.Handler;
253162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavovimport android.os.Looper;
26bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport android.support.test.InstrumentationRegistry;
27bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport android.support.test.rule.ServiceTestRule;
28bd90909c4ef180602ac088758ffdc13d37d24629Jack He
29bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport com.android.bluetooth.btservice.AdapterService;
30bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport com.android.bluetooth.btservice.ProfileService;
31bd90909c4ef180602ac088758ffdc13d37d24629Jack He
32bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport org.junit.Assert;
3335bbcaba2cfd2285e3897b501c34469043108a08Myles Watsonimport org.mockito.ArgumentCaptor;
34bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport org.mockito.internal.util.MockUtil;
35bd90909c4ef180602ac088758ffdc13d37d24629Jack He
36448309eada01c130b2fee8977f7fd74875978cbcJack Heimport java.lang.reflect.Field;
37bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport java.lang.reflect.InvocationTargetException;
38bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport java.lang.reflect.Method;
39a639b81f96c534c2f1066354b4a574c0dda2f713Jack Heimport java.util.concurrent.BlockingQueue;
40bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport java.util.concurrent.TimeUnit;
41bd90909c4ef180602ac088758ffdc13d37d24629Jack Heimport java.util.concurrent.TimeoutException;
42bd90909c4ef180602ac088758ffdc13d37d24629Jack He
43bd90909c4ef180602ac088758ffdc13d37d24629Jack He/**
44bd90909c4ef180602ac088758ffdc13d37d24629Jack He * A set of methods useful in Bluetooth instrumentation tests
45bd90909c4ef180602ac088758ffdc13d37d24629Jack He */
46bd90909c4ef180602ac088758ffdc13d37d24629Jack Hepublic class TestUtils {
47bd90909c4ef180602ac088758ffdc13d37d24629Jack He    private static final int SERVICE_TOGGLE_TIMEOUT_MS = 1000;    // 1s
48bd90909c4ef180602ac088758ffdc13d37d24629Jack He
49bd90909c4ef180602ac088758ffdc13d37d24629Jack He    /**
50448309eada01c130b2fee8977f7fd74875978cbcJack He     * Utility method to replace obj.fieldName with newValue where obj is of type c
51448309eada01c130b2fee8977f7fd74875978cbcJack He     *
52448309eada01c130b2fee8977f7fd74875978cbcJack He     * @param c type of obj
53448309eada01c130b2fee8977f7fd74875978cbcJack He     * @param fieldName field name to be replaced
54448309eada01c130b2fee8977f7fd74875978cbcJack He     * @param obj instance of type c whose fieldName is to be replaced, null for static fields
55448309eada01c130b2fee8977f7fd74875978cbcJack He     * @param newValue object used to replace fieldName
56448309eada01c130b2fee8977f7fd74875978cbcJack He     * @return the old value of fieldName that got replaced, caller is responsible for restoring
57448309eada01c130b2fee8977f7fd74875978cbcJack He     *         it back to obj
58448309eada01c130b2fee8977f7fd74875978cbcJack He     * @throws NoSuchFieldException when fieldName is not found in type c
59448309eada01c130b2fee8977f7fd74875978cbcJack He     * @throws IllegalAccessException when fieldName cannot be accessed in type c
60bd90909c4ef180602ac088758ffdc13d37d24629Jack He     */
61448309eada01c130b2fee8977f7fd74875978cbcJack He    public static Object replaceField(final Class c, final String fieldName, final Object obj,
62448309eada01c130b2fee8977f7fd74875978cbcJack He            final Object newValue) throws NoSuchFieldException, IllegalAccessException {
63448309eada01c130b2fee8977f7fd74875978cbcJack He        Field field = c.getDeclaredField(fieldName);
64448309eada01c130b2fee8977f7fd74875978cbcJack He        field.setAccessible(true);
65448309eada01c130b2fee8977f7fd74875978cbcJack He
66448309eada01c130b2fee8977f7fd74875978cbcJack He        Object oldValue = field.get(obj);
67448309eada01c130b2fee8977f7fd74875978cbcJack He        field.set(obj, newValue);
68448309eada01c130b2fee8977f7fd74875978cbcJack He        return oldValue;
69448309eada01c130b2fee8977f7fd74875978cbcJack He    }
70bd90909c4ef180602ac088758ffdc13d37d24629Jack He
71bd90909c4ef180602ac088758ffdc13d37d24629Jack He    /**
72bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * Set the return value of {@link AdapterService#getAdapterService()} to a test specified value
73bd90909c4ef180602ac088758ffdc13d37d24629Jack He     *
74bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @param adapterService the designated {@link AdapterService} in test, must not be null, can
75bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * be mocked or spied
76bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @throws NoSuchMethodException when setAdapterService method is not found
77bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @throws IllegalAccessException when setAdapterService method cannot be accessed
78bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @throws InvocationTargetException when setAdapterService method cannot be invoked, which
79bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * should never happen since setAdapterService is a static method
80bd90909c4ef180602ac088758ffdc13d37d24629Jack He     */
81bd90909c4ef180602ac088758ffdc13d37d24629Jack He    public static void setAdapterService(AdapterService adapterService)
82bd90909c4ef180602ac088758ffdc13d37d24629Jack He            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
83bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Assert.assertNull("AdapterService.getAdapterService() must be null before setting another"
84bd90909c4ef180602ac088758ffdc13d37d24629Jack He                + " AdapterService", AdapterService.getAdapterService());
85bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Assert.assertNotNull(adapterService);
86bd90909c4ef180602ac088758ffdc13d37d24629Jack He        // We cannot mock AdapterService.getAdapterService() with Mockito.
87bd90909c4ef180602ac088758ffdc13d37d24629Jack He        // Hence we need to use reflection to call a private method to
88bd90909c4ef180602ac088758ffdc13d37d24629Jack He        // initialize properly the AdapterService.sAdapterService field.
89bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Method method =
90bd90909c4ef180602ac088758ffdc13d37d24629Jack He                AdapterService.class.getDeclaredMethod("setAdapterService", AdapterService.class);
91bd90909c4ef180602ac088758ffdc13d37d24629Jack He        method.setAccessible(true);
92bd90909c4ef180602ac088758ffdc13d37d24629Jack He        method.invoke(null, adapterService);
93bd90909c4ef180602ac088758ffdc13d37d24629Jack He    }
94bd90909c4ef180602ac088758ffdc13d37d24629Jack He
95bd90909c4ef180602ac088758ffdc13d37d24629Jack He    /**
96bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * Clear the return value of {@link AdapterService#getAdapterService()} to null
97bd90909c4ef180602ac088758ffdc13d37d24629Jack He     *
98bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @param adapterService the {@link AdapterService} used when calling
99bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link TestUtils#setAdapterService(AdapterService)}
100bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @throws NoSuchMethodException when clearAdapterService method is not found
101bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @throws IllegalAccessException when clearAdapterService method cannot be accessed
102bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @throws InvocationTargetException when clearAdappterService method cannot be invoked,
103bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * which should never happen since clearAdapterService is a static method
104bd90909c4ef180602ac088758ffdc13d37d24629Jack He     */
105bd90909c4ef180602ac088758ffdc13d37d24629Jack He    public static void clearAdapterService(AdapterService adapterService)
106bd90909c4ef180602ac088758ffdc13d37d24629Jack He            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
107bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Assert.assertSame("AdapterService.getAdapterService() must return the same object as the"
108bd90909c4ef180602ac088758ffdc13d37d24629Jack He                        + " supplied adapterService in this method", adapterService,
109bd90909c4ef180602ac088758ffdc13d37d24629Jack He                AdapterService.getAdapterService());
110bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Assert.assertNotNull(adapterService);
111bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Method method =
112bd90909c4ef180602ac088758ffdc13d37d24629Jack He                AdapterService.class.getDeclaredMethod("clearAdapterService", AdapterService.class);
113bd90909c4ef180602ac088758ffdc13d37d24629Jack He        method.setAccessible(true);
114bd90909c4ef180602ac088758ffdc13d37d24629Jack He        method.invoke(null, adapterService);
115bd90909c4ef180602ac088758ffdc13d37d24629Jack He    }
116bd90909c4ef180602ac088758ffdc13d37d24629Jack He
117bd90909c4ef180602ac088758ffdc13d37d24629Jack He    /**
118bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * Start a profile service using the given {@link ServiceTestRule} and verify through
119bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link AdapterService#getAdapterService()} that the service is actually started within
120bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link TestUtils#SERVICE_TOGGLE_TIMEOUT_MS} milliseconds.
121bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link #setAdapterService(AdapterService)} must be called with a mocked
122bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link AdapterService} before calling this method
123bd90909c4ef180602ac088758ffdc13d37d24629Jack He     *
124bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @param serviceTestRule the {@link ServiceTestRule} used to execute the service start request
125bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @param profileServiceClass a class from one of {@link ProfileService}'s child classes
126bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @throws TimeoutException when service failed to start within either default timeout of
127bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link ServiceTestRule#DEFAULT_TIMEOUT} (normally 5s) or user specified time when creating
128bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link ServiceTestRule} through {@link ServiceTestRule#withTimeout(long, TimeUnit)} method
129bd90909c4ef180602ac088758ffdc13d37d24629Jack He     */
130bd90909c4ef180602ac088758ffdc13d37d24629Jack He    public static <T extends ProfileService> void startService(ServiceTestRule serviceTestRule,
131bd90909c4ef180602ac088758ffdc13d37d24629Jack He            Class<T> profileServiceClass) throws TimeoutException {
132bd90909c4ef180602ac088758ffdc13d37d24629Jack He        AdapterService adapterService = AdapterService.getAdapterService();
133bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Assert.assertNotNull(adapterService);
134bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Assert.assertTrue("AdapterService.getAdapterService() must return a mocked or spied object"
135bd90909c4ef180602ac088758ffdc13d37d24629Jack He                + " before calling this method", MockUtil.isMock(adapterService));
136bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Intent startIntent =
137bd90909c4ef180602ac088758ffdc13d37d24629Jack He                new Intent(InstrumentationRegistry.getTargetContext(), profileServiceClass);
138bd90909c4ef180602ac088758ffdc13d37d24629Jack He        startIntent.putExtra(AdapterService.EXTRA_ACTION,
139bd90909c4ef180602ac088758ffdc13d37d24629Jack He                AdapterService.ACTION_SERVICE_STATE_CHANGED);
140bd90909c4ef180602ac088758ffdc13d37d24629Jack He        startIntent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON);
141bd90909c4ef180602ac088758ffdc13d37d24629Jack He        serviceTestRule.startService(startIntent);
14235bbcaba2cfd2285e3897b501c34469043108a08Myles Watson        ArgumentCaptor<ProfileService> profile = ArgumentCaptor.forClass(profileServiceClass);
143bd90909c4ef180602ac088758ffdc13d37d24629Jack He        verify(adapterService, timeout(SERVICE_TOGGLE_TIMEOUT_MS)).onProfileServiceStateChanged(
14435bbcaba2cfd2285e3897b501c34469043108a08Myles Watson                profile.capture(), eq(BluetoothAdapter.STATE_ON));
14535bbcaba2cfd2285e3897b501c34469043108a08Myles Watson        Assert.assertEquals(profileServiceClass.getName(), profile.getValue().getClass().getName());
146bd90909c4ef180602ac088758ffdc13d37d24629Jack He    }
147bd90909c4ef180602ac088758ffdc13d37d24629Jack He
148bd90909c4ef180602ac088758ffdc13d37d24629Jack He    /**
149bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * Stop a profile service using the given {@link ServiceTestRule} and verify through
150bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link AdapterService#getAdapterService()} that the service is actually stopped within
151bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link TestUtils#SERVICE_TOGGLE_TIMEOUT_MS} milliseconds.
152bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link #setAdapterService(AdapterService)} must be called with a mocked
153bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link AdapterService} before calling this method
154bd90909c4ef180602ac088758ffdc13d37d24629Jack He     *
155bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @param serviceTestRule the {@link ServiceTestRule} used to execute the service start request
156bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @param profileServiceClass a class from one of {@link ProfileService}'s child classes
157bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * @throws TimeoutException when service failed to start within either default timeout of
158bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link ServiceTestRule#DEFAULT_TIMEOUT} (normally 5s) or user specified time when creating
159bd90909c4ef180602ac088758ffdc13d37d24629Jack He     * {@link ServiceTestRule} through {@link ServiceTestRule#withTimeout(long, TimeUnit)} method
160bd90909c4ef180602ac088758ffdc13d37d24629Jack He     */
161bd90909c4ef180602ac088758ffdc13d37d24629Jack He    public static <T extends ProfileService> void stopService(ServiceTestRule serviceTestRule,
162bd90909c4ef180602ac088758ffdc13d37d24629Jack He            Class<T> profileServiceClass) throws TimeoutException {
163bd90909c4ef180602ac088758ffdc13d37d24629Jack He        AdapterService adapterService = AdapterService.getAdapterService();
164bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Assert.assertNotNull(adapterService);
165bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Assert.assertTrue("AdapterService.getAdapterService() must return a mocked or spied object"
166bd90909c4ef180602ac088758ffdc13d37d24629Jack He                + " before calling this method", MockUtil.isMock(adapterService));
167bd90909c4ef180602ac088758ffdc13d37d24629Jack He        Intent stopIntent =
168bd90909c4ef180602ac088758ffdc13d37d24629Jack He                new Intent(InstrumentationRegistry.getTargetContext(), profileServiceClass);
169bd90909c4ef180602ac088758ffdc13d37d24629Jack He        stopIntent.putExtra(AdapterService.EXTRA_ACTION,
170bd90909c4ef180602ac088758ffdc13d37d24629Jack He                AdapterService.ACTION_SERVICE_STATE_CHANGED);
171bd90909c4ef180602ac088758ffdc13d37d24629Jack He        stopIntent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
172bd90909c4ef180602ac088758ffdc13d37d24629Jack He        serviceTestRule.startService(stopIntent);
17335bbcaba2cfd2285e3897b501c34469043108a08Myles Watson        ArgumentCaptor<ProfileService> profile = ArgumentCaptor.forClass(profileServiceClass);
174bd90909c4ef180602ac088758ffdc13d37d24629Jack He        verify(adapterService, timeout(SERVICE_TOGGLE_TIMEOUT_MS)).onProfileServiceStateChanged(
17535bbcaba2cfd2285e3897b501c34469043108a08Myles Watson                profile.capture(), eq(BluetoothAdapter.STATE_OFF));
17635bbcaba2cfd2285e3897b501c34469043108a08Myles Watson        Assert.assertEquals(profileServiceClass.getName(), profile.getValue().getClass().getName());
177bd90909c4ef180602ac088758ffdc13d37d24629Jack He    }
1787b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov
1797b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov    /**
1807b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov     * Create a test device.
1817b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov     *
1827b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov     * @param bluetoothAdapter the Bluetooth adapter to use
1837b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov     * @param id the test device ID. It must be an integer in the interval [0, 0xFF].
1847b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov     * @return {@link BluetoothDevice} test device for the device ID
1857b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov     */
1867b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov    public static BluetoothDevice getTestDevice(BluetoothAdapter bluetoothAdapter, int id) {
1877b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov        Assert.assertTrue(id <= 0xFF);
1887b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov        Assert.assertNotNull(bluetoothAdapter);
1897b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov        BluetoothDevice testDevice =
1907b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov                bluetoothAdapter.getRemoteDevice(String.format("00:01:02:03:04:%02X", id));
1917b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov        Assert.assertNotNull(testDevice);
1927b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov        return testDevice;
1937b52eb7550500a570937c6164018005b14ea359bPavlin Radoslavov    }
1943162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov
1953162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov    /**
196a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * Wait and verify that an intent has been received.
197a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     *
198a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * @param timeoutMs the time (in milliseconds) to wait for the intent
199a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * @param queue the queue for the intent
200a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * @return the received intent
201a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     */
202a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    public static Intent waitForIntent(int timeoutMs, BlockingQueue<Intent> queue) {
203a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        try {
204a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Intent intent = queue.poll(timeoutMs, TimeUnit.MILLISECONDS);
205a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Assert.assertNotNull(intent);
206a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            return intent;
207a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        } catch (InterruptedException e) {
208a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Assert.fail("Cannot obtain an Intent from the queue: " + e.getMessage());
209a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        }
210a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        return null;
211a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    }
212a639b81f96c534c2f1066354b4a574c0dda2f713Jack He
213a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    /**
214a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * Wait and verify that no intent has been received.
215a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     *
216a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * @param timeoutMs the time (in milliseconds) to wait and verify no intent
217a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * has been received
218a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * @param queue the queue for the intent
219a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * @return the received intent. Should be null under normal circumstances
220a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     */
221a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    public static Intent waitForNoIntent(int timeoutMs, BlockingQueue<Intent> queue) {
222a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        try {
223a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Intent intent = queue.poll(timeoutMs, TimeUnit.MILLISECONDS);
224a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Assert.assertNull(intent);
225a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            return intent;
226a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        } catch (InterruptedException e) {
227a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            Assert.fail("Cannot obtain an Intent from the queue: " + e.getMessage());
228a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        }
229a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        return null;
230a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    }
231a639b81f96c534c2f1066354b4a574c0dda2f713Jack He
232a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    /**
233a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * Wait for looper to finish its current task and all tasks schedule before this
234a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     *
235a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     * @param looper looper of interest
236a639b81f96c534c2f1066354b4a574c0dda2f713Jack He     */
237a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    public static void waitForLooperToFinishScheduledTask(Looper looper) {
238a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        runOnLooperSync(looper, () -> {
239a639b81f96c534c2f1066354b4a574c0dda2f713Jack He            // do nothing, just need to make sure looper finishes current task
240a639b81f96c534c2f1066354b4a574c0dda2f713Jack He        });
241a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    }
242a639b81f96c534c2f1066354b4a574c0dda2f713Jack He
243a639b81f96c534c2f1066354b4a574c0dda2f713Jack He    /**
2443162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * Run synchronously a runnable action on a looper.
2453162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * The method will return after the action has been execution to completion.
2463162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     *
2473162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * Example:
2483162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * <pre>
2493162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * {@code
2503162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * TestUtils.runOnMainSync(new Runnable() {
2513162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     *       public void run() {
2523162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     *           Assert.assertTrue(mA2dpService.stop());
2533162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     *       }
2543162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     *   });
2553162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * }
2563162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * </pre>
2573162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     *
2583162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * @param looper the looper used to run the action
2593162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * @param action the action to run
2603162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     */
2613162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov    public static void runOnLooperSync(Looper looper, Runnable action) {
2623162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        if (Looper.myLooper() == looper) {
2633162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            // requested thread is the same as the current thread. call directly.
2643162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            action.run();
2653162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        } else {
2663162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            Handler handler = new Handler(looper);
2673162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            SyncRunnable sr = new SyncRunnable(action);
2683162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            handler.post(sr);
2693162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            sr.waitForComplete();
2703162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        }
2713162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov    }
2723162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov
2733162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov    /**
2743162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     * Helper class used to run synchronously a runnable action on a looper.
2753162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov     */
2763162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov    private static final class SyncRunnable implements Runnable {
2773162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        private final Runnable mTarget;
2783162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        private volatile boolean mComplete = false;
2793162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov
2803162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        SyncRunnable(Runnable target) {
2813162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            mTarget = target;
2823162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        }
2833162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov
2843162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        @Override
2853162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        public void run() {
2863162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            mTarget.run();
2873162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            synchronized (this) {
2883162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov                mComplete = true;
2893162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov                notifyAll();
2903162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            }
2913162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        }
2923162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov
2933162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        public void waitForComplete() {
2943162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            synchronized (this) {
2953162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov                while (!mComplete) {
2963162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov                    try {
2973162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov                        wait();
2983162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov                    } catch (InterruptedException e) {
2993162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov                    }
3003162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov                }
3013162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov            }
3023162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov        }
3033162432cb2cb1238c95123b412dd326f31ba1610Pavlin Radoslavov    }
304bd90909c4ef180602ac088758ffdc13d37d24629Jack He}
305