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