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