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.mockito.Mockito.*; 20 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothHeadset; 24 import android.bluetooth.BluetoothProfile; 25 import android.bluetooth.BluetoothUuid; 26 import android.bluetooth.IBluetoothHeadset; 27 import android.content.Context; 28 import android.media.AudioManager; 29 import android.os.ParcelUuid; 30 import android.os.RemoteException; 31 import android.os.SystemClock; 32 33 import androidx.test.InstrumentationRegistry; 34 import androidx.test.filters.MediumTest; 35 import androidx.test.rule.ServiceTestRule; 36 import androidx.test.runner.AndroidJUnit4; 37 38 import com.android.bluetooth.R; 39 import com.android.bluetooth.TestUtils; 40 import com.android.bluetooth.btservice.AdapterService; 41 import com.android.bluetooth.btservice.storage.DatabaseManager; 42 43 import org.hamcrest.Matchers; 44 import org.junit.After; 45 import org.junit.Assert; 46 import org.junit.Assume; 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.Mock; 52 import org.mockito.MockitoAnnotations; 53 import org.mockito.Spy; 54 55 import java.lang.reflect.Method; 56 import java.util.ArrayList; 57 import java.util.Collections; 58 import java.util.HashMap; 59 import java.util.Set; 60 61 /** 62 * Tests for {@link HeadsetService} 63 */ 64 @MediumTest 65 @RunWith(AndroidJUnit4.class) 66 public class HeadsetServiceTest { 67 private static final int MAX_HEADSET_CONNECTIONS = 5; 68 private static final ParcelUuid[] FAKE_HEADSET_UUID = {BluetoothUuid.HFP}; 69 private static final int ASYNC_CALL_TIMEOUT_MILLIS = 250; 70 private static final String TEST_PHONE_NUMBER = "1234567890"; 71 72 private Context mTargetContext; 73 private HeadsetService mHeadsetService; 74 private IBluetoothHeadset.Stub mHeadsetServiceBinder; 75 private BluetoothAdapter mAdapter; 76 private HeadsetNativeInterface mNativeInterface; 77 private BluetoothDevice mCurrentDevice; 78 private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>(); 79 80 @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule(); 81 82 @Spy private HeadsetObjectsFactory mObjectsFactory = HeadsetObjectsFactory.getInstance(); 83 @Mock private AdapterService mAdapterService; 84 @Mock private DatabaseManager mDatabaseManager; 85 @Mock private HeadsetSystemInterface mSystemInterface; 86 @Mock private AudioManager mAudioManager; 87 @Mock private HeadsetPhoneState mPhoneState; 88 89 @Before setUp()90 public void setUp() throws Exception { 91 mTargetContext = InstrumentationRegistry.getTargetContext(); 92 Assume.assumeTrue("Ignore test when HeadsetService is not enabled", 93 mTargetContext.getResources().getBoolean(R.bool.profile_supported_hs_hfp)); 94 MockitoAnnotations.initMocks(this); 95 TestUtils.setAdapterService(mAdapterService); 96 // We cannot mock HeadsetObjectsFactory.getInstance() with Mockito. 97 // Hence we need to use reflection to call a private method to 98 // initialize properly the HeadsetObjectsFactory.sInstance field. 99 Method method = HeadsetObjectsFactory.class.getDeclaredMethod("setInstanceForTesting", 100 HeadsetObjectsFactory.class); 101 method.setAccessible(true); 102 method.invoke(null, mObjectsFactory); 103 doReturn(MAX_HEADSET_CONNECTIONS).when(mAdapterService).getMaxConnectedAudioDevices(); 104 doReturn(new ParcelUuid[]{BluetoothUuid.HFP}).when(mAdapterService) 105 .getRemoteUuids(any(BluetoothDevice.class)); 106 // This line must be called to make sure relevant objects are initialized properly 107 mAdapter = BluetoothAdapter.getDefaultAdapter(); 108 // Mock methods in AdapterService 109 doReturn(FAKE_HEADSET_UUID).when(mAdapterService) 110 .getRemoteUuids(any(BluetoothDevice.class)); 111 doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService) 112 .getBondState(any(BluetoothDevice.class)); 113 doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); 114 doReturn(true, false).when(mAdapterService).isStartedProfile(anyString()); 115 doAnswer(invocation -> { 116 Set<BluetoothDevice> keys = mStateMachines.keySet(); 117 return keys.toArray(new BluetoothDevice[keys.size()]); 118 }).when(mAdapterService).getBondedDevices(); 119 // Mock system interface 120 doNothing().when(mSystemInterface).stop(); 121 when(mSystemInterface.getHeadsetPhoneState()).thenReturn(mPhoneState); 122 when(mSystemInterface.getAudioManager()).thenReturn(mAudioManager); 123 when(mSystemInterface.isCallIdle()).thenReturn(true, false, true, false); 124 // Mock methods in HeadsetNativeInterface 125 mNativeInterface = spy(HeadsetNativeInterface.getInstance()); 126 doNothing().when(mNativeInterface).init(anyInt(), anyBoolean()); 127 doNothing().when(mNativeInterface).cleanup(); 128 doReturn(true).when(mNativeInterface).connectHfp(any(BluetoothDevice.class)); 129 doReturn(true).when(mNativeInterface).disconnectHfp(any(BluetoothDevice.class)); 130 doReturn(true).when(mNativeInterface).connectAudio(any(BluetoothDevice.class)); 131 doReturn(true).when(mNativeInterface).disconnectAudio(any(BluetoothDevice.class)); 132 doReturn(true).when(mNativeInterface).setActiveDevice(any(BluetoothDevice.class)); 133 doReturn(true).when(mNativeInterface).sendBsir(any(BluetoothDevice.class), anyBoolean()); 134 // Mock methods in HeadsetObjectsFactory 135 doAnswer(invocation -> { 136 Assert.assertNotNull(mCurrentDevice); 137 final HeadsetStateMachine stateMachine = mock(HeadsetStateMachine.class); 138 doReturn(BluetoothProfile.STATE_DISCONNECTED).when(stateMachine).getConnectionState(); 139 doReturn(BluetoothHeadset.STATE_AUDIO_DISCONNECTED).when(stateMachine).getAudioState(); 140 mStateMachines.put(mCurrentDevice, stateMachine); 141 return stateMachine; 142 }).when(mObjectsFactory).makeStateMachine(any(), any(), any(), any(), any(), any()); 143 doReturn(mSystemInterface).when(mObjectsFactory).makeSystemInterface(any()); 144 doReturn(mNativeInterface).when(mObjectsFactory).getNativeInterface(); 145 TestUtils.startService(mServiceRule, HeadsetService.class); 146 mHeadsetService = HeadsetService.getHeadsetService(); 147 Assert.assertNotNull(mHeadsetService); 148 verify(mObjectsFactory).makeSystemInterface(mHeadsetService); 149 verify(mObjectsFactory).getNativeInterface(); 150 mHeadsetServiceBinder = (IBluetoothHeadset.Stub) mHeadsetService.initBinder(); 151 Assert.assertNotNull(mHeadsetServiceBinder); 152 mHeadsetServiceBinder.setForceScoAudio(true, mAdapter.getAttributionSource()); 153 } 154 155 @After tearDown()156 public void tearDown() throws Exception { 157 if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_hs_hfp)) { 158 return; 159 } 160 TestUtils.stopService(mServiceRule, HeadsetService.class); 161 mHeadsetService = HeadsetService.getHeadsetService(); 162 Assert.assertNull(mHeadsetService); 163 mStateMachines.clear(); 164 mCurrentDevice = null; 165 Method method = HeadsetObjectsFactory.class.getDeclaredMethod("setInstanceForTesting", 166 HeadsetObjectsFactory.class); 167 method.setAccessible(true); 168 method.invoke(null, (HeadsetObjectsFactory) null); 169 TestUtils.clearAdapterService(mAdapterService); 170 } 171 172 /** 173 * Test to verify that HeadsetService can be successfully started 174 */ 175 @Test testGetHeadsetService()176 public void testGetHeadsetService() { 177 Assert.assertEquals(mHeadsetService, HeadsetService.getHeadsetService()); 178 // Verify default connection and audio states 179 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 180 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 181 mHeadsetService.getConnectionState(mCurrentDevice)); 182 Assert.assertEquals(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 183 mHeadsetService.getAudioState(mCurrentDevice)); 184 } 185 186 /** 187 * Test okToAcceptConnection method using various test cases 188 */ 189 @Test testOkToAcceptConnection()190 public void testOkToAcceptConnection() { 191 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 192 int badPriorityValue = 1024; 193 int badBondState = 42; 194 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_NONE, 195 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 196 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_NONE, 197 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 198 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_NONE, 199 BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 200 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_NONE, badPriorityValue, 201 false); 202 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDING, 203 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 204 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDING, 205 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 206 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDING, 207 BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 208 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDING, badPriorityValue, 209 false); 210 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDED, 211 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, true); 212 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDED, 213 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 214 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDED, 215 BluetoothProfile.CONNECTION_POLICY_ALLOWED, true); 216 testOkToAcceptConnectionCase(mCurrentDevice, BluetoothDevice.BOND_BONDED, badPriorityValue, 217 false); 218 testOkToAcceptConnectionCase(mCurrentDevice, badBondState, 219 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 220 testOkToAcceptConnectionCase(mCurrentDevice, badBondState, 221 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 222 testOkToAcceptConnectionCase(mCurrentDevice, badBondState, 223 BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 224 testOkToAcceptConnectionCase(mCurrentDevice, badBondState, badPriorityValue, false); 225 } 226 227 /** 228 * Test to verify that {@link HeadsetService#connect(BluetoothDevice)} returns true when the 229 * device was not connected and number of connected devices is less than 230 * {@link #MAX_HEADSET_CONNECTIONS} 231 */ 232 @Test testConnectDevice_connectDeviceBelowLimit()233 public void testConnectDevice_connectDeviceBelowLimit() { 234 when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class), 235 eq(BluetoothProfile.HEADSET))) 236 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 237 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 238 Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); 239 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 240 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService, 241 mNativeInterface, mSystemInterface); 242 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT, 243 mCurrentDevice); 244 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 245 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 246 BluetoothProfile.STATE_CONNECTING); 247 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 248 mHeadsetService.getConnectionState(mCurrentDevice)); 249 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 250 BluetoothProfile.STATE_CONNECTED); 251 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 252 mHeadsetService.getConnectionState(mCurrentDevice)); 253 Assert.assertEquals(Collections.singletonList(mCurrentDevice), 254 mHeadsetService.getConnectedDevices()); 255 // 2nd connection attempt will fail 256 Assert.assertFalse(mHeadsetService.connect(mCurrentDevice)); 257 // Verify makeStateMachine is only called once 258 verify(mObjectsFactory).makeStateMachine(any(), any(), any(), any(), any(), any()); 259 // Verify CONNECT is only sent once 260 verify(mStateMachines.get(mCurrentDevice)).sendMessage(eq(HeadsetStateMachine.CONNECT), 261 any()); 262 } 263 264 /** 265 * Test that {@link HeadsetService#messageFromNative(HeadsetStackEvent)} will send correct 266 * message to the underlying state machine 267 */ 268 @Test testMessageFromNative_deviceConnected()269 public void testMessageFromNative_deviceConnected() { 270 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 271 // Test connect from native 272 HeadsetStackEvent connectedEvent = 273 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED, 274 HeadsetHalConstants.CONNECTION_STATE_CONNECTED, mCurrentDevice); 275 mHeadsetService.messageFromNative(connectedEvent); 276 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 277 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService, 278 mNativeInterface, mSystemInterface); 279 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.STACK_EVENT, 280 connectedEvent); 281 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 282 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 283 BluetoothProfile.STATE_CONNECTED); 284 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 285 mHeadsetService.getConnectionState(mCurrentDevice)); 286 Assert.assertEquals(Collections.singletonList(mCurrentDevice), 287 mHeadsetService.getConnectedDevices()); 288 // Test disconnect from native 289 HeadsetStackEvent disconnectEvent = 290 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED, 291 HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, mCurrentDevice); 292 mHeadsetService.messageFromNative(disconnectEvent); 293 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.STACK_EVENT, 294 disconnectEvent); 295 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 296 BluetoothProfile.STATE_DISCONNECTED); 297 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 298 mHeadsetService.getConnectionState(mCurrentDevice)); 299 Assert.assertEquals(Collections.EMPTY_LIST, mHeadsetService.getConnectedDevices()); 300 } 301 302 /** 303 * Stack connection event to {@link HeadsetHalConstants#CONNECTION_STATE_CONNECTING} should 304 * create new state machine 305 */ 306 @Test testMessageFromNative_deviceConnectingUnknown()307 public void testMessageFromNative_deviceConnectingUnknown() { 308 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 309 HeadsetStackEvent connectingEvent = 310 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED, 311 HeadsetHalConstants.CONNECTION_STATE_CONNECTING, mCurrentDevice); 312 mHeadsetService.messageFromNative(connectingEvent); 313 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 314 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService, 315 mNativeInterface, mSystemInterface); 316 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.STACK_EVENT, 317 connectingEvent); 318 } 319 320 /** 321 * Stack connection event to {@link HeadsetHalConstants#CONNECTION_STATE_DISCONNECTED} should 322 * crash by throwing {@link IllegalStateException} if the device is unknown 323 */ 324 @Test testMessageFromNative_deviceDisconnectedUnknown()325 public void testMessageFromNative_deviceDisconnectedUnknown() { 326 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 327 HeadsetStackEvent connectingEvent = 328 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED, 329 HeadsetHalConstants.CONNECTION_STATE_DISCONNECTED, mCurrentDevice); 330 try { 331 mHeadsetService.messageFromNative(connectingEvent); 332 Assert.fail("Expect an IllegalStateException"); 333 } catch (IllegalStateException exception) { 334 // Do nothing 335 } 336 verifyNoMoreInteractions(mObjectsFactory); 337 } 338 339 /** 340 * Test to verify that {@link HeadsetService#connect(BluetoothDevice)} fails after 341 * {@link #MAX_HEADSET_CONNECTIONS} connection requests 342 */ 343 @Test testConnectDevice_connectDeviceAboveLimit()344 public void testConnectDevice_connectDeviceAboveLimit() { 345 ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); 346 when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class), 347 eq(BluetoothProfile.HEADSET))) 348 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 349 for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) { 350 mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); 351 Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); 352 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 353 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, 354 mAdapterService, mNativeInterface, mSystemInterface); 355 verify(mObjectsFactory, times(i + 1)).makeStateMachine(any(BluetoothDevice.class), 356 eq(mHeadsetService.getStateMachinesThreadLooper()), eq(mHeadsetService), 357 eq(mAdapterService), eq(mNativeInterface), eq(mSystemInterface)); 358 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT, 359 mCurrentDevice); 360 verify(mStateMachines.get(mCurrentDevice)).sendMessage(eq(HeadsetStateMachine.CONNECT), 361 any(BluetoothDevice.class)); 362 // Put device to connecting 363 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 364 BluetoothProfile.STATE_CONNECTING); 365 Assert.assertThat(mHeadsetService.getConnectedDevices(), 366 Matchers.containsInAnyOrder(connectedDevices.toArray())); 367 // Put device to connected 368 connectedDevices.add(mCurrentDevice); 369 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 370 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 371 BluetoothProfile.STATE_CONNECTED); 372 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 373 mHeadsetService.getConnectionState(mCurrentDevice)); 374 Assert.assertThat(mHeadsetService.getConnectedDevices(), 375 Matchers.containsInAnyOrder(connectedDevices.toArray())); 376 } 377 // Connect the next device will fail 378 mCurrentDevice = TestUtils.getTestDevice(mAdapter, MAX_HEADSET_CONNECTIONS); 379 Assert.assertFalse(mHeadsetService.connect(mCurrentDevice)); 380 // Though connection failed, a new state machine is still lazily created for the device 381 verify(mObjectsFactory, times(MAX_HEADSET_CONNECTIONS + 1)).makeStateMachine( 382 any(BluetoothDevice.class), eq(mHeadsetService.getStateMachinesThreadLooper()), 383 eq(mHeadsetService), eq(mAdapterService), eq(mNativeInterface), 384 eq(mSystemInterface)); 385 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 386 mHeadsetService.getConnectionState(mCurrentDevice)); 387 Assert.assertThat(mHeadsetService.getConnectedDevices(), 388 Matchers.containsInAnyOrder(connectedDevices.toArray())); 389 } 390 391 /** 392 * Test to verify that {@link HeadsetService#connectAudio(BluetoothDevice)} return true when 393 * the device is connected and audio is not connected and returns false when audio is already 394 * connecting 395 */ 396 @Test testConnectAudio_withOneDevice()397 public void testConnectAudio_withOneDevice() { 398 when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class), 399 eq(BluetoothProfile.HEADSET))) 400 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 401 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 402 Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); 403 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 404 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService, 405 mNativeInterface, mSystemInterface); 406 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT, 407 mCurrentDevice); 408 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 409 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 410 BluetoothProfile.STATE_CONNECTED); 411 when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs()).thenReturn( 412 SystemClock.uptimeMillis()); 413 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 414 mHeadsetService.getConnectionState(mCurrentDevice)); 415 Assert.assertEquals(Collections.singletonList(mCurrentDevice), 416 mHeadsetService.getConnectedDevices()); 417 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 418 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); 419 // Test connect audio - set the device first as the active device 420 Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); 421 Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); 422 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, 423 mCurrentDevice); 424 when(mStateMachines.get(mCurrentDevice).getAudioState()).thenReturn( 425 BluetoothHeadset.STATE_AUDIO_CONNECTING); 426 // 2nd connection attempt for the same device will succeed as well 427 Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); 428 // Verify CONNECT_AUDIO is only sent once 429 verify(mStateMachines.get(mCurrentDevice)).sendMessage( 430 eq(HeadsetStateMachine.CONNECT_AUDIO), any()); 431 // Test disconnect audio 432 Assert.assertTrue(mHeadsetService.disconnectAudio(mCurrentDevice)); 433 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.DISCONNECT_AUDIO, 434 mCurrentDevice); 435 when(mStateMachines.get(mCurrentDevice).getAudioState()).thenReturn( 436 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 437 // Further disconnection requests will fail 438 Assert.assertFalse(mHeadsetService.disconnectAudio(mCurrentDevice)); 439 verify(mStateMachines.get(mCurrentDevice)).sendMessage( 440 eq(HeadsetStateMachine.DISCONNECT_AUDIO), any(BluetoothDevice.class)); 441 } 442 443 /** 444 * Test to verify that HFP audio connection can be initiated when multiple devices are connected 445 * and can be canceled or disconnected as well 446 */ 447 @Test testConnectAudio_withMultipleDevices()448 public void testConnectAudio_withMultipleDevices() { 449 ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); 450 when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class), 451 eq(BluetoothProfile.HEADSET))) 452 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 453 for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) { 454 mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); 455 Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); 456 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 457 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, 458 mAdapterService, mNativeInterface, mSystemInterface); 459 verify(mObjectsFactory, times(i + 1)).makeStateMachine(any(BluetoothDevice.class), 460 eq(mHeadsetService.getStateMachinesThreadLooper()), eq(mHeadsetService), 461 eq(mAdapterService), eq(mNativeInterface), eq(mSystemInterface)); 462 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT, 463 mCurrentDevice); 464 verify(mStateMachines.get(mCurrentDevice)).sendMessage(eq(HeadsetStateMachine.CONNECT), 465 any(BluetoothDevice.class)); 466 // Put device to connecting 467 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 468 BluetoothProfile.STATE_CONNECTING); 469 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 470 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); 471 Assert.assertThat(mHeadsetService.getConnectedDevices(), 472 Matchers.containsInAnyOrder(connectedDevices.toArray())); 473 // Put device to connected 474 connectedDevices.add(mCurrentDevice); 475 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 476 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 477 BluetoothProfile.STATE_CONNECTED); 478 when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs()).thenReturn( 479 SystemClock.uptimeMillis()); 480 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 481 mHeadsetService.getConnectionState(mCurrentDevice)); 482 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 483 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED); 484 Assert.assertThat(mHeadsetService.getConnectedDevices(), 485 Matchers.containsInAnyOrder(connectedDevices.toArray())); 486 // Try to connect audio 487 // Should fail 488 Assert.assertFalse(mHeadsetService.connectAudio(mCurrentDevice)); 489 // Should succeed after setActiveDevice() 490 Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); 491 Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); 492 verify(mStateMachines.get(mCurrentDevice)).sendMessage( 493 HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice); 494 // Put device to audio connecting state 495 when(mStateMachines.get(mCurrentDevice).getAudioState()).thenReturn( 496 BluetoothHeadset.STATE_AUDIO_CONNECTING); 497 // 2nd connection attempt will also succeed 498 Assert.assertTrue(mHeadsetService.connectAudio(mCurrentDevice)); 499 // Verify CONNECT_AUDIO is only sent once 500 verify(mStateMachines.get(mCurrentDevice)).sendMessage( 501 eq(HeadsetStateMachine.CONNECT_AUDIO), any()); 502 // Put device to audio connected state 503 when(mStateMachines.get(mCurrentDevice).getAudioState()).thenReturn( 504 BluetoothHeadset.STATE_AUDIO_CONNECTED); 505 // Disconnect audio 506 Assert.assertTrue(mHeadsetService.disconnectAudio(mCurrentDevice)); 507 verify(mStateMachines.get(mCurrentDevice)).sendMessage( 508 HeadsetStateMachine.DISCONNECT_AUDIO, mCurrentDevice); 509 when(mStateMachines.get(mCurrentDevice).getAudioState()).thenReturn( 510 BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 511 // Further disconnection requests will fail 512 Assert.assertFalse(mHeadsetService.disconnectAudio(mCurrentDevice)); 513 verify(mStateMachines.get(mCurrentDevice)).sendMessage( 514 eq(HeadsetStateMachine.DISCONNECT_AUDIO), any(BluetoothDevice.class)); 515 } 516 } 517 518 /** 519 * Verify that only one device can be in audio connecting or audio connected state, further 520 * attempt to call {@link HeadsetService#connectAudio(BluetoothDevice)} should fail by returning 521 * false 522 */ 523 @Test testConnectAudio_connectTwoAudioChannelsShouldFail()524 public void testConnectAudio_connectTwoAudioChannelsShouldFail() { 525 ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); 526 when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class), 527 eq(BluetoothProfile.HEADSET))) 528 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 529 for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) { 530 mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); 531 Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); 532 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 533 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, 534 mAdapterService, mNativeInterface, mSystemInterface); 535 verify(mObjectsFactory, times(i + 1)).makeStateMachine(any(BluetoothDevice.class), 536 eq(mHeadsetService.getStateMachinesThreadLooper()), eq(mHeadsetService), 537 eq(mAdapterService), eq(mNativeInterface), eq(mSystemInterface)); 538 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT, 539 mCurrentDevice); 540 verify(mStateMachines.get(mCurrentDevice)).sendMessage(eq(HeadsetStateMachine.CONNECT), 541 any(BluetoothDevice.class)); 542 // Put device to connecting 543 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 544 BluetoothProfile.STATE_CONNECTING); 545 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 546 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); 547 Assert.assertThat(mHeadsetService.getConnectedDevices(), 548 Matchers.containsInAnyOrder(connectedDevices.toArray())); 549 // Put device to connected 550 connectedDevices.add(mCurrentDevice); 551 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 552 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 553 BluetoothProfile.STATE_CONNECTED); 554 when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs()).thenReturn( 555 SystemClock.uptimeMillis()); 556 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 557 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED); 558 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 559 mHeadsetService.getConnectionState(mCurrentDevice)); 560 Assert.assertThat(mHeadsetService.getConnectedDevices(), 561 Matchers.containsInAnyOrder(connectedDevices.toArray())); 562 } 563 if (MAX_HEADSET_CONNECTIONS >= 2) { 564 // Try to connect audio 565 BluetoothDevice firstDevice = connectedDevices.get(0); 566 BluetoothDevice secondDevice = connectedDevices.get(1); 567 // Set the first device as the active device 568 Assert.assertTrue(mHeadsetService.setActiveDevice(firstDevice)); 569 Assert.assertTrue(mHeadsetService.connectAudio(firstDevice)); 570 verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, 571 firstDevice); 572 // Put device to audio connecting state 573 when(mStateMachines.get(firstDevice).getAudioState()).thenReturn( 574 BluetoothHeadset.STATE_AUDIO_CONNECTING); 575 // 2nd connection attempt will succeed for the same device 576 Assert.assertTrue(mHeadsetService.connectAudio(firstDevice)); 577 // Connect to 2nd device will fail 578 Assert.assertFalse(mHeadsetService.connectAudio(secondDevice)); 579 verify(mStateMachines.get(secondDevice), never()).sendMessage( 580 HeadsetStateMachine.CONNECT_AUDIO, secondDevice); 581 // Put device to audio connected state 582 when(mStateMachines.get(firstDevice).getAudioState()).thenReturn( 583 BluetoothHeadset.STATE_AUDIO_CONNECTED); 584 // Connect to 2nd device will fail 585 Assert.assertFalse(mHeadsetService.connectAudio(secondDevice)); 586 verify(mStateMachines.get(secondDevice), never()).sendMessage( 587 HeadsetStateMachine.CONNECT_AUDIO, secondDevice); 588 } 589 } 590 591 /** 592 * Verify that {@link HeadsetService#connectAudio()} will connect to first connected/connecting 593 * device 594 */ 595 @Test testConnectAudio_firstConnectedAudioDevice()596 public void testConnectAudio_firstConnectedAudioDevice() { 597 ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); 598 when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class), 599 eq(BluetoothProfile.HEADSET))) 600 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 601 doAnswer(invocation -> { 602 BluetoothDevice[] devicesArray = new BluetoothDevice[connectedDevices.size()]; 603 return connectedDevices.toArray(devicesArray); 604 }).when(mAdapterService).getBondedDevices(); 605 for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) { 606 mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); 607 Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); 608 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 609 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, 610 mAdapterService, mNativeInterface, mSystemInterface); 611 verify(mObjectsFactory, times(i + 1)).makeStateMachine(any(BluetoothDevice.class), 612 eq(mHeadsetService.getStateMachinesThreadLooper()), eq(mHeadsetService), 613 eq(mAdapterService), eq(mNativeInterface), eq(mSystemInterface)); 614 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT, 615 mCurrentDevice); 616 verify(mStateMachines.get(mCurrentDevice)).sendMessage(eq(HeadsetStateMachine.CONNECT), 617 any(BluetoothDevice.class)); 618 // Put device to connecting 619 when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs()).thenReturn( 620 SystemClock.uptimeMillis()); 621 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 622 BluetoothProfile.STATE_CONNECTING); 623 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 624 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); 625 Assert.assertThat(mHeadsetService.getConnectedDevices(), 626 Matchers.containsInAnyOrder(connectedDevices.toArray())); 627 // Put device to connected 628 connectedDevices.add(mCurrentDevice); 629 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 630 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 631 BluetoothProfile.STATE_CONNECTED); 632 when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs()).thenReturn( 633 SystemClock.uptimeMillis()); 634 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 635 mHeadsetService.getConnectionState(mCurrentDevice)); 636 Assert.assertThat(mHeadsetService.getConnectedDevices(), 637 Matchers.containsInAnyOrder(connectedDevices.toArray())); 638 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 639 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED); 640 } 641 // Try to connect audio 642 BluetoothDevice firstDevice = connectedDevices.get(0); 643 Assert.assertTrue(mHeadsetService.setActiveDevice(firstDevice)); 644 Assert.assertTrue(mHeadsetService.connectAudio()); 645 verify(mStateMachines.get(firstDevice)).sendMessage(HeadsetStateMachine.CONNECT_AUDIO, 646 firstDevice); 647 } 648 649 /** 650 * Test to verify that {@link HeadsetService#connectAudio(BluetoothDevice)} fails if device 651 * was never connected 652 */ 653 @Test testConnectAudio_deviceNeverConnected()654 public void testConnectAudio_deviceNeverConnected() { 655 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 656 Assert.assertFalse(mHeadsetService.connectAudio(mCurrentDevice)); 657 } 658 659 /** 660 * Test to verify that {@link HeadsetService#connectAudio(BluetoothDevice)} fails if device 661 * is disconnected 662 */ 663 @Test testConnectAudio_deviceDisconnected()664 public void testConnectAudio_deviceDisconnected() { 665 when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class), 666 eq(BluetoothProfile.HEADSET))) 667 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 668 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 669 HeadsetCallState headsetCallState = 670 new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_ALERTING, 671 TEST_PHONE_NUMBER, 128, ""); 672 Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); 673 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 674 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService, 675 mNativeInterface, mSystemInterface); 676 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT, 677 mCurrentDevice); 678 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 679 // Put device in disconnected state 680 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 681 BluetoothProfile.STATE_DISCONNECTED); 682 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 683 mHeadsetService.getConnectionState(mCurrentDevice)); 684 Assert.assertEquals(Collections.EMPTY_LIST, mHeadsetService.getConnectedDevices()); 685 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 686 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 687 // connectAudio should fail 688 Assert.assertFalse(mHeadsetService.connectAudio(mCurrentDevice)); 689 verify(mStateMachines.get(mCurrentDevice), never()).sendMessage( 690 eq(HeadsetStateMachine.CONNECT_AUDIO), any()); 691 } 692 693 /** 694 * Verifies that phone state change will trigger a system-wide saving of call state even when 695 * no device is connected 696 * 697 * @throws RemoteException if binder call fails 698 */ 699 @Test testPhoneStateChange_noDeviceSaveState()700 public void testPhoneStateChange_noDeviceSaveState() throws RemoteException { 701 HeadsetCallState headsetCallState = 702 new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_ALERTING, 703 TEST_PHONE_NUMBER, 128, ""); 704 mHeadsetServiceBinder.phoneStateChanged(headsetCallState.mNumActive, 705 headsetCallState.mNumHeld, headsetCallState.mCallState, headsetCallState.mNumber, 706 headsetCallState.mType, headsetCallState.mName, mAdapter.getAttributionSource()); 707 TestUtils.waitForLooperToFinishScheduledTask( 708 mHeadsetService.getStateMachinesThreadLooper()); 709 verify(mAudioManager, never()).setParameters("A2dpSuspended=true"); 710 HeadsetTestUtils.verifyPhoneStateChangeSetters(mPhoneState, headsetCallState, 711 ASYNC_CALL_TIMEOUT_MILLIS); 712 } 713 714 /** 715 * Verifies that phone state change will trigger a system-wide saving of call state and send 716 * state change to connected devices 717 * 718 * @throws RemoteException if binder call fails 719 */ 720 @Test testPhoneStateChange_oneDeviceSaveState()721 public void testPhoneStateChange_oneDeviceSaveState() throws RemoteException { 722 HeadsetCallState headsetCallState = 723 new HeadsetCallState(0, 0, HeadsetHalConstants.CALL_STATE_IDLE, 724 TEST_PHONE_NUMBER, 128, ""); 725 when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class), 726 eq(BluetoothProfile.HEADSET))) 727 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 728 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 729 final ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); 730 // Connect one device 731 Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); 732 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 733 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService, 734 mNativeInterface, mSystemInterface); 735 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT, 736 mCurrentDevice); 737 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 738 // Put device to connecting 739 when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs()).thenReturn( 740 SystemClock.uptimeMillis()); 741 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 742 BluetoothProfile.STATE_CONNECTING); 743 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 744 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); 745 Assert.assertThat(mHeadsetService.getConnectedDevices(), 746 Matchers.containsInAnyOrder(connectedDevices.toArray())); 747 // Put device to connected 748 connectedDevices.add(mCurrentDevice); 749 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 750 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 751 BluetoothProfile.STATE_CONNECTED); 752 when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs()).thenReturn( 753 SystemClock.uptimeMillis()); 754 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 755 mHeadsetService.getConnectionState(mCurrentDevice)); 756 Assert.assertThat(mHeadsetService.getConnectedDevices(), 757 Matchers.containsInAnyOrder(connectedDevices.toArray())); 758 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 759 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED); 760 // Change phone state 761 mHeadsetServiceBinder.phoneStateChanged(headsetCallState.mNumActive, 762 headsetCallState.mNumHeld, headsetCallState.mCallState, headsetCallState.mNumber, 763 headsetCallState.mType, headsetCallState.mName, mAdapter.getAttributionSource()); 764 TestUtils.waitForLooperToFinishScheduledTask( 765 mHeadsetService.getStateMachinesThreadLooper()); 766 767 // Should not ask Audio HAL to suspend A2DP without active device 768 verify(mAudioManager, never()).setParameters("A2dpSuspended=true"); 769 // Make sure we notify device about this change 770 verify(mStateMachines.get(mCurrentDevice)).sendMessage( 771 HeadsetStateMachine.CALL_STATE_CHANGED, headsetCallState); 772 // Make sure state is updated once in phone state holder 773 HeadsetTestUtils.verifyPhoneStateChangeSetters(mPhoneState, headsetCallState, 774 ASYNC_CALL_TIMEOUT_MILLIS); 775 776 // Set the device first as the active device 777 Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); 778 // Change phone state 779 headsetCallState.mCallState = HeadsetHalConstants.CALL_STATE_ALERTING; 780 mHeadsetServiceBinder.phoneStateChanged(headsetCallState.mNumActive, 781 headsetCallState.mNumHeld, headsetCallState.mCallState, headsetCallState.mNumber, 782 headsetCallState.mType, headsetCallState.mName, mAdapter.getAttributionSource()); 783 TestUtils.waitForLooperToFinishScheduledTask( 784 mHeadsetService.getStateMachinesThreadLooper()); 785 // Ask Audio HAL to suspend A2DP 786 verify(mAudioManager).setParameters("A2dpSuspended=true"); 787 // Make sure state is updated 788 verify(mStateMachines.get(mCurrentDevice)).sendMessage( 789 HeadsetStateMachine.CALL_STATE_CHANGED, headsetCallState); 790 verify(mPhoneState).setCallState(eq(headsetCallState.mCallState)); 791 } 792 793 /** 794 * Verifies that phone state change will trigger a system-wide saving of call state and send 795 * state change to connected devices 796 * 797 * @throws RemoteException if binder call fails 798 */ 799 @Test testPhoneStateChange_multipleDevicesSaveState()800 public void testPhoneStateChange_multipleDevicesSaveState() throws RemoteException { 801 HeadsetCallState headsetCallState = 802 new HeadsetCallState(1, 0, HeadsetHalConstants.CALL_STATE_ALERTING, 803 TEST_PHONE_NUMBER, 128, ""); 804 final ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); 805 when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class), 806 eq(BluetoothProfile.HEADSET))) 807 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 808 for (int i = 0; i < MAX_HEADSET_CONNECTIONS; ++i) { 809 mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); 810 Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); 811 verify(mObjectsFactory).makeStateMachine(mCurrentDevice, 812 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, 813 mAdapterService, mNativeInterface, mSystemInterface); 814 verify(mObjectsFactory, times(i + 1)).makeStateMachine(any(BluetoothDevice.class), 815 eq(mHeadsetService.getStateMachinesThreadLooper()), eq(mHeadsetService), 816 eq(mAdapterService), eq(mNativeInterface), eq(mSystemInterface)); 817 verify(mStateMachines.get(mCurrentDevice)).sendMessage(HeadsetStateMachine.CONNECT, 818 mCurrentDevice); 819 verify(mStateMachines.get(mCurrentDevice)).sendMessage(eq(HeadsetStateMachine.CONNECT), 820 any(BluetoothDevice.class)); 821 // Put device to connecting 822 when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs()).thenReturn( 823 SystemClock.uptimeMillis()); 824 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 825 BluetoothProfile.STATE_CONNECTING); 826 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 827 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); 828 Assert.assertThat(mHeadsetService.getConnectedDevices(), 829 Matchers.containsInAnyOrder(connectedDevices.toArray())); 830 // Put device to connected 831 connectedDevices.add(mCurrentDevice); 832 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 833 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 834 BluetoothProfile.STATE_CONNECTED); 835 when(mStateMachines.get(mCurrentDevice).getConnectingTimestampMs()).thenReturn( 836 SystemClock.uptimeMillis()); 837 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 838 mHeadsetService.getConnectionState(mCurrentDevice)); 839 Assert.assertThat(mHeadsetService.getConnectedDevices(), 840 Matchers.containsInAnyOrder(connectedDevices.toArray())); 841 mHeadsetService.onConnectionStateChangedFromStateMachine(mCurrentDevice, 842 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_CONNECTED); 843 Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); 844 } 845 // Change phone state 846 mHeadsetServiceBinder.phoneStateChanged(headsetCallState.mNumActive, 847 headsetCallState.mNumHeld, headsetCallState.mCallState, headsetCallState.mNumber, 848 headsetCallState.mType, headsetCallState.mName, mAdapter.getAttributionSource()); 849 // Ask Audio HAL to suspend A2DP 850 verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS)) 851 .setParameters("A2dpSuspended=true"); 852 // Make sure we notify devices about this change 853 for (BluetoothDevice device : connectedDevices) { 854 verify(mStateMachines.get(device)).sendMessage(HeadsetStateMachine.CALL_STATE_CHANGED, 855 headsetCallState); 856 } 857 // Make sure state is updated once in phone state holder 858 HeadsetTestUtils.verifyPhoneStateChangeSetters(mPhoneState, headsetCallState, 859 ASYNC_CALL_TIMEOUT_MILLIS); 860 } 861 862 /** 863 * Test that whether active device been removed after enable silence mode 864 */ 865 @Test testSetSilenceMode()866 public void testSetSilenceMode() { 867 when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class), 868 eq(BluetoothProfile.HEADSET))) 869 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 870 for (int i = 0; i < 2; i++) { 871 mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); 872 Assert.assertTrue(mHeadsetService.connect(mCurrentDevice)); 873 when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); 874 when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn( 875 BluetoothProfile.STATE_CONNECTED); 876 when(mStateMachines.get(mCurrentDevice).setSilenceDevice( 877 anyBoolean())).thenReturn(true); 878 } 879 mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); 880 BluetoothDevice otherDevice = TestUtils.getTestDevice(mAdapter, 1); 881 882 // Test whether active device been removed after enable silence mode. 883 Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); 884 Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); 885 Assert.assertTrue(mHeadsetService.setSilenceMode(mCurrentDevice, true)); 886 Assert.assertNull(mHeadsetService.getActiveDevice()); 887 888 // Test whether active device been resumed after disable silence mode. 889 Assert.assertTrue(mHeadsetService.setSilenceMode(mCurrentDevice, false)); 890 Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); 891 892 // Test that active device should not be changed when silence a non-active device 893 Assert.assertTrue(mHeadsetService.setActiveDevice(mCurrentDevice)); 894 Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); 895 Assert.assertTrue(mHeadsetService.setSilenceMode(otherDevice, true)); 896 Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); 897 898 // Test that active device should not be changed when another device exits silence mode 899 Assert.assertTrue(mHeadsetService.setSilenceMode(otherDevice, false)); 900 Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice()); 901 } 902 903 /* 904 * Helper function to test okToAcceptConnection() method 905 * 906 * @param device test device 907 * @param bondState bond state value, could be invalid 908 * @param priority value, could be invalid, coudl be invalid 909 * @param expected expected result from okToAcceptConnection() 910 */ testOkToAcceptConnectionCase(BluetoothDevice device, int bondState, int priority, boolean expected)911 private void testOkToAcceptConnectionCase(BluetoothDevice device, int bondState, int priority, 912 boolean expected) { 913 doReturn(bondState).when(mAdapterService).getBondState(device); 914 when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEADSET)) 915 .thenReturn(priority); 916 Assert.assertEquals(expected, mHeadsetService.okToAcceptConnection(device)); 917 } 918 919 } 920