1 /* 2 * Copyright 2020 HIMSA II K/S - www.himsa.com. 3 * Represented by EHIMA - www.ehima.com 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.bluetooth.vc; 19 20 import static org.mockito.Mockito.*; 21 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothProfile; 25 import android.bluetooth.BluetoothUuid; 26 import android.bluetooth.BluetoothVolumeControl; 27 import android.bluetooth.IBluetoothVolumeControlCallback; 28 import android.content.AttributionSource; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.media.AudioManager; 34 import android.os.Binder; 35 import android.os.Looper; 36 import android.os.ParcelUuid; 37 38 import androidx.test.InstrumentationRegistry; 39 import androidx.test.filters.MediumTest; 40 import androidx.test.rule.ServiceTestRule; 41 import androidx.test.runner.AndroidJUnit4; 42 43 import com.android.bluetooth.btservice.AdapterService; 44 import com.android.bluetooth.btservice.ServiceFactory; 45 import com.android.bluetooth.btservice.storage.DatabaseManager; 46 import com.android.bluetooth.csip.CsipSetCoordinatorService; 47 import com.android.bluetooth.R; 48 import com.android.bluetooth.TestUtils; 49 import com.android.bluetooth.x.com.android.modules.utils.SynchronousResultReceiver; 50 51 import org.junit.After; 52 import org.junit.Assert; 53 import org.junit.Assume; 54 import org.junit.Before; 55 import org.junit.Rule; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 import org.mockito.Mock; 59 import org.mockito.Mockito; 60 import org.mockito.MockitoAnnotations; 61 62 import java.time.Duration; 63 import java.util.HashMap; 64 import java.util.List; 65 import java.util.concurrent.LinkedBlockingQueue; 66 import java.util.concurrent.TimeoutException; 67 import java.util.stream.IntStream; 68 69 @MediumTest 70 @RunWith(AndroidJUnit4.class) 71 public class VolumeControlServiceTest { 72 private BluetoothAdapter mAdapter; 73 private AttributionSource mAttributionSource; 74 private Context mTargetContext; 75 private VolumeControlService mService; 76 private VolumeControlService.BluetoothVolumeControlBinder mServiceBinder; 77 private BluetoothDevice mDevice; 78 private BluetoothDevice mDeviceTwo; 79 private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mDeviceQueueMap; 80 private static final int TIMEOUT_MS = 1000; 81 private static final int BT_LE_AUDIO_MAX_VOL = 255; 82 private static final int MEDIA_MIN_VOL = 0; 83 private static final int MEDIA_MAX_VOL = 25; 84 private static final int CALL_MIN_VOL = 1; 85 private static final int CALL_MAX_VOL = 8; 86 87 private BroadcastReceiver mVolumeControlIntentReceiver; 88 89 @Mock private AdapterService mAdapterService; 90 @Mock private DatabaseManager mDatabaseManager; 91 @Mock private VolumeControlNativeInterface mNativeInterface; 92 @Mock private AudioManager mAudioManager; 93 @Mock private ServiceFactory mServiceFactory; 94 @Mock private CsipSetCoordinatorService mCsipService; 95 96 @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule(); 97 98 @Before setUp()99 public void setUp() throws Exception { 100 mTargetContext = InstrumentationRegistry.getTargetContext(); 101 // Set up mocks and test assets 102 MockitoAnnotations.initMocks(this); 103 104 if (Looper.myLooper() == null) { 105 Looper.prepare(); 106 } 107 108 TestUtils.setAdapterService(mAdapterService); 109 doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); 110 doReturn(true, false).when(mAdapterService).isStartedProfile(anyString()); 111 112 mAdapter = BluetoothAdapter.getDefaultAdapter(); 113 mAttributionSource = mAdapter.getAttributionSource(); 114 115 doReturn(MEDIA_MIN_VOL).when(mAudioManager) 116 .getStreamMinVolume(eq(AudioManager.STREAM_MUSIC)); 117 doReturn(MEDIA_MAX_VOL).when(mAudioManager) 118 .getStreamMaxVolume(eq(AudioManager.STREAM_MUSIC)); 119 doReturn(CALL_MIN_VOL).when(mAudioManager) 120 .getStreamMinVolume(eq(AudioManager.STREAM_VOICE_CALL)); 121 doReturn(CALL_MAX_VOL).when(mAudioManager) 122 .getStreamMaxVolume(eq(AudioManager.STREAM_VOICE_CALL)); 123 124 startService(); 125 mService.mVolumeControlNativeInterface = mNativeInterface; 126 mService.mAudioManager = mAudioManager; 127 mService.mFactory = mServiceFactory; 128 mServiceBinder = (VolumeControlService.BluetoothVolumeControlBinder) mService.initBinder(); 129 mServiceBinder.mIsTesting = true; 130 131 doReturn(mCsipService).when(mServiceFactory).getCsipSetCoordinatorService(); 132 133 // Override the timeout value to speed up the test 134 VolumeControlStateMachine.sConnectTimeoutMs = TIMEOUT_MS; // 1s 135 136 // Set up the Connection State Changed receiver 137 IntentFilter filter = new IntentFilter(); 138 filter.addAction(BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED); 139 140 mVolumeControlIntentReceiver = new VolumeControlIntentReceiver(); 141 mTargetContext.registerReceiver(mVolumeControlIntentReceiver, filter); 142 143 // Get a device for testing 144 mDevice = TestUtils.getTestDevice(mAdapter, 0); 145 mDeviceTwo = TestUtils.getTestDevice(mAdapter, 1); 146 mDeviceQueueMap = new HashMap<>(); 147 mDeviceQueueMap.put(mDevice, new LinkedBlockingQueue<>()); 148 mDeviceQueueMap.put(mDeviceTwo, new LinkedBlockingQueue<>()); 149 doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService) 150 .getBondState(any(BluetoothDevice.class)); 151 doReturn(new ParcelUuid[]{BluetoothUuid.VOLUME_CONTROL}).when(mAdapterService) 152 .getRemoteUuids(any(BluetoothDevice.class)); 153 } 154 155 @After tearDown()156 public void tearDown() throws Exception { 157 if (mService == null) { 158 return; 159 } 160 161 stopService(); 162 mTargetContext.unregisterReceiver(mVolumeControlIntentReceiver); 163 mDeviceQueueMap.clear(); 164 TestUtils.clearAdapterService(mAdapterService); 165 reset(mAudioManager); 166 } 167 startService()168 private void startService() throws TimeoutException { 169 TestUtils.startService(mServiceRule, VolumeControlService.class); 170 mService = VolumeControlService.getVolumeControlService(); 171 Assert.assertNotNull(mService); 172 } 173 stopService()174 private void stopService() throws TimeoutException { 175 TestUtils.stopService(mServiceRule, VolumeControlService.class); 176 mService = VolumeControlService.getVolumeControlService(); 177 Assert.assertNull(mService); 178 } 179 180 private class VolumeControlIntentReceiver extends BroadcastReceiver { 181 @Override onReceive(Context context, Intent intent)182 public void onReceive(Context context, Intent intent) { 183 try { 184 BluetoothDevice device = intent.getParcelableExtra( 185 BluetoothDevice.EXTRA_DEVICE); 186 Assert.assertNotNull(device); 187 LinkedBlockingQueue<Intent> queue = mDeviceQueueMap.get(device); 188 Assert.assertNotNull(queue); 189 queue.put(intent); 190 } catch (InterruptedException e) { 191 Assert.fail("Cannot add Intent to the Connection State queue: " 192 + e.getMessage()); 193 } 194 } 195 } 196 verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)197 private void verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, 198 int newState, int prevState) { 199 Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device)); 200 Assert.assertNotNull(intent); 201 Assert.assertEquals(BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED, 202 intent.getAction()); 203 Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); 204 Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); 205 Assert.assertEquals(prevState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 206 -1)); 207 } 208 verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device)209 private void verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device) { 210 Intent intent = TestUtils.waitForNoIntent(timeoutMs, mDeviceQueueMap.get(device)); 211 Assert.assertNull(intent); 212 } 213 214 /** 215 * Test getting VolumeControl Service: getVolumeControlService() 216 */ 217 @Test testGetVolumeControlService()218 public void testGetVolumeControlService() { 219 Assert.assertEquals(mService, VolumeControlService.getVolumeControlService()); 220 } 221 222 /** 223 * Test stop VolumeControl Service 224 */ 225 @Test testStopVolumeControlService()226 public void testStopVolumeControlService() throws Exception { 227 // Prepare: connect 228 connectDevice(mDevice); 229 // VolumeControl Service is already running: test stop(). 230 // Note: must be done on the main thread 231 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 232 public void run() { 233 Assert.assertTrue(mService.stop()); 234 } 235 }); 236 // Try to restart the service. Note: must be done on the main thread 237 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 238 public void run() { 239 Assert.assertTrue(mService.start()); 240 } 241 }); 242 } 243 244 /** 245 * Test get/set policy for BluetoothDevice 246 */ 247 @Test testGetSetPolicy()248 public void testGetSetPolicy() { 249 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 250 when(mDatabaseManager 251 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 252 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 253 Assert.assertEquals("Initial device policy", 254 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, 255 mService.getConnectionPolicy(mDevice)); 256 257 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 258 when(mDatabaseManager 259 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 260 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 261 Assert.assertEquals("Setting device policy to POLICY_FORBIDDEN", 262 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, 263 mService.getConnectionPolicy(mDevice)); 264 265 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 266 when(mDatabaseManager 267 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 268 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 269 Assert.assertEquals("Setting device policy to POLICY_ALLOWED", 270 BluetoothProfile.CONNECTION_POLICY_ALLOWED, 271 mService.getConnectionPolicy(mDevice)); 272 } 273 274 /** 275 * Test if getProfileConnectionPolicy works after the service is stopped. 276 */ 277 @Test testGetPolicyAfterStopped()278 public void testGetPolicyAfterStopped() throws Exception { 279 mService.stop(); 280 when(mDatabaseManager 281 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 282 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 283 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 284 int defaultRecvValue = -1000; 285 mServiceBinder.getConnectionPolicy(mDevice, mAttributionSource, recv); 286 int policy = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)) 287 .getValue(defaultRecvValue); 288 Assert.assertEquals("Initial device policy", 289 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, policy); 290 } 291 292 /** 293 * Test okToConnect method using various test cases 294 */ 295 @Test testOkToConnect()296 public void testOkToConnect() { 297 int badPolicyValue = 1024; 298 int badBondState = 42; 299 testOkToConnectCase(mDevice, 300 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 301 testOkToConnectCase(mDevice, 302 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 303 testOkToConnectCase(mDevice, 304 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 305 testOkToConnectCase(mDevice, 306 BluetoothDevice.BOND_NONE, badPolicyValue, false); 307 testOkToConnectCase(mDevice, 308 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 309 testOkToConnectCase(mDevice, 310 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 311 testOkToConnectCase(mDevice, 312 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 313 testOkToConnectCase(mDevice, 314 BluetoothDevice.BOND_BONDING, badPolicyValue, false); 315 testOkToConnectCase(mDevice, 316 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, true); 317 testOkToConnectCase(mDevice, 318 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 319 testOkToConnectCase(mDevice, 320 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_ALLOWED, true); 321 testOkToConnectCase(mDevice, 322 BluetoothDevice.BOND_BONDED, badPolicyValue, false); 323 testOkToConnectCase(mDevice, 324 badBondState, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 325 testOkToConnectCase(mDevice, 326 badBondState, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 327 testOkToConnectCase(mDevice, 328 badBondState, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 329 testOkToConnectCase(mDevice, 330 badBondState, badPolicyValue, false); 331 } 332 333 /** 334 * Test that an outgoing connection to device that does not have Volume Control UUID is rejected 335 */ 336 @Test testOutgoingConnectMissingVolumeControlUuid()337 public void testOutgoingConnectMissingVolumeControlUuid() { 338 // Update the device policy so okToConnect() returns true 339 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 340 when(mDatabaseManager 341 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 342 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 343 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 344 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 345 346 // Return No UUID 347 doReturn(new ParcelUuid[]{}).when(mAdapterService) 348 .getRemoteUuids(any(BluetoothDevice.class)); 349 350 // Send a connect request 351 Assert.assertFalse("Connect expected to fail", mService.connect(mDevice)); 352 } 353 354 /** 355 * Test that an outgoing connection to device that have Volume Control UUID is successful 356 */ 357 @Test testOutgoingConnectDisconnectExistingVolumeControlUuid()358 public void testOutgoingConnectDisconnectExistingVolumeControlUuid() throws Exception { 359 // Update the device policy so okToConnect() returns true 360 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 361 when(mDatabaseManager 362 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 363 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 364 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 365 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 366 367 // Return Volume Control UUID 368 doReturn(new ParcelUuid[]{BluetoothUuid.VOLUME_CONTROL}).when(mAdapterService) 369 .getRemoteUuids(any(BluetoothDevice.class)); 370 371 // Send a connect request via binder 372 SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 373 mServiceBinder.connect(mDevice, mAttributionSource, recv); 374 Assert.assertTrue("Connect expected to succeed", 375 recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(false)); 376 377 // Verify the connection state broadcast, and that we are in Connecting state 378 verifyConnectionStateIntent(TIMEOUT_MS, mDevice, BluetoothProfile.STATE_CONNECTING, 379 BluetoothProfile.STATE_DISCONNECTED); 380 381 // Send a disconnect request via binder 382 recv = SynchronousResultReceiver.get(); 383 mServiceBinder.disconnect(mDevice, mAttributionSource, recv); 384 Assert.assertTrue("Disconnect expected to succeed", 385 recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(false)); 386 387 // Verify the connection state broadcast, and that we are in Connecting state 388 verifyConnectionStateIntent(TIMEOUT_MS, mDevice, BluetoothProfile.STATE_DISCONNECTED, 389 BluetoothProfile.STATE_CONNECTING); 390 } 391 392 /** 393 * Test that an outgoing connection to device with POLICY_FORBIDDEN is rejected 394 */ 395 @Test testOutgoingConnectPolicyForbidden()396 public void testOutgoingConnectPolicyForbidden() { 397 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 398 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 399 400 // Set the device policy to POLICY_FORBIDDEN so connect() should fail 401 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 402 when(mDatabaseManager 403 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 404 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 405 406 // Send a connect request 407 Assert.assertFalse("Connect expected to fail", mService.connect(mDevice)); 408 } 409 410 /** 411 * Test that an outgoing connection times out 412 */ 413 @Test testOutgoingConnectTimeout()414 public void testOutgoingConnectTimeout() throws Exception { 415 // Update the device policy so okToConnect() returns true 416 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 417 when(mDatabaseManager 418 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 419 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 420 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 421 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 422 423 // Send a connect request 424 Assert.assertTrue("Connect failed", mService.connect(mDevice)); 425 426 // Verify the connection state broadcast, and that we are in Connecting state 427 verifyConnectionStateIntent(TIMEOUT_MS, mDevice, BluetoothProfile.STATE_CONNECTING, 428 BluetoothProfile.STATE_DISCONNECTED); 429 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 430 mService.getConnectionState(mDevice)); 431 432 // Verify the connection state broadcast, and that we are in Disconnected state 433 verifyConnectionStateIntent(VolumeControlStateMachine.sConnectTimeoutMs * 2, 434 mDevice, BluetoothProfile.STATE_DISCONNECTED, 435 BluetoothProfile.STATE_CONNECTING); 436 437 final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get(); 438 int defaultRecvValue = -1000; 439 mServiceBinder.getConnectionState(mDevice, mAttributionSource, recv); 440 int state = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)) 441 .getValue(defaultRecvValue); 442 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, state); 443 } 444 445 /** 446 * Test that only CONNECTION_STATE_CONNECTED or CONNECTION_STATE_CONNECTING Volume Control stack 447 * events will create a state machine. 448 */ 449 @Test testCreateStateMachineStackEvents()450 public void testCreateStateMachineStackEvents() { 451 // Update the device policy so okToConnect() returns true 452 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 453 when(mDatabaseManager 454 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 455 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 456 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 457 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 458 459 // stack event: CONNECTION_STATE_CONNECTING - state machine should be created 460 generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTING, 461 BluetoothProfile.STATE_DISCONNECTED); 462 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 463 mService.getConnectionState(mDevice)); 464 Assert.assertTrue(mService.getDevices().contains(mDevice)); 465 466 // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed 467 generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_DISCONNECTED, 468 BluetoothProfile.STATE_CONNECTING); 469 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 470 mService.getConnectionState(mDevice)); 471 Assert.assertTrue(mService.getDevices().contains(mDevice)); 472 mService.bondStateChanged(mDevice, BluetoothDevice.BOND_NONE); 473 Assert.assertFalse(mService.getDevices().contains(mDevice)); 474 475 // stack event: CONNECTION_STATE_CONNECTED - state machine should be created 476 generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTED, 477 BluetoothProfile.STATE_DISCONNECTED); 478 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 479 mService.getConnectionState(mDevice)); 480 Assert.assertTrue(mService.getDevices().contains(mDevice)); 481 482 // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed 483 generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_DISCONNECTED, 484 BluetoothProfile.STATE_CONNECTED); 485 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 486 mService.getConnectionState(mDevice)); 487 Assert.assertTrue(mService.getDevices().contains(mDevice)); 488 mService.bondStateChanged(mDevice, BluetoothDevice.BOND_NONE); 489 Assert.assertFalse(mService.getDevices().contains(mDevice)); 490 491 // stack event: CONNECTION_STATE_DISCONNECTING - state machine should not be created 492 generateUnexpectedConnectionMessageFromNative(mDevice, 493 BluetoothProfile.STATE_DISCONNECTING, 494 BluetoothProfile.STATE_DISCONNECTED); 495 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 496 mService.getConnectionState(mDevice)); 497 Assert.assertFalse(mService.getDevices().contains(mDevice)); 498 499 // stack event: CONNECTION_STATE_DISCONNECTED - state machine should not be created 500 generateUnexpectedConnectionMessageFromNative(mDevice, 501 BluetoothProfile.STATE_DISCONNECTED, 502 BluetoothProfile.STATE_DISCONNECTED); 503 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 504 mService.getConnectionState(mDevice)); 505 Assert.assertFalse(mService.getDevices().contains(mDevice)); 506 } 507 508 /** 509 * Test that a CONNECTION_STATE_DISCONNECTED Volume Control stack event will remove the state 510 * machine only if the device is unbond. 511 */ 512 @Test testDeleteStateMachineDisconnectEvents()513 public void testDeleteStateMachineDisconnectEvents() { 514 // Update the device policy so okToConnect() returns true 515 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 516 when(mDatabaseManager 517 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 518 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 519 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 520 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 521 522 // stack event: CONNECTION_STATE_CONNECTING - state machine should be created 523 generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTING, 524 BluetoothProfile.STATE_DISCONNECTED); 525 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 526 mService.getConnectionState(mDevice)); 527 Assert.assertTrue(mService.getDevices().contains(mDevice)); 528 529 // stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed 530 generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_DISCONNECTED, 531 BluetoothProfile.STATE_CONNECTING); 532 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 533 mService.getConnectionState(mDevice)); 534 Assert.assertTrue(mService.getDevices().contains(mDevice)); 535 536 // stack event: CONNECTION_STATE_CONNECTING - state machine remains 537 generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTING, 538 BluetoothProfile.STATE_DISCONNECTED); 539 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 540 mService.getConnectionState(mDevice)); 541 Assert.assertTrue(mService.getDevices().contains(mDevice)); 542 543 // device bond state marked as unbond - state machine is not removed 544 doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService) 545 .getBondState(any(BluetoothDevice.class)); 546 Assert.assertTrue(mService.getDevices().contains(mDevice)); 547 548 // stack event: CONNECTION_STATE_DISCONNECTED - state machine is removed 549 generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_DISCONNECTED, 550 BluetoothProfile.STATE_CONNECTING); 551 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 552 mService.getConnectionState(mDevice)); 553 Assert.assertFalse(mService.getDevices().contains(mDevice)); 554 } 555 556 /** 557 * Test that various Volume Control stack events will broadcast related states. 558 */ 559 @Test testVolumeControlStackEvents()560 public void testVolumeControlStackEvents() { 561 int group_id = -1; 562 int volume = 6; 563 boolean mute = false; 564 565 // Send a message to trigger volume state changed broadcast 566 VolumeControlStackEvent stackEvent = new VolumeControlStackEvent( 567 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 568 stackEvent.device = mDevice; 569 stackEvent.valueInt1 = group_id; 570 stackEvent.valueInt2 = volume; 571 stackEvent.valueBool1 = mute; 572 mService.messageFromNative(stackEvent); 573 } 574 getLeAudioVolume(int index, int minIndex, int maxIndex, int streamType)575 int getLeAudioVolume(int index, int minIndex, int maxIndex, int streamType) { 576 // Note: This has to be the same as mBtHelper.setLeAudioVolume() 577 return (int) Math.round((double) index * BT_LE_AUDIO_MAX_VOL / maxIndex); 578 } 579 testVolumeCalculations(int streamType, int minIdx, int maxIdx)580 void testVolumeCalculations(int streamType, int minIdx, int maxIdx) { 581 // Send a message to trigger volume state changed broadcast 582 final VolumeControlStackEvent stackEvent = new VolumeControlStackEvent( 583 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 584 stackEvent.device = null; 585 stackEvent.valueInt1 = 1; // groupId 586 stackEvent.valueBool1 = false; // isMuted 587 stackEvent.valueBool2 = true; // isAutonomous 588 589 IntStream.range(minIdx, maxIdx).forEach(idx -> { 590 // Given the reference volume index, set the LeAudio Volume 591 stackEvent.valueInt2 = getLeAudioVolume(idx, 592 mAudioManager.getStreamMinVolume(streamType), 593 mAudioManager.getStreamMaxVolume(streamType), streamType); 594 mService.messageFromNative(stackEvent); 595 596 // Verify that setting LeAudio Volume, sets the original volume index to Audio FW 597 verify(mAudioManager, times(1)).setStreamVolume(eq(streamType), eq(idx), anyInt()); 598 }); 599 } 600 601 @Test testAutonomousVolumeStateChange()602 public void testAutonomousVolumeStateChange() { 603 doReturn(AudioManager.MODE_IN_CALL).when(mAudioManager).getMode(); 604 testVolumeCalculations(AudioManager.STREAM_VOICE_CALL, CALL_MIN_VOL, CALL_MAX_VOL); 605 606 doReturn(AudioManager.MODE_NORMAL).when(mAudioManager).getMode(); 607 testVolumeCalculations(AudioManager.STREAM_MUSIC, MEDIA_MIN_VOL, MEDIA_MAX_VOL); 608 } 609 610 /** 611 * Test Volume Control cache. 612 */ 613 @Test testVolumeCache()614 public void testVolumeCache() throws Exception { 615 int groupId = 1; 616 int volume = 6; 617 618 Assert.assertEquals(-1, mService.getGroupVolume(groupId)); 619 final SynchronousResultReceiver<Void> voidRecv = SynchronousResultReceiver.get(); 620 mServiceBinder.setGroupVolume(groupId, volume, mAttributionSource, voidRecv); 621 voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)); 622 623 final SynchronousResultReceiver<Integer> intRecv = SynchronousResultReceiver.get(); 624 int defaultRecvValue = -100; 625 mServiceBinder.getGroupVolume(groupId, mAttributionSource, intRecv); 626 int groupVolume = intRecv.awaitResultNoInterrupt( 627 Duration.ofMillis(TIMEOUT_MS)).getValue(defaultRecvValue); 628 Assert.assertEquals(volume, groupVolume); 629 630 volume = 10; 631 // Send autonomous volume change. 632 VolumeControlStackEvent stackEvent = new VolumeControlStackEvent( 633 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 634 stackEvent.device = null; 635 stackEvent.valueInt1 = groupId; 636 stackEvent.valueInt2 = volume; 637 stackEvent.valueBool1 = false; 638 stackEvent.valueBool2 = true; /* autonomous */ 639 mService.messageFromNative(stackEvent); 640 641 Assert.assertEquals(volume, mService.getGroupVolume(groupId)); 642 } 643 644 /** 645 * Test setting volume for a group member who connects after the volume level 646 * for a group was already changed and cached. 647 */ 648 @Test testLateConnectingDevice()649 public void testLateConnectingDevice() throws Exception { 650 int groupId = 1; 651 int groupVolume = 56; 652 653 // Both devices are in the same group 654 when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); 655 when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); 656 657 // Update the device policy so okToConnect() returns true 658 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 659 when(mDatabaseManager 660 .getProfileConnectionPolicy(any(BluetoothDevice.class), 661 eq(BluetoothProfile.VOLUME_CONTROL))) 662 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 663 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 664 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 665 666 generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTED, 667 BluetoothProfile.STATE_DISCONNECTED); 668 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 669 mService.getConnectionState(mDevice)); 670 Assert.assertTrue(mService.getDevices().contains(mDevice)); 671 672 mService.setGroupVolume(groupId, groupVolume); 673 verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(groupVolume)); 674 verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume)); 675 676 // Verify that second device gets the proper group volume level when connected 677 generateConnectionMessageFromNative(mDeviceTwo, BluetoothProfile.STATE_CONNECTED, 678 BluetoothProfile.STATE_DISCONNECTED); 679 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 680 mService.getConnectionState(mDeviceTwo)); 681 Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); 682 verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume)); 683 } 684 685 /** 686 * Test setting volume for a new group member who is discovered after the volume level 687 * for a group was already changed and cached. 688 */ 689 @Test testLateDiscoveredGroupMember()690 public void testLateDiscoveredGroupMember() throws Exception { 691 int groupId = 1; 692 int groupVolume = 56; 693 694 // For now only one device is in the group 695 when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); 696 when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(-1); 697 698 // Update the device policy so okToConnect() returns true 699 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 700 when(mDatabaseManager 701 .getProfileConnectionPolicy(any(BluetoothDevice.class), 702 eq(BluetoothProfile.VOLUME_CONTROL))) 703 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 704 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 705 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 706 707 generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTED, 708 BluetoothProfile.STATE_DISCONNECTED); 709 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 710 mService.getConnectionState(mDevice)); 711 Assert.assertTrue(mService.getDevices().contains(mDevice)); 712 713 // Set the group volume 714 mService.setGroupVolume(groupId, groupVolume); 715 716 // Verify that second device will not get the group volume level if it is not a group member 717 generateConnectionMessageFromNative(mDeviceTwo, BluetoothProfile.STATE_CONNECTED, 718 BluetoothProfile.STATE_DISCONNECTED); 719 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 720 mService.getConnectionState(mDeviceTwo)); 721 Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); 722 verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume)); 723 724 // But gets the volume when it becomes the group member 725 when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); 726 mService.handleGroupNodeAdded(groupId, mDeviceTwo); 727 verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume)); 728 } 729 730 @Test testServiceBinderGetDevicesMatchingConnectionStates()731 public void testServiceBinderGetDevicesMatchingConnectionStates() throws Exception { 732 final SynchronousResultReceiver<List<BluetoothDevice>> recv = 733 SynchronousResultReceiver.get(); 734 mServiceBinder.getDevicesMatchingConnectionStates(null, mAttributionSource, recv); 735 List<BluetoothDevice> devices = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)) 736 .getValue(null); 737 Assert.assertEquals(0, devices.size()); 738 } 739 740 @Test testServiceBinderSetConnectionPolicy()741 public void testServiceBinderSetConnectionPolicy() throws Exception { 742 final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get(); 743 boolean defaultRecvValue = false; 744 mServiceBinder.setConnectionPolicy( 745 mDevice, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, mAttributionSource, recv); 746 Assert.assertTrue(recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)) 747 .getValue(defaultRecvValue)); 748 verify(mDatabaseManager).setProfileConnectionPolicy( 749 mDevice, BluetoothProfile.VOLUME_CONTROL, BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 750 } 751 752 @Test testServiceBinderVolumeOffsetMethods()753 public void testServiceBinderVolumeOffsetMethods() throws Exception { 754 // Send a message to trigger connection completed 755 VolumeControlStackEvent event = new VolumeControlStackEvent( 756 VolumeControlStackEvent.EVENT_TYPE_DEVICE_AVAILABLE); 757 event.device = mDevice; 758 event.valueInt1 = 2; // number of external outputs 759 mService.messageFromNative(event); 760 761 final SynchronousResultReceiver<Boolean> boolRecv = SynchronousResultReceiver.get(); 762 boolean defaultRecvValue = false; 763 mServiceBinder.isVolumeOffsetAvailable(mDevice, mAttributionSource, boolRecv); 764 Assert.assertTrue(boolRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)) 765 .getValue(defaultRecvValue)); 766 767 int volumeOffset = 100; 768 final SynchronousResultReceiver<Void> voidRecv = SynchronousResultReceiver.get(); 769 mServiceBinder.setVolumeOffset(mDevice, volumeOffset, mAttributionSource, voidRecv); 770 voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)); 771 verify(mNativeInterface).setExtAudioOutVolumeOffset(mDevice, 1, volumeOffset); 772 } 773 774 @Test testServiceBinderRegisterUnregisterCallback()775 public void testServiceBinderRegisterUnregisterCallback() throws Exception { 776 IBluetoothVolumeControlCallback callback = 777 Mockito.mock(IBluetoothVolumeControlCallback.class); 778 Binder binder = Mockito.mock(Binder.class); 779 when(callback.asBinder()).thenReturn(binder); 780 781 int size = mService.mCallbacks.getRegisteredCallbackCount(); 782 SynchronousResultReceiver<Void> recv = SynchronousResultReceiver.get(); 783 mServiceBinder.registerCallback(callback, mAttributionSource, recv); 784 recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(null); 785 Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount()); 786 787 recv = SynchronousResultReceiver.get(); 788 mServiceBinder.unregisterCallback(callback, mAttributionSource, recv); 789 recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(null); 790 Assert.assertEquals(size, mService.mCallbacks.getRegisteredCallbackCount()); 791 } 792 793 @Test testServiceBinderMuteMethods()794 public void testServiceBinderMuteMethods() throws Exception { 795 SynchronousResultReceiver<Void> voidRecv = SynchronousResultReceiver.get(); 796 mServiceBinder.mute(mDevice, mAttributionSource, voidRecv); 797 voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)); 798 verify(mNativeInterface).mute(mDevice); 799 800 voidRecv = SynchronousResultReceiver.get(); 801 mServiceBinder.unmute(mDevice, mAttributionSource, voidRecv); 802 voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)); 803 verify(mNativeInterface).unmute(mDevice); 804 805 int groupId = 1; 806 voidRecv = SynchronousResultReceiver.get(); 807 mServiceBinder.muteGroup(groupId, mAttributionSource, voidRecv); 808 voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)); 809 verify(mNativeInterface).muteGroup(groupId); 810 811 voidRecv = SynchronousResultReceiver.get(); 812 mServiceBinder.unmuteGroup(groupId, mAttributionSource, voidRecv); 813 voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)); 814 verify(mNativeInterface).unmuteGroup(groupId); 815 } 816 817 @Test testVolumeControlOffsetDescriptor()818 public void testVolumeControlOffsetDescriptor() { 819 VolumeControlService.VolumeControlOffsetDescriptor descriptor = 820 new VolumeControlService.VolumeControlOffsetDescriptor(); 821 int invalidId = -1; 822 int validId = 10; 823 int testValue = 100; 824 String testDesc = "testDescription"; 825 int testLocation = 10000; 826 827 Assert.assertEquals(0, descriptor.size()); 828 descriptor.add(validId); 829 Assert.assertEquals(1, descriptor.size()); 830 831 Assert.assertFalse(descriptor.setValue(invalidId, testValue)); 832 Assert.assertTrue(descriptor.setValue(validId, testValue)); 833 Assert.assertEquals(0, descriptor.getValue(invalidId)); 834 Assert.assertEquals(testValue, descriptor.getValue(validId)); 835 836 Assert.assertFalse(descriptor.setDescription(invalidId, testDesc)); 837 Assert.assertTrue(descriptor.setDescription(validId, testDesc)); 838 Assert.assertEquals(null, descriptor.getDescription(invalidId)); 839 Assert.assertEquals(testDesc, descriptor.getDescription(validId)); 840 841 Assert.assertFalse(descriptor.setLocation(invalidId, testLocation)); 842 Assert.assertTrue(descriptor.setLocation(validId, testLocation)); 843 Assert.assertEquals(0, descriptor.getLocation(invalidId)); 844 Assert.assertEquals(testLocation, descriptor.getLocation(validId)); 845 846 StringBuilder sb = new StringBuilder(); 847 descriptor.dump(sb); 848 Assert.assertTrue(sb.toString().contains(testDesc)); 849 850 descriptor.add(validId + 1); 851 Assert.assertEquals(2, descriptor.size()); 852 descriptor.remove(validId); 853 Assert.assertEquals(1, descriptor.size()); 854 descriptor.clear(); 855 Assert.assertEquals(0, descriptor.size()); 856 } 857 858 @Test testDump_doesNotCrash()859 public void testDump_doesNotCrash() throws Exception { 860 connectDevice(mDevice); 861 862 StringBuilder sb = new StringBuilder(); 863 mService.dump(sb); 864 } 865 connectDevice(BluetoothDevice device)866 private void connectDevice(BluetoothDevice device) throws Exception { 867 VolumeControlStackEvent connCompletedEvent; 868 869 List<BluetoothDevice> prevConnectedDevices = mService.getConnectedDevices(); 870 871 // Update the device policy so okToConnect() returns true 872 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 873 when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL)) 874 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 875 doReturn(true).when(mNativeInterface).connectVolumeControl(device); 876 doReturn(true).when(mNativeInterface).disconnectVolumeControl(device); 877 878 // Send a connect request 879 Assert.assertTrue("Connect failed", mService.connect(device)); 880 881 // Verify the connection state broadcast, and that we are in Connecting state 882 verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTING, 883 BluetoothProfile.STATE_DISCONNECTED); 884 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 885 mService.getConnectionState(device)); 886 887 // Send a message to trigger connection completed 888 connCompletedEvent = new VolumeControlStackEvent( 889 VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 890 connCompletedEvent.device = device; 891 connCompletedEvent.valueInt1 = VolumeControlStackEvent.CONNECTION_STATE_CONNECTED; 892 mService.messageFromNative(connCompletedEvent); 893 894 // Verify the connection state broadcast, and that we are in Connected state 895 verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTED, 896 BluetoothProfile.STATE_CONNECTING); 897 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, 898 mService.getConnectionState(device)); 899 900 // Verify that the device is in the list of connected devices 901 final SynchronousResultReceiver<List<BluetoothDevice>> recv = 902 SynchronousResultReceiver.get(); 903 mServiceBinder.getConnectedDevices(mAttributionSource, recv); 904 List<BluetoothDevice> connectedDevices = 905 recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(null); 906 Assert.assertTrue(connectedDevices.contains(device)); 907 // Verify the list of previously connected devices 908 for (BluetoothDevice prevDevice : prevConnectedDevices) { 909 Assert.assertTrue(connectedDevices.contains(prevDevice)); 910 } 911 } 912 generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)913 private void generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, 914 int oldConnectionState) { 915 VolumeControlStackEvent stackEvent = 916 new VolumeControlStackEvent( 917 VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 918 stackEvent.device = device; 919 stackEvent.valueInt1 = newConnectionState; 920 mService.messageFromNative(stackEvent); 921 // Verify the connection state broadcast 922 verifyConnectionStateIntent(TIMEOUT_MS, device, newConnectionState, oldConnectionState); 923 } 924 generateUnexpectedConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)925 private void generateUnexpectedConnectionMessageFromNative(BluetoothDevice device, 926 int newConnectionState, int oldConnectionState) { 927 VolumeControlStackEvent stackEvent = 928 new VolumeControlStackEvent( 929 VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 930 stackEvent.device = device; 931 stackEvent.valueInt1 = newConnectionState; 932 mService.messageFromNative(stackEvent); 933 // Verify the connection state broadcast 934 verifyNoConnectionStateIntent(TIMEOUT_MS, device); 935 } 936 937 /** 938 * Helper function to test okToConnect() method 939 * 940 * @param device test device 941 * @param bondState bond state value, could be invalid 942 * @param policy value, could be invalid 943 * @param expected expected result from okToConnect() 944 */ testOkToConnectCase(BluetoothDevice device, int bondState, int policy, boolean expected)945 private void testOkToConnectCase(BluetoothDevice device, int bondState, int policy, 946 boolean expected) { 947 doReturn(bondState).when(mAdapterService).getBondState(device); 948 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 949 when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL)) 950 .thenReturn(policy); 951 Assert.assertEquals(expected, mService.okToConnect(device)); 952 } 953 } 954