• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.hfp;
18 
19 import static org.hamcrest.Matchers.*;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.anyBoolean;
22 import static org.mockito.ArgumentMatchers.anyInt;
23 import static org.mockito.ArgumentMatchers.eq;
24 import static org.mockito.Mockito.*;
25 
26 import android.app.Activity;
27 import android.app.Instrumentation;
28 import android.bluetooth.BluetoothAdapter;
29 import android.bluetooth.BluetoothDevice;
30 import android.bluetooth.BluetoothHeadset;
31 import android.bluetooth.BluetoothProfile;
32 import android.bluetooth.BluetoothUuid;
33 import android.bluetooth.IBluetoothHeadset;
34 import android.content.BroadcastReceiver;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.IntentFilter;
38 import android.media.AudioManager;
39 import android.net.Uri;
40 import android.os.ParcelUuid;
41 import android.os.PowerManager;
42 import android.os.RemoteException;
43 import android.telecom.PhoneAccount;
44 
45 import androidx.test.InstrumentationRegistry;
46 import androidx.test.espresso.intent.Intents;
47 import androidx.test.espresso.intent.matcher.IntentMatchers;
48 import androidx.test.filters.MediumTest;
49 import androidx.test.rule.ServiceTestRule;
50 import androidx.test.runner.AndroidJUnit4;
51 
52 import com.android.bluetooth.R;
53 import com.android.bluetooth.TestUtils;
54 import com.android.bluetooth.btservice.AdapterService;
55 import com.android.bluetooth.btservice.storage.DatabaseManager;
56 
57 import org.hamcrest.Matchers;
58 import org.junit.After;
59 import org.junit.Assert;
60 import org.junit.Assume;
61 import org.junit.Before;
62 import org.junit.Rule;
63 import org.junit.Test;
64 import org.junit.runner.RunWith;
65 import org.mockito.ArgumentCaptor;
66 import org.mockito.Mock;
67 import org.mockito.MockitoAnnotations;
68 import org.mockito.Spy;
69 
70 import java.lang.reflect.Method;
71 import java.util.Collections;
72 import java.util.HashSet;
73 import java.util.List;
74 import java.util.concurrent.BlockingQueue;
75 import java.util.concurrent.LinkedBlockingQueue;
76 
77 /**
78  * A set of integration test that involves both {@link HeadsetService} and
79  * {@link HeadsetStateMachine}
80  */
81 @MediumTest
82 @RunWith(AndroidJUnit4.class)
83 public class HeadsetServiceAndStateMachineTest {
84     private static final int ASYNC_CALL_TIMEOUT_MILLIS = 250;
85     private static final int START_VR_TIMEOUT_MILLIS = 1000;
86     private static final int START_VR_TIMEOUT_WAIT_MILLIS = START_VR_TIMEOUT_MILLIS * 3 / 2;
87     private static final int MAX_HEADSET_CONNECTIONS = 5;
88     private static final ParcelUuid[] FAKE_HEADSET_UUID = {BluetoothUuid.Handsfree};
89     private static final String TEST_PHONE_NUMBER = "1234567890";
90     private static final String TEST_CALLER_ID = "Test Name";
91 
92     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
93 
94     private Context mTargetContext;
95     private HeadsetService mHeadsetService;
96     private IBluetoothHeadset.Stub mHeadsetServiceBinder;
97     private BluetoothAdapter mAdapter;
98     private HeadsetNativeInterface mNativeInterface;
99     private ArgumentCaptor<HeadsetStateMachine> mStateMachineArgument =
100             ArgumentCaptor.forClass(HeadsetStateMachine.class);
101     private HashSet<BluetoothDevice> mBondedDevices = new HashSet<>();
102     private final BlockingQueue<Intent> mConnectionStateChangedQueue = new LinkedBlockingQueue<>();
103     private final BlockingQueue<Intent> mActiveDeviceChangedQueue = new LinkedBlockingQueue<>();
104     private final BlockingQueue<Intent> mAudioStateChangedQueue = new LinkedBlockingQueue<>();
105     private HeadsetIntentReceiver mHeadsetIntentReceiver;
106     private int mOriginalVrTimeoutMs = 5000;
107     private PowerManager.WakeLock mVoiceRecognitionWakeLock;
108 
109     private class HeadsetIntentReceiver extends BroadcastReceiver {
110         @Override
onReceive(Context context, Intent intent)111         public void onReceive(Context context, Intent intent) {
112             String action = intent.getAction();
113             if (action == null) {
114                 Assert.fail("Action is null for intent " + intent);
115                 return;
116             }
117             switch (action) {
118                 case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
119                     try {
120                         mConnectionStateChangedQueue.put(intent);
121                     } catch (InterruptedException e) {
122                         Assert.fail("Cannot add Intent to the Connection State Changed queue: "
123                                 + e.getMessage());
124                     }
125                     break;
126                 case BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED:
127                     try {
128                         mActiveDeviceChangedQueue.put(intent);
129                     } catch (InterruptedException e) {
130                         Assert.fail("Cannot add Intent to the Active Device Changed queue: "
131                                 + e.getMessage());
132                     }
133                     break;
134                 case BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED:
135                     try {
136                         mAudioStateChangedQueue.put(intent);
137                     } catch (InterruptedException e) {
138                         Assert.fail("Cannot add Intent to the Audio State Changed queue: "
139                                 + e.getMessage());
140                     }
141                     break;
142                 default:
143                     Assert.fail("Unknown action " + action);
144             }
145 
146         }
147     }
148 
149     @Spy private HeadsetObjectsFactory mObjectsFactory = HeadsetObjectsFactory.getInstance();
150     @Mock private AdapterService mAdapterService;
151     @Mock private DatabaseManager mDatabaseManager;
152     @Mock private HeadsetSystemInterface mSystemInterface;
153     @Mock private AudioManager mAudioManager;
154     @Mock private HeadsetPhoneState mPhoneState;
155 
156     @Before
setUp()157     public void setUp() throws Exception {
158         mTargetContext = InstrumentationRegistry.getTargetContext();
159         Assume.assumeTrue("Ignore test when HeadsetService is not enabled",
160                 mTargetContext.getResources().getBoolean(R.bool.profile_supported_hs_hfp));
161         MockitoAnnotations.initMocks(this);
162         PowerManager powerManager =
163                 (PowerManager) mTargetContext.getSystemService(Context.POWER_SERVICE);
164         mVoiceRecognitionWakeLock =
165                 powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "VoiceRecognitionTest");
166         TestUtils.setAdapterService(mAdapterService);
167         doReturn(true).when(mAdapterService).isEnabled();
168         doReturn(MAX_HEADSET_CONNECTIONS).when(mAdapterService).getMaxConnectedAudioDevices();
169         doReturn(new ParcelUuid[]{BluetoothUuid.Handsfree}).when(mAdapterService)
170                 .getRemoteUuids(any(BluetoothDevice.class));
171         // We cannot mock HeadsetObjectsFactory.getInstance() with Mockito.
172         // Hence we need to use reflection to call a private method to
173         // initialize properly the HeadsetObjectsFactory.sInstance field.
174         Method method = HeadsetObjectsFactory.class.getDeclaredMethod("setInstanceForTesting",
175                 HeadsetObjectsFactory.class);
176         method.setAccessible(true);
177         method.invoke(null, mObjectsFactory);
178         // This line must be called to make sure relevant objects are initialized properly
179         mAdapter = BluetoothAdapter.getDefaultAdapter();
180         // Mock methods in AdapterService
181         doReturn(FAKE_HEADSET_UUID).when(mAdapterService)
182                 .getRemoteUuids(any(BluetoothDevice.class));
183         doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService)
184                 .getBondState(any(BluetoothDevice.class));
185         doAnswer(invocation -> mBondedDevices.toArray(new BluetoothDevice[]{})).when(
186                 mAdapterService).getBondedDevices();
187         // Mock system interface
188         doNothing().when(mSystemInterface).init();
189         doNothing().when(mSystemInterface).stop();
190         when(mSystemInterface.getHeadsetPhoneState()).thenReturn(mPhoneState);
191         when(mSystemInterface.getAudioManager()).thenReturn(mAudioManager);
192         when(mSystemInterface.activateVoiceRecognition()).thenReturn(true);
193         when(mSystemInterface.deactivateVoiceRecognition()).thenReturn(true);
194         when(mSystemInterface.getVoiceRecognitionWakeLock()).thenReturn(mVoiceRecognitionWakeLock);
195         when(mSystemInterface.isCallIdle()).thenReturn(true);
196         // Mock methods in HeadsetNativeInterface
197         mNativeInterface = spy(HeadsetNativeInterface.getInstance());
198         doNothing().when(mNativeInterface).init(anyInt(), anyBoolean());
199         doNothing().when(mNativeInterface).cleanup();
200         doReturn(true).when(mNativeInterface).connectHfp(any(BluetoothDevice.class));
201         doReturn(true).when(mNativeInterface).disconnectHfp(any(BluetoothDevice.class));
202         doReturn(true).when(mNativeInterface).connectAudio(any(BluetoothDevice.class));
203         doReturn(true).when(mNativeInterface).disconnectAudio(any(BluetoothDevice.class));
204         doReturn(true).when(mNativeInterface).setActiveDevice(any(BluetoothDevice.class));
205         doReturn(true).when(mNativeInterface).sendBsir(any(BluetoothDevice.class), anyBoolean());
206         doReturn(true).when(mNativeInterface).startVoiceRecognition(any(BluetoothDevice.class));
207         doReturn(true).when(mNativeInterface).stopVoiceRecognition(any(BluetoothDevice.class));
208         doReturn(true).when(mNativeInterface)
209                 .atResponseCode(any(BluetoothDevice.class), anyInt(), anyInt());
210         // Use real state machines here
211         doCallRealMethod().when(mObjectsFactory)
212                 .makeStateMachine(any(), any(), any(), any(), any(), any());
213         // Mock methods in HeadsetObjectsFactory
214         doReturn(mSystemInterface).when(mObjectsFactory).makeSystemInterface(any());
215         doReturn(mNativeInterface).when(mObjectsFactory).getNativeInterface();
216         Intents.init();
217         // Modify start VR timeout to a smaller value for testing
218         mOriginalVrTimeoutMs = HeadsetService.sStartVrTimeoutMs;
219         HeadsetService.sStartVrTimeoutMs = START_VR_TIMEOUT_MILLIS;
220         TestUtils.startService(mServiceRule, HeadsetService.class);
221         mHeadsetService = HeadsetService.getHeadsetService();
222         Assert.assertNotNull(mHeadsetService);
223         verify(mObjectsFactory).makeSystemInterface(mHeadsetService);
224         verify(mObjectsFactory).getNativeInterface();
225         verify(mNativeInterface).init(MAX_HEADSET_CONNECTIONS + 1, true /* inband ringtone */);
226         mHeadsetServiceBinder = (IBluetoothHeadset.Stub) mHeadsetService.initBinder();
227         Assert.assertNotNull(mHeadsetServiceBinder);
228 
229         // Set up the Connection State Changed receiver
230         IntentFilter filter = new IntentFilter();
231         filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
232         filter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
233         filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
234         mHeadsetIntentReceiver = new HeadsetIntentReceiver();
235         mTargetContext.registerReceiver(mHeadsetIntentReceiver, filter);
236     }
237 
238     @After
tearDown()239     public void tearDown() throws Exception {
240         if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_hs_hfp)) {
241             return;
242         }
243         mTargetContext.unregisterReceiver(mHeadsetIntentReceiver);
244         TestUtils.stopService(mServiceRule, HeadsetService.class);
245         HeadsetService.sStartVrTimeoutMs = mOriginalVrTimeoutMs;
246         Intents.release();
247         mHeadsetService = HeadsetService.getHeadsetService();
248         Assert.assertNull(mHeadsetService);
249         Method method = HeadsetObjectsFactory.class.getDeclaredMethod("setInstanceForTesting",
250                 HeadsetObjectsFactory.class);
251         method.setAccessible(true);
252         method.invoke(null, (HeadsetObjectsFactory) null);
253         TestUtils.clearAdapterService(mAdapterService);
254         mBondedDevices.clear();
255         mConnectionStateChangedQueue.clear();
256         mActiveDeviceChangedQueue.clear();
257         // Clear classes that is spied on and has static life time
258         clearInvocations(mNativeInterface);
259     }
260 
261     /**
262      * Test to verify that HeadsetService can be successfully started
263      */
264     @Test
testGetHeadsetService()265     public void testGetHeadsetService() {
266         Assert.assertEquals(mHeadsetService, HeadsetService.getHeadsetService());
267         // Verify default connection and audio states
268         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
269         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
270                 mHeadsetService.getConnectionState(device));
271         Assert.assertEquals(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
272                 mHeadsetService.getAudioState(device));
273     }
274 
275     /**
276      * Test to verify that {@link HeadsetService#connect(BluetoothDevice)} actually result in a
277      * call to native interface to create HFP
278      */
279     @Test
testConnectFromApi()280     public void testConnectFromApi() {
281         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
282         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
283         when(mDatabaseManager.getProfilePriority(device, BluetoothProfile.HEADSET))
284                 .thenReturn(BluetoothProfile.PRIORITY_UNDEFINED);
285         mBondedDevices.add(device);
286         Assert.assertTrue(mHeadsetService.connect(device));
287         verify(mObjectsFactory).makeStateMachine(device,
288                 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService,
289                 mNativeInterface, mSystemInterface);
290         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
291         // 250ms for processing two messages should be way more than enough. Anything that breaks
292         // this indicate some breakage in other part of Android OS
293         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
294                 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
295         verify(mNativeInterface).connectHfp(device);
296         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
297                 mHeadsetService.getConnectionState(device));
298         Assert.assertEquals(Collections.singletonList(device),
299                 mHeadsetService.getDevicesMatchingConnectionStates(
300                         new int[]{BluetoothProfile.STATE_CONNECTING}));
301         // Get feedback from native to put device into connected state
302         HeadsetStackEvent connectedEvent =
303                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
304                         HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED, device);
305         mHeadsetService.messageFromNative(connectedEvent);
306         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
307         // 250ms for processing two messages should be way more than enough. Anything that breaks
308         // this indicate some breakage in other part of Android OS
309         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
310                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING);
311         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
312                 mHeadsetService.getConnectionState(device));
313         Assert.assertEquals(Collections.singletonList(device),
314                 mHeadsetService.getDevicesMatchingConnectionStates(
315                         new int[]{BluetoothProfile.STATE_CONNECTED}));
316     }
317 
318     /**
319      * Test to verify that {@link BluetoothDevice#ACTION_BOND_STATE_CHANGED} intent with
320      * {@link BluetoothDevice#EXTRA_BOND_STATE} as {@link BluetoothDevice#BOND_NONE} will cause a
321      * disconnected device to be removed from state machine map
322      */
323     @Test
testUnbondDevice_disconnectBeforeUnbond()324     public void testUnbondDevice_disconnectBeforeUnbond() {
325         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
326         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
327         when(mDatabaseManager.getProfilePriority(device, BluetoothProfile.HEADSET))
328                 .thenReturn(BluetoothProfile.PRIORITY_UNDEFINED);
329         mBondedDevices.add(device);
330         Assert.assertTrue(mHeadsetService.connect(device));
331         verify(mObjectsFactory).makeStateMachine(device,
332                 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService,
333                 mNativeInterface, mSystemInterface);
334         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
335         // 250ms for processing two messages should be way more than enough. Anything that breaks
336         // this indicate some breakage in other part of Android OS
337         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
338                 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
339         verify(mNativeInterface).connectHfp(device);
340         // Get feedback from native layer to go back to disconnected state
341         HeadsetStackEvent connectedEvent =
342                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
343                         HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, device);
344         mHeadsetService.messageFromNative(connectedEvent);
345         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
346         // 250ms for processing two messages should be way more than enough. Anything that breaks
347         // this indicate some breakage in other part of Android OS
348         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
349                 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING);
350         // Send unbond intent
351         doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService).getBondState(eq(device));
352         Intent unbondIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
353         unbondIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
354         unbondIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
355         InstrumentationRegistry.getTargetContext().sendBroadcast(unbondIntent);
356         // Check that the state machine is actually destroyed
357         verify(mObjectsFactory, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).destroyStateMachine(
358                 mStateMachineArgument.capture());
359         Assert.assertEquals(device, mStateMachineArgument.getValue().getDevice());
360     }
361 
362     /**
363      * Test to verify that if a device can be property disconnected after
364      * {@link BluetoothDevice#ACTION_BOND_STATE_CHANGED} intent with
365      * {@link BluetoothDevice#EXTRA_BOND_STATE} as {@link BluetoothDevice#BOND_NONE} is received.
366      */
367     @Test
testUnbondDevice_disconnectAfterUnbond()368     public void testUnbondDevice_disconnectAfterUnbond() {
369         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
370         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
371         when(mDatabaseManager.getProfilePriority(device, BluetoothProfile.HEADSET))
372                 .thenReturn(BluetoothProfile.PRIORITY_UNDEFINED);
373         mBondedDevices.add(device);
374         Assert.assertTrue(mHeadsetService.connect(device));
375         verify(mObjectsFactory).makeStateMachine(device,
376                 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService,
377                 mNativeInterface, mSystemInterface);
378         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
379         // 250ms for processing two messages should be way more than enough. Anything that breaks
380         // this indicate some breakage in other part of Android OS
381         verify(mNativeInterface, after(ASYNC_CALL_TIMEOUT_MILLIS)).connectHfp(device);
382         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
383                 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
384         // Get feedback from native layer to go to connected state
385         HeadsetStackEvent connectedEvent =
386                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
387                         HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED, device);
388         mHeadsetService.messageFromNative(connectedEvent);
389         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
390         // 250ms for processing two messages should be way more than enough. Anything that breaks
391         // this indicate some breakage in other part of Android OS
392         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
393                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING);
394         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
395                 mHeadsetService.getConnectionState(device));
396         Assert.assertEquals(Collections.singletonList(device),
397                 mHeadsetService.getConnectedDevices());
398         // Send unbond intent
399         doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService).getBondState(eq(device));
400         Intent unbondIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
401         unbondIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
402         unbondIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
403         InstrumentationRegistry.getTargetContext().sendBroadcast(unbondIntent);
404         // Check that the state machine is not destroyed
405         verify(mObjectsFactory, after(ASYNC_CALL_TIMEOUT_MILLIS).never()).destroyStateMachine(
406                 any());
407         // Now disconnect the device
408         HeadsetStackEvent connectingEvent =
409                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
410                         HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, device);
411         mHeadsetService.messageFromNative(connectingEvent);
412         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
413                 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
414         // Check that the state machine is destroyed after another async call
415         verify(mObjectsFactory, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).destroyStateMachine(
416                 mStateMachineArgument.capture());
417         Assert.assertEquals(device, mStateMachineArgument.getValue().getDevice());
418 
419     }
420 
421     /**
422      * Test the functionality of
423      * {@link BluetoothHeadset#startScoUsingVirtualVoiceCall()} and
424      * {@link BluetoothHeadset#stopScoUsingVirtualVoiceCall()}
425      *
426      * Normal start and stop
427      */
428     @Test
testVirtualCall_normalStartStop()429     public void testVirtualCall_normalStartStop() throws RemoteException {
430         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
431             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
432             connectTestDevice(device);
433             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
434                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
435             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
436                     new int[]{BluetoothProfile.STATE_CONNECTED}),
437                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
438         }
439         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
440         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
441         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
442         BluetoothDevice activeDevice = connectedDevices.get(MAX_HEADSET_CONNECTIONS / 2);
443         Assert.assertTrue(mHeadsetServiceBinder.setActiveDevice(activeDevice));
444         verify(mNativeInterface).setActiveDevice(activeDevice);
445         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
446         Assert.assertEquals(activeDevice, mHeadsetServiceBinder.getActiveDevice());
447         // Start virtual call
448         Assert.assertTrue(mHeadsetServiceBinder.startScoUsingVirtualVoiceCall());
449         Assert.assertTrue(mHeadsetService.isVirtualCallStarted());
450         verifyVirtualCallStartSequenceInvocations(connectedDevices);
451         // End virtual call
452         Assert.assertTrue(mHeadsetServiceBinder.stopScoUsingVirtualVoiceCall());
453         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
454         verifyVirtualCallStopSequenceInvocations(connectedDevices);
455     }
456 
457     /**
458      * Test the functionality of
459      * {@link BluetoothHeadset#startScoUsingVirtualVoiceCall()} and
460      * {@link BluetoothHeadset#stopScoUsingVirtualVoiceCall()}
461      *
462      * Virtual call should be preempted by telecom call
463      */
464     @Test
testVirtualCall_preemptedByTelecomCall()465     public void testVirtualCall_preemptedByTelecomCall() throws RemoteException {
466         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
467             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
468             connectTestDevice(device);
469             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
470                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
471             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
472                     new int[]{BluetoothProfile.STATE_CONNECTED}),
473                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
474         }
475         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
476         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
477         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
478         BluetoothDevice activeDevice = connectedDevices.get(MAX_HEADSET_CONNECTIONS / 2);
479         Assert.assertTrue(mHeadsetServiceBinder.setActiveDevice(activeDevice));
480         verify(mNativeInterface).setActiveDevice(activeDevice);
481         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
482         Assert.assertEquals(activeDevice, mHeadsetServiceBinder.getActiveDevice());
483         // Start virtual call
484         Assert.assertTrue(mHeadsetServiceBinder.startScoUsingVirtualVoiceCall());
485         Assert.assertTrue(mHeadsetService.isVirtualCallStarted());
486         verifyVirtualCallStartSequenceInvocations(connectedDevices);
487         // Virtual call should be preempted by telecom call
488         mHeadsetServiceBinder.phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_INCOMING,
489                 TEST_PHONE_NUMBER, 128, "");
490         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
491         verifyVirtualCallStopSequenceInvocations(connectedDevices);
492         verifyCallStateToNativeInvocation(
493                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_INCOMING,
494                         TEST_PHONE_NUMBER, 128, ""), connectedDevices);
495     }
496 
497     /**
498      * Test the functionality of
499      * {@link BluetoothHeadset#startScoUsingVirtualVoiceCall()} and
500      * {@link BluetoothHeadset#stopScoUsingVirtualVoiceCall()}
501      *
502      * Virtual call should be rejected when there is a telecom call
503      */
504     @Test
testVirtualCall_rejectedWhenThereIsTelecomCall()505     public void testVirtualCall_rejectedWhenThereIsTelecomCall() throws RemoteException {
506         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
507             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
508             connectTestDevice(device);
509             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
510                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
511             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
512                     new int[]{BluetoothProfile.STATE_CONNECTED}),
513                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
514         }
515         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
516         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
517         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
518         BluetoothDevice activeDevice = connectedDevices.get(MAX_HEADSET_CONNECTIONS / 2);
519         Assert.assertTrue(mHeadsetServiceBinder.setActiveDevice(activeDevice));
520         verify(mNativeInterface).setActiveDevice(activeDevice);
521         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
522         Assert.assertEquals(activeDevice, mHeadsetServiceBinder.getActiveDevice());
523         // Reject virtual call setup if call state is not idle
524         when(mSystemInterface.isCallIdle()).thenReturn(false);
525         Assert.assertFalse(mHeadsetServiceBinder.startScoUsingVirtualVoiceCall());
526         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
527     }
528 
529     /**
530      * Test the behavior when dialing outgoing call from the headset
531      */
532     @Test
testDialingOutCall_NormalDialingOut()533     public void testDialingOutCall_NormalDialingOut() throws RemoteException {
534         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
535             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
536             connectTestDevice(device);
537             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
538                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
539             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
540                     new int[]{BluetoothProfile.STATE_CONNECTED}),
541                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
542         }
543         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
544         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
545         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
546         BluetoothDevice activeDevice = connectedDevices.get(0);
547         Assert.assertTrue(mHeadsetServiceBinder.setActiveDevice(activeDevice));
548         verify(mNativeInterface).setActiveDevice(activeDevice);
549         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
550         Assert.assertEquals(activeDevice, mHeadsetServiceBinder.getActiveDevice());
551         // Try dialing out from the a non active Headset
552         BluetoothDevice dialingOutDevice = connectedDevices.get(1);
553         HeadsetStackEvent dialingOutEvent =
554                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_DIAL_CALL, TEST_PHONE_NUMBER,
555                         dialingOutDevice);
556         Uri dialOutUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, TEST_PHONE_NUMBER, null);
557         Instrumentation.ActivityResult result =
558                 new Instrumentation.ActivityResult(Activity.RESULT_OK, null);
559         Intents.intending(IntentMatchers.hasAction(Intent.ACTION_CALL_PRIVILEGED))
560                 .respondWith(result);
561         mHeadsetService.messageFromNative(dialingOutEvent);
562         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, dialingOutDevice);
563         TestUtils.waitForLooperToFinishScheduledTask(
564                 mHeadsetService.getStateMachinesThreadLooper());
565         Assert.assertTrue(mHeadsetService.hasDeviceInitiatedDialingOut());
566         // Make sure the correct intent is fired
567         Intents.intended(allOf(IntentMatchers.hasAction(Intent.ACTION_CALL_PRIVILEGED),
568                 IntentMatchers.hasData(dialOutUri)), Intents.times(1));
569         // Further dial out attempt from same device will fail
570         mHeadsetService.messageFromNative(dialingOutEvent);
571         TestUtils.waitForLooperToFinishScheduledTask(
572                 mHeadsetService.getStateMachinesThreadLooper());
573         verify(mNativeInterface).atResponseCode(dialingOutDevice,
574                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
575         // Further dial out attempt from other device will fail
576         HeadsetStackEvent dialingOutEventOtherDevice =
577                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_DIAL_CALL, TEST_PHONE_NUMBER,
578                         activeDevice);
579         mHeadsetService.messageFromNative(dialingOutEventOtherDevice);
580         TestUtils.waitForLooperToFinishScheduledTask(
581                 mHeadsetService.getStateMachinesThreadLooper());
582         verify(mNativeInterface).atResponseCode(activeDevice, HeadsetHalConstants.AT_RESPONSE_ERROR,
583                 0);
584         TestUtils.waitForNoIntent(ASYNC_CALL_TIMEOUT_MILLIS, mActiveDeviceChangedQueue);
585         Assert.assertEquals(dialingOutDevice, mHeadsetServiceBinder.getActiveDevice());
586         // Make sure only one intent is fired
587         Intents.intended(allOf(IntentMatchers.hasAction(Intent.ACTION_CALL_PRIVILEGED),
588                 IntentMatchers.hasData(dialOutUri)), Intents.times(1));
589         // Verify that phone state update confirms the dial out event
590         mHeadsetServiceBinder.phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_DIALING,
591                 TEST_PHONE_NUMBER, 128, "");
592         HeadsetCallState dialingCallState =
593                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING,
594                         TEST_PHONE_NUMBER, 128, "");
595         verifyCallStateToNativeInvocation(dialingCallState, connectedDevices);
596         verify(mNativeInterface).atResponseCode(dialingOutDevice,
597                 HeadsetHalConstants.AT_RESPONSE_OK, 0);
598         // Verify that IDLE phone state clears the dialing out flag
599         mHeadsetServiceBinder.phoneStateChanged(1, 0, HeadsetHalConstants.CALL_STATE_IDLE,
600                 TEST_PHONE_NUMBER, 128, "");
601         HeadsetCallState activeCallState =
602                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING,
603                         TEST_PHONE_NUMBER, 128, "");
604         verifyCallStateToNativeInvocation(activeCallState, connectedDevices);
605         Assert.assertFalse(mHeadsetService.hasDeviceInitiatedDialingOut());
606     }
607 
608     /**
609      * Test the behavior when dialing outgoing call from the headset
610      */
611     @Test
testDialingOutCall_DialingOutPreemptVirtualCall()612     public void testDialingOutCall_DialingOutPreemptVirtualCall() throws RemoteException {
613         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
614             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
615             connectTestDevice(device);
616             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
617                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
618             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
619                     new int[]{BluetoothProfile.STATE_CONNECTED}),
620                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
621         }
622         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
623         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
624         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
625         BluetoothDevice activeDevice = connectedDevices.get(0);
626         Assert.assertTrue(mHeadsetServiceBinder.setActiveDevice(activeDevice));
627         verify(mNativeInterface).setActiveDevice(activeDevice);
628         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
629         Assert.assertEquals(activeDevice, mHeadsetServiceBinder.getActiveDevice());
630         // Start virtual call
631         Assert.assertTrue(mHeadsetServiceBinder.startScoUsingVirtualVoiceCall());
632         Assert.assertTrue(mHeadsetService.isVirtualCallStarted());
633         verifyVirtualCallStartSequenceInvocations(connectedDevices);
634         // Try dialing out from the a non active Headset
635         BluetoothDevice dialingOutDevice = connectedDevices.get(1);
636         HeadsetStackEvent dialingOutEvent =
637                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_DIAL_CALL, TEST_PHONE_NUMBER,
638                         dialingOutDevice);
639         Uri dialOutUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, TEST_PHONE_NUMBER, null);
640         Instrumentation.ActivityResult result =
641                 new Instrumentation.ActivityResult(Activity.RESULT_OK, null);
642         Intents.intending(IntentMatchers.hasAction(Intent.ACTION_CALL_PRIVILEGED))
643                 .respondWith(result);
644         mHeadsetService.messageFromNative(dialingOutEvent);
645         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, dialingOutDevice);
646         TestUtils.waitForLooperToFinishScheduledTask(
647                 mHeadsetService.getStateMachinesThreadLooper());
648         Assert.assertTrue(mHeadsetService.hasDeviceInitiatedDialingOut());
649         // Make sure the correct intent is fired
650         Intents.intended(allOf(IntentMatchers.hasAction(Intent.ACTION_CALL_PRIVILEGED),
651                 IntentMatchers.hasData(dialOutUri)), Intents.times(1));
652         // Virtual call should be preempted by dialing out call
653         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
654         verifyVirtualCallStopSequenceInvocations(connectedDevices);
655     }
656 
657     /**
658      * Test to verify the following behavior regarding active HF initiated voice recognition
659      * in the successful scenario
660      *   1. HF device sends AT+BVRA=1
661      *   2. HeadsetStateMachine sends out {@link Intent#ACTION_VOICE_COMMAND}
662      *   3. AG call {@link BluetoothHeadset#stopVoiceRecognition(BluetoothDevice)} to indicate
663      *      that voice recognition has stopped
664      *   4. AG sends OK to HF
665      *
666      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
667      */
668     @Test
testVoiceRecognition_SingleHfInitiatedSuccess()669     public void testVoiceRecognition_SingleHfInitiatedSuccess() {
670         // Connect HF
671         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
672         connectTestDevice(device);
673         // Make device active
674         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
675         verify(mNativeInterface).setActiveDevice(device);
676         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
677         // Start voice recognition
678         startVoiceRecognitionFromHf(device);
679     }
680 
681     /**
682      * Test to verify the following behavior regarding active HF stop voice recognition
683      * in the successful scenario
684      *   1. HF device sends AT+BVRA=0
685      *   2. Let voice recognition app to stop
686      *   3. AG respond with OK
687      *   4. Disconnect audio
688      *
689      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
690      */
691     @Test
testVoiceRecognition_SingleHfStopSuccess()692     public void testVoiceRecognition_SingleHfStopSuccess() {
693         // Connect HF
694         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
695         connectTestDevice(device);
696         // Make device active
697         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
698         verify(mNativeInterface).setActiveDevice(device);
699         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
700         // Start voice recognition
701         startVoiceRecognitionFromHf(device);
702         // Stop voice recognition
703         HeadsetStackEvent stopVrEvent =
704                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
705                         HeadsetHalConstants.VR_STATE_STOPPED, device);
706         mHeadsetService.messageFromNative(stopVrEvent);
707         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).deactivateVoiceRecognition();
708         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).atResponseCode(device,
709                 HeadsetHalConstants.AT_RESPONSE_OK, 0);
710         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).disconnectAudio(device);
711         verifyNoMoreInteractions(mNativeInterface);
712     }
713 
714     /**
715      * Test to verify the following behavior regarding active HF initiated voice recognition
716      * in the failed to activate scenario
717      *   1. HF device sends AT+BVRA=1
718      *   2. HeadsetStateMachine sends out {@link Intent#ACTION_VOICE_COMMAND}
719      *   3. Failed to activate voice recognition through intent
720      *   4. AG sends ERROR to HF
721      *
722      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
723      */
724     @Test
testVoiceRecognition_SingleHfInitiatedFailedToActivate()725     public void testVoiceRecognition_SingleHfInitiatedFailedToActivate() {
726         when(mSystemInterface.activateVoiceRecognition()).thenReturn(false);
727         // Connect HF
728         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
729         connectTestDevice(device);
730         // Make device active
731         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
732         verify(mNativeInterface).setActiveDevice(device);
733         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
734         // Start voice recognition
735         HeadsetStackEvent startVrEvent =
736                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
737                         HeadsetHalConstants.VR_STATE_STARTED, device);
738         mHeadsetService.messageFromNative(startVrEvent);
739         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
740         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(device,
741                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
742         verifyNoMoreInteractions(mNativeInterface);
743         verifyZeroInteractions(mAudioManager);
744     }
745 
746 
747     /**
748      * Test to verify the following behavior regarding active HF initiated voice recognition
749      * in the timeout scenario
750      *   1. HF device sends AT+BVRA=1
751      *   2. HeadsetStateMachine sends out {@link Intent#ACTION_VOICE_COMMAND}
752      *   3. AG failed to get back to us on time
753      *   4. AG sends ERROR to HF
754      *
755      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
756      */
757     @Test
testVoiceRecognition_SingleHfInitiatedTimeout()758     public void testVoiceRecognition_SingleHfInitiatedTimeout() {
759         // Connect HF
760         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
761         connectTestDevice(device);
762         // Make device active
763         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
764         verify(mNativeInterface).setActiveDevice(device);
765         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
766         // Start voice recognition
767         HeadsetStackEvent startVrEvent =
768                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
769                         HeadsetHalConstants.VR_STATE_STARTED, device);
770         mHeadsetService.messageFromNative(startVrEvent);
771         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
772         verify(mNativeInterface, timeout(START_VR_TIMEOUT_WAIT_MILLIS)).atResponseCode(device,
773                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
774         verifyNoMoreInteractions(mNativeInterface);
775         verifyZeroInteractions(mAudioManager);
776     }
777 
778     /**
779      * Test to verify the following behavior regarding AG initiated voice recognition
780      * in the successful scenario
781      *   1. AG starts voice recognition and notify the Bluetooth stack via
782      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
783      *      recognition has started
784      *   2. AG send +BVRA:1 to HF
785      *   3. AG start SCO connection if SCO has not been started
786      *
787      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
788      */
789     @Test
testVoiceRecognition_SingleAgInitiatedSuccess()790     public void testVoiceRecognition_SingleAgInitiatedSuccess() {
791         // Connect HF
792         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
793         connectTestDevice(device);
794         // Make device active
795         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
796         verify(mNativeInterface).setActiveDevice(device);
797         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
798         // Start voice recognition
799         startVoiceRecognitionFromAg();
800     }
801 
802     /**
803      * Test to verify the following behavior regarding AG initiated voice recognition
804      * in the successful scenario
805      *   1. AG starts voice recognition and notify the Bluetooth stack via
806      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
807      *      recognition has started, BluetoothDevice is null in this case
808      *   2. AG send +BVRA:1 to current active HF
809      *   3. AG start SCO connection if SCO has not been started
810      *
811      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
812      */
813     @Test
testVoiceRecognition_SingleAgInitiatedSuccessNullInput()814     public void testVoiceRecognition_SingleAgInitiatedSuccessNullInput() {
815         // Connect HF
816         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
817         connectTestDevice(device);
818         // Make device active
819         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
820         verify(mNativeInterface).setActiveDevice(device);
821         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
822         // Start voice recognition on null argument should go to active device
823         Assert.assertTrue(mHeadsetService.startVoiceRecognition(null));
824         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).startVoiceRecognition(device);
825     }
826 
827     /**
828      * Test to verify the following behavior regarding AG initiated voice recognition
829      * in the successful scenario
830      *   1. AG starts voice recognition and notify the Bluetooth stack via
831      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
832      *      recognition has started, BluetoothDevice is null and active device is null
833      *   2. The call should fail
834      *
835      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
836      */
837     @Test
testVoiceRecognition_SingleAgInitiatedFailNullActiveDevice()838     public void testVoiceRecognition_SingleAgInitiatedFailNullActiveDevice() {
839         // Connect HF
840         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
841         connectTestDevice(device);
842         // Make device active
843         Assert.assertTrue(mHeadsetService.setActiveDevice(null));
844         // TODO(b/79760385): setActiveDevice(null) does not propagate to native layer
845         // verify(mNativeInterface).setActiveDevice(null);
846         Assert.assertNull(mHeadsetService.getActiveDevice());
847         // Start voice recognition on null argument should fail
848         Assert.assertFalse(mHeadsetService.startVoiceRecognition(null));
849     }
850 
851     /**
852      * Test to verify the following behavior regarding AG stops voice recognition
853      * in the successful scenario
854      *   1. AG stops voice recognition and notify the Bluetooth stack via
855      *      {@link BluetoothHeadset#stopVoiceRecognition(BluetoothDevice)} to indicate that voice
856      *      recognition has stopped
857      *   2. AG send +BVRA:0 to HF
858      *   3. AG stop SCO connection
859      *
860      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
861      */
862     @Test
testVoiceRecognition_SingleAgStopSuccess()863     public void testVoiceRecognition_SingleAgStopSuccess() {
864         // Connect HF
865         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
866         connectTestDevice(device);
867         // Make device active
868         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
869         verify(mNativeInterface).setActiveDevice(device);
870         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
871         // Start voice recognition
872         startVoiceRecognitionFromAg();
873         // Stop voice recognition
874         Assert.assertTrue(mHeadsetService.stopVoiceRecognition(device));
875         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).stopVoiceRecognition(device);
876         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).disconnectAudio(device);
877         verifyNoMoreInteractions(mNativeInterface);
878     }
879 
880     /**
881      * Test to verify the following behavior regarding AG initiated voice recognition
882      * in the device not connected failure scenario
883      *   1. AG starts voice recognition and notify the Bluetooth stack via
884      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
885      *      recognition has started
886      *   2. Device is not connected, return false
887      *
888      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
889      */
890     @Test
testVoiceRecognition_SingleAgInitiatedDeviceNotConnected()891     public void testVoiceRecognition_SingleAgInitiatedDeviceNotConnected() {
892         // Start voice recognition
893         BluetoothDevice disconnectedDevice = TestUtils.getTestDevice(mAdapter, 0);
894         Assert.assertFalse(mHeadsetService.startVoiceRecognition(disconnectedDevice));
895         verifyNoMoreInteractions(mNativeInterface);
896         verifyZeroInteractions(mAudioManager);
897     }
898 
899     /**
900      * Test to verify the following behavior regarding non active HF initiated voice recognition
901      * in the successful scenario
902      *   1. HF device sends AT+BVRA=1
903      *   2. HeadsetStateMachine sends out {@link Intent#ACTION_VOICE_COMMAND}
904      *   3. AG call {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate
905      *      that voice recognition has started
906      *   4. AG sends OK to HF
907      *   5. Suspend A2DP
908      *   6. Start SCO if SCO hasn't been started
909      *
910      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
911      */
912     @Test
testVoiceRecognition_MultiHfInitiatedSwitchActiveDeviceSuccess()913     public void testVoiceRecognition_MultiHfInitiatedSwitchActiveDeviceSuccess() {
914         // Connect two devices
915         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
916         connectTestDevice(deviceA);
917         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
918         connectTestDevice(deviceB);
919         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceA, false);
920         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceB, false);
921         // Set active device to device B
922         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
923         verify(mNativeInterface).setActiveDevice(deviceB);
924         Assert.assertEquals(deviceB, mHeadsetService.getActiveDevice());
925         // Start voice recognition from non active device A
926         HeadsetStackEvent startVrEventA =
927                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
928                         HeadsetHalConstants.VR_STATE_STARTED, deviceA);
929         mHeadsetService.messageFromNative(startVrEventA);
930         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
931         // Active device should have been swapped to device A
932         verify(mNativeInterface).setActiveDevice(deviceA);
933         Assert.assertEquals(deviceA, mHeadsetService.getActiveDevice());
934         // Start voice recognition from other device should fail
935         HeadsetStackEvent startVrEventB =
936                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
937                         HeadsetHalConstants.VR_STATE_STARTED, deviceB);
938         mHeadsetService.messageFromNative(startVrEventB);
939         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(deviceB,
940                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
941         // Reply to continue voice recognition
942         mHeadsetService.startVoiceRecognition(deviceA);
943         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(deviceA,
944                 HeadsetHalConstants.AT_RESPONSE_OK, 0);
945         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
946                 .setParameters("A2dpSuspended=true");
947         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connectAudio(deviceA);
948         verifyNoMoreInteractions(mNativeInterface);
949     }
950 
951     /**
952      * Test to verify the following behavior regarding non active HF initiated voice recognition
953      * in the successful scenario
954      *   1. HF device sends AT+BVRA=1
955      *   2. HeadsetStateMachine sends out {@link Intent#ACTION_VOICE_COMMAND}
956      *   3. AG call {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate
957      *      that voice recognition has started, but on a wrong HF
958      *   4. Headset service instead keep using the initiating HF
959      *   5. AG sends OK to HF
960      *   6. Suspend A2DP
961      *   7. Start SCO if SCO hasn't been started
962      *
963      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
964      */
965     @Test
testVoiceRecognition_MultiHfInitiatedSwitchActiveDeviceReplyWrongHfSuccess()966     public void testVoiceRecognition_MultiHfInitiatedSwitchActiveDeviceReplyWrongHfSuccess() {
967         // Connect two devices
968         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
969         connectTestDevice(deviceA);
970         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
971         connectTestDevice(deviceB);
972         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceA, false);
973         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceB, false);
974         // Set active device to device B
975         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
976         verify(mNativeInterface).setActiveDevice(deviceB);
977         Assert.assertEquals(deviceB, mHeadsetService.getActiveDevice());
978         // Start voice recognition from non active device A
979         HeadsetStackEvent startVrEventA =
980                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
981                         HeadsetHalConstants.VR_STATE_STARTED, deviceA);
982         mHeadsetService.messageFromNative(startVrEventA);
983         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
984         // Active device should have been swapped to device A
985         verify(mNativeInterface).setActiveDevice(deviceA);
986         Assert.assertEquals(deviceA, mHeadsetService.getActiveDevice());
987         // Start voice recognition from other device should fail
988         HeadsetStackEvent startVrEventB =
989                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
990                         HeadsetHalConstants.VR_STATE_STARTED, deviceB);
991         mHeadsetService.messageFromNative(startVrEventB);
992         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(deviceB,
993                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
994         // Reply to continue voice recognition on a wrong device
995         mHeadsetService.startVoiceRecognition(deviceB);
996         // We still continue on the initiating HF
997         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(deviceA,
998                 HeadsetHalConstants.AT_RESPONSE_OK, 0);
999         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1000                 .setParameters("A2dpSuspended=true");
1001         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connectAudio(deviceA);
1002         verifyNoMoreInteractions(mNativeInterface);
1003     }
1004 
1005 
1006     /**
1007      * Test to verify the following behavior regarding AG initiated voice recognition
1008      * in the successful scenario
1009      *   1. AG starts voice recognition and notify the Bluetooth stack via
1010      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
1011      *      recognition has started
1012      *   2. Suspend A2DP
1013      *   3. AG send +BVRA:1 to HF
1014      *   4. AG start SCO connection if SCO has not been started
1015      *
1016      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
1017      */
1018     @Test
testVoiceRecognition_MultiAgInitiatedSuccess()1019     public void testVoiceRecognition_MultiAgInitiatedSuccess() {
1020         // Connect two devices
1021         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
1022         connectTestDevice(deviceA);
1023         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
1024         connectTestDevice(deviceB);
1025         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceA, false);
1026         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceB, false);
1027         // Set active device to device B
1028         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
1029         verify(mNativeInterface).setActiveDevice(deviceB);
1030         Assert.assertEquals(deviceB, mHeadsetService.getActiveDevice());
1031         // Start voice recognition
1032         startVoiceRecognitionFromAg();
1033         // Start voice recognition from other device should fail
1034         HeadsetStackEvent startVrEventA =
1035                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
1036                         HeadsetHalConstants.VR_STATE_STARTED, deviceA);
1037         mHeadsetService.messageFromNative(startVrEventA);
1038         // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
1039         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).stopVoiceRecognition(deviceB);
1040         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).disconnectAudio(deviceB);
1041         // This request should still fail
1042         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(deviceA,
1043                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1044         verifyNoMoreInteractions(mNativeInterface);
1045     }
1046 
1047     /**
1048      * Test to verify the following behavior regarding AG initiated voice recognition
1049      * in the device not active failure scenario
1050      *   1. AG starts voice recognition and notify the Bluetooth stack via
1051      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
1052      *      recognition has started
1053      *   2. Device is not active, should do voice recognition on active device only
1054      *
1055      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
1056      */
1057     @Test
testVoiceRecognition_MultiAgInitiatedDeviceNotActive()1058     public void testVoiceRecognition_MultiAgInitiatedDeviceNotActive() {
1059         // Connect two devices
1060         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
1061         connectTestDevice(deviceA);
1062         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
1063         connectTestDevice(deviceB);
1064         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceA, false);
1065         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceB, false);
1066         // Set active device to device B
1067         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
1068         verify(mNativeInterface).setActiveDevice(deviceB);
1069         Assert.assertEquals(deviceB, mHeadsetService.getActiveDevice());
1070         // Start voice recognition should succeed
1071         Assert.assertTrue(mHeadsetService.startVoiceRecognition(deviceA));
1072         verify(mNativeInterface).setActiveDevice(deviceA);
1073         Assert.assertEquals(deviceA, mHeadsetService.getActiveDevice());
1074         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).startVoiceRecognition(deviceA);
1075         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1076                 .setParameters("A2dpSuspended=true");
1077         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connectAudio(deviceA);
1078         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, deviceA,
1079                 BluetoothHeadset.STATE_AUDIO_CONNECTING, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1080         mHeadsetService.messageFromNative(
1081                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED,
1082                         HeadsetHalConstants.AUDIO_STATE_CONNECTED, deviceA));
1083         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, deviceA,
1084                 BluetoothHeadset.STATE_AUDIO_CONNECTED, BluetoothHeadset.STATE_AUDIO_CONNECTING);
1085         verifyNoMoreInteractions(mNativeInterface);
1086     }
1087 
1088     /**
1089      * Test to verify the call state and caller information are correctly delivered
1090      * {@link BluetoothHeadset#phoneStateChanged(int, int, int, String, int, String, boolean)}
1091      */
1092     @Test
testPhoneStateChangedWithIncomingCallState()1093     public void testPhoneStateChangedWithIncomingCallState() throws RemoteException {
1094         // Connect HF
1095         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
1096             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
1097             connectTestDevice(device);
1098             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
1099                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
1100             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
1101                     new int[]{BluetoothProfile.STATE_CONNECTED}),
1102                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
1103         }
1104         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
1105         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
1106         // Incoming call update by telecom
1107         mHeadsetServiceBinder.phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_INCOMING,
1108                 TEST_PHONE_NUMBER, 128, TEST_CALLER_ID);
1109         HeadsetCallState incomingCallState = new HeadsetCallState(0, 0,
1110                 HeadsetHalConstants.CALL_STATE_INCOMING, TEST_PHONE_NUMBER, 128, TEST_CALLER_ID);
1111         verifyCallStateToNativeInvocation(incomingCallState, connectedDevices);
1112     }
1113 
startVoiceRecognitionFromHf(BluetoothDevice device)1114     private void startVoiceRecognitionFromHf(BluetoothDevice device) {
1115         // Start voice recognition
1116         HeadsetStackEvent startVrEvent =
1117                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
1118                         HeadsetHalConstants.VR_STATE_STARTED, device);
1119         mHeadsetService.messageFromNative(startVrEvent);
1120         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
1121         Assert.assertTrue(mHeadsetService.startVoiceRecognition(device));
1122         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(device,
1123                 HeadsetHalConstants.AT_RESPONSE_OK, 0);
1124         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1125                 .setParameters("A2dpSuspended=true");
1126         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connectAudio(device);
1127         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1128                 BluetoothHeadset.STATE_AUDIO_CONNECTING, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1129         mHeadsetService.messageFromNative(
1130                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED,
1131                         HeadsetHalConstants.AUDIO_STATE_CONNECTED, device));
1132         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1133                 BluetoothHeadset.STATE_AUDIO_CONNECTED, BluetoothHeadset.STATE_AUDIO_CONNECTING);
1134         verifyNoMoreInteractions(mNativeInterface);
1135     }
1136 
startVoiceRecognitionFromAg()1137     private void startVoiceRecognitionFromAg() {
1138         BluetoothDevice device = mHeadsetService.getActiveDevice();
1139         Assert.assertNotNull(device);
1140         Assert.assertTrue(mHeadsetService.startVoiceRecognition(device));
1141         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).startVoiceRecognition(device);
1142         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1143                 .setParameters("A2dpSuspended=true");
1144         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connectAudio(device);
1145         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1146                 BluetoothHeadset.STATE_AUDIO_CONNECTING, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1147         mHeadsetService.messageFromNative(
1148                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED,
1149                         HeadsetHalConstants.AUDIO_STATE_CONNECTED, device));
1150         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1151                 BluetoothHeadset.STATE_AUDIO_CONNECTED, BluetoothHeadset.STATE_AUDIO_CONNECTING);
1152         verifyNoMoreInteractions(mNativeInterface);
1153     }
1154 
connectTestDevice(BluetoothDevice device)1155     private void connectTestDevice(BluetoothDevice device) {
1156         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
1157         when(mDatabaseManager.getProfilePriority(device, BluetoothProfile.HEADSET))
1158                 .thenReturn(BluetoothProfile.PRIORITY_UNDEFINED);
1159         // Make device bonded
1160         mBondedDevices.add(device);
1161         // Use connecting event to indicate that device is connecting
1162         HeadsetStackEvent rfcommConnectedEvent =
1163                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
1164                         HeadsetHalConstants.CONNECTION_STATE_CONNECTED, device);
1165         mHeadsetService.messageFromNative(rfcommConnectedEvent);
1166         verify(mObjectsFactory).makeStateMachine(device,
1167                 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService,
1168                 mNativeInterface, mSystemInterface);
1169         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
1170         // 250ms for processing two messages should be way more than enough. Anything that breaks
1171         // this indicate some breakage in other part of Android OS
1172         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1173                 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
1174         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
1175                 mHeadsetService.getConnectionState(device));
1176         Assert.assertEquals(Collections.singletonList(device),
1177                 mHeadsetService.getDevicesMatchingConnectionStates(
1178                         new int[]{BluetoothProfile.STATE_CONNECTING}));
1179         // Get feedback from native to put device into connected state
1180         HeadsetStackEvent slcConnectedEvent =
1181                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
1182                         HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED, device);
1183         mHeadsetService.messageFromNative(slcConnectedEvent);
1184         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
1185         // 250ms for processing two messages should be way more than enough. Anything that breaks
1186         // this indicate some breakage in other part of Android OS
1187         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1188                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING);
1189         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
1190                 mHeadsetService.getConnectionState(device));
1191     }
1192 
waitAndVerifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)1193     private void waitAndVerifyConnectionStateIntent(int timeoutMs, BluetoothDevice device,
1194             int newState, int prevState) {
1195         Intent intent = TestUtils.waitForIntent(timeoutMs, mConnectionStateChangedQueue);
1196         Assert.assertNotNull(intent);
1197         HeadsetTestUtils.verifyConnectionStateBroadcast(device, newState, prevState, intent, false);
1198     }
1199 
waitAndVerifyActiveDeviceChangedIntent(int timeoutMs, BluetoothDevice device)1200     private void waitAndVerifyActiveDeviceChangedIntent(int timeoutMs, BluetoothDevice device) {
1201         Intent intent = TestUtils.waitForIntent(timeoutMs, mActiveDeviceChangedQueue);
1202         Assert.assertNotNull(intent);
1203         HeadsetTestUtils.verifyActiveDeviceChangedBroadcast(device, intent, false);
1204     }
1205 
waitAndVerifyAudioStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)1206     private void waitAndVerifyAudioStateIntent(int timeoutMs, BluetoothDevice device, int newState,
1207             int prevState) {
1208         Intent intent = TestUtils.waitForIntent(timeoutMs, mAudioStateChangedQueue);
1209         Assert.assertNotNull(intent);
1210         HeadsetTestUtils.verifyAudioStateBroadcast(device, newState, prevState, intent);
1211     }
1212 
1213     /**
1214      * Verify the series of invocations after
1215      * {@link BluetoothHeadset#startScoUsingVirtualVoiceCall()}
1216      *
1217      * @param connectedDevices must be in the same sequence as
1218      * {@link BluetoothHeadset#getConnectedDevices()}
1219      */
verifyVirtualCallStartSequenceInvocations(List<BluetoothDevice> connectedDevices)1220     private void verifyVirtualCallStartSequenceInvocations(List<BluetoothDevice> connectedDevices) {
1221         // Do not verify HeadsetPhoneState changes as it is verified in HeadsetServiceTest
1222         verifyCallStateToNativeInvocation(
1223                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0, ""),
1224                 connectedDevices);
1225         verifyCallStateToNativeInvocation(
1226                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0, ""),
1227                 connectedDevices);
1228         verifyCallStateToNativeInvocation(
1229                 new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, ""),
1230                 connectedDevices);
1231     }
1232 
verifyVirtualCallStopSequenceInvocations(List<BluetoothDevice> connectedDevices)1233     private void verifyVirtualCallStopSequenceInvocations(List<BluetoothDevice> connectedDevices) {
1234         verifyCallStateToNativeInvocation(
1235                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, ""),
1236                 connectedDevices);
1237     }
1238 
verifyCallStateToNativeInvocation(HeadsetCallState headsetCallState, List<BluetoothDevice> connectedDevices)1239     private void verifyCallStateToNativeInvocation(HeadsetCallState headsetCallState,
1240             List<BluetoothDevice> connectedDevices) {
1241         for (BluetoothDevice device : connectedDevices) {
1242             verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).phoneStateChange(device,
1243                     headsetCallState);
1244         }
1245     }
1246 }
1247