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