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 import android.platform.test.flag.junit.SetFlagsRule; 38 39 import androidx.test.filters.MediumTest; 40 import androidx.test.platform.app.InstrumentationRegistry; 41 import androidx.test.rule.ServiceTestRule; 42 import androidx.test.runner.AndroidJUnit4; 43 44 import com.android.bluetooth.TestUtils; 45 import com.android.bluetooth.btservice.AdapterService; 46 import com.android.bluetooth.btservice.ServiceFactory; 47 import com.android.bluetooth.btservice.storage.DatabaseManager; 48 import com.android.bluetooth.csip.CsipSetCoordinatorService; 49 import com.android.bluetooth.flags.Flags; 50 import com.android.bluetooth.le_audio.LeAudioService; 51 52 import org.junit.After; 53 import org.junit.Assert; 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.junit.MockitoJUnit; 61 import org.mockito.junit.MockitoRule; 62 63 import java.util.Arrays; 64 import java.util.HashMap; 65 import java.util.List; 66 import java.util.concurrent.LinkedBlockingQueue; 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 @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); 90 91 @Mock private AdapterService mAdapterService; 92 @Mock private LeAudioService mLeAudioService; 93 @Mock private DatabaseManager mDatabaseManager; 94 @Mock private VolumeControlNativeInterface mNativeInterface; 95 @Mock private AudioManager mAudioManager; 96 @Mock private ServiceFactory mServiceFactory; 97 @Mock private CsipSetCoordinatorService mCsipService; 98 99 @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule(); 100 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 101 102 @Before setUp()103 public void setUp() throws Exception { 104 mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 105 106 if (Looper.myLooper() == null) { 107 Looper.prepare(); 108 } 109 110 TestUtils.setAdapterService(mAdapterService); 111 doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); 112 113 mAdapter = BluetoothAdapter.getDefaultAdapter(); 114 mAttributionSource = mAdapter.getAttributionSource(); 115 116 doReturn(MEDIA_MIN_VOL) 117 .when(mAudioManager) 118 .getStreamMinVolume(eq(AudioManager.STREAM_MUSIC)); 119 doReturn(MEDIA_MAX_VOL) 120 .when(mAudioManager) 121 .getStreamMaxVolume(eq(AudioManager.STREAM_MUSIC)); 122 doReturn(CALL_MIN_VOL) 123 .when(mAudioManager) 124 .getStreamMinVolume(eq(AudioManager.STREAM_VOICE_CALL)); 125 doReturn(CALL_MAX_VOL) 126 .when(mAudioManager) 127 .getStreamMaxVolume(eq(AudioManager.STREAM_VOICE_CALL)); 128 129 VolumeControlNativeInterface.setInstance(mNativeInterface); 130 mService = new VolumeControlService(mTargetContext); 131 mService.start(); 132 mService.setAvailable(true); 133 134 mService.mAudioManager = mAudioManager; 135 mService.mFactory = mServiceFactory; 136 mServiceBinder = (VolumeControlService.BluetoothVolumeControlBinder) mService.initBinder(); 137 mServiceBinder.mIsTesting = true; 138 139 doReturn(mCsipService).when(mServiceFactory).getCsipSetCoordinatorService(); 140 doReturn(mLeAudioService).when(mServiceFactory).getLeAudioService(); 141 142 // Override the timeout value to speed up the test 143 VolumeControlStateMachine.sConnectTimeoutMs = TIMEOUT_MS; // 1s 144 145 // Set up the Connection State Changed receiver 146 IntentFilter filter = new IntentFilter(); 147 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 148 filter.addAction(BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED); 149 150 mVolumeControlIntentReceiver = new VolumeControlIntentReceiver(); 151 mTargetContext.registerReceiver(mVolumeControlIntentReceiver, filter); 152 153 // Get a device for testing 154 mDevice = TestUtils.getTestDevice(mAdapter, 0); 155 mDeviceTwo = TestUtils.getTestDevice(mAdapter, 1); 156 mDeviceQueueMap = new HashMap<>(); 157 mDeviceQueueMap.put(mDevice, new LinkedBlockingQueue<>()); 158 mDeviceQueueMap.put(mDeviceTwo, new LinkedBlockingQueue<>()); 159 doReturn(BluetoothDevice.BOND_BONDED) 160 .when(mAdapterService) 161 .getBondState(any(BluetoothDevice.class)); 162 doReturn(new ParcelUuid[] {BluetoothUuid.VOLUME_CONTROL}) 163 .when(mAdapterService) 164 .getRemoteUuids(any(BluetoothDevice.class)); 165 } 166 167 @After tearDown()168 public void tearDown() throws Exception { 169 if (mService == null) { 170 return; 171 } 172 173 mService.stop(); 174 VolumeControlNativeInterface.setInstance(null); 175 mTargetContext.unregisterReceiver(mVolumeControlIntentReceiver); 176 mDeviceQueueMap.clear(); 177 TestUtils.clearAdapterService(mAdapterService); 178 reset(mAudioManager); 179 } 180 181 private class VolumeControlIntentReceiver extends BroadcastReceiver { 182 @Override onReceive(Context context, Intent intent)183 public void onReceive(Context context, Intent intent) { 184 try { 185 BluetoothDevice device = intent.getParcelableExtra(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: " + e.getMessage()); 192 } 193 } 194 } 195 verifyConnectionStateIntent( int timeoutMs, BluetoothDevice device, int newState, int prevState)196 private void verifyConnectionStateIntent( 197 int timeoutMs, BluetoothDevice device, int newState, int prevState) { 198 Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device)); 199 Assert.assertNotNull(intent); 200 Assert.assertEquals( 201 BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED, intent.getAction()); 202 Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); 203 Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); 204 Assert.assertEquals( 205 prevState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1)); 206 } 207 verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device)208 private void verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device) { 209 Intent intent = TestUtils.waitForNoIntent(timeoutMs, mDeviceQueueMap.get(device)); 210 Assert.assertNull(intent); 211 } 212 213 /** Test getting VolumeControl Service: getVolumeControlService() */ 214 @Test testGetVolumeControlService()215 public void testGetVolumeControlService() { 216 Assert.assertEquals(mService, VolumeControlService.getVolumeControlService()); 217 } 218 219 /** Test stop VolumeControl Service */ 220 @Test testStopVolumeControlService()221 public void testStopVolumeControlService() throws Exception { 222 // Prepare: connect 223 connectDevice(mDevice); 224 // VolumeControl Service is already running: test stop(). 225 // Note: must be done on the main thread 226 InstrumentationRegistry.getInstrumentation().runOnMainSync(mService::stop); 227 // Try to restart the service. Note: must be done on the main thread 228 InstrumentationRegistry.getInstrumentation().runOnMainSync(mService::start); 229 } 230 231 /** Test get/set policy for BluetoothDevice */ 232 @Test testGetSetPolicy()233 public void testGetSetPolicy() { 234 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 235 when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 236 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 237 Assert.assertEquals( 238 "Initial device policy", 239 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, 240 mService.getConnectionPolicy(mDevice)); 241 242 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 243 when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 244 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 245 Assert.assertEquals( 246 "Setting device policy to POLICY_FORBIDDEN", 247 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, 248 mService.getConnectionPolicy(mDevice)); 249 250 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 251 when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 252 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 253 Assert.assertEquals( 254 "Setting device policy to POLICY_ALLOWED", 255 BluetoothProfile.CONNECTION_POLICY_ALLOWED, 256 mService.getConnectionPolicy(mDevice)); 257 } 258 259 /** Test if getProfileConnectionPolicy works after the service is stopped. */ 260 @Test testGetPolicyAfterStopped()261 public void testGetPolicyAfterStopped() throws Exception { 262 mService.stop(); 263 when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 264 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 265 int policy = mServiceBinder.getConnectionPolicy(mDevice, mAttributionSource); 266 Assert.assertEquals( 267 "Initial device policy", BluetoothProfile.CONNECTION_POLICY_UNKNOWN, policy); 268 } 269 270 /** Test okToConnect method using various test cases */ 271 @Test testOkToConnect()272 public void testOkToConnect() { 273 int badPolicyValue = 1024; 274 int badBondState = 42; 275 testOkToConnectCase( 276 mDevice, 277 BluetoothDevice.BOND_NONE, 278 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, 279 false); 280 testOkToConnectCase( 281 mDevice, 282 BluetoothDevice.BOND_NONE, 283 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, 284 false); 285 testOkToConnectCase( 286 mDevice, 287 BluetoothDevice.BOND_NONE, 288 BluetoothProfile.CONNECTION_POLICY_ALLOWED, 289 false); 290 testOkToConnectCase(mDevice, BluetoothDevice.BOND_NONE, badPolicyValue, false); 291 testOkToConnectCase( 292 mDevice, 293 BluetoothDevice.BOND_BONDING, 294 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, 295 false); 296 testOkToConnectCase( 297 mDevice, 298 BluetoothDevice.BOND_BONDING, 299 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, 300 false); 301 testOkToConnectCase( 302 mDevice, 303 BluetoothDevice.BOND_BONDING, 304 BluetoothProfile.CONNECTION_POLICY_ALLOWED, 305 false); 306 testOkToConnectCase(mDevice, BluetoothDevice.BOND_BONDING, badPolicyValue, false); 307 testOkToConnectCase( 308 mDevice, 309 BluetoothDevice.BOND_BONDED, 310 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, 311 true); 312 testOkToConnectCase( 313 mDevice, 314 BluetoothDevice.BOND_BONDED, 315 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, 316 false); 317 testOkToConnectCase( 318 mDevice, 319 BluetoothDevice.BOND_BONDED, 320 BluetoothProfile.CONNECTION_POLICY_ALLOWED, 321 true); 322 testOkToConnectCase(mDevice, BluetoothDevice.BOND_BONDED, badPolicyValue, false); 323 testOkToConnectCase( 324 mDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 325 testOkToConnectCase( 326 mDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 327 testOkToConnectCase( 328 mDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 329 testOkToConnectCase(mDevice, badBondState, badPolicyValue, false); 330 } 331 332 /** 333 * Test that an outgoing connection to device that does not have Volume Control UUID is rejected 334 */ 335 @Test testOutgoingConnectMissingVolumeControlUuid()336 public void testOutgoingConnectMissingVolumeControlUuid() { 337 // Update the device policy so okToConnect() returns true 338 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 339 when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 340 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 341 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 342 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 343 344 // Return No UUID 345 doReturn(new ParcelUuid[] {}) 346 .when(mAdapterService) 347 .getRemoteUuids(any(BluetoothDevice.class)); 348 349 // Send a connect request 350 Assert.assertFalse("Connect expected to fail", mService.connect(mDevice)); 351 } 352 353 /** Test that an outgoing connection to device that have Volume Control UUID is successful */ 354 @Test testOutgoingConnectDisconnectExistingVolumeControlUuid()355 public void testOutgoingConnectDisconnectExistingVolumeControlUuid() throws Exception { 356 // Update the device policy so okToConnect() returns true 357 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 358 when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 359 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 360 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 361 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 362 363 // Return Volume Control UUID 364 doReturn(new ParcelUuid[] {BluetoothUuid.VOLUME_CONTROL}) 365 .when(mAdapterService) 366 .getRemoteUuids(any(BluetoothDevice.class)); 367 368 // Send a connect request via binder 369 Assert.assertTrue( 370 "Connect expected to succeed", mServiceBinder.connect(mDevice, mAttributionSource)); 371 372 // Verify the connection state broadcast, and that we are in Connecting state 373 verifyConnectionStateIntent( 374 TIMEOUT_MS, 375 mDevice, 376 BluetoothProfile.STATE_CONNECTING, 377 BluetoothProfile.STATE_DISCONNECTED); 378 379 // Send a disconnect request via binder 380 Assert.assertTrue( 381 "Disconnect expected to succeed", 382 mServiceBinder.disconnect(mDevice, mAttributionSource)); 383 384 // Verify the connection state broadcast, and that we are in Connecting state 385 verifyConnectionStateIntent( 386 TIMEOUT_MS, 387 mDevice, 388 BluetoothProfile.STATE_DISCONNECTED, 389 BluetoothProfile.STATE_CONNECTING); 390 } 391 392 /** Test that an outgoing connection to device with POLICY_FORBIDDEN is rejected */ 393 @Test testOutgoingConnectPolicyForbidden()394 public void testOutgoingConnectPolicyForbidden() { 395 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 396 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 397 398 // Set the device policy to POLICY_FORBIDDEN so connect() should fail 399 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 400 when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 401 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 402 403 // Send a connect request 404 Assert.assertFalse("Connect expected to fail", mService.connect(mDevice)); 405 } 406 407 /** Test that an outgoing connection times out */ 408 @Test testOutgoingConnectTimeout()409 public void testOutgoingConnectTimeout() throws Exception { 410 // Update the device policy so okToConnect() returns true 411 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 412 when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 413 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 414 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 415 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 416 417 // Send a connect request 418 Assert.assertTrue("Connect failed", mService.connect(mDevice)); 419 420 // Verify the connection state broadcast, and that we are in Connecting state 421 verifyConnectionStateIntent( 422 TIMEOUT_MS, 423 mDevice, 424 BluetoothProfile.STATE_CONNECTING, 425 BluetoothProfile.STATE_DISCONNECTED); 426 Assert.assertEquals( 427 BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(mDevice)); 428 429 // Verify the connection state broadcast, and that we are in Disconnected state 430 verifyConnectionStateIntent( 431 VolumeControlStateMachine.sConnectTimeoutMs * 2, 432 mDevice, 433 BluetoothProfile.STATE_DISCONNECTED, 434 BluetoothProfile.STATE_CONNECTING); 435 436 int state = mServiceBinder.getConnectionState(mDevice, mAttributionSource); 437 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, state); 438 } 439 440 /** 441 * Test that only CONNECTION_STATE_CONNECTED or CONNECTION_STATE_CONNECTING Volume Control stack 442 * events will create a state machine. 443 */ 444 @Test testCreateStateMachineStackEvents()445 public void testCreateStateMachineStackEvents() { 446 // Update the device policy so okToConnect() returns true 447 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 448 when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 449 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 450 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 451 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 452 453 // stack event: CONNECTION_STATE_CONNECTING - state machine should be created 454 generateConnectionMessageFromNative( 455 mDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); 456 Assert.assertEquals( 457 BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(mDevice)); 458 Assert.assertTrue(mService.getDevices().contains(mDevice)); 459 460 // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed 461 generateConnectionMessageFromNative( 462 mDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); 463 Assert.assertEquals( 464 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice)); 465 Assert.assertTrue(mService.getDevices().contains(mDevice)); 466 mService.bondStateChanged(mDevice, BluetoothDevice.BOND_NONE); 467 Assert.assertFalse(mService.getDevices().contains(mDevice)); 468 469 // stack event: CONNECTION_STATE_CONNECTED - state machine should be created 470 generateConnectionMessageFromNative( 471 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 472 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice)); 473 Assert.assertTrue(mService.getDevices().contains(mDevice)); 474 475 // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed 476 generateConnectionMessageFromNative( 477 mDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED); 478 Assert.assertEquals( 479 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice)); 480 Assert.assertTrue(mService.getDevices().contains(mDevice)); 481 mService.bondStateChanged(mDevice, BluetoothDevice.BOND_NONE); 482 Assert.assertFalse(mService.getDevices().contains(mDevice)); 483 484 // stack event: CONNECTION_STATE_DISCONNECTING - state machine should not be created 485 generateUnexpectedConnectionMessageFromNative( 486 mDevice, BluetoothProfile.STATE_DISCONNECTING); 487 Assert.assertEquals( 488 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice)); 489 Assert.assertFalse(mService.getDevices().contains(mDevice)); 490 491 // stack event: CONNECTION_STATE_DISCONNECTED - state machine should not be created 492 generateUnexpectedConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_DISCONNECTED); 493 Assert.assertEquals( 494 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice)); 495 Assert.assertFalse(mService.getDevices().contains(mDevice)); 496 } 497 498 /** 499 * Test that a CONNECTION_STATE_DISCONNECTED Volume Control stack event will remove the state 500 * machine only if the device is unbond. 501 */ 502 @Test testDeleteStateMachineDisconnectEvents()503 public void testDeleteStateMachineDisconnectEvents() { 504 // Update the device policy so okToConnect() returns true 505 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 506 when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL)) 507 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 508 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 509 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 510 511 // stack event: CONNECTION_STATE_CONNECTING - state machine should be created 512 generateConnectionMessageFromNative( 513 mDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); 514 Assert.assertEquals( 515 BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(mDevice)); 516 Assert.assertTrue(mService.getDevices().contains(mDevice)); 517 518 // stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed 519 generateConnectionMessageFromNative( 520 mDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); 521 Assert.assertEquals( 522 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice)); 523 Assert.assertTrue(mService.getDevices().contains(mDevice)); 524 525 // stack event: CONNECTION_STATE_CONNECTING - state machine remains 526 generateConnectionMessageFromNative( 527 mDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); 528 Assert.assertEquals( 529 BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(mDevice)); 530 Assert.assertTrue(mService.getDevices().contains(mDevice)); 531 532 // device bond state marked as unbond - state machine is not removed 533 doReturn(BluetoothDevice.BOND_NONE) 534 .when(mAdapterService) 535 .getBondState(any(BluetoothDevice.class)); 536 Assert.assertTrue(mService.getDevices().contains(mDevice)); 537 538 // stack event: CONNECTION_STATE_DISCONNECTED - state machine is removed 539 generateConnectionMessageFromNative( 540 mDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); 541 Assert.assertEquals( 542 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice)); 543 Assert.assertFalse(mService.getDevices().contains(mDevice)); 544 } 545 546 /** Test that various Volume Control stack events will broadcast related states. */ 547 @Test testVolumeControlStackEvents()548 public void testVolumeControlStackEvents() { 549 int group_id = -1; 550 int volume = 6; 551 boolean mute = false; 552 553 // Send a message to trigger volume state changed broadcast 554 VolumeControlStackEvent stackEvent = 555 new VolumeControlStackEvent( 556 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 557 stackEvent.device = mDevice; 558 stackEvent.valueInt1 = group_id; 559 stackEvent.valueInt2 = volume; 560 stackEvent.valueBool1 = mute; 561 mService.messageFromNative(stackEvent); 562 } 563 getLeAudioVolume(int index, int minIndex, int maxIndex, int streamType)564 int getLeAudioVolume(int index, int minIndex, int maxIndex, int streamType) { 565 // Note: This has to be the same as mBtHelper.setLeAudioVolume() 566 return (int) Math.round((double) index * BT_LE_AUDIO_MAX_VOL / maxIndex); 567 } 568 testVolumeCalculations(int streamType, int minIdx, int maxIdx)569 void testVolumeCalculations(int streamType, int minIdx, int maxIdx) { 570 // Send a message to trigger volume state changed broadcast 571 final VolumeControlStackEvent stackEvent = 572 new VolumeControlStackEvent( 573 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 574 stackEvent.device = null; 575 stackEvent.valueInt1 = 1; // groupId 576 stackEvent.valueBool1 = false; // isMuted 577 stackEvent.valueBool2 = true; // isAutonomous 578 579 IntStream.range(minIdx, maxIdx) 580 .forEach( 581 idx -> { 582 // Given the reference volume index, set the LeAudio Volume 583 stackEvent.valueInt2 = 584 getLeAudioVolume( 585 idx, 586 mAudioManager.getStreamMinVolume(streamType), 587 mAudioManager.getStreamMaxVolume(streamType), 588 streamType); 589 mService.messageFromNative(stackEvent); 590 591 // Verify that setting LeAudio Volume, sets the original volume index to 592 // Audio FW 593 verify(mAudioManager, times(1)) 594 .setStreamVolume(eq(streamType), eq(idx), anyInt()); 595 }); 596 } 597 598 @Test testAutonomousVolumeStateChange()599 public void testAutonomousVolumeStateChange() { 600 // TODO: b/329163385 - This test should be modified to run without having to set the flag to 601 // a specific value 602 mSetFlagsRule.disableFlags( 603 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES); 604 doReturn(AudioManager.MODE_IN_CALL).when(mAudioManager).getMode(); 605 testVolumeCalculations(AudioManager.STREAM_VOICE_CALL, CALL_MIN_VOL, CALL_MAX_VOL); 606 607 doReturn(AudioManager.MODE_NORMAL).when(mAudioManager).getMode(); 608 testVolumeCalculations(AudioManager.STREAM_MUSIC, MEDIA_MIN_VOL, MEDIA_MAX_VOL); 609 } 610 611 /** Test if autonomous Mute/Unmute propagates the event to audio manager. */ 612 @Test testAutonomousMuteUnmute()613 public void testAutonomousMuteUnmute() { 614 // TODO: b/329163385 - This test should be modified to run without having to set the flag to 615 // a specific value 616 mSetFlagsRule.disableFlags( 617 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES); 618 int streamType = AudioManager.STREAM_MUSIC; 619 int streamVol = getLeAudioVolume(19, MEDIA_MIN_VOL, MEDIA_MAX_VOL, streamType); 620 621 // Send a message to trigger volume state changed broadcast 622 final VolumeControlStackEvent stackEvent = 623 new VolumeControlStackEvent( 624 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 625 stackEvent.device = null; 626 stackEvent.valueInt1 = 1; // groupId 627 stackEvent.valueInt2 = streamVol; 628 stackEvent.valueBool1 = false; // isMuted 629 stackEvent.valueBool2 = true; // isAutonomous 630 631 doReturn(false).when(mAudioManager).isStreamMute(eq(AudioManager.STREAM_MUSIC)); 632 633 // Verify that muting LeAudio device, sets the mute state on the audio device 634 stackEvent.valueBool1 = true; 635 mService.messageFromNative(stackEvent); 636 verify(mAudioManager, times(1)) 637 .adjustStreamVolume(eq(streamType), eq(AudioManager.ADJUST_MUTE), anyInt()); 638 639 doReturn(true).when(mAudioManager).isStreamMute(eq(AudioManager.STREAM_MUSIC)); 640 641 // Verify that unmuting LeAudio device, unsets the mute state on the audio device 642 stackEvent.valueBool1 = false; 643 mService.messageFromNative(stackEvent); 644 verify(mAudioManager, times(1)) 645 .adjustStreamVolume(eq(streamType), eq(AudioManager.ADJUST_UNMUTE), anyInt()); 646 } 647 648 /** Test Volume Control cache. */ 649 @Test testVolumeCache()650 public void testVolumeCache() throws Exception { 651 int groupId = 1; 652 int volume = 6; 653 654 Assert.assertEquals(-1, mService.getGroupVolume(groupId)); 655 mServiceBinder.setGroupVolume(groupId, volume, mAttributionSource); 656 657 int groupVolume = mServiceBinder.getGroupVolume(groupId, mAttributionSource); 658 Assert.assertEquals(volume, groupVolume); 659 660 volume = 10; 661 // Send autonomous volume change. 662 VolumeControlStackEvent stackEvent = 663 new VolumeControlStackEvent( 664 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 665 stackEvent.device = null; 666 stackEvent.valueInt1 = groupId; 667 stackEvent.valueInt2 = volume; 668 stackEvent.valueBool1 = false; 669 stackEvent.valueBool2 = true; /* autonomous */ 670 mService.messageFromNative(stackEvent); 671 672 Assert.assertEquals(volume, mService.getGroupVolume(groupId)); 673 } 674 675 /** Test Active Group change */ 676 @Test testActiveGroupChange()677 public void testActiveGroupChange() throws Exception { 678 // TODO: b/329163385 - This test should be modified to run without having to set the flag to 679 // a specific value 680 mSetFlagsRule.disableFlags( 681 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES); 682 int groupId_1 = 1; 683 int volume_groupId_1 = 6; 684 685 int groupId_2 = 2; 686 int volume_groupId_2 = 20; 687 688 Assert.assertEquals(-1, mService.getGroupVolume(groupId_1)); 689 Assert.assertEquals(-1, mService.getGroupVolume(groupId_2)); 690 mServiceBinder.setGroupVolume(groupId_1, volume_groupId_1, mAttributionSource); 691 692 mServiceBinder.setGroupVolume(groupId_2, volume_groupId_2, mAttributionSource); 693 694 mServiceBinder.setGroupActive(groupId_1, true, mAttributionSource); 695 696 // Expected index for STREAM_MUSIC 697 int expectedVol = 698 (int) Math.round((double) (volume_groupId_1 * MEDIA_MAX_VOL) / BT_LE_AUDIO_MAX_VOL); 699 verify(mAudioManager, times(1)).setStreamVolume(anyInt(), eq(expectedVol), anyInt()); 700 701 mServiceBinder.setGroupActive(groupId_2, true, mAttributionSource); 702 703 expectedVol = 704 (int) Math.round((double) (volume_groupId_2 * MEDIA_MAX_VOL) / BT_LE_AUDIO_MAX_VOL); 705 verify(mAudioManager, times(1)).setStreamVolume(anyInt(), eq(expectedVol), anyInt()); 706 } 707 708 /** Test Volume Control Mute cache. */ 709 @Test testMuteCache()710 public void testMuteCache() throws Exception { 711 int groupId = 1; 712 int volume = 6; 713 714 Assert.assertEquals(false, mService.getGroupMute(groupId)); 715 716 // Send autonomous volume change 717 VolumeControlStackEvent stackEvent = 718 new VolumeControlStackEvent( 719 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 720 stackEvent.device = null; 721 stackEvent.valueInt1 = groupId; 722 stackEvent.valueInt2 = volume; 723 stackEvent.valueBool1 = false; /* unmuted */ 724 stackEvent.valueBool2 = true; /* autonomous */ 725 mService.messageFromNative(stackEvent); 726 727 // Mute 728 mServiceBinder.muteGroup(groupId, mAttributionSource); 729 Assert.assertEquals(true, mService.getGroupMute(groupId)); 730 731 // Make sure the volume is kept even when muted 732 Assert.assertEquals(volume, mService.getGroupVolume(groupId)); 733 734 // Send autonomous unmute 735 stackEvent.valueBool1 = false; /* unmuted */ 736 mService.messageFromNative(stackEvent); 737 738 Assert.assertEquals(false, mService.getGroupMute(groupId)); 739 } 740 741 /** Test Volume Control with muted stream. */ 742 @Test testVolumeChangeWhileMuted()743 public void testVolumeChangeWhileMuted() throws Exception { 744 int groupId = 1; 745 int volume = 6; 746 747 Assert.assertEquals(false, mService.getGroupMute(groupId)); 748 749 // Set the initial volume state 750 VolumeControlStackEvent stackEvent = 751 new VolumeControlStackEvent( 752 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 753 stackEvent.device = null; 754 stackEvent.valueInt1 = groupId; 755 stackEvent.valueInt2 = volume; 756 stackEvent.valueBool1 = false; /* unmuted */ 757 stackEvent.valueBool2 = true; /* autonomous */ 758 mService.messageFromNative(stackEvent); 759 760 // Mute 761 mService.muteGroup(groupId); 762 Assert.assertEquals(true, mService.getGroupMute(groupId)); 763 verify(mNativeInterface, times(1)).muteGroup(eq(groupId)); 764 765 // Make sure the volume is kept even when muted 766 doReturn(true).when(mAudioManager).isStreamMute(eq(AudioManager.STREAM_MUSIC)); 767 Assert.assertEquals(volume, mService.getGroupVolume(groupId)); 768 769 // Lower the volume and keep it mute 770 mService.setGroupVolume(groupId, --volume); 771 Assert.assertEquals(true, mService.getGroupMute(groupId)); 772 verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(volume)); 773 verify(mNativeInterface, times(0)).unmuteGroup(eq(groupId)); 774 775 // Don't unmute on consecutive calls either 776 mService.setGroupVolume(groupId, --volume); 777 Assert.assertEquals(true, mService.getGroupMute(groupId)); 778 verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(volume)); 779 verify(mNativeInterface, times(0)).unmuteGroup(eq(groupId)); 780 781 // Raise the volume and unmute 782 volume += 10; // avoid previous volume levels and simplify mock verification 783 doReturn(false).when(mAudioManager).isStreamMute(eq(AudioManager.STREAM_MUSIC)); 784 mService.setGroupVolume(groupId, ++volume); 785 Assert.assertEquals(false, mService.getGroupMute(groupId)); 786 verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(volume)); 787 // Verify the number of unmute calls after the second volume change 788 mService.setGroupVolume(groupId, ++volume); 789 Assert.assertEquals(false, mService.getGroupMute(groupId)); 790 verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(volume)); 791 // Make sure we unmuted only once 792 verify(mNativeInterface, times(1)).unmuteGroup(eq(groupId)); 793 } 794 795 /** 796 * Test setting volume for a group member who connects after the volume level for a group was 797 * already changed and cached. 798 */ 799 @Test testLateConnectingDevice()800 public void testLateConnectingDevice() throws Exception { 801 int groupId = 1; 802 int groupVolume = 56; 803 804 // Both devices are in the same group 805 when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); 806 when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); 807 808 // Update the device policy so okToConnect() returns true 809 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 810 when(mDatabaseManager.getProfileConnectionPolicy( 811 any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL))) 812 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 813 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 814 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 815 816 generateConnectionMessageFromNative( 817 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 818 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice)); 819 Assert.assertTrue(mService.getDevices().contains(mDevice)); 820 821 mService.setGroupVolume(groupId, groupVolume); 822 verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(groupVolume)); 823 verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume)); 824 825 // Verify that second device gets the proper group volume level when connected 826 generateConnectionMessageFromNative( 827 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 828 Assert.assertEquals( 829 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo)); 830 Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); 831 verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume)); 832 } 833 834 /** 835 * Test setting volume for a new group member who is discovered after the volume level for a 836 * group was already changed and cached. 837 */ 838 @Test testLateDiscoveredGroupMember()839 public void testLateDiscoveredGroupMember() throws Exception { 840 int groupId = 1; 841 int groupVolume = 56; 842 843 // For now only one device is in the group 844 when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); 845 when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(-1); 846 847 // Update the device policy so okToConnect() returns true 848 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 849 when(mDatabaseManager.getProfileConnectionPolicy( 850 any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL))) 851 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 852 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 853 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 854 855 generateConnectionMessageFromNative( 856 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 857 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice)); 858 Assert.assertTrue(mService.getDevices().contains(mDevice)); 859 860 // Set the group volume 861 mService.setGroupVolume(groupId, groupVolume); 862 863 // Verify that second device will not get the group volume level if it is not a group member 864 generateConnectionMessageFromNative( 865 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 866 Assert.assertEquals( 867 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo)); 868 Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); 869 verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume)); 870 871 // But gets the volume when it becomes the group member 872 when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); 873 mService.handleGroupNodeAdded(groupId, mDeviceTwo); 874 verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume)); 875 } 876 877 /** 878 * Test setting volume to 0 for a group member who connects after the volume level for a group 879 * was already changed and cached. LeAudio has no knowledge of mute for anything else than 880 * telephony, thus setting volume level to 0 is considered as muting. 881 */ 882 @Test testMuteLateConnectingDevice()883 public void testMuteLateConnectingDevice() throws Exception { 884 int groupId = 1; 885 int volume = 100; 886 887 // Both devices are in the same group 888 when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); 889 when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); 890 891 // Update the device policy so okToConnect() returns true 892 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 893 when(mDatabaseManager.getProfileConnectionPolicy( 894 any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL))) 895 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 896 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 897 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 898 899 generateConnectionMessageFromNative( 900 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 901 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice)); 902 Assert.assertTrue(mService.getDevices().contains(mDevice)); 903 904 // Set the initial volume and mute conditions 905 doReturn(true).when(mAudioManager).isStreamMute(anyInt()); 906 mService.setGroupVolume(groupId, volume); 907 908 verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(volume)); 909 verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(volume)); 910 // Check if it was muted 911 verify(mNativeInterface, times(1)).muteGroup(eq(groupId)); 912 913 Assert.assertEquals(true, mService.getGroupMute(groupId)); 914 915 // Verify that second device gets the proper group volume level when connected 916 generateConnectionMessageFromNative( 917 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 918 Assert.assertEquals( 919 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo)); 920 Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); 921 verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(volume)); 922 // Check if new device was muted 923 verify(mNativeInterface, times(1)).mute(eq(mDeviceTwo)); 924 } 925 926 /** 927 * Test setting volume to 0 for a new group member who is discovered after the volume level for 928 * a group was already changed and cached. LeAudio has no knowledge of mute for anything else 929 * than telephony, thus setting volume level to 0 is considered as muting. 930 */ 931 @Test testMuteLateDiscoveredGroupMember()932 public void testMuteLateDiscoveredGroupMember() throws Exception { 933 int groupId = 1; 934 int volume = 100; 935 936 // For now only one device is in the group 937 when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); 938 when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(-1); 939 940 // Update the device policy so okToConnect() returns true 941 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 942 when(mDatabaseManager.getProfileConnectionPolicy( 943 any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL))) 944 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 945 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 946 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 947 948 generateConnectionMessageFromNative( 949 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 950 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice)); 951 Assert.assertTrue(mService.getDevices().contains(mDevice)); 952 953 // Set the initial volume and mute conditions 954 doReturn(true).when(mAudioManager).isStreamMute(anyInt()); 955 mService.setGroupVolume(groupId, volume); 956 957 // Verify that second device will not get the group volume level if it is not a group member 958 generateConnectionMessageFromNative( 959 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 960 Assert.assertEquals( 961 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo)); 962 Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); 963 verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(volume)); 964 // Check if it was not muted 965 verify(mNativeInterface, times(0)).mute(eq(mDeviceTwo)); 966 967 // But gets the volume when it becomes the group member 968 when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); 969 mService.handleGroupNodeAdded(groupId, mDeviceTwo); 970 verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(volume)); 971 verify(mNativeInterface, times(1)).mute(eq(mDeviceTwo)); 972 } 973 974 @Test testServiceBinderGetDevicesMatchingConnectionStates()975 public void testServiceBinderGetDevicesMatchingConnectionStates() throws Exception { 976 List<BluetoothDevice> devices = 977 mServiceBinder.getDevicesMatchingConnectionStates(null, mAttributionSource); 978 Assert.assertEquals(0, devices.size()); 979 } 980 981 @Test testServiceBinderSetConnectionPolicy()982 public void testServiceBinderSetConnectionPolicy() throws Exception { 983 Assert.assertTrue( 984 mServiceBinder.setConnectionPolicy( 985 mDevice, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, mAttributionSource)); 986 verify(mDatabaseManager) 987 .setProfileConnectionPolicy( 988 mDevice, 989 BluetoothProfile.VOLUME_CONTROL, 990 BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 991 } 992 993 @Test testServiceBinderVolumeOffsetMethods()994 public void testServiceBinderVolumeOffsetMethods() throws Exception { 995 // Send a message to trigger connection completed 996 generateDeviceAvailableMessageFromNative(mDevice, 2); 997 998 Assert.assertTrue(mServiceBinder.isVolumeOffsetAvailable(mDevice, mAttributionSource)); 999 1000 int numberOfInstances = 1001 mServiceBinder.getNumberOfVolumeOffsetInstances(mDevice, mAttributionSource); 1002 Assert.assertEquals(2, numberOfInstances); 1003 1004 int id = 1; 1005 int volumeOffset = 100; 1006 mServiceBinder.setVolumeOffset(mDevice, id, volumeOffset, mAttributionSource); 1007 verify(mNativeInterface).setExtAudioOutVolumeOffset(mDevice, id, volumeOffset); 1008 } 1009 1010 @Test testServiceBinderSetDeviceVolumeMethods()1011 public void testServiceBinderSetDeviceVolumeMethods() throws Exception { 1012 mSetFlagsRule.enableFlags( 1013 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES); 1014 1015 int groupId = 1; 1016 int groupVolume = 56; 1017 int deviceOneVolume = 46; 1018 int deviceTwoVolume = 36; 1019 1020 // Both devices are in the same group 1021 when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId); 1022 when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId); 1023 1024 // Update the device policy so okToConnect() returns true 1025 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 1026 when(mDatabaseManager.getProfileConnectionPolicy( 1027 any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL))) 1028 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 1029 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 1030 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 1031 1032 generateDeviceAvailableMessageFromNative(mDevice, 1); 1033 generateConnectionMessageFromNative( 1034 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 1035 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice)); 1036 Assert.assertTrue(mService.getDevices().contains(mDevice)); 1037 1038 mServiceBinder.setDeviceVolume(mDevice, groupVolume, true, mAttributionSource); 1039 verify(mNativeInterface).setGroupVolume(groupId, groupVolume); 1040 Assert.assertEquals(groupVolume, mService.getGroupVolume(groupId)); 1041 1042 mServiceBinder.setDeviceVolume(mDevice, deviceOneVolume, false, mAttributionSource); 1043 verify(mNativeInterface).setVolume(mDevice, deviceOneVolume); 1044 Assert.assertEquals(deviceOneVolume, mService.getDeviceVolume(mDevice)); 1045 Assert.assertNotEquals(deviceOneVolume, mService.getDeviceVolume(mDeviceTwo)); 1046 1047 mServiceBinder.setDeviceVolume(mDeviceTwo, deviceTwoVolume, false, mAttributionSource); 1048 verify(mNativeInterface).setVolume(mDeviceTwo, deviceTwoVolume); 1049 Assert.assertEquals(deviceTwoVolume, mService.getDeviceVolume(mDeviceTwo)); 1050 Assert.assertNotEquals(deviceTwoVolume, mService.getDeviceVolume(mDevice)); 1051 } 1052 1053 @Test testServiceBinderRegisterUnregisterCallback()1054 public void testServiceBinderRegisterUnregisterCallback() throws Exception { 1055 IBluetoothVolumeControlCallback callback = 1056 Mockito.mock(IBluetoothVolumeControlCallback.class); 1057 Binder binder = Mockito.mock(Binder.class); 1058 when(callback.asBinder()).thenReturn(binder); 1059 1060 int size = mService.mCallbacks.getRegisteredCallbackCount(); 1061 mServiceBinder.registerCallback(callback, mAttributionSource); 1062 Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount()); 1063 1064 mServiceBinder.unregisterCallback(callback, mAttributionSource); 1065 Assert.assertEquals(size, mService.mCallbacks.getRegisteredCallbackCount()); 1066 } 1067 1068 @Test testServiceBinderRegisterCallbackWhenDeviceAlreadyConnected()1069 public void testServiceBinderRegisterCallbackWhenDeviceAlreadyConnected() throws Exception { 1070 mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_MULTIPLE_VOCS_INSTANCES_API); 1071 1072 int groupId = 1; 1073 int groupVolume = 56; 1074 1075 // Both devices are in the same group 1076 when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId); 1077 when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId); 1078 1079 // Update the device policy so okToConnect() returns true 1080 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 1081 when(mDatabaseManager.getProfileConnectionPolicy( 1082 any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL))) 1083 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 1084 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 1085 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 1086 1087 generateDeviceAvailableMessageFromNative(mDevice, 2); 1088 generateConnectionMessageFromNative( 1089 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 1090 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice)); 1091 Assert.assertTrue(mService.getDevices().contains(mDevice)); 1092 1093 mService.setGroupVolume(groupId, groupVolume); 1094 verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(groupVolume)); 1095 verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume)); 1096 1097 // Verify that second device gets the proper group volume level when connected 1098 generateDeviceAvailableMessageFromNative(mDeviceTwo, 1); 1099 generateConnectionMessageFromNative( 1100 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 1101 Assert.assertEquals( 1102 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo)); 1103 Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); 1104 verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume)); 1105 1106 // Generate events for both devices 1107 generateDeviceOffsetChangedMessageFromNative(mDevice, 1, 100); 1108 generateDeviceLocationChangedMessageFromNative(mDevice, 1, 1); 1109 final String testDevice1Desc1 = "testDevice1Desc1"; 1110 generateDeviceDescriptionChangedMessageFromNative(mDevice, 1, testDevice1Desc1); 1111 1112 generateDeviceOffsetChangedMessageFromNative(mDevice, 2, 200); 1113 generateDeviceLocationChangedMessageFromNative(mDevice, 2, 2); 1114 final String testDevice1Desc2 = "testDevice1Desc2"; 1115 generateDeviceDescriptionChangedMessageFromNative(mDevice, 2, testDevice1Desc2); 1116 1117 generateDeviceOffsetChangedMessageFromNative(mDeviceTwo, 1, 250); 1118 generateDeviceLocationChangedMessageFromNative(mDeviceTwo, 1, 3); 1119 final String testDevice2Desc = "testDevice2Desc"; 1120 generateDeviceDescriptionChangedMessageFromNative(mDeviceTwo, 1, testDevice2Desc); 1121 1122 // Register callback and verify it is called with known devices 1123 IBluetoothVolumeControlCallback callback = 1124 Mockito.mock(IBluetoothVolumeControlCallback.class); 1125 Binder binder = Mockito.mock(Binder.class); 1126 when(callback.asBinder()).thenReturn(binder); 1127 1128 int size = mService.mCallbacks.getRegisteredCallbackCount(); 1129 mServiceBinder.registerCallback(callback, mAttributionSource); 1130 Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount()); 1131 1132 verify(callback).onVolumeOffsetChanged(eq(mDevice), eq(1), eq(100)); 1133 verify(callback).onVolumeOffsetAudioLocationChanged(eq(mDevice), eq(1), eq(1)); 1134 verify(callback) 1135 .onVolumeOffsetAudioDescriptionChanged(eq(mDevice), eq(1), eq(testDevice1Desc1)); 1136 1137 verify(callback).onVolumeOffsetChanged(eq(mDevice), eq(2), eq(200)); 1138 verify(callback).onVolumeOffsetAudioLocationChanged(eq(mDevice), eq(2), eq(2)); 1139 verify(callback) 1140 .onVolumeOffsetAudioDescriptionChanged(eq(mDevice), eq(2), eq(testDevice1Desc2)); 1141 1142 verify(callback).onVolumeOffsetChanged(eq(mDeviceTwo), eq(1), eq(250)); 1143 verify(callback).onVolumeOffsetAudioLocationChanged(eq(mDeviceTwo), eq(1), eq(3)); 1144 verify(callback) 1145 .onVolumeOffsetAudioDescriptionChanged(eq(mDeviceTwo), eq(1), eq(testDevice2Desc)); 1146 1147 generateDeviceOffsetChangedMessageFromNative(mDevice, 1, 50); 1148 generateDeviceLocationChangedMessageFromNative(mDevice, 1, 0); 1149 final String testDevice1Desc3 = "testDevice1Desc3"; 1150 generateDeviceDescriptionChangedMessageFromNative(mDevice, 1, testDevice1Desc3); 1151 1152 verify(callback).onVolumeOffsetChanged(eq(mDevice), eq(1), eq(50)); 1153 verify(callback).onVolumeOffsetAudioLocationChanged(eq(mDevice), eq(1), eq(0)); 1154 verify(callback) 1155 .onVolumeOffsetAudioDescriptionChanged(eq(mDevice), eq(1), eq(testDevice1Desc3)); 1156 } 1157 1158 @Test testServiceBinderRegisterVolumeChangedCallbackWhenDeviceAlreadyConnected()1159 public void testServiceBinderRegisterVolumeChangedCallbackWhenDeviceAlreadyConnected() 1160 throws Exception { 1161 mSetFlagsRule.enableFlags( 1162 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES); 1163 int groupId = 1; 1164 int deviceOneVolume = 46; 1165 int deviceTwoVolume = 36; 1166 1167 // Update the device policy so okToConnect() returns true 1168 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 1169 when(mDatabaseManager.getProfileConnectionPolicy( 1170 any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL))) 1171 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 1172 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 1173 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 1174 1175 generateDeviceAvailableMessageFromNative(mDevice, 1); 1176 generateConnectionMessageFromNative( 1177 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 1178 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice)); 1179 Assert.assertTrue(mService.getDevices().contains(mDevice)); 1180 mService.setDeviceVolume(mDevice, deviceOneVolume, false); 1181 verify(mNativeInterface, times(1)).setVolume(eq(mDevice), eq(deviceOneVolume)); 1182 1183 // Verify that second device gets the proper group volume level when connected 1184 generateDeviceAvailableMessageFromNative(mDeviceTwo, 1); 1185 generateConnectionMessageFromNative( 1186 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 1187 Assert.assertEquals( 1188 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo)); 1189 Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); 1190 mService.setDeviceVolume(mDeviceTwo, deviceTwoVolume, false); 1191 verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(deviceTwoVolume)); 1192 1193 // Both devices are in the same group 1194 when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId); 1195 when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId); 1196 1197 // Register callback and verify it is called with known devices 1198 IBluetoothVolumeControlCallback callback = 1199 Mockito.mock(IBluetoothVolumeControlCallback.class); 1200 Binder binder = Mockito.mock(Binder.class); 1201 when(callback.asBinder()).thenReturn(binder); 1202 1203 int size = mService.mCallbacks.getRegisteredCallbackCount(); 1204 mServiceBinder.registerCallback(callback, mAttributionSource); 1205 Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount()); 1206 1207 verify(callback, times(1)).onDeviceVolumeChanged(eq(mDevice), eq(deviceOneVolume)); 1208 verify(callback, times(1)).onDeviceVolumeChanged(eq(mDeviceTwo), eq(deviceTwoVolume)); 1209 } 1210 1211 @Test testServiceBinderTestNotifyNewRegisteredCallback()1212 public void testServiceBinderTestNotifyNewRegisteredCallback() throws Exception { 1213 mSetFlagsRule.enableFlags( 1214 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES); 1215 int groupId = 1; 1216 int deviceOneVolume = 46; 1217 int deviceTwoVolume = 36; 1218 1219 // Update the device policy so okToConnect() returns true 1220 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 1221 when(mDatabaseManager.getProfileConnectionPolicy( 1222 any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL))) 1223 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 1224 doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class)); 1225 doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class)); 1226 1227 generateDeviceAvailableMessageFromNative(mDevice, 1); 1228 generateConnectionMessageFromNative( 1229 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 1230 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice)); 1231 Assert.assertTrue(mService.getDevices().contains(mDevice)); 1232 mService.setDeviceVolume(mDevice, deviceOneVolume, false); 1233 verify(mNativeInterface, times(1)).setVolume(eq(mDevice), eq(deviceOneVolume)); 1234 1235 // Verify that second device gets the proper group volume level when connected 1236 generateDeviceAvailableMessageFromNative(mDeviceTwo, 1); 1237 generateConnectionMessageFromNative( 1238 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); 1239 Assert.assertEquals( 1240 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo)); 1241 Assert.assertTrue(mService.getDevices().contains(mDeviceTwo)); 1242 mService.setDeviceVolume(mDeviceTwo, deviceTwoVolume, false); 1243 verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(deviceTwoVolume)); 1244 1245 // Both devices are in the same group 1246 when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId); 1247 when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId); 1248 1249 // Register callback and verify it is called with known devices 1250 IBluetoothVolumeControlCallback callback = 1251 Mockito.mock(IBluetoothVolumeControlCallback.class); 1252 Binder binder = Mockito.mock(Binder.class); 1253 when(callback.asBinder()).thenReturn(binder); 1254 1255 int size = mService.mCallbacks.getRegisteredCallbackCount(); 1256 mServiceBinder.registerCallback(callback, mAttributionSource); 1257 Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount()); 1258 1259 IBluetoothVolumeControlCallback callback_new_client = 1260 Mockito.mock(IBluetoothVolumeControlCallback.class); 1261 Binder binder_new_client = Mockito.mock(Binder.class); 1262 when(callback_new_client.asBinder()).thenReturn(binder_new_client); 1263 1264 mServiceBinder.notifyNewRegisteredCallback(callback_new_client, mAttributionSource); 1265 Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount()); 1266 1267 // This shall be done only once after mServiceBinder.registerCallback 1268 verify(callback, times(1)).onDeviceVolumeChanged(eq(mDevice), eq(deviceOneVolume)); 1269 verify(callback, times(1)).onDeviceVolumeChanged(eq(mDeviceTwo), eq(deviceTwoVolume)); 1270 1271 // This shall be done only once after mServiceBinder.updateNewRegistedCallback 1272 verify(callback_new_client, times(1)) 1273 .onDeviceVolumeChanged(eq(mDevice), eq(deviceOneVolume)); 1274 verify(callback_new_client, times(1)) 1275 .onDeviceVolumeChanged(eq(mDeviceTwo), eq(deviceTwoVolume)); 1276 } 1277 1278 @Test testServiceBinderMuteMethods()1279 public void testServiceBinderMuteMethods() throws Exception { 1280 mServiceBinder.mute(mDevice, mAttributionSource); 1281 verify(mNativeInterface).mute(mDevice); 1282 1283 mServiceBinder.unmute(mDevice, mAttributionSource); 1284 verify(mNativeInterface).unmute(mDevice); 1285 1286 int groupId = 1; 1287 mServiceBinder.muteGroup(groupId, mAttributionSource); 1288 verify(mNativeInterface).muteGroup(groupId); 1289 1290 mServiceBinder.unmuteGroup(groupId, mAttributionSource); 1291 verify(mNativeInterface).unmuteGroup(groupId); 1292 } 1293 1294 @Test testVolumeControlOffsetDescriptor()1295 public void testVolumeControlOffsetDescriptor() { 1296 VolumeControlService.VolumeControlOffsetDescriptor descriptor = 1297 new VolumeControlService.VolumeControlOffsetDescriptor(); 1298 int invalidId = -1; 1299 int validId = 10; 1300 int testValue = 100; 1301 String testDesc = "testDescription"; 1302 int testLocation = 10000; 1303 1304 Assert.assertEquals(0, descriptor.size()); 1305 descriptor.add(validId); 1306 Assert.assertEquals(1, descriptor.size()); 1307 1308 Assert.assertFalse(descriptor.setValue(invalidId, testValue)); 1309 Assert.assertTrue(descriptor.setValue(validId, testValue)); 1310 Assert.assertEquals(0, descriptor.getValue(invalidId)); 1311 Assert.assertEquals(testValue, descriptor.getValue(validId)); 1312 1313 Assert.assertFalse(descriptor.setDescription(invalidId, testDesc)); 1314 Assert.assertTrue(descriptor.setDescription(validId, testDesc)); 1315 Assert.assertEquals(null, descriptor.getDescription(invalidId)); 1316 Assert.assertEquals(testDesc, descriptor.getDescription(validId)); 1317 1318 Assert.assertFalse(descriptor.setLocation(invalidId, testLocation)); 1319 Assert.assertTrue(descriptor.setLocation(validId, testLocation)); 1320 Assert.assertEquals(0, descriptor.getLocation(invalidId)); 1321 Assert.assertEquals(testLocation, descriptor.getLocation(validId)); 1322 1323 StringBuilder sb = new StringBuilder(); 1324 descriptor.dump(sb); 1325 Assert.assertTrue(sb.toString().contains(testDesc)); 1326 1327 descriptor.add(validId + 1); 1328 Assert.assertEquals(2, descriptor.size()); 1329 descriptor.remove(validId); 1330 Assert.assertEquals(1, descriptor.size()); 1331 descriptor.clear(); 1332 Assert.assertEquals(0, descriptor.size()); 1333 } 1334 1335 @Test testDump_doesNotCrash()1336 public void testDump_doesNotCrash() throws Exception { 1337 connectDevice(mDevice); 1338 1339 StringBuilder sb = new StringBuilder(); 1340 mService.dump(sb); 1341 } 1342 1343 /** Test Volume Control changed callback. */ 1344 @Test testVolumeControlChangedCallback()1345 public void testVolumeControlChangedCallback() throws Exception { 1346 mSetFlagsRule.enableFlags( 1347 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES); 1348 1349 int groupId = 1; 1350 int groupVolume = 56; 1351 int deviceOneVolume = 46; 1352 1353 // Both devices are in the same group 1354 when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId); 1355 when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId); 1356 1357 // Send a message to trigger connection completed 1358 generateDeviceAvailableMessageFromNative(mDevice, 2); 1359 1360 mServiceBinder.setDeviceVolume(mDevice, groupVolume, true, mAttributionSource); 1361 verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(groupVolume)); 1362 1363 // Register callback and verify it is called with known devices 1364 IBluetoothVolumeControlCallback callback = 1365 Mockito.mock(IBluetoothVolumeControlCallback.class); 1366 Binder binder = Mockito.mock(Binder.class); 1367 when(callback.asBinder()).thenReturn(binder); 1368 1369 int size = mService.mCallbacks.getRegisteredCallbackCount(); 1370 mServiceBinder.registerCallback(callback, mAttributionSource); 1371 Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount()); 1372 1373 when(mLeAudioService.getGroupDevices(groupId)) 1374 .thenReturn(Arrays.asList(mDevice, mDeviceTwo)); 1375 // Send group volume change. 1376 VolumeControlStackEvent stackEvent = 1377 new VolumeControlStackEvent( 1378 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 1379 stackEvent.device = null; 1380 stackEvent.valueInt1 = groupId; 1381 stackEvent.valueInt2 = groupVolume; 1382 stackEvent.valueBool1 = false; 1383 stackEvent.valueBool2 = true; 1384 mService.messageFromNative(stackEvent); 1385 1386 verify(callback).onDeviceVolumeChanged(eq(mDeviceTwo), eq(groupVolume)); 1387 verify(callback).onDeviceVolumeChanged(eq(mDevice), eq(groupVolume)); 1388 1389 // Send device volume change only for one device 1390 VolumeControlStackEvent stackEvent2 = 1391 new VolumeControlStackEvent( 1392 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED); 1393 stackEvent2.device = mDevice; 1394 stackEvent2.valueInt1 = -1; 1395 stackEvent2.valueInt2 = deviceOneVolume; 1396 stackEvent2.valueBool1 = false; 1397 stackEvent2.valueBool2 = false; 1398 mService.messageFromNative(stackEvent2); 1399 1400 verify(callback).onDeviceVolumeChanged(eq(mDevice), eq(deviceOneVolume)); 1401 verify(callback, never()).onDeviceVolumeChanged(eq(mDeviceTwo), eq(deviceOneVolume)); 1402 } 1403 connectDevice(BluetoothDevice device)1404 private void connectDevice(BluetoothDevice device) throws Exception { 1405 VolumeControlStackEvent connCompletedEvent; 1406 1407 List<BluetoothDevice> prevConnectedDevices = mService.getConnectedDevices(); 1408 1409 // Update the device policy so okToConnect() returns true 1410 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 1411 when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL)) 1412 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 1413 doReturn(true).when(mNativeInterface).connectVolumeControl(device); 1414 doReturn(true).when(mNativeInterface).disconnectVolumeControl(device); 1415 1416 // Send a connect request 1417 Assert.assertTrue("Connect failed", mService.connect(device)); 1418 1419 // Verify the connection state broadcast, and that we are in Connecting state 1420 verifyConnectionStateIntent( 1421 TIMEOUT_MS, 1422 device, 1423 BluetoothProfile.STATE_CONNECTING, 1424 BluetoothProfile.STATE_DISCONNECTED); 1425 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(device)); 1426 1427 // Send a message to trigger connection completed 1428 connCompletedEvent = 1429 new VolumeControlStackEvent( 1430 VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1431 connCompletedEvent.device = device; 1432 connCompletedEvent.valueInt1 = VolumeControlStackEvent.CONNECTION_STATE_CONNECTED; 1433 mService.messageFromNative(connCompletedEvent); 1434 1435 // Verify the connection state broadcast, and that we are in Connected state 1436 verifyConnectionStateIntent( 1437 TIMEOUT_MS, 1438 device, 1439 BluetoothProfile.STATE_CONNECTED, 1440 BluetoothProfile.STATE_CONNECTING); 1441 Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(device)); 1442 1443 // Verify that the device is in the list of connected devices 1444 List<BluetoothDevice> connectedDevices = 1445 mServiceBinder.getConnectedDevices(mAttributionSource); 1446 Assert.assertTrue(connectedDevices.contains(device)); 1447 // Verify the list of previously connected devices 1448 for (BluetoothDevice prevDevice : prevConnectedDevices) { 1449 Assert.assertTrue(connectedDevices.contains(prevDevice)); 1450 } 1451 } 1452 generateConnectionMessageFromNative( BluetoothDevice device, int newConnectionState, int oldConnectionState)1453 private void generateConnectionMessageFromNative( 1454 BluetoothDevice device, int newConnectionState, int oldConnectionState) { 1455 VolumeControlStackEvent stackEvent = 1456 new VolumeControlStackEvent( 1457 VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1458 stackEvent.device = device; 1459 stackEvent.valueInt1 = newConnectionState; 1460 mService.messageFromNative(stackEvent); 1461 // Verify the connection state broadcast 1462 verifyConnectionStateIntent(TIMEOUT_MS, device, newConnectionState, oldConnectionState); 1463 } 1464 generateUnexpectedConnectionMessageFromNative( BluetoothDevice device, int newConnectionState)1465 private void generateUnexpectedConnectionMessageFromNative( 1466 BluetoothDevice device, int newConnectionState) { 1467 VolumeControlStackEvent stackEvent = 1468 new VolumeControlStackEvent( 1469 VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1470 stackEvent.device = device; 1471 stackEvent.valueInt1 = newConnectionState; 1472 mService.messageFromNative(stackEvent); 1473 // Verify the connection state broadcast 1474 verifyNoConnectionStateIntent(TIMEOUT_MS, device); 1475 } 1476 generateDeviceAvailableMessageFromNative( BluetoothDevice device, int numberOfExtOffsets)1477 private void generateDeviceAvailableMessageFromNative( 1478 BluetoothDevice device, int numberOfExtOffsets) { 1479 // Send a message to trigger connection completed 1480 VolumeControlStackEvent event = 1481 new VolumeControlStackEvent(VolumeControlStackEvent.EVENT_TYPE_DEVICE_AVAILABLE); 1482 event.device = device; 1483 event.valueInt1 = numberOfExtOffsets; // number of external outputs 1484 mService.messageFromNative(event); 1485 } 1486 generateDeviceOffsetChangedMessageFromNative( BluetoothDevice device, int extOffsetIndex, int offset)1487 private void generateDeviceOffsetChangedMessageFromNative( 1488 BluetoothDevice device, int extOffsetIndex, int offset) { 1489 // Send a message to trigger connection completed 1490 VolumeControlStackEvent event = 1491 new VolumeControlStackEvent( 1492 VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED); 1493 event.device = device; 1494 event.valueInt1 = extOffsetIndex; // external output index 1495 event.valueInt2 = offset; // offset value 1496 mService.messageFromNative(event); 1497 } 1498 generateDeviceLocationChangedMessageFromNative( BluetoothDevice device, int extOffsetIndex, int location)1499 private void generateDeviceLocationChangedMessageFromNative( 1500 BluetoothDevice device, int extOffsetIndex, int location) { 1501 // Send a message to trigger connection completed 1502 VolumeControlStackEvent event = 1503 new VolumeControlStackEvent( 1504 VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED); 1505 event.device = device; 1506 event.valueInt1 = extOffsetIndex; // external output index 1507 event.valueInt2 = location; // location 1508 mService.messageFromNative(event); 1509 } 1510 generateDeviceDescriptionChangedMessageFromNative( BluetoothDevice device, int extOffsetIndex, String description)1511 private void generateDeviceDescriptionChangedMessageFromNative( 1512 BluetoothDevice device, int extOffsetIndex, String description) { 1513 // Send a message to trigger connection completed 1514 VolumeControlStackEvent event = 1515 new VolumeControlStackEvent( 1516 VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED); 1517 event.device = device; 1518 event.valueInt1 = extOffsetIndex; // external output index 1519 event.valueString1 = description; // description 1520 mService.messageFromNative(event); 1521 } 1522 1523 /** 1524 * Helper function to test okToConnect() method 1525 * 1526 * @param device test device 1527 * @param bondState bond state value, could be invalid 1528 * @param policy value, could be invalid 1529 * @param expected expected result from okToConnect() 1530 */ testOkToConnectCase( BluetoothDevice device, int bondState, int policy, boolean expected)1531 private void testOkToConnectCase( 1532 BluetoothDevice device, int bondState, int policy, boolean expected) { 1533 doReturn(bondState).when(mAdapterService).getBondState(device); 1534 when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager); 1535 when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL)) 1536 .thenReturn(policy); 1537 Assert.assertEquals(expected, mService.okToConnect(device)); 1538 } 1539 } 1540