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.HFP};
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(MAX_HEADSET_CONNECTIONS).when(mAdapterService).getMaxConnectedAudioDevices();
168         doReturn(new ParcelUuid[]{BluetoothUuid.HFP}).when(mAdapterService)
169                 .getRemoteUuids(any(BluetoothDevice.class));
170         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
171         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
172         // We cannot mock HeadsetObjectsFactory.getInstance() with Mockito.
173         // Hence we need to use reflection to call a private method to
174         // initialize properly the HeadsetObjectsFactory.sInstance field.
175         Method method = HeadsetObjectsFactory.class.getDeclaredMethod("setInstanceForTesting",
176                 HeadsetObjectsFactory.class);
177         method.setAccessible(true);
178         method.invoke(null, mObjectsFactory);
179         // This line must be called to make sure relevant objects are initialized properly
180         mAdapter = BluetoothAdapter.getDefaultAdapter();
181         // Mock methods in AdapterService
182         doReturn(FAKE_HEADSET_UUID).when(mAdapterService)
183                 .getRemoteUuids(any(BluetoothDevice.class));
184         doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService)
185                 .getBondState(any(BluetoothDevice.class));
186         doAnswer(invocation -> mBondedDevices.toArray(new BluetoothDevice[]{})).when(
187                 mAdapterService).getBondedDevices();
188         // Mock system interface
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(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET))
283                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
284         mBondedDevices.add(device);
285         Assert.assertTrue(mHeadsetService.connect(device));
286         verify(mObjectsFactory).makeStateMachine(device,
287                 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService,
288                 mNativeInterface, mSystemInterface);
289         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
290         // 250ms for processing two messages should be way more than enough. Anything that breaks
291         // this indicate some breakage in other part of Android OS
292         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
293                 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
294         verify(mNativeInterface).connectHfp(device);
295         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
296                 mHeadsetService.getConnectionState(device));
297         Assert.assertEquals(Collections.singletonList(device),
298                 mHeadsetService.getDevicesMatchingConnectionStates(
299                         new int[]{BluetoothProfile.STATE_CONNECTING}));
300         // Get feedback from native to put device into connected state
301         HeadsetStackEvent connectedEvent =
302                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
303                         HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED, device);
304         mHeadsetService.messageFromNative(connectedEvent);
305         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
306         // 250ms for processing two messages should be way more than enough. Anything that breaks
307         // this indicate some breakage in other part of Android OS
308         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
309                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING);
310         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
311                 mHeadsetService.getConnectionState(device));
312         Assert.assertEquals(Collections.singletonList(device),
313                 mHeadsetService.getDevicesMatchingConnectionStates(
314                         new int[]{BluetoothProfile.STATE_CONNECTED}));
315     }
316 
317     /**
318      * Test to verify that {@link BluetoothDevice#ACTION_BOND_STATE_CHANGED} intent with
319      * {@link BluetoothDevice#EXTRA_BOND_STATE} as {@link BluetoothDevice#BOND_NONE} will cause a
320      * disconnected device to be removed from state machine map
321      */
322     @Test
testUnbondDevice_disconnectBeforeUnbond()323     public void testUnbondDevice_disconnectBeforeUnbond() {
324         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
325         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET))
326                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
327         mBondedDevices.add(device);
328         Assert.assertTrue(mHeadsetService.connect(device));
329         verify(mObjectsFactory).makeStateMachine(device,
330                 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService,
331                 mNativeInterface, mSystemInterface);
332         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
333         // 250ms for processing two messages should be way more than enough. Anything that breaks
334         // this indicate some breakage in other part of Android OS
335         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
336                 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
337         verify(mNativeInterface).connectHfp(device);
338         // Get feedback from native layer to go back to disconnected state
339         HeadsetStackEvent connectedEvent =
340                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
341                         HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, device);
342         mHeadsetService.messageFromNative(connectedEvent);
343         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
344         // 250ms for processing two messages should be way more than enough. Anything that breaks
345         // this indicate some breakage in other part of Android OS
346         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
347                 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING);
348         // Send unbond intent
349         doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService).getBondState(eq(device));
350         Intent unbondIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
351         unbondIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
352         unbondIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
353         InstrumentationRegistry.getTargetContext().sendBroadcast(unbondIntent);
354         // Check that the state machine is actually destroyed
355         verify(mObjectsFactory, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).destroyStateMachine(
356                 mStateMachineArgument.capture());
357         Assert.assertEquals(device, mStateMachineArgument.getValue().getDevice());
358     }
359 
360     /**
361      * Test to verify that if a device can be property disconnected after
362      * {@link BluetoothDevice#ACTION_BOND_STATE_CHANGED} intent with
363      * {@link BluetoothDevice#EXTRA_BOND_STATE} as {@link BluetoothDevice#BOND_NONE} is received.
364      */
365     @Test
testUnbondDevice_disconnectAfterUnbond()366     public void testUnbondDevice_disconnectAfterUnbond() {
367         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
368         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET))
369                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
370         mBondedDevices.add(device);
371         Assert.assertTrue(mHeadsetService.connect(device));
372         verify(mObjectsFactory).makeStateMachine(device,
373                 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService,
374                 mNativeInterface, mSystemInterface);
375         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
376         // 250ms for processing two messages should be way more than enough. Anything that breaks
377         // this indicate some breakage in other part of Android OS
378         verify(mNativeInterface, after(ASYNC_CALL_TIMEOUT_MILLIS)).connectHfp(device);
379         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
380                 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
381         // Get feedback from native layer to go to connected state
382         HeadsetStackEvent connectedEvent =
383                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
384                         HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED, device);
385         mHeadsetService.messageFromNative(connectedEvent);
386         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
387         // 250ms for processing two messages should be way more than enough. Anything that breaks
388         // this indicate some breakage in other part of Android OS
389         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
390                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING);
391         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
392                 mHeadsetService.getConnectionState(device));
393         Assert.assertEquals(Collections.singletonList(device),
394                 mHeadsetService.getConnectedDevices());
395         // Send unbond intent
396         doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService).getBondState(eq(device));
397         Intent unbondIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
398         unbondIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
399         unbondIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
400         InstrumentationRegistry.getTargetContext().sendBroadcast(unbondIntent);
401         // Check that the state machine is not destroyed
402         verify(mObjectsFactory, after(ASYNC_CALL_TIMEOUT_MILLIS).never()).destroyStateMachine(
403                 any());
404         // Now disconnect the device
405         HeadsetStackEvent connectingEvent =
406                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
407                         HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, device);
408         mHeadsetService.messageFromNative(connectingEvent);
409         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
410                 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
411         // Check that the state machine is destroyed after another async call
412         verify(mObjectsFactory, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).destroyStateMachine(
413                 mStateMachineArgument.capture());
414         Assert.assertEquals(device, mStateMachineArgument.getValue().getDevice());
415 
416     }
417 
418     /**
419      * Test the functionality of
420      * {@link BluetoothHeadset#startScoUsingVirtualVoiceCall()} and
421      * {@link BluetoothHeadset#stopScoUsingVirtualVoiceCall()}
422      *
423      * Normal start and stop
424      */
425     @Test
testVirtualCall_normalStartStop()426     public void testVirtualCall_normalStartStop() throws RemoteException {
427         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
428             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
429             connectTestDevice(device);
430             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
431                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
432             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
433                     new int[]{BluetoothProfile.STATE_CONNECTED}, mAdapter.getAttributionSource()),
434                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
435         }
436         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
437         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
438         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
439         BluetoothDevice activeDevice = connectedDevices.get(MAX_HEADSET_CONNECTIONS / 2);
440         Assert.assertTrue(mHeadsetServiceBinder.setActiveDevice(activeDevice,
441                 mAdapter.getAttributionSource()));
442         verify(mNativeInterface).setActiveDevice(activeDevice);
443         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
444         Assert.assertEquals(activeDevice,
445                 mHeadsetServiceBinder.getActiveDevice(mAdapter.getAttributionSource()));
446         // Start virtual call
447         Assert.assertTrue(mHeadsetServiceBinder
448                 .startScoUsingVirtualVoiceCall(mAdapter.getAttributionSource()));
449         Assert.assertTrue(mHeadsetService.isVirtualCallStarted());
450         verifyVirtualCallStartSequenceInvocations(connectedDevices);
451         // End virtual call
452         Assert.assertTrue(mHeadsetServiceBinder
453                 .stopScoUsingVirtualVoiceCall(mAdapter.getAttributionSource()));
454         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
455         verifyVirtualCallStopSequenceInvocations(connectedDevices);
456     }
457 
458     /**
459      * Test the functionality of
460      * {@link BluetoothHeadset#startScoUsingVirtualVoiceCall()} and
461      * {@link BluetoothHeadset#stopScoUsingVirtualVoiceCall()}
462      *
463      * Virtual call should be preempted by telecom call
464      */
465     @Test
testVirtualCall_preemptedByTelecomCall()466     public void testVirtualCall_preemptedByTelecomCall() throws RemoteException {
467         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
468             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
469             connectTestDevice(device);
470             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
471                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
472             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
473                     new int[]{BluetoothProfile.STATE_CONNECTED}, mAdapter.getAttributionSource()),
474                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
475         }
476         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
477         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
478         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
479         BluetoothDevice activeDevice = connectedDevices.get(MAX_HEADSET_CONNECTIONS / 2);
480         Assert.assertTrue(mHeadsetServiceBinder.setActiveDevice(activeDevice,
481                 mAdapter.getAttributionSource()));
482         verify(mNativeInterface).setActiveDevice(activeDevice);
483         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
484         Assert.assertEquals(activeDevice,
485                 mHeadsetServiceBinder.getActiveDevice(mAdapter.getAttributionSource()));
486         // Start virtual call
487         Assert.assertTrue(mHeadsetServiceBinder
488                 .startScoUsingVirtualVoiceCall(mAdapter.getAttributionSource()));
489         Assert.assertTrue(mHeadsetService.isVirtualCallStarted());
490         verifyVirtualCallStartSequenceInvocations(connectedDevices);
491         // Virtual call should be preempted by telecom call
492         mHeadsetServiceBinder.phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_INCOMING,
493                 TEST_PHONE_NUMBER, 128, "", mAdapter.getAttributionSource());
494         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
495         verifyVirtualCallStopSequenceInvocations(connectedDevices);
496         verifyCallStateToNativeInvocation(
497                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_INCOMING,
498                         TEST_PHONE_NUMBER, 128, ""), connectedDevices);
499     }
500 
501     /**
502      * Test the functionality of
503      * {@link BluetoothHeadset#startScoUsingVirtualVoiceCall()} and
504      * {@link BluetoothHeadset#stopScoUsingVirtualVoiceCall()}
505      *
506      * Virtual call should be rejected when there is a telecom call
507      */
508     @Test
testVirtualCall_rejectedWhenThereIsTelecomCall()509     public void testVirtualCall_rejectedWhenThereIsTelecomCall() throws RemoteException {
510         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
511             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
512             connectTestDevice(device);
513             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
514                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
515             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
516                     new int[]{BluetoothProfile.STATE_CONNECTED}, mAdapter.getAttributionSource()),
517                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
518         }
519         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
520         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
521         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
522         BluetoothDevice activeDevice = connectedDevices.get(MAX_HEADSET_CONNECTIONS / 2);
523         Assert.assertTrue(mHeadsetServiceBinder.setActiveDevice(activeDevice,
524                 mAdapter.getAttributionSource()));
525         verify(mNativeInterface).setActiveDevice(activeDevice);
526         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
527         Assert.assertEquals(activeDevice,
528                 mHeadsetServiceBinder.getActiveDevice(mAdapter.getAttributionSource()));
529         // Reject virtual call setup if call state is not idle
530         when(mSystemInterface.isCallIdle()).thenReturn(false);
531         Assert.assertFalse(mHeadsetServiceBinder
532                 .startScoUsingVirtualVoiceCall(mAdapter.getAttributionSource()));
533         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
534     }
535 
536     /**
537      * Test the behavior when dialing outgoing call from the headset
538      */
539     @Test
testDialingOutCall_NormalDialingOut()540     public void testDialingOutCall_NormalDialingOut() throws RemoteException {
541         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
542             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
543             connectTestDevice(device);
544             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
545                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
546             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
547                     new int[]{BluetoothProfile.STATE_CONNECTED}, mAdapter.getAttributionSource()),
548                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
549         }
550         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
551         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
552         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
553         BluetoothDevice activeDevice = connectedDevices.get(0);
554         Assert.assertTrue(mHeadsetServiceBinder.setActiveDevice(activeDevice,
555                 mAdapter.getAttributionSource()));
556         verify(mNativeInterface).setActiveDevice(activeDevice);
557         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
558         Assert.assertEquals(activeDevice,
559                 mHeadsetServiceBinder.getActiveDevice(mAdapter.getAttributionSource()));
560         // Try dialing out from the a non active Headset
561         BluetoothDevice dialingOutDevice = connectedDevices.get(1);
562         HeadsetStackEvent dialingOutEvent =
563                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_DIAL_CALL, TEST_PHONE_NUMBER,
564                         dialingOutDevice);
565         Uri dialOutUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, TEST_PHONE_NUMBER, null);
566         Instrumentation.ActivityResult result =
567                 new Instrumentation.ActivityResult(Activity.RESULT_OK, null);
568         Intents.intending(IntentMatchers.hasAction(Intent.ACTION_CALL_PRIVILEGED))
569                 .respondWith(result);
570         mHeadsetService.messageFromNative(dialingOutEvent);
571         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, dialingOutDevice);
572         TestUtils.waitForLooperToFinishScheduledTask(
573                 mHeadsetService.getStateMachinesThreadLooper());
574         Assert.assertTrue(mHeadsetService.hasDeviceInitiatedDialingOut());
575         // Make sure the correct intent is fired
576         Intents.intended(allOf(IntentMatchers.hasAction(Intent.ACTION_CALL_PRIVILEGED),
577                 IntentMatchers.hasData(dialOutUri)), Intents.times(1));
578         // Further dial out attempt from same device will fail
579         mHeadsetService.messageFromNative(dialingOutEvent);
580         TestUtils.waitForLooperToFinishScheduledTask(
581                 mHeadsetService.getStateMachinesThreadLooper());
582         verify(mNativeInterface).atResponseCode(dialingOutDevice,
583                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
584         // Further dial out attempt from other device will fail
585         HeadsetStackEvent dialingOutEventOtherDevice =
586                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_DIAL_CALL, TEST_PHONE_NUMBER,
587                         activeDevice);
588         mHeadsetService.messageFromNative(dialingOutEventOtherDevice);
589         TestUtils.waitForLooperToFinishScheduledTask(
590                 mHeadsetService.getStateMachinesThreadLooper());
591         verify(mNativeInterface).atResponseCode(activeDevice, HeadsetHalConstants.AT_RESPONSE_ERROR,
592                 0);
593         TestUtils.waitForNoIntent(ASYNC_CALL_TIMEOUT_MILLIS, mActiveDeviceChangedQueue);
594         Assert.assertEquals(dialingOutDevice,
595                 mHeadsetServiceBinder.getActiveDevice(mAdapter.getAttributionSource()));
596         // Make sure only one intent is fired
597         Intents.intended(allOf(IntentMatchers.hasAction(Intent.ACTION_CALL_PRIVILEGED),
598                 IntentMatchers.hasData(dialOutUri)), Intents.times(1));
599         // Verify that phone state update confirms the dial out event
600         mHeadsetServiceBinder.phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_DIALING,
601                 TEST_PHONE_NUMBER, 128, "", mAdapter.getAttributionSource());
602         HeadsetCallState dialingCallState =
603                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING,
604                         TEST_PHONE_NUMBER, 128, "");
605         verifyCallStateToNativeInvocation(dialingCallState, connectedDevices);
606         verify(mNativeInterface).atResponseCode(dialingOutDevice,
607                 HeadsetHalConstants.AT_RESPONSE_OK, 0);
608         // Verify that IDLE phone state clears the dialing out flag
609         mHeadsetServiceBinder.phoneStateChanged(1, 0, HeadsetHalConstants.CALL_STATE_IDLE,
610                 TEST_PHONE_NUMBER, 128, "", mAdapter.getAttributionSource());
611         HeadsetCallState activeCallState =
612                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING,
613                         TEST_PHONE_NUMBER, 128, "");
614         verifyCallStateToNativeInvocation(activeCallState, connectedDevices);
615         Assert.assertFalse(mHeadsetService.hasDeviceInitiatedDialingOut());
616     }
617 
618     /**
619      * Test the behavior when dialing outgoing call from the headset
620      */
621     @Test
testDialingOutCall_DialingOutPreemptVirtualCall()622     public void testDialingOutCall_DialingOutPreemptVirtualCall() throws RemoteException {
623         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
624             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
625             connectTestDevice(device);
626             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
627                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
628             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
629                     new int[]{BluetoothProfile.STATE_CONNECTED}, mAdapter.getAttributionSource()),
630                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
631         }
632         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
633         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
634         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
635         BluetoothDevice activeDevice = connectedDevices.get(0);
636         Assert.assertTrue(mHeadsetServiceBinder.setActiveDevice(activeDevice,
637                 mAdapter.getAttributionSource()));
638         verify(mNativeInterface).setActiveDevice(activeDevice);
639         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, activeDevice);
640         Assert.assertEquals(activeDevice,
641                 mHeadsetServiceBinder.getActiveDevice(mAdapter.getAttributionSource()));
642         // Start virtual call
643         Assert.assertTrue(mHeadsetServiceBinder
644                 .startScoUsingVirtualVoiceCall(mAdapter.getAttributionSource()));
645         Assert.assertTrue(mHeadsetService.isVirtualCallStarted());
646         verifyVirtualCallStartSequenceInvocations(connectedDevices);
647         // Try dialing out from the a non active Headset
648         BluetoothDevice dialingOutDevice = connectedDevices.get(1);
649         HeadsetStackEvent dialingOutEvent =
650                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_DIAL_CALL, TEST_PHONE_NUMBER,
651                         dialingOutDevice);
652         Uri dialOutUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, TEST_PHONE_NUMBER, null);
653         Instrumentation.ActivityResult result =
654                 new Instrumentation.ActivityResult(Activity.RESULT_OK, null);
655         Intents.intending(IntentMatchers.hasAction(Intent.ACTION_CALL_PRIVILEGED))
656                 .respondWith(result);
657         mHeadsetService.messageFromNative(dialingOutEvent);
658         waitAndVerifyActiveDeviceChangedIntent(ASYNC_CALL_TIMEOUT_MILLIS, dialingOutDevice);
659         TestUtils.waitForLooperToFinishScheduledTask(
660                 mHeadsetService.getStateMachinesThreadLooper());
661         Assert.assertTrue(mHeadsetService.hasDeviceInitiatedDialingOut());
662         // Make sure the correct intent is fired
663         Intents.intended(allOf(IntentMatchers.hasAction(Intent.ACTION_CALL_PRIVILEGED),
664                 IntentMatchers.hasData(dialOutUri)), Intents.times(1));
665         // Virtual call should be preempted by dialing out call
666         Assert.assertFalse(mHeadsetService.isVirtualCallStarted());
667         verifyVirtualCallStopSequenceInvocations(connectedDevices);
668     }
669 
670     /**
671      * Test to verify the following behavior regarding active HF initiated voice recognition
672      * in the successful scenario
673      *   1. HF device sends AT+BVRA=1
674      *   2. HeadsetStateMachine sends out {@link Intent#ACTION_VOICE_COMMAND}
675      *   3. AG call {@link BluetoothHeadset#stopVoiceRecognition(BluetoothDevice)} to indicate
676      *      that voice recognition has stopped
677      *   4. AG sends OK to HF
678      *
679      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
680      */
681     @Test
testVoiceRecognition_SingleHfInitiatedSuccess()682     public void testVoiceRecognition_SingleHfInitiatedSuccess() {
683         // Connect HF
684         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
685         connectTestDevice(device);
686         // Make device active
687         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
688         verify(mNativeInterface).setActiveDevice(device);
689         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
690         // Start voice recognition
691         startVoiceRecognitionFromHf(device);
692     }
693 
694     /**
695      * Test to verify the following behavior regarding active HF stop voice recognition
696      * in the successful scenario
697      *   1. HF device sends AT+BVRA=0
698      *   2. Let voice recognition app to stop
699      *   3. AG respond with OK
700      *   4. Disconnect audio
701      *
702      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
703      */
704     @Test
testVoiceRecognition_SingleHfStopSuccess()705     public void testVoiceRecognition_SingleHfStopSuccess() {
706         // Connect HF
707         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
708         connectTestDevice(device);
709         // Make device active
710         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
711         verify(mNativeInterface).setActiveDevice(device);
712         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
713         // Start voice recognition
714         startVoiceRecognitionFromHf(device);
715         // Stop voice recognition
716         HeadsetStackEvent stopVrEvent =
717                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
718                         HeadsetHalConstants.VR_STATE_STOPPED, device);
719         mHeadsetService.messageFromNative(stopVrEvent);
720         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).deactivateVoiceRecognition();
721         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).atResponseCode(device,
722                 HeadsetHalConstants.AT_RESPONSE_OK, 0);
723         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).disconnectAudio(device);
724         verifyNoMoreInteractions(mNativeInterface);
725     }
726 
727     /**
728      * Test to verify the following behavior regarding active HF initiated voice recognition
729      * in the failed to activate scenario
730      *   1. HF device sends AT+BVRA=1
731      *   2. HeadsetStateMachine sends out {@link Intent#ACTION_VOICE_COMMAND}
732      *   3. Failed to activate voice recognition through intent
733      *   4. AG sends ERROR to HF
734      *
735      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
736      */
737     @Test
testVoiceRecognition_SingleHfInitiatedFailedToActivate()738     public void testVoiceRecognition_SingleHfInitiatedFailedToActivate() {
739         when(mSystemInterface.activateVoiceRecognition()).thenReturn(false);
740         // Connect HF
741         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
742         connectTestDevice(device);
743         // Make device active
744         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
745         verify(mNativeInterface).setActiveDevice(device);
746         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
747         // Start voice recognition
748         HeadsetStackEvent startVrEvent =
749                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
750                         HeadsetHalConstants.VR_STATE_STARTED, device);
751         mHeadsetService.messageFromNative(startVrEvent);
752         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
753         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(device,
754                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
755         verifyNoMoreInteractions(mNativeInterface);
756         verifyZeroInteractions(mAudioManager);
757     }
758 
759 
760     /**
761      * Test to verify the following behavior regarding active HF initiated voice recognition
762      * in the timeout scenario
763      *   1. HF device sends AT+BVRA=1
764      *   2. HeadsetStateMachine sends out {@link Intent#ACTION_VOICE_COMMAND}
765      *   3. AG failed to get back to us on time
766      *   4. AG sends ERROR to HF
767      *
768      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
769      */
770     @Test
testVoiceRecognition_SingleHfInitiatedTimeout()771     public void testVoiceRecognition_SingleHfInitiatedTimeout() {
772         // Connect HF
773         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
774         connectTestDevice(device);
775         // Make device active
776         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
777         verify(mNativeInterface).setActiveDevice(device);
778         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
779         // Start voice recognition
780         HeadsetStackEvent startVrEvent =
781                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
782                         HeadsetHalConstants.VR_STATE_STARTED, device);
783         mHeadsetService.messageFromNative(startVrEvent);
784         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
785         verify(mNativeInterface, timeout(START_VR_TIMEOUT_WAIT_MILLIS)).atResponseCode(device,
786                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
787         verifyNoMoreInteractions(mNativeInterface);
788         verifyZeroInteractions(mAudioManager);
789     }
790 
791     /**
792      * Test to verify the following behavior regarding AG initiated voice recognition
793      * in the successful scenario
794      *   1. AG starts voice recognition and notify the Bluetooth stack via
795      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
796      *      recognition has started
797      *   2. AG send +BVRA:1 to HF
798      *   3. AG start SCO connection if SCO has not been started
799      *
800      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
801      */
802     @Test
testVoiceRecognition_SingleAgInitiatedSuccess()803     public void testVoiceRecognition_SingleAgInitiatedSuccess() {
804         // Connect HF
805         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
806         connectTestDevice(device);
807         // Make device active
808         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
809         verify(mNativeInterface).setActiveDevice(device);
810         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
811         // Start voice recognition
812         startVoiceRecognitionFromAg();
813     }
814 
815     /**
816      * Test to verify the following behavior regarding AG initiated voice recognition
817      * in the successful scenario
818      *   1. AG starts voice recognition and notify the Bluetooth stack via
819      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
820      *      recognition has started, BluetoothDevice is null in this case
821      *   2. AG send +BVRA:1 to current active HF
822      *   3. AG start SCO connection if SCO has not been started
823      *
824      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
825      */
826     @Test
testVoiceRecognition_SingleAgInitiatedSuccessNullInput()827     public void testVoiceRecognition_SingleAgInitiatedSuccessNullInput() {
828         // Connect HF
829         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
830         connectTestDevice(device);
831         // Make device active
832         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
833         verify(mNativeInterface).setActiveDevice(device);
834         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
835         // Start voice recognition on null argument should go to active device
836         Assert.assertTrue(mHeadsetService.startVoiceRecognition(null));
837         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).startVoiceRecognition(device);
838     }
839 
840     /**
841      * Test to verify the following behavior regarding AG initiated voice recognition
842      * in the successful scenario
843      *   1. AG starts voice recognition and notify the Bluetooth stack via
844      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
845      *      recognition has started, BluetoothDevice is null and active device is null
846      *   2. The call should fail
847      *
848      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
849      */
850     @Test
testVoiceRecognition_SingleAgInitiatedFailNullActiveDevice()851     public void testVoiceRecognition_SingleAgInitiatedFailNullActiveDevice() {
852         // Connect HF
853         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
854         connectTestDevice(device);
855         // Make device active
856         Assert.assertTrue(mHeadsetService.setActiveDevice(null));
857         // TODO(b/79760385): setActiveDevice(null) does not propagate to native layer
858         // verify(mNativeInterface).setActiveDevice(null);
859         Assert.assertNull(mHeadsetService.getActiveDevice());
860         // Start voice recognition on null argument should fail
861         Assert.assertFalse(mHeadsetService.startVoiceRecognition(null));
862     }
863 
864     /**
865      * Test to verify the following behavior regarding AG stops voice recognition
866      * in the successful scenario
867      *   1. AG stops voice recognition and notify the Bluetooth stack via
868      *      {@link BluetoothHeadset#stopVoiceRecognition(BluetoothDevice)} to indicate that voice
869      *      recognition has stopped
870      *   2. AG send +BVRA:0 to HF
871      *   3. AG stop SCO connection
872      *
873      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
874      */
875     @Test
testVoiceRecognition_SingleAgStopSuccess()876     public void testVoiceRecognition_SingleAgStopSuccess() {
877         // Connect HF
878         BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
879         connectTestDevice(device);
880         // Make device active
881         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
882         verify(mNativeInterface).setActiveDevice(device);
883         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
884         // Start voice recognition
885         startVoiceRecognitionFromAg();
886         // Stop voice recognition
887         Assert.assertTrue(mHeadsetService.stopVoiceRecognition(device));
888         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).stopVoiceRecognition(device);
889         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).disconnectAudio(device);
890         verifyNoMoreInteractions(mNativeInterface);
891     }
892 
893     /**
894      * Test to verify the following behavior regarding AG initiated voice recognition
895      * in the device not connected failure scenario
896      *   1. AG starts voice recognition and notify the Bluetooth stack via
897      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
898      *      recognition has started
899      *   2. Device is not connected, return false
900      *
901      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
902      */
903     @Test
testVoiceRecognition_SingleAgInitiatedDeviceNotConnected()904     public void testVoiceRecognition_SingleAgInitiatedDeviceNotConnected() {
905         // Start voice recognition
906         BluetoothDevice disconnectedDevice = TestUtils.getTestDevice(mAdapter, 0);
907         Assert.assertFalse(mHeadsetService.startVoiceRecognition(disconnectedDevice));
908         verifyNoMoreInteractions(mNativeInterface);
909         verifyZeroInteractions(mAudioManager);
910     }
911 
912     /**
913      * Test to verify the following behavior regarding non active HF initiated voice recognition
914      * in the successful scenario
915      *   1. HF device sends AT+BVRA=1
916      *   2. HeadsetStateMachine sends out {@link Intent#ACTION_VOICE_COMMAND}
917      *   3. AG call {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate
918      *      that voice recognition has started
919      *   4. AG sends OK to HF
920      *   5. Suspend A2DP
921      *   6. Start SCO if SCO hasn't been started
922      *
923      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
924      */
925     @Test
testVoiceRecognition_MultiHfInitiatedSwitchActiveDeviceSuccess()926     public void testVoiceRecognition_MultiHfInitiatedSwitchActiveDeviceSuccess() {
927         // Connect two devices
928         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
929         connectTestDevice(deviceA);
930         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
931         connectTestDevice(deviceB);
932         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceA, false);
933         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceB, false);
934         // Set active device to device B
935         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
936         verify(mNativeInterface).setActiveDevice(deviceB);
937         Assert.assertEquals(deviceB, mHeadsetService.getActiveDevice());
938         // Start voice recognition from non active device A
939         HeadsetStackEvent startVrEventA =
940                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
941                         HeadsetHalConstants.VR_STATE_STARTED, deviceA);
942         mHeadsetService.messageFromNative(startVrEventA);
943         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
944         // Active device should have been swapped to device A
945         verify(mNativeInterface).setActiveDevice(deviceA);
946         Assert.assertEquals(deviceA, mHeadsetService.getActiveDevice());
947         // Start voice recognition from other device should fail
948         HeadsetStackEvent startVrEventB =
949                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
950                         HeadsetHalConstants.VR_STATE_STARTED, deviceB);
951         mHeadsetService.messageFromNative(startVrEventB);
952         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(deviceB,
953                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
954         // Reply to continue voice recognition
955         mHeadsetService.startVoiceRecognition(deviceA);
956         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(deviceA,
957                 HeadsetHalConstants.AT_RESPONSE_OK, 0);
958         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
959                 .setParameters("A2dpSuspended=true");
960         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connectAudio(deviceA);
961         verifyNoMoreInteractions(mNativeInterface);
962     }
963 
964     /**
965      * Test to verify the following behavior regarding non active HF initiated voice recognition
966      * in the successful scenario
967      *   1. HF device sends AT+BVRA=1
968      *   2. HeadsetStateMachine sends out {@link Intent#ACTION_VOICE_COMMAND}
969      *   3. AG call {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate
970      *      that voice recognition has started, but on a wrong HF
971      *   4. Headset service instead keep using the initiating HF
972      *   5. AG sends OK to HF
973      *   6. Suspend A2DP
974      *   7. Start SCO if SCO hasn't been started
975      *
976      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
977      */
978     @Test
testVoiceRecognition_MultiHfInitiatedSwitchActiveDeviceReplyWrongHfSuccess()979     public void testVoiceRecognition_MultiHfInitiatedSwitchActiveDeviceReplyWrongHfSuccess() {
980         // Connect two devices
981         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
982         connectTestDevice(deviceA);
983         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
984         connectTestDevice(deviceB);
985         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceA, false);
986         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceB, false);
987         // Set active device to device B
988         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
989         verify(mNativeInterface).setActiveDevice(deviceB);
990         Assert.assertEquals(deviceB, mHeadsetService.getActiveDevice());
991         // Start voice recognition from non active device A
992         HeadsetStackEvent startVrEventA =
993                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
994                         HeadsetHalConstants.VR_STATE_STARTED, deviceA);
995         mHeadsetService.messageFromNative(startVrEventA);
996         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
997         // Active device should have been swapped to device A
998         verify(mNativeInterface).setActiveDevice(deviceA);
999         Assert.assertEquals(deviceA, mHeadsetService.getActiveDevice());
1000         // Start voice recognition from other device should fail
1001         HeadsetStackEvent startVrEventB =
1002                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
1003                         HeadsetHalConstants.VR_STATE_STARTED, deviceB);
1004         mHeadsetService.messageFromNative(startVrEventB);
1005         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(deviceB,
1006                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1007         // Reply to continue voice recognition on a wrong device
1008         mHeadsetService.startVoiceRecognition(deviceB);
1009         // We still continue on the initiating HF
1010         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(deviceA,
1011                 HeadsetHalConstants.AT_RESPONSE_OK, 0);
1012         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1013                 .setParameters("A2dpSuspended=true");
1014         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connectAudio(deviceA);
1015         verifyNoMoreInteractions(mNativeInterface);
1016     }
1017 
1018 
1019     /**
1020      * Test to verify the following behavior regarding AG initiated voice recognition
1021      * in the successful scenario
1022      *   1. AG starts voice recognition and notify the Bluetooth stack via
1023      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
1024      *      recognition has started
1025      *   2. Suspend A2DP
1026      *   3. AG send +BVRA:1 to HF
1027      *   4. AG start SCO connection if SCO has not been started
1028      *
1029      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
1030      */
1031     @Test
testVoiceRecognition_MultiAgInitiatedSuccess()1032     public void testVoiceRecognition_MultiAgInitiatedSuccess() {
1033         // Connect two devices
1034         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
1035         connectTestDevice(deviceA);
1036         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
1037         connectTestDevice(deviceB);
1038         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceA, false);
1039         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceB, false);
1040         // Set active device to device B
1041         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
1042         verify(mNativeInterface).setActiveDevice(deviceB);
1043         Assert.assertEquals(deviceB, mHeadsetService.getActiveDevice());
1044         // Start voice recognition
1045         startVoiceRecognitionFromAg();
1046         // Start voice recognition from other device should fail
1047         HeadsetStackEvent startVrEventA =
1048                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
1049                         HeadsetHalConstants.VR_STATE_STARTED, deviceA);
1050         mHeadsetService.messageFromNative(startVrEventA);
1051         // TODO(b/79660380): Workaround in case voice recognition was not terminated properly
1052         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).stopVoiceRecognition(deviceB);
1053         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).disconnectAudio(deviceB);
1054         // This request should still fail
1055         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(deviceA,
1056                 HeadsetHalConstants.AT_RESPONSE_ERROR, 0);
1057         verifyNoMoreInteractions(mNativeInterface);
1058     }
1059 
1060     /**
1061      * Test to verify the following behavior regarding AG initiated voice recognition
1062      * in the device not active failure scenario
1063      *   1. AG starts voice recognition and notify the Bluetooth stack via
1064      *      {@link BluetoothHeadset#startVoiceRecognition(BluetoothDevice)} to indicate that voice
1065      *      recognition has started
1066      *   2. Device is not active, should do voice recognition on active device only
1067      *
1068      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
1069      */
1070     @Test
testVoiceRecognition_MultiAgInitiatedDeviceNotActive()1071     public void testVoiceRecognition_MultiAgInitiatedDeviceNotActive() {
1072         // Connect two devices
1073         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
1074         connectTestDevice(deviceA);
1075         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
1076         connectTestDevice(deviceB);
1077         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceA, false);
1078         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(deviceB, false);
1079         // Set active device to device B
1080         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
1081         verify(mNativeInterface).setActiveDevice(deviceB);
1082         Assert.assertEquals(deviceB, mHeadsetService.getActiveDevice());
1083         // Start voice recognition should succeed
1084         Assert.assertTrue(mHeadsetService.startVoiceRecognition(deviceA));
1085         verify(mNativeInterface).setActiveDevice(deviceA);
1086         Assert.assertEquals(deviceA, mHeadsetService.getActiveDevice());
1087         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).startVoiceRecognition(deviceA);
1088         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1089                 .setParameters("A2dpSuspended=true");
1090         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connectAudio(deviceA);
1091         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, deviceA,
1092                 BluetoothHeadset.STATE_AUDIO_CONNECTING, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1093         mHeadsetService.messageFromNative(
1094                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED,
1095                         HeadsetHalConstants.AUDIO_STATE_CONNECTED, deviceA));
1096         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, deviceA,
1097                 BluetoothHeadset.STATE_AUDIO_CONNECTED, BluetoothHeadset.STATE_AUDIO_CONNECTING);
1098         verifyNoMoreInteractions(mNativeInterface);
1099     }
1100 
1101     /**
1102      * Test to verify the call state and caller information are correctly delivered
1103      * {@link BluetoothHeadset#phoneStateChanged(int, int, int, String, int, String, boolean)}
1104      */
1105     @Test
testPhoneStateChangedWithIncomingCallState()1106     public void testPhoneStateChangedWithIncomingCallState() throws RemoteException {
1107         // Connect HF
1108         for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) {
1109             BluetoothDevice device = TestUtils.getTestDevice(mAdapter, i);
1110             connectTestDevice(device);
1111             Assert.assertThat(mHeadsetServiceBinder.getConnectedDevices(),
1112                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
1113             Assert.assertThat(mHeadsetServiceBinder.getDevicesMatchingConnectionStates(
1114                     new int[]{BluetoothProfile.STATE_CONNECTED}, mAdapter.getAttributionSource()),
1115                     Matchers.containsInAnyOrder(mBondedDevices.toArray()));
1116         }
1117         List<BluetoothDevice> connectedDevices = mHeadsetServiceBinder.getConnectedDevices();
1118         Assert.assertThat(connectedDevices, Matchers.containsInAnyOrder(mBondedDevices.toArray()));
1119         // Incoming call update by telecom
1120         mHeadsetServiceBinder.phoneStateChanged(0, 0, HeadsetHalConstants.CALL_STATE_INCOMING,
1121                 TEST_PHONE_NUMBER, 128, TEST_CALLER_ID, mAdapter.getAttributionSource());
1122         HeadsetCallState incomingCallState = new HeadsetCallState(0, 0,
1123                 HeadsetHalConstants.CALL_STATE_INCOMING, TEST_PHONE_NUMBER, 128, TEST_CALLER_ID);
1124         verifyCallStateToNativeInvocation(incomingCallState, connectedDevices);
1125     }
1126 
startVoiceRecognitionFromHf(BluetoothDevice device)1127     private void startVoiceRecognitionFromHf(BluetoothDevice device) {
1128         // Start voice recognition
1129         HeadsetStackEvent startVrEvent =
1130                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
1131                         HeadsetHalConstants.VR_STATE_STARTED, device);
1132         mHeadsetService.messageFromNative(startVrEvent);
1133         verify(mSystemInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).activateVoiceRecognition();
1134         Assert.assertTrue(mHeadsetService.startVoiceRecognition(device));
1135         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).atResponseCode(device,
1136                 HeadsetHalConstants.AT_RESPONSE_OK, 0);
1137         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1138                 .setParameters("A2dpSuspended=true");
1139         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connectAudio(device);
1140         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1141                 BluetoothHeadset.STATE_AUDIO_CONNECTING, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1142         mHeadsetService.messageFromNative(
1143                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED,
1144                         HeadsetHalConstants.AUDIO_STATE_CONNECTED, device));
1145         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1146                 BluetoothHeadset.STATE_AUDIO_CONNECTED, BluetoothHeadset.STATE_AUDIO_CONNECTING);
1147         verifyNoMoreInteractions(mNativeInterface);
1148     }
1149 
startVoiceRecognitionFromAg()1150     private void startVoiceRecognitionFromAg() {
1151         BluetoothDevice device = mHeadsetService.getActiveDevice();
1152         Assert.assertNotNull(device);
1153         Assert.assertTrue(mHeadsetService.startVoiceRecognition(device));
1154         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).startVoiceRecognition(device);
1155         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1156                 .setParameters("A2dpSuspended=true");
1157         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).connectAudio(device);
1158         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1159                 BluetoothHeadset.STATE_AUDIO_CONNECTING, BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1160         mHeadsetService.messageFromNative(
1161                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED,
1162                         HeadsetHalConstants.AUDIO_STATE_CONNECTED, device));
1163         waitAndVerifyAudioStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1164                 BluetoothHeadset.STATE_AUDIO_CONNECTED, BluetoothHeadset.STATE_AUDIO_CONNECTING);
1165         verifyNoMoreInteractions(mNativeInterface);
1166     }
1167 
connectTestDevice(BluetoothDevice device)1168     private void connectTestDevice(BluetoothDevice device) {
1169         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET))
1170                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
1171         // Make device bonded
1172         mBondedDevices.add(device);
1173         // Use connecting event to indicate that device is connecting
1174         HeadsetStackEvent rfcommConnectedEvent =
1175                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
1176                         HeadsetHalConstants.CONNECTION_STATE_CONNECTED, device);
1177         mHeadsetService.messageFromNative(rfcommConnectedEvent);
1178         verify(mObjectsFactory).makeStateMachine(device,
1179                 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService,
1180                 mNativeInterface, mSystemInterface);
1181         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
1182         // 250ms for processing two messages should be way more than enough. Anything that breaks
1183         // this indicate some breakage in other part of Android OS
1184         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1185                 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
1186         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
1187                 mHeadsetService.getConnectionState(device));
1188         Assert.assertEquals(Collections.singletonList(device),
1189                 mHeadsetService.getDevicesMatchingConnectionStates(
1190                         new int[]{BluetoothProfile.STATE_CONNECTING}));
1191         // Get feedback from native to put device into connected state
1192         HeadsetStackEvent slcConnectedEvent =
1193                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED,
1194                         HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED, device);
1195         mHeadsetService.messageFromNative(slcConnectedEvent);
1196         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
1197         // 250ms for processing two messages should be way more than enough. Anything that breaks
1198         // this indicate some breakage in other part of Android OS
1199         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1200                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING);
1201         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
1202                 mHeadsetService.getConnectionState(device));
1203     }
1204 
waitAndVerifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)1205     private void waitAndVerifyConnectionStateIntent(int timeoutMs, BluetoothDevice device,
1206             int newState, int prevState) {
1207         Intent intent = TestUtils.waitForIntent(timeoutMs, mConnectionStateChangedQueue);
1208         Assert.assertNotNull(intent);
1209         HeadsetTestUtils.verifyConnectionStateBroadcast(device, newState, prevState, intent, false);
1210     }
1211 
waitAndVerifyActiveDeviceChangedIntent(int timeoutMs, BluetoothDevice device)1212     private void waitAndVerifyActiveDeviceChangedIntent(int timeoutMs, BluetoothDevice device) {
1213         Intent intent = TestUtils.waitForIntent(timeoutMs, mActiveDeviceChangedQueue);
1214         Assert.assertNotNull(intent);
1215         HeadsetTestUtils.verifyActiveDeviceChangedBroadcast(device, intent, false);
1216     }
1217 
waitAndVerifyAudioStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)1218     private void waitAndVerifyAudioStateIntent(int timeoutMs, BluetoothDevice device, int newState,
1219             int prevState) {
1220         Intent intent = TestUtils.waitForIntent(timeoutMs, mAudioStateChangedQueue);
1221         Assert.assertNotNull(intent);
1222         HeadsetTestUtils.verifyAudioStateBroadcast(device, newState, prevState, intent);
1223     }
1224 
1225     /**
1226      * Verify the series of invocations after
1227      * {@link BluetoothHeadset#startScoUsingVirtualVoiceCall()}
1228      *
1229      * @param connectedDevices must be in the same sequence as
1230      * {@link BluetoothHeadset#getConnectedDevices()}
1231      */
verifyVirtualCallStartSequenceInvocations(List<BluetoothDevice> connectedDevices)1232     private void verifyVirtualCallStartSequenceInvocations(List<BluetoothDevice> connectedDevices) {
1233         // Do not verify HeadsetPhoneState changes as it is verified in HeadsetServiceTest
1234         verifyCallStateToNativeInvocation(
1235                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_DIALING, "", 0, ""),
1236                 connectedDevices);
1237         verifyCallStateToNativeInvocation(
1238                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_ALERTING, "", 0, ""),
1239                 connectedDevices);
1240         verifyCallStateToNativeInvocation(
1241                 new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, ""),
1242                 connectedDevices);
1243     }
1244 
verifyVirtualCallStopSequenceInvocations(List<BluetoothDevice> connectedDevices)1245     private void verifyVirtualCallStopSequenceInvocations(List<BluetoothDevice> connectedDevices) {
1246         verifyCallStateToNativeInvocation(
1247                 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, "", 0, ""),
1248                 connectedDevices);
1249     }
1250 
verifyCallStateToNativeInvocation(HeadsetCallState headsetCallState, List<BluetoothDevice> connectedDevices)1251     private void verifyCallStateToNativeInvocation(HeadsetCallState headsetCallState,
1252             List<BluetoothDevice> connectedDevices) {
1253         for (BluetoothDevice device : connectedDevices) {
1254             verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).phoneStateChange(device,
1255                     headsetCallState);
1256         }
1257     }
1258 }
1259