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.le_audio; 19 20 import static com.google.common.truth.Truth.assertThat; 21 import static com.google.common.truth.Truth.assertWithMessage; 22 23 import static org.mockito.Mockito.any; 24 import static org.mockito.Mockito.anyInt; 25 import static org.mockito.Mockito.anyString; 26 import static org.mockito.Mockito.doAnswer; 27 import static org.mockito.Mockito.doNothing; 28 import static org.mockito.Mockito.doReturn; 29 import static org.mockito.Mockito.eq; 30 import static org.mockito.Mockito.never; 31 import static org.mockito.Mockito.timeout; 32 import static org.mockito.Mockito.times; 33 import static org.mockito.Mockito.verify; 34 import static org.mockito.Mockito.when; 35 36 import android.bluetooth.BluetoothAdapter; 37 import android.bluetooth.BluetoothDevice; 38 import android.bluetooth.BluetoothLeAudio; 39 import android.bluetooth.BluetoothLeAudioCodecConfig; 40 import android.bluetooth.BluetoothLeAudioCodecStatus; 41 import android.bluetooth.BluetoothManager; 42 import android.bluetooth.BluetoothProfile; 43 import android.bluetooth.BluetoothUuid; 44 import android.bluetooth.IBluetoothLeAudioCallback; 45 import android.content.BroadcastReceiver; 46 import android.content.Context; 47 import android.content.Intent; 48 import android.content.IntentFilter; 49 import android.media.AudioManager; 50 import android.media.BluetoothProfileConnectionInfo; 51 import android.os.ParcelUuid; 52 53 import androidx.test.InstrumentationRegistry; 54 import androidx.test.filters.MediumTest; 55 import androidx.test.rule.ServiceTestRule; 56 import androidx.test.runner.AndroidJUnit4; 57 58 import com.android.bluetooth.TestUtils; 59 import com.android.bluetooth.btservice.AdapterService; 60 import com.android.bluetooth.btservice.ServiceFactory; 61 import com.android.bluetooth.btservice.storage.DatabaseManager; 62 import com.android.bluetooth.csip.CsipSetCoordinatorService; 63 import com.android.bluetooth.hfp.HeadsetService; 64 import com.android.bluetooth.mcp.McpService; 65 import com.android.bluetooth.tbs.TbsService; 66 import com.android.bluetooth.vc.VolumeControlService; 67 68 import org.junit.After; 69 import org.junit.Before; 70 import org.junit.Rule; 71 import org.junit.Test; 72 import org.junit.runner.RunWith; 73 import org.mockito.ArgumentCaptor; 74 import org.mockito.Mock; 75 import org.mockito.Mockito; 76 import org.mockito.MockitoAnnotations; 77 import org.mockito.Spy; 78 79 import java.util.HashMap; 80 import java.util.HashSet; 81 import java.util.List; 82 import java.util.Objects; 83 import java.util.concurrent.LinkedBlockingQueue; 84 import java.util.concurrent.TimeoutException; 85 86 @MediumTest 87 @RunWith(AndroidJUnit4.class) 88 public class LeAudioServiceTest { 89 private static final int ASYNC_CALL_TIMEOUT_MILLIS = 250; 90 private static final int TIMEOUT_MS = 1000; 91 private static final int AUDIO_MANAGER_DEVICE_ADD_TIMEOUT_MS = 3000; 92 private static final int MAX_LE_AUDIO_CONNECTIONS = 5; 93 private static final int LE_AUDIO_GROUP_ID_INVALID = -1; 94 95 private BluetoothAdapter mAdapter; 96 private Context mTargetContext; 97 private LeAudioService mService; 98 private BluetoothDevice mLeftDevice; 99 private BluetoothDevice mRightDevice; 100 private BluetoothDevice mSingleDevice; 101 private HashSet<BluetoothDevice> mBondedDevices = new HashSet<>(); 102 private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mDeviceQueueMap; 103 private LinkedBlockingQueue<Intent> mGroupIntentQueue = new LinkedBlockingQueue<>(); 104 private int testGroupId = 1; 105 private boolean onGroupStatusCallbackCalled = false; 106 private boolean onGroupCodecConfChangedCallbackCalled = false; 107 private BluetoothLeAudioCodecStatus testCodecStatus = null; 108 109 private BroadcastReceiver mLeAudioIntentReceiver; 110 111 @Mock private AdapterService mAdapterService; 112 @Mock private AudioManager mAudioManager; 113 @Mock private DatabaseManager mDatabaseManager; 114 @Mock private LeAudioNativeInterface mNativeInterface; 115 @Mock private LeAudioTmapGattServer mTmapGattServer; 116 @Mock private McpService mMcpService; 117 @Mock private TbsService mTbsService; 118 @Mock private VolumeControlService mVolumeControlService; 119 @Mock private CsipSetCoordinatorService mCsipSetCoordinatorService; 120 @Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance(); 121 @Spy private ServiceFactory mServiceFactory = new ServiceFactory(); 122 123 @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule(); 124 125 private static final BluetoothLeAudioCodecConfig LC3_16KHZ_CONFIG = 126 new BluetoothLeAudioCodecConfig.Builder() 127 .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3) 128 .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000) 129 .build(); 130 private static final BluetoothLeAudioCodecConfig LC3_48KHZ_CONFIG = 131 new BluetoothLeAudioCodecConfig.Builder() 132 .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3) 133 .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000) 134 .build(); 135 136 private static final BluetoothLeAudioCodecConfig LC3_48KHZ_16KHZ_CONFIG = 137 new BluetoothLeAudioCodecConfig.Builder() 138 .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3) 139 .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000 140 | BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000) 141 .build(); 142 143 private static final List<BluetoothLeAudioCodecConfig> INPUT_CAPABILITIES_CONFIG = List.of( 144 LC3_48KHZ_16KHZ_CONFIG); 145 146 private static final List<BluetoothLeAudioCodecConfig> OUTPUT_CAPABILITIES_CONFIG = List.of( 147 LC3_48KHZ_16KHZ_CONFIG); 148 149 private static final List<BluetoothLeAudioCodecConfig> INPUT_SELECTABLE_CONFIG = List.of( 150 LC3_16KHZ_CONFIG); 151 152 private static final List<BluetoothLeAudioCodecConfig> OUTPUT_SELECTABLE_CONFIG = List.of( 153 LC3_48KHZ_16KHZ_CONFIG); 154 155 @Before setUp()156 public void setUp() throws Exception { 157 mTargetContext = InstrumentationRegistry.getTargetContext(); 158 159 // Set up mocks and test assets 160 MockitoAnnotations.initMocks(this); 161 162 // Use spied objects factory 163 doNothing().when(mTmapGattServer).start(anyInt()); 164 doNothing().when(mTmapGattServer).stop(); 165 LeAudioObjectsFactory.setInstanceForTesting(mObjectsFactory); 166 doReturn(mTmapGattServer).when(mObjectsFactory).getTmapGattServer(any()); 167 168 TestUtils.setAdapterService(mAdapterService); 169 doReturn(MAX_LE_AUDIO_CONNECTIONS).when(mAdapterService).getMaxConnectedAudioDevices(); 170 doReturn(new ParcelUuid[]{BluetoothUuid.LE_AUDIO}).when(mAdapterService) 171 .getRemoteUuids(any(BluetoothDevice.class)); 172 doReturn(mDatabaseManager).when(mAdapterService).getDatabase(); 173 doReturn(true, false).when(mAdapterService).isStartedProfile(anyString()); 174 175 BluetoothManager manager = mTargetContext.getSystemService(BluetoothManager.class); 176 assertThat(manager).isNotNull(); 177 mAdapter = manager.getAdapter(); 178 // Mock methods in AdapterService 179 doAnswer(invocation -> mBondedDevices.toArray(new BluetoothDevice[]{})).when( 180 mAdapterService).getBondedDevices(); 181 182 LeAudioNativeInterface.setInstance(mNativeInterface); 183 startService(); 184 mService.mAudioManager = mAudioManager; 185 mService.mMcpService = mMcpService; 186 mService.mTbsService = mTbsService; 187 mService.mServiceFactory = mServiceFactory; 188 when(mServiceFactory.getVolumeControlService()).thenReturn(mVolumeControlService); 189 when(mServiceFactory.getCsipSetCoordinatorService()).thenReturn(mCsipSetCoordinatorService); 190 191 LeAudioStackEvent stackEvent = 192 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED); 193 mService.messageFromNative(stackEvent); 194 assertThat(mService.mLeAudioNativeIsInitialized).isTrue(); 195 196 // Override the timeout value to speed up the test 197 LeAudioStateMachine.sConnectTimeoutMs = TIMEOUT_MS; // 1s 198 199 mGroupIntentQueue = new LinkedBlockingQueue<>(); 200 201 // Set up the Connection State Changed receiver 202 IntentFilter filter = new IntentFilter(); 203 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 204 filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); 205 filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); 206 mLeAudioIntentReceiver = new LeAudioIntentReceiver(); 207 mTargetContext.registerReceiver(mLeAudioIntentReceiver, filter); 208 209 doAnswer(invocation -> mBondedDevices.toArray(new BluetoothDevice[]{})).when( 210 mAdapterService).getBondedDevices(); 211 212 // Get a device for testing 213 mLeftDevice = TestUtils.getTestDevice(mAdapter, 0); 214 mRightDevice = TestUtils.getTestDevice(mAdapter, 1); 215 mSingleDevice = TestUtils.getTestDevice(mAdapter, 2); 216 mDeviceQueueMap = new HashMap<>(); 217 mDeviceQueueMap.put(mLeftDevice, new LinkedBlockingQueue<>()); 218 mDeviceQueueMap.put(mRightDevice, new LinkedBlockingQueue<>()); 219 mDeviceQueueMap.put(mSingleDevice, new LinkedBlockingQueue<>()); 220 doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService) 221 .getBondState(any(BluetoothDevice.class)); 222 doReturn(new ParcelUuid[]{BluetoothUuid.LE_AUDIO}).when(mAdapterService) 223 .getRemoteUuids(any(BluetoothDevice.class)); 224 225 verify(mNativeInterface, timeout(3000).times(1)).init(any()); 226 } 227 228 @After tearDown()229 public void tearDown() throws Exception { 230 if ((mService == null) || (mAdapter == null)) { 231 return; 232 } 233 234 mBondedDevices.clear(); 235 mGroupIntentQueue.clear(); 236 stopService(); 237 mTargetContext.unregisterReceiver(mLeAudioIntentReceiver); 238 mDeviceQueueMap.clear(); 239 TestUtils.clearAdapterService(mAdapterService); 240 LeAudioNativeInterface.setInstance(null); 241 } 242 startService()243 private void startService() throws TimeoutException { 244 TestUtils.startService(mServiceRule, LeAudioService.class); 245 mService = LeAudioService.getLeAudioService(); 246 assertThat(mService).isNotNull(); 247 } 248 stopService()249 private void stopService() throws TimeoutException { 250 TestUtils.stopService(mServiceRule, LeAudioService.class); 251 mService = LeAudioService.getLeAudioService(); 252 assertThat(mService).isNull(); 253 } 254 255 private class LeAudioIntentReceiver extends BroadcastReceiver { 256 @Override onReceive(Context context, Intent intent)257 public void onReceive(Context context, Intent intent) { 258 if (BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED 259 .equals(intent.getAction())) { 260 try { 261 BluetoothDevice device = intent.getParcelableExtra( 262 BluetoothDevice.EXTRA_DEVICE); 263 assertThat(device).isNotNull(); 264 LinkedBlockingQueue<Intent> queue = mDeviceQueueMap.get(device); 265 assertThat(queue).isNotNull(); 266 queue.put(intent); 267 } catch (InterruptedException e) { 268 assertWithMessage("Cannot add Intent to the Connection State queue: " 269 + e.getMessage()).fail(); 270 } 271 } 272 if (BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED.equals(intent.getAction())) { 273 try { 274 BluetoothDevice device = intent.getParcelableExtra( 275 BluetoothDevice.EXTRA_DEVICE); 276 if (device != null) { 277 LinkedBlockingQueue<Intent> queue = mDeviceQueueMap.get(device); 278 assertThat(queue).isNotNull(); 279 queue.put(intent); 280 } 281 } catch (InterruptedException e) { 282 assertWithMessage("Cannot add Le Audio Intent to the Connection State queue: " 283 + e.getMessage()).fail(); 284 } 285 } 286 } 287 } 288 verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)289 private void verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, 290 int newState, int prevState) { 291 Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device)); 292 assertThat(intent).isNotNull(); 293 assertThat(intent.getAction()) 294 .isEqualTo(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); 295 assertThat((BluetoothDevice)intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).isEqualTo(device); 296 assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)).isEqualTo(newState); 297 assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1)).isEqualTo(prevState); 298 299 if (newState == BluetoothProfile.STATE_CONNECTED) { 300 // ActiveDeviceManager calls deviceConnected when connected. 301 mService.deviceConnected(device); 302 } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { 303 // ActiveDeviceManager calls deviceDisconnected when connected. 304 mService.deviceDisconnected(device, false); 305 } 306 } 307 308 /** 309 * Test getting LeAudio Service: getLeAudioService() 310 */ 311 @Test testGetLeAudioService()312 public void testGetLeAudioService() { 313 assertThat(mService).isEqualTo(LeAudioService.getLeAudioService()); 314 } 315 316 /** 317 * Test stop LeAudio Service 318 */ 319 @Test testStopLeAudioService()320 public void testStopLeAudioService() { 321 // Prepare: connect 322 connectDevice(mLeftDevice); 323 // LeAudio Service is already running: test stop(). Note: must be done on the main thread 324 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 325 public void run() { 326 assertThat(mService.stop()).isTrue(); 327 } 328 }); 329 } 330 331 /** 332 * Test if stop during init is ok. 333 */ 334 @Test testStopStartStopService()335 public void testStopStartStopService() throws Exception { 336 InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() { 337 public void run() { 338 assertThat(mService.stop()).isTrue(); 339 assertThat(mService.start()).isTrue(); 340 assertThat(mService.stop()).isTrue(); 341 assertThat(mService.start()).isTrue(); 342 } 343 }); 344 } 345 346 /** 347 * Test get/set priority for BluetoothDevice 348 */ 349 @Test testGetSetPriority()350 public void testGetSetPriority() { 351 when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) 352 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 353 assertWithMessage("Initial device priority") 354 .that(BluetoothProfile.CONNECTION_POLICY_UNKNOWN) 355 .isEqualTo(mService.getConnectionPolicy(mLeftDevice)); 356 357 when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) 358 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 359 assertWithMessage("Setting device priority to PRIORITY_OFF") 360 .that(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) 361 .isEqualTo(mService.getConnectionPolicy(mLeftDevice)); 362 363 when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) 364 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 365 assertWithMessage("Setting device priority to PRIORITY_ON") 366 .that(BluetoothProfile.CONNECTION_POLICY_ALLOWED) 367 .isEqualTo(mService.getConnectionPolicy(mLeftDevice)); 368 } 369 370 /** 371 * Helper function to test okToConnect() method 372 * 373 * @param device test device 374 * @param bondState bond state value, could be invalid 375 * @param priority value, could be invalid, could be invalid 376 * @param expected expected result from okToConnect() 377 */ testOkToConnectCase(BluetoothDevice device, int bondState, int priority, boolean expected)378 private void testOkToConnectCase(BluetoothDevice device, int bondState, int priority, 379 boolean expected) { 380 doReturn(bondState).when(mAdapterService).getBondState(device); 381 when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO)) 382 .thenReturn(priority); 383 assertThat(expected).isEqualTo(mService.okToConnect(device)); 384 } 385 386 /** 387 * Test okToConnect method using various test cases 388 */ 389 @Test testOkToConnect()390 public void testOkToConnect() { 391 int badPriorityValue = 1024; 392 int badBondState = 42; 393 testOkToConnectCase(mSingleDevice, 394 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 395 testOkToConnectCase(mSingleDevice, 396 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 397 testOkToConnectCase(mSingleDevice, 398 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 399 testOkToConnectCase(mSingleDevice, 400 BluetoothDevice.BOND_NONE, badPriorityValue, false); 401 testOkToConnectCase(mSingleDevice, 402 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 403 testOkToConnectCase(mSingleDevice, 404 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 405 testOkToConnectCase(mSingleDevice, 406 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 407 testOkToConnectCase(mSingleDevice, 408 BluetoothDevice.BOND_BONDING, badPriorityValue, false); 409 testOkToConnectCase(mSingleDevice, 410 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, true); 411 testOkToConnectCase(mSingleDevice, 412 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 413 testOkToConnectCase(mSingleDevice, 414 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_ALLOWED, true); 415 testOkToConnectCase(mSingleDevice, 416 BluetoothDevice.BOND_BONDED, badPriorityValue, false); 417 testOkToConnectCase(mSingleDevice, 418 badBondState, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false); 419 testOkToConnectCase(mSingleDevice, 420 badBondState, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false); 421 testOkToConnectCase(mSingleDevice, 422 badBondState, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false); 423 testOkToConnectCase(mSingleDevice, 424 badBondState, badPriorityValue, false); 425 } 426 427 /** 428 * Test that an outgoing connection to device that does not have Le Audio UUID is rejected 429 */ 430 @Test testOutgoingConnectMissingLeAudioUuid()431 public void testOutgoingConnectMissingLeAudioUuid() { 432 // Update the device priority so okToConnect() returns true 433 when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) 434 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 435 when(mDatabaseManager 436 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO)) 437 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 438 when(mDatabaseManager 439 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) 440 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 441 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 442 doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); 443 444 // Return No UUID 445 doReturn(new ParcelUuid[]{}).when(mAdapterService) 446 .getRemoteUuids(any(BluetoothDevice.class)); 447 448 // Send a connect request 449 assertWithMessage("Connect expected to fail").that(mService.connect(mLeftDevice)).isFalse(); 450 } 451 452 /** 453 * Test that an outgoing connection to device with PRIORITY_OFF is rejected 454 */ 455 @Test testOutgoingConnectPriorityOff()456 public void testOutgoingConnectPriorityOff() { 457 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 458 doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); 459 460 // Set the device priority to PRIORITY_OFF so connect() should fail 461 when(mDatabaseManager 462 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) 463 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 464 465 // Send a connect request 466 assertWithMessage("Connect expected to fail").that(mService.connect(mLeftDevice)).isFalse(); 467 } 468 469 /** 470 * Test that an outgoing connection times out 471 */ 472 @Test testOutgoingConnectTimeout()473 public void testOutgoingConnectTimeout() { 474 // Update the device priority so okToConnect() returns true 475 when(mDatabaseManager 476 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) 477 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 478 when(mDatabaseManager 479 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO)) 480 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 481 when(mDatabaseManager 482 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) 483 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 484 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 485 doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); 486 487 // Send a connect request 488 assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); 489 490 // Verify the connection state broadcast, and that we are in Connecting state 491 verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING, 492 BluetoothProfile.STATE_DISCONNECTED); 493 assertThat(mService.getConnectionState(mLeftDevice)) 494 .isEqualTo(BluetoothProfile.STATE_CONNECTING); 495 496 // Verify the connection state broadcast, and that we are in Disconnected state 497 verifyConnectionStateIntent(LeAudioStateMachine.sConnectTimeoutMs * 2, 498 mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, 499 BluetoothProfile.STATE_CONNECTING); 500 assertThat(mService.getConnectionState(mLeftDevice)) 501 .isEqualTo(BluetoothProfile.STATE_DISCONNECTED); 502 } 503 injectNoVerifyDeviceConnected(BluetoothDevice device)504 private void injectNoVerifyDeviceConnected(BluetoothDevice device) { 505 generateUnexpectedConnectionMessageFromNative(device, 506 LeAudioStackEvent.CONNECTION_STATE_CONNECTED, 507 LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED); 508 } 509 injectAndVerifyDeviceDisconnected(BluetoothDevice device)510 private void injectAndVerifyDeviceDisconnected(BluetoothDevice device) { 511 generateConnectionMessageFromNative(device, 512 LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED, 513 LeAudioStackEvent.CONNECTION_STATE_CONNECTED); 514 } 515 injectNoVerifyDeviceDisconnected(BluetoothDevice device)516 private void injectNoVerifyDeviceDisconnected(BluetoothDevice device) { 517 generateUnexpectedConnectionMessageFromNative(device, 518 LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED, 519 LeAudioStackEvent.CONNECTION_STATE_CONNECTED); 520 } 521 /** 522 * Test that the outgoing connect/disconnect and audio switch is successful. 523 */ 524 @Test testAudioManagerConnectDisconnect()525 public void testAudioManagerConnectDisconnect() { 526 // Update the device priority so okToConnect() returns true 527 when(mDatabaseManager 528 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) 529 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 530 when(mDatabaseManager 531 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO)) 532 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 533 when(mDatabaseManager 534 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) 535 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 536 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 537 doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); 538 539 // Send a connect request 540 assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); 541 assertWithMessage("Connect failed").that(mService.connect(mRightDevice)).isTrue(); 542 543 // Verify the connection state broadcast, and that we are in Connecting state 544 verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING, 545 BluetoothProfile.STATE_DISCONNECTED); 546 assertThat(mService.getConnectionState(mLeftDevice)) 547 .isEqualTo(BluetoothProfile.STATE_CONNECTING); 548 verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTING, 549 BluetoothProfile.STATE_DISCONNECTED); 550 assertThat(mService.getConnectionState(mRightDevice)) 551 .isEqualTo(BluetoothProfile.STATE_CONNECTING); 552 553 LeAudioStackEvent connCompletedEvent; 554 // Send a message to trigger connection completed 555 connCompletedEvent = new LeAudioStackEvent( 556 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 557 connCompletedEvent.device = mLeftDevice; 558 connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED; 559 mService.messageFromNative(connCompletedEvent); 560 561 // Verify the connection state broadcast, and that we are in Connected state 562 verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTED, 563 BluetoothProfile.STATE_CONNECTING); 564 assertThat(mService.getConnectionState(mLeftDevice)) 565 .isEqualTo(BluetoothProfile.STATE_CONNECTED); 566 567 // Send a message to trigger connection completed for right side 568 connCompletedEvent = new LeAudioStackEvent( 569 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 570 connCompletedEvent.device = mRightDevice; 571 connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED; 572 mService.messageFromNative(connCompletedEvent); 573 574 // Verify the connection state broadcast, and that we are in Connected state for right side 575 verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTED, 576 BluetoothProfile.STATE_CONNECTING); 577 assertThat(mService.getConnectionState(mRightDevice)) 578 .isEqualTo(BluetoothProfile.STATE_CONNECTED); 579 580 // Verify the list of connected devices 581 assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue(); 582 assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue(); 583 584 // Send a disconnect request 585 assertWithMessage("Disconnect failed").that(mService.disconnect(mLeftDevice)).isTrue(); 586 assertWithMessage("Disconnect failed").that(mService.disconnect(mRightDevice)).isTrue(); 587 588 // Verify the connection state broadcast, and that we are in Disconnecting state 589 verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTING, 590 BluetoothProfile.STATE_CONNECTED); 591 assertThat(BluetoothProfile.STATE_DISCONNECTING) 592 .isEqualTo(mService.getConnectionState(mLeftDevice)); 593 verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_DISCONNECTING, 594 BluetoothProfile.STATE_CONNECTED); 595 assertThat(BluetoothProfile.STATE_DISCONNECTING) 596 .isEqualTo(mService.getConnectionState(mRightDevice)); 597 598 // Send a message to trigger disconnection completed 599 connCompletedEvent = new LeAudioStackEvent( 600 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 601 connCompletedEvent.device = mLeftDevice; 602 connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED; 603 mService.messageFromNative(connCompletedEvent); 604 605 // Verify the connection state broadcast, and that we are in Disconnected state 606 verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, 607 BluetoothProfile.STATE_DISCONNECTING); 608 assertThat(BluetoothProfile.STATE_DISCONNECTED) 609 .isEqualTo(mService.getConnectionState(mLeftDevice)); 610 611 // Send a message to trigger disconnection completed to the right device 612 connCompletedEvent = new LeAudioStackEvent( 613 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 614 connCompletedEvent.device = mRightDevice; 615 connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED; 616 mService.messageFromNative(connCompletedEvent); 617 618 // Verify the connection state broadcast, and that we are in Disconnected state 619 verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_DISCONNECTED, 620 BluetoothProfile.STATE_DISCONNECTING); 621 assertThat(BluetoothProfile.STATE_DISCONNECTED) 622 .isEqualTo(mService.getConnectionState(mRightDevice)); 623 624 // Verify the list of connected devices 625 assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isFalse(); 626 assertThat(mService.getConnectedDevices().contains(mRightDevice)).isFalse(); 627 } 628 629 /** 630 * Test that only CONNECTION_STATE_CONNECTED or CONNECTION_STATE_CONNECTING Le Audio stack 631 * events will create a state machine. 632 */ 633 @Test testCreateStateMachineStackEvents()634 public void testCreateStateMachineStackEvents() { 635 // Update the device priority so okToConnect() returns true 636 when(mDatabaseManager 637 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) 638 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 639 when(mDatabaseManager 640 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO)) 641 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 642 when(mDatabaseManager 643 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) 644 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 645 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 646 doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); 647 648 // Create device descriptor with connect request 649 assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); 650 651 // Le Audio stack event: CONNECTION_STATE_CONNECTING - state machine should be created 652 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING, 653 BluetoothProfile.STATE_DISCONNECTED); 654 assertThat(BluetoothProfile.STATE_CONNECTING) 655 .isEqualTo(mService.getConnectionState(mLeftDevice)); 656 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 657 658 // LeAudio stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed 659 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, 660 BluetoothProfile.STATE_CONNECTING); 661 assertThat(BluetoothProfile.STATE_DISCONNECTED) 662 .isEqualTo(mService.getConnectionState(mLeftDevice)); 663 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 664 mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); 665 assertThat(mService.getDevices().contains(mLeftDevice)).isFalse(); 666 667 // Remove bond will remove also device descriptor. Device has to be connected again 668 assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); 669 verifyConnectionStateIntent(LeAudioStateMachine.sConnectTimeoutMs * 2, 670 mLeftDevice, BluetoothProfile.STATE_CONNECTING, 671 BluetoothProfile.STATE_DISCONNECTED); 672 673 // stack event: CONNECTION_STATE_CONNECTED - state machine should be created 674 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTED, 675 BluetoothProfile.STATE_CONNECTING); 676 assertThat(BluetoothProfile.STATE_CONNECTED) 677 .isEqualTo(mService.getConnectionState(mLeftDevice)); 678 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 679 680 // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed 681 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, 682 BluetoothProfile.STATE_CONNECTED); 683 assertThat(BluetoothProfile.STATE_DISCONNECTED) 684 .isEqualTo(mService.getConnectionState(mLeftDevice)); 685 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 686 mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); 687 assertThat(mService.getDevices().contains(mLeftDevice)).isFalse(); 688 689 // stack event: CONNECTION_STATE_DISCONNECTING - state machine should not be created 690 generateUnexpectedConnectionMessageFromNative(mLeftDevice, 691 BluetoothProfile.STATE_DISCONNECTING, 692 BluetoothProfile.STATE_DISCONNECTED); 693 assertThat(BluetoothProfile.STATE_DISCONNECTED) 694 .isEqualTo(mService.getConnectionState(mLeftDevice)); 695 assertThat(mService.getDevices().contains(mLeftDevice)).isFalse(); 696 697 // stack event: CONNECTION_STATE_DISCONNECTED - state machine should not be created 698 generateUnexpectedConnectionMessageFromNative(mLeftDevice, 699 BluetoothProfile.STATE_DISCONNECTED, 700 BluetoothProfile.STATE_DISCONNECTED); 701 assertThat(BluetoothProfile.STATE_DISCONNECTED) 702 .isEqualTo(mService.getConnectionState(mLeftDevice)); 703 assertThat(mService.getDevices().contains(mLeftDevice)).isFalse(); 704 } 705 706 /** 707 * Test that a state machine in DISCONNECTED state is removed only after the device is unbond. 708 */ 709 @Test testDeleteStateMachineUnbondEvents()710 public void testDeleteStateMachineUnbondEvents() { 711 // Update the device priority so okToConnect() returns true 712 when(mDatabaseManager 713 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) 714 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 715 when(mDatabaseManager 716 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO)) 717 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 718 when(mDatabaseManager 719 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) 720 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 721 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 722 doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); 723 724 // Create device descriptor with connect request 725 assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); 726 727 // LeAudio stack event: CONNECTION_STATE_CONNECTING - state machine should be created 728 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING, 729 BluetoothProfile.STATE_DISCONNECTED); 730 assertThat(mService.getConnectionState(mLeftDevice)) 731 .isEqualTo(BluetoothProfile.STATE_CONNECTING); 732 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 733 // Device unbond - state machine is not removed 734 mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); 735 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 736 verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, 737 BluetoothProfile.STATE_CONNECTING); 738 739 // LeAudio stack event: CONNECTION_STATE_CONNECTED - state machine is not removed 740 mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED); 741 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTED, 742 BluetoothProfile.STATE_DISCONNECTED); 743 assertThat(mService.getConnectionState(mLeftDevice)) 744 .isEqualTo(BluetoothProfile.STATE_CONNECTED); 745 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 746 // Device unbond - state machine is not removed 747 mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); 748 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 749 verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTING, 750 BluetoothProfile.STATE_CONNECTED); 751 assertThat(mService.getConnectionState(mLeftDevice)) 752 .isEqualTo(BluetoothProfile.STATE_DISCONNECTING); 753 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 754 755 // LeAudio stack event: CONNECTION_STATE_DISCONNECTING - state machine is not removed 756 mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED); 757 assertThat(mService.getConnectionState(mLeftDevice)) 758 .isEqualTo(BluetoothProfile.STATE_DISCONNECTING); 759 // Device unbond - state machine is not removed 760 mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); 761 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 762 763 // LeAudio stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed 764 mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED); 765 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, 766 BluetoothProfile.STATE_DISCONNECTING); 767 assertThat(mService.getConnectionState(mLeftDevice)) 768 .isEqualTo(BluetoothProfile.STATE_DISCONNECTED); 769 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 770 // Device unbond - state machine is removed 771 mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE); 772 assertThat(mService.getDevices().contains(mLeftDevice)).isFalse(); 773 } 774 775 /** 776 * Test that a CONNECTION_STATE_DISCONNECTED Le Audio stack event will remove the state 777 * machine only if the device is unbond. 778 */ 779 @Test testDeleteStateMachineDisconnectEvents()780 public void testDeleteStateMachineDisconnectEvents() { 781 // Update the device priority so okToConnect() returns true 782 when(mDatabaseManager 783 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO)) 784 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 785 when(mDatabaseManager 786 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO)) 787 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 788 when(mDatabaseManager 789 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) 790 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 791 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 792 doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); 793 794 // Create device descriptor with connect request 795 assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue(); 796 797 // LeAudio stack event: CONNECTION_STATE_CONNECTING - state machine should be created 798 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING, 799 BluetoothProfile.STATE_DISCONNECTED); 800 assertThat(BluetoothProfile.STATE_CONNECTING) 801 .isEqualTo(mService.getConnectionState(mLeftDevice)); 802 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 803 804 // LeAudio stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed 805 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, 806 BluetoothProfile.STATE_CONNECTING); 807 assertThat(BluetoothProfile.STATE_DISCONNECTED) 808 .isEqualTo(mService.getConnectionState(mLeftDevice)); 809 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 810 811 // LeAudio stack event: CONNECTION_STATE_CONNECTING - state machine remains 812 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING, 813 BluetoothProfile.STATE_DISCONNECTED); 814 assertThat(BluetoothProfile.STATE_CONNECTING) 815 .isEqualTo(mService.getConnectionState(mLeftDevice)); 816 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 817 818 // Device bond state marked as unbond - state machine is not removed 819 doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService) 820 .getBondState(any(BluetoothDevice.class)); 821 assertThat(mService.getDevices().contains(mLeftDevice)).isTrue(); 822 823 // LeAudio stack event: CONNECTION_STATE_DISCONNECTED - state machine is removed 824 generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED, 825 BluetoothProfile.STATE_CONNECTING); 826 assertThat(BluetoothProfile.STATE_DISCONNECTED) 827 .isEqualTo(mService.getConnectionState(mLeftDevice)); 828 assertThat(mService.getDevices().contains(mLeftDevice)).isFalse(); 829 } 830 connectDevice(BluetoothDevice device)831 private void connectDevice(BluetoothDevice device) { 832 LeAudioStackEvent connCompletedEvent; 833 834 List<BluetoothDevice> prevConnectedDevices = mService.getConnectedDevices(); 835 836 when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO)) 837 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 838 doReturn(true).when(mNativeInterface).connectLeAudio(device); 839 doReturn(true).when(mNativeInterface).disconnectLeAudio(device); 840 841 // Send a connect request 842 assertWithMessage("Connect failed").that(mService.connect(device)).isTrue(); 843 844 // Verify the connection state broadcast, and that we are in Connecting state 845 verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTING, 846 BluetoothProfile.STATE_DISCONNECTED); 847 assertThat(BluetoothProfile.STATE_CONNECTING) 848 .isEqualTo(mService.getConnectionState(device)); 849 850 // Send a message to trigger connection completed 851 connCompletedEvent = new LeAudioStackEvent( 852 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 853 connCompletedEvent.device = device; 854 connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED; 855 mService.messageFromNative(connCompletedEvent); 856 857 // Verify the connection state broadcast, and that we are in Connected state 858 verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTED, 859 BluetoothProfile.STATE_CONNECTING); 860 assertThat(BluetoothProfile.STATE_CONNECTED) 861 .isEqualTo(mService.getConnectionState(device)); 862 863 // Verify that the device is in the list of connected devices 864 assertThat(mService.getConnectedDevices().contains(device)).isTrue(); 865 // Verify the list of previously connected devices 866 for (BluetoothDevice prevDevice : prevConnectedDevices) { 867 assertThat(mService.getConnectedDevices().contains(prevDevice)).isTrue(); 868 } 869 } 870 generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)871 private void generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, 872 int oldConnectionState) { 873 LeAudioStackEvent stackEvent = 874 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 875 stackEvent.device = device; 876 stackEvent.valueInt1 = newConnectionState; 877 mService.messageFromNative(stackEvent); 878 // Verify the connection state broadcast 879 verifyConnectionStateIntent(TIMEOUT_MS, device, newConnectionState, oldConnectionState); 880 } 881 generateUnexpectedConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)882 private void generateUnexpectedConnectionMessageFromNative(BluetoothDevice device, 883 int newConnectionState, int oldConnectionState) { 884 LeAudioStackEvent stackEvent = 885 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 886 stackEvent.device = device; 887 stackEvent.valueInt1 = newConnectionState; 888 mService.messageFromNative(stackEvent); 889 // Verify the connection state broadcast 890 verifyNoConnectionStateIntent(TIMEOUT_MS, device); 891 } 892 generateGroupNodeAdded(BluetoothDevice device, int groupId)893 private void generateGroupNodeAdded(BluetoothDevice device, int groupId) { 894 LeAudioStackEvent nodeGroupAdded = 895 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED); 896 nodeGroupAdded.device = device; 897 nodeGroupAdded.valueInt1 = groupId; 898 nodeGroupAdded.valueInt2 = LeAudioStackEvent.GROUP_NODE_ADDED; 899 mService.messageFromNative(nodeGroupAdded); 900 } 901 generateGroupNodeRemoved(BluetoothDevice device, int groupId)902 private void generateGroupNodeRemoved(BluetoothDevice device, int groupId) { 903 LeAudioStackEvent nodeGroupRemoved = 904 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED); 905 nodeGroupRemoved.device = device; 906 nodeGroupRemoved.valueInt1 = groupId; 907 nodeGroupRemoved.valueInt2 = LeAudioStackEvent.GROUP_NODE_REMOVED; 908 mService.messageFromNative(nodeGroupRemoved); 909 } 910 verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device)911 private void verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device) { 912 Intent intent = TestUtils.waitForNoIntent(timeoutMs, mDeviceQueueMap.get(device)); 913 assertThat(intent).isNull(); 914 } 915 916 /** 917 * Test setting connection policy 918 */ 919 @Test testSetConnectionPolicy()920 public void testSetConnectionPolicy() { 921 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 922 doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); 923 doReturn(true).when(mDatabaseManager).setProfileConnectionPolicy(any(BluetoothDevice.class), 924 anyInt(), anyInt()); 925 when(mVolumeControlService.setConnectionPolicy(any(), anyInt())).thenReturn(true); 926 when(mCsipSetCoordinatorService.setConnectionPolicy(any(), anyInt())).thenReturn(true); 927 when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) 928 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 929 930 assertThat(mService.setConnectionPolicy(mSingleDevice, 931 BluetoothProfile.CONNECTION_POLICY_ALLOWED)).isTrue(); 932 933 // Verify connection policy for CSIP and VCP are also set 934 verify(mVolumeControlService, times(1)).setConnectionPolicy( 935 mSingleDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 936 verify(mCsipSetCoordinatorService, times(1)).setConnectionPolicy( 937 mSingleDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 938 939 // Verify the connection state broadcast, and that we are in Connecting state 940 verifyConnectionStateIntent(TIMEOUT_MS, mSingleDevice, BluetoothProfile.STATE_CONNECTING, 941 BluetoothProfile.STATE_DISCONNECTED); 942 assertThat(BluetoothProfile.STATE_CONNECTING) 943 .isEqualTo(mService.getConnectionState(mSingleDevice)); 944 945 LeAudioStackEvent connCompletedEvent; 946 // Send a message to trigger connection completed 947 connCompletedEvent = new LeAudioStackEvent( 948 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 949 connCompletedEvent.device = mSingleDevice; 950 connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED; 951 mService.messageFromNative(connCompletedEvent); 952 953 // Verify the connection state broadcast, and that we are in Connected state 954 verifyConnectionStateIntent(TIMEOUT_MS, mSingleDevice, BluetoothProfile.STATE_CONNECTED, 955 BluetoothProfile.STATE_CONNECTING); 956 assertThat(BluetoothProfile.STATE_CONNECTED) 957 .isEqualTo(mService.getConnectionState(mSingleDevice)); 958 959 // Set connection policy to forbidden 960 assertThat(mService.setConnectionPolicy(mSingleDevice, 961 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)).isTrue(); 962 963 // Verify connection policy for CSIP and VCP are also set 964 verify(mVolumeControlService, times(1)).setConnectionPolicy( 965 mSingleDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 966 verify(mCsipSetCoordinatorService, times(1)).setConnectionPolicy( 967 mSingleDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); 968 969 // Verify the connection state broadcast, and that we are in Connecting state 970 verifyConnectionStateIntent(TIMEOUT_MS, mSingleDevice, BluetoothProfile.STATE_DISCONNECTING, 971 BluetoothProfile.STATE_CONNECTED); 972 assertThat(BluetoothProfile.STATE_DISCONNECTING) 973 .isEqualTo(mService.getConnectionState(mSingleDevice)); 974 975 // Send a message to trigger disconnection completed 976 connCompletedEvent = new LeAudioStackEvent( 977 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 978 connCompletedEvent.device = mSingleDevice; 979 connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED; 980 mService.messageFromNative(connCompletedEvent); 981 982 // Verify the connection state broadcast, and that we are in Disconnected state 983 verifyConnectionStateIntent(TIMEOUT_MS, mSingleDevice, BluetoothProfile.STATE_DISCONNECTED, 984 BluetoothProfile.STATE_DISCONNECTING); 985 assertThat(BluetoothProfile.STATE_DISCONNECTED) 986 .isEqualTo(mService.getConnectionState(mSingleDevice)); 987 } 988 989 /** 990 * Helper function to connect Test device 991 * 992 * @param device test device 993 */ connectTestDevice(BluetoothDevice device, int GroupId)994 private void connectTestDevice(BluetoothDevice device, int GroupId) { 995 List<BluetoothDevice> prevConnectedDevices = mService.getConnectedDevices(); 996 997 when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO)) 998 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 999 // Send a connect request 1000 assertWithMessage("Connect failed").that(mService.connect(device)).isTrue(); 1001 1002 // Make device bonded 1003 mBondedDevices.add(device); 1004 1005 // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and 1006 // 250ms for processing two messages should be way more than enough. Anything that breaks 1007 // this indicate some breakage in other part of Android OS 1008 1009 verifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device, 1010 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED); 1011 assertThat(BluetoothProfile.STATE_CONNECTING) 1012 .isEqualTo(mService.getConnectionState(device)); 1013 1014 // Use connected event to indicate that device is connected 1015 LeAudioStackEvent connCompletedEvent = 1016 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1017 connCompletedEvent.device = device; 1018 connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED; 1019 mService.messageFromNative(connCompletedEvent); 1020 1021 verifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device, 1022 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING); 1023 1024 assertThat(BluetoothProfile.STATE_CONNECTED) 1025 .isEqualTo(mService.getConnectionState(device)); 1026 1027 LeAudioStackEvent nodeGroupAdded = 1028 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED); 1029 nodeGroupAdded.device = device; 1030 nodeGroupAdded.valueInt1 = GroupId; 1031 nodeGroupAdded.valueInt2 = LeAudioStackEvent.GROUP_NODE_ADDED; 1032 mService.messageFromNative(nodeGroupAdded); 1033 1034 // Verify that the device is in the list of connected devices 1035 assertThat(mService.getConnectedDevices().contains(device)).isTrue(); 1036 // Verify the list of previously connected devices 1037 for (BluetoothDevice prevDevice : prevConnectedDevices) { 1038 assertThat(mService.getConnectedDevices().contains(prevDevice)).isTrue(); 1039 } 1040 } 1041 1042 /** 1043 * Test adding node 1044 */ 1045 @Test testGroupAddRemoveNode()1046 public void testGroupAddRemoveNode() { 1047 int groupId = 1; 1048 1049 doReturn(true).when(mNativeInterface).groupAddNode(groupId, mSingleDevice); 1050 doReturn(true).when(mNativeInterface).groupRemoveNode(groupId, mSingleDevice); 1051 1052 assertThat(mService.groupAddNode(groupId, mSingleDevice)).isTrue(); 1053 assertThat(mService.groupRemoveNode(groupId, mSingleDevice)).isTrue(); 1054 } 1055 1056 /** 1057 * Test setting active device group with Ringtone context 1058 */ 1059 @Test testSetActiveDeviceGroup()1060 public void testSetActiveDeviceGroup() { 1061 int groupId = 1; 1062 /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ 1063 int direction = 1; 1064 int snkAudioLocation = 3; 1065 int srcAudioLocation = 4; 1066 int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; 1067 1068 // Not connected device 1069 assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); 1070 1071 // Connected device 1072 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1073 connectTestDevice(mSingleDevice, testGroupId); 1074 1075 // Add location support 1076 LeAudioStackEvent audioConfChangedEvent = 1077 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); 1078 audioConfChangedEvent.device = mSingleDevice; 1079 audioConfChangedEvent.valueInt1 = direction; 1080 audioConfChangedEvent.valueInt2 = groupId; 1081 audioConfChangedEvent.valueInt3 = snkAudioLocation; 1082 audioConfChangedEvent.valueInt4 = srcAudioLocation; 1083 audioConfChangedEvent.valueInt5 = availableContexts; 1084 mService.messageFromNative(audioConfChangedEvent); 1085 1086 assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); 1087 verify(mNativeInterface).groupSetActive(groupId); 1088 1089 //Set group and device as active 1090 LeAudioStackEvent groupStatusChangedEvent = 1091 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); 1092 groupStatusChangedEvent.valueInt1 = groupId; 1093 groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_ACTIVE; 1094 mService.messageFromNative(groupStatusChangedEvent); 1095 1096 verify(mTbsService).setInbandRingtoneSupport(mSingleDevice); 1097 1098 // no active device 1099 assertThat(mService.removeActiveDevice(false)).isTrue(); 1100 verify(mNativeInterface).groupSetActive(BluetoothLeAudio.GROUP_ID_INVALID); 1101 1102 //Set group and device as inactive active 1103 groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_INACTIVE; 1104 mService.messageFromNative(groupStatusChangedEvent); 1105 1106 verify(mTbsService).clearInbandRingtoneSupport(mSingleDevice); 1107 } 1108 1109 /** 1110 * Test setting active device group without Ringtone context 1111 */ 1112 @Test testSetActiveDeviceGroupWithoutRingtoneContext()1113 public void testSetActiveDeviceGroupWithoutRingtoneContext() { 1114 int groupId = 1; 1115 /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ 1116 int direction = 1; 1117 int snkAudioLocation = 3; 1118 int srcAudioLocation = 4; 1119 int availableContexts = 5; 1120 1121 // Not connected device 1122 assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); 1123 1124 // Connected device 1125 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1126 connectTestDevice(mSingleDevice, testGroupId); 1127 1128 // Add location support 1129 LeAudioStackEvent audioConfChangedEvent = 1130 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); 1131 audioConfChangedEvent.device = mSingleDevice; 1132 audioConfChangedEvent.valueInt1 = direction; 1133 audioConfChangedEvent.valueInt2 = groupId; 1134 audioConfChangedEvent.valueInt3 = snkAudioLocation; 1135 audioConfChangedEvent.valueInt4 = srcAudioLocation; 1136 audioConfChangedEvent.valueInt5 = availableContexts; 1137 mService.messageFromNative(audioConfChangedEvent); 1138 1139 assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); 1140 verify(mNativeInterface).groupSetActive(groupId); 1141 1142 1143 //Set group and device as active 1144 LeAudioStackEvent groupStatusChangedEvent = 1145 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); 1146 groupStatusChangedEvent.valueInt1 = groupId; 1147 groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_ACTIVE; 1148 mService.messageFromNative(groupStatusChangedEvent); 1149 1150 // no active device 1151 assertThat(mService.removeActiveDevice(false)).isTrue(); 1152 verify(mNativeInterface).groupSetActive(BluetoothLeAudio.GROUP_ID_INVALID); 1153 1154 //Set group and device as inactive active 1155 groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_INACTIVE; 1156 mService.messageFromNative(groupStatusChangedEvent); 1157 1158 verify(mTbsService, times(0)).clearInbandRingtoneSupport(mSingleDevice); 1159 } 1160 1161 /** 1162 * Test getting active device 1163 */ 1164 @Test testGetActiveDevices()1165 public void testGetActiveDevices() { 1166 int groupId = 1; 1167 /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ 1168 int direction = 1; 1169 int snkAudioLocation = 3; 1170 int srcAudioLocation = 4; 1171 int availableContexts = 5; 1172 int nodeStatus = LeAudioStackEvent.GROUP_NODE_ADDED; 1173 int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE; 1174 1175 // Single active device 1176 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1177 connectTestDevice(mSingleDevice, testGroupId); 1178 1179 // Add device to group 1180 LeAudioStackEvent nodeStatusChangedEvent = 1181 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED); 1182 nodeStatusChangedEvent.device = mSingleDevice; 1183 nodeStatusChangedEvent.valueInt1 = groupId; 1184 nodeStatusChangedEvent.valueInt2 = nodeStatus; 1185 mService.messageFromNative(nodeStatusChangedEvent); 1186 1187 assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); 1188 1189 // Add location support 1190 LeAudioStackEvent audioConfChangedEvent = 1191 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); 1192 audioConfChangedEvent.device = mSingleDevice; 1193 audioConfChangedEvent.valueInt1 = direction; 1194 audioConfChangedEvent.valueInt2 = groupId; 1195 audioConfChangedEvent.valueInt3 = snkAudioLocation; 1196 audioConfChangedEvent.valueInt4 = srcAudioLocation; 1197 audioConfChangedEvent.valueInt5 = availableContexts; 1198 mService.messageFromNative(audioConfChangedEvent); 1199 1200 // Set group and device as active 1201 LeAudioStackEvent groupStatusChangedEvent = 1202 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); 1203 groupStatusChangedEvent.device = mSingleDevice; 1204 groupStatusChangedEvent.valueInt1 = groupId; 1205 groupStatusChangedEvent.valueInt2 = groupStatus; 1206 mService.messageFromNative(groupStatusChangedEvent); 1207 1208 assertThat(mService.getActiveDevices().contains(mSingleDevice)).isTrue(); 1209 1210 // Remove device from group 1211 groupStatusChangedEvent = 1212 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED); 1213 groupStatusChangedEvent.device = mSingleDevice; 1214 groupStatusChangedEvent.valueInt1 = groupId; 1215 groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_NODE_REMOVED; 1216 mService.messageFromNative(groupStatusChangedEvent); 1217 1218 assertThat(mService.getActiveDevices().contains(mSingleDevice)).isFalse(); 1219 } 1220 injectGroupStatusChange(int groupId, int groupStatus)1221 private void injectGroupStatusChange(int groupId, int groupStatus) { 1222 int eventType = LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED; 1223 LeAudioStackEvent groupStatusChangedEvent = new LeAudioStackEvent(eventType); 1224 groupStatusChangedEvent.valueInt1 = groupId; 1225 groupStatusChangedEvent.valueInt2 = groupStatus; 1226 mService.messageFromNative(groupStatusChangedEvent); 1227 } 1228 injectAudioConfChanged(int groupId, Integer availableContexts, int direction)1229 private void injectAudioConfChanged(int groupId, Integer availableContexts, int direction) { 1230 int snkAudioLocation = 3; 1231 int srcAudioLocation = 4; 1232 int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED; 1233 1234 // Add device to group 1235 LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(eventType); 1236 audioConfChangedEvent.device = mSingleDevice; 1237 audioConfChangedEvent.valueInt1 = direction; 1238 audioConfChangedEvent.valueInt2 = groupId; 1239 audioConfChangedEvent.valueInt3 = snkAudioLocation; 1240 audioConfChangedEvent.valueInt4 = srcAudioLocation; 1241 audioConfChangedEvent.valueInt5 = availableContexts; 1242 mService.messageFromNative(audioConfChangedEvent); 1243 } 1244 /** 1245 * Test native interface audio configuration changed message handling 1246 */ 1247 @Test testMessageFromNativeAudioConfChangedActiveGroup()1248 public void testMessageFromNativeAudioConfChangedActiveGroup() { 1249 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1250 connectTestDevice(mSingleDevice, testGroupId); 1251 injectAudioConfChanged(testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | 1252 BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3); 1253 injectGroupStatusChange(testGroupId, BluetoothLeAudio.GROUP_STATUS_ACTIVE); 1254 1255 /* Expect 2 calles to Audio Manager - one for output and second for input as this is 1256 * Conversational use case */ 1257 verify(mAudioManager, times(2)).handleBluetoothActiveDeviceChanged(any(), any(), 1258 any(BluetoothProfileConnectionInfo.class)); 1259 /* Since LeAudioService called AudioManager - assume Audio manager calles properly callback 1260 * mAudioManager.onAudioDeviceAdded 1261 */ 1262 mService.notifyActiveDeviceChanged(mSingleDevice); 1263 1264 String action = BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED; 1265 Intent intent = TestUtils.waitForIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice)); 1266 assertThat(intent).isNotNull(); 1267 assertThat(action).isEqualTo(intent.getAction()); 1268 assertThat(mSingleDevice) 1269 .isEqualTo(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); 1270 } 1271 /** 1272 * Test native interface audio configuration changed message handling 1273 */ 1274 @Test testMessageFromNativeAudioConfChangedInactiveGroup()1275 public void testMessageFromNativeAudioConfChangedInactiveGroup() { 1276 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1277 connectTestDevice(mSingleDevice, testGroupId); 1278 1279 String action = BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED; 1280 Integer contexts = BluetoothLeAudio.CONTEXT_TYPE_MEDIA | 1281 BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL; 1282 injectAudioConfChanged(testGroupId, contexts, 3); 1283 1284 Intent intent = TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice)); 1285 assertThat(intent).isNull(); 1286 } 1287 /** 1288 * Test native interface audio configuration changed message handling 1289 */ 1290 @Test testMessageFromNativeAudioConfChangedNoGroupChanged()1291 public void testMessageFromNativeAudioConfChangedNoGroupChanged() { 1292 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1293 connectTestDevice(mSingleDevice, testGroupId); 1294 1295 String action = BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED; 1296 1297 injectAudioConfChanged(testGroupId, 0, 3); 1298 Intent intent = TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice)); 1299 assertThat(intent).isNull(); 1300 } 1301 1302 sendEventAndVerifyIntentForGroupStatusChanged(int groupId, int groupStatus)1303 private void sendEventAndVerifyIntentForGroupStatusChanged(int groupId, int groupStatus) { 1304 1305 onGroupStatusCallbackCalled = false; 1306 1307 IBluetoothLeAudioCallback leAudioCallbacks = 1308 new IBluetoothLeAudioCallback.Stub() { 1309 @Override 1310 public void onCodecConfigChanged(int gid, BluetoothLeAudioCodecStatus status) {} 1311 @Override 1312 public void onGroupStatusChanged(int gid, int gStatus) { 1313 onGroupStatusCallbackCalled = true; 1314 assertThat(gid == groupId).isTrue(); 1315 assertThat(gStatus == groupStatus).isTrue(); 1316 } 1317 @Override 1318 public void onGroupNodeAdded(BluetoothDevice device, int gid) {} 1319 @Override 1320 public void onGroupNodeRemoved(BluetoothDevice device, int gid) {} 1321 }; 1322 1323 mService.mLeAudioCallbacks.register(leAudioCallbacks); 1324 1325 injectGroupStatusChange(groupId, groupStatus); 1326 1327 TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); 1328 assertThat(onGroupStatusCallbackCalled).isTrue(); 1329 1330 onGroupStatusCallbackCalled = false; 1331 mService.mLeAudioCallbacks.unregister(leAudioCallbacks); 1332 } 1333 1334 /** 1335 * Test native interface group status message handling 1336 */ 1337 @Test testMessageFromNativeGroupStatusChanged()1338 public void testMessageFromNativeGroupStatusChanged() { 1339 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1340 connectTestDevice(mSingleDevice, testGroupId); 1341 1342 injectAudioConfChanged(testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | 1343 BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3); 1344 1345 sendEventAndVerifyIntentForGroupStatusChanged(testGroupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); 1346 sendEventAndVerifyIntentForGroupStatusChanged(testGroupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE); 1347 } 1348 injectLocalCodecConfigCapaChanged(List<BluetoothLeAudioCodecConfig> inputCodecCapa, List<BluetoothLeAudioCodecConfig> outputCodecCapa)1349 private void injectLocalCodecConfigCapaChanged(List<BluetoothLeAudioCodecConfig> inputCodecCapa, 1350 List<BluetoothLeAudioCodecConfig> outputCodecCapa) { 1351 int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED; 1352 1353 // Add device to group 1354 LeAudioStackEvent localCodecCapaEvent = new LeAudioStackEvent(eventType); 1355 localCodecCapaEvent.valueCodecList1 = inputCodecCapa; 1356 localCodecCapaEvent.valueCodecList2 = outputCodecCapa; 1357 mService.messageFromNative(localCodecCapaEvent); 1358 } 1359 injectGroupCodecConfigChanged(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig, List<BluetoothLeAudioCodecConfig> inputSelectableCodecConfig, List<BluetoothLeAudioCodecConfig> outputSelectableCodecConfig)1360 private void injectGroupCodecConfigChanged(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, 1361 BluetoothLeAudioCodecConfig outputCodecConfig, 1362 List<BluetoothLeAudioCodecConfig> inputSelectableCodecConfig, 1363 List<BluetoothLeAudioCodecConfig> outputSelectableCodecConfig) { 1364 int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED; 1365 1366 // Add device to group 1367 LeAudioStackEvent groupCodecConfigChangedEvent = new LeAudioStackEvent(eventType); 1368 groupCodecConfigChangedEvent.valueInt1 = groupId; 1369 groupCodecConfigChangedEvent.valueCodec1 = inputCodecConfig; 1370 groupCodecConfigChangedEvent.valueCodec2 = outputCodecConfig; 1371 groupCodecConfigChangedEvent.valueCodecList1 = inputSelectableCodecConfig; 1372 groupCodecConfigChangedEvent.valueCodecList2 = outputSelectableCodecConfig; 1373 mService.messageFromNative(groupCodecConfigChangedEvent); 1374 } 1375 1376 /** 1377 * Test native interface group status message handling 1378 */ 1379 @Test testMessageFromNativeGroupCodecConfigChanged()1380 public void testMessageFromNativeGroupCodecConfigChanged() { 1381 onGroupCodecConfChangedCallbackCalled = false; 1382 1383 injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG); 1384 1385 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1386 connectTestDevice(mSingleDevice, testGroupId); 1387 1388 testCodecStatus = new BluetoothLeAudioCodecStatus(LC3_16KHZ_CONFIG, 1389 LC3_48KHZ_CONFIG, INPUT_CAPABILITIES_CONFIG, 1390 OUTPUT_CAPABILITIES_CONFIG, INPUT_SELECTABLE_CONFIG, 1391 OUTPUT_SELECTABLE_CONFIG); 1392 1393 IBluetoothLeAudioCallback leAudioCallbacks = 1394 new IBluetoothLeAudioCallback.Stub() { 1395 @Override 1396 public void onCodecConfigChanged(int gid, BluetoothLeAudioCodecStatus status) { 1397 onGroupCodecConfChangedCallbackCalled = true; 1398 assertThat(status.equals(testCodecStatus)).isTrue(); 1399 } 1400 @Override 1401 public void onGroupStatusChanged(int gid, int gStatus) {} 1402 @Override 1403 public void onGroupNodeAdded(BluetoothDevice device, int gid) {} 1404 @Override 1405 public void onGroupNodeRemoved(BluetoothDevice device, int gid) {} 1406 }; 1407 1408 mService.mLeAudioCallbacks.register(leAudioCallbacks); 1409 1410 injectGroupCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG, 1411 INPUT_SELECTABLE_CONFIG, 1412 OUTPUT_SELECTABLE_CONFIG); 1413 1414 TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); 1415 assertThat(onGroupCodecConfChangedCallbackCalled).isTrue(); 1416 1417 onGroupCodecConfChangedCallbackCalled = false; 1418 mService.mLeAudioCallbacks.unregister(leAudioCallbacks); 1419 } 1420 verifyActiveDeviceStateIntent(int timeoutMs, BluetoothDevice device)1421 private void verifyActiveDeviceStateIntent(int timeoutMs, BluetoothDevice device) { 1422 Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device)); 1423 assertThat(intent).isNotNull(); 1424 assertThat(intent.getAction()) 1425 .isEqualTo(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); 1426 assertThat(device).isEqualTo(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)); 1427 } 1428 1429 /** 1430 * Test native interface group status message handling 1431 */ 1432 @Test testLeadGroupDeviceDisconnects()1433 public void testLeadGroupDeviceDisconnects() { 1434 int groupId = 1; 1435 /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ 1436 int direction = 1; 1437 int snkAudioLocation = 3; 1438 int srcAudioLocation = 4; 1439 int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;; 1440 int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE; 1441 BluetoothDevice leadDevice; 1442 BluetoothDevice memberDevice = mLeftDevice; 1443 1444 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1445 connectTestDevice(mLeftDevice, groupId); 1446 connectTestDevice(mRightDevice, groupId); 1447 1448 leadDevice = mService.getConnectedGroupLeadDevice(groupId); 1449 if (Objects.equals(leadDevice, mLeftDevice)) { 1450 memberDevice = mRightDevice; 1451 } 1452 1453 assertThat(mService.setActiveDevice(leadDevice)).isTrue(); 1454 1455 //Add location support 1456 LeAudioStackEvent audioConfChangedEvent = 1457 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); 1458 audioConfChangedEvent.valueInt1 = direction; 1459 audioConfChangedEvent.valueInt2 = groupId; 1460 audioConfChangedEvent.valueInt3 = snkAudioLocation; 1461 audioConfChangedEvent.valueInt4 = srcAudioLocation; 1462 audioConfChangedEvent.valueInt5 = availableContexts; 1463 mService.messageFromNative(audioConfChangedEvent); 1464 1465 //Set group and device as active 1466 LeAudioStackEvent groupStatusChangedEvent = 1467 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); 1468 groupStatusChangedEvent.valueInt1 = groupId; 1469 groupStatusChangedEvent.valueInt2 = groupStatus; 1470 mService.messageFromNative(groupStatusChangedEvent); 1471 1472 assertThat(mService.getActiveDevices().contains(leadDevice)).isTrue(); 1473 verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(leadDevice), any(), 1474 any(BluetoothProfileConnectionInfo.class)); 1475 /* Since LeAudioService called AudioManager - assume Audio manager calles properly callback 1476 * mAudioManager.onAudioDeviceAdded 1477 */ 1478 mService.notifyActiveDeviceChanged(leadDevice); 1479 doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService).getBondState(leadDevice); 1480 verifyActiveDeviceStateIntent(AUDIO_MANAGER_DEVICE_ADD_TIMEOUT_MS, leadDevice); 1481 injectNoVerifyDeviceDisconnected(leadDevice); 1482 1483 // We should not change the audio device 1484 assertThat(mService.getConnectionState(leadDevice)) 1485 .isEqualTo(BluetoothProfile.STATE_CONNECTED); 1486 1487 injectAndVerifyDeviceDisconnected(memberDevice); 1488 1489 // Verify the connection state broadcast, and that we are in Connecting state 1490 verifyConnectionStateIntent(TIMEOUT_MS, leadDevice, BluetoothProfile.STATE_DISCONNECTED, 1491 BluetoothProfile.STATE_CONNECTED); 1492 1493 verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(any(), eq(leadDevice), 1494 any(BluetoothProfileConnectionInfo.class)); 1495 1496 verify(mTbsService).setInbandRingtoneSupport(mLeftDevice); 1497 verify(mTbsService).setInbandRingtoneSupport(mRightDevice); 1498 } 1499 1500 /** 1501 * Test native interface group status message handling 1502 */ 1503 @Test testLeadGroupDeviceReconnects()1504 public void testLeadGroupDeviceReconnects() { 1505 int groupId = 1; 1506 /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ 1507 int direction = 1; 1508 int snkAudioLocation = 3; 1509 int srcAudioLocation = 4; 1510 int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;; 1511 int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE; 1512 BluetoothDevice leadDevice; 1513 BluetoothDevice memberDevice = mLeftDevice; 1514 1515 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1516 connectTestDevice(mLeftDevice, groupId); 1517 connectTestDevice(mRightDevice, groupId); 1518 1519 leadDevice = mService.getConnectedGroupLeadDevice(groupId); 1520 if (Objects.equals(leadDevice, mLeftDevice)) { 1521 memberDevice = mRightDevice; 1522 } 1523 1524 assertThat(mService.setActiveDevice(leadDevice)).isTrue(); 1525 1526 //Add location support 1527 LeAudioStackEvent audioConfChangedEvent = 1528 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); 1529 audioConfChangedEvent.valueInt1 = direction; 1530 audioConfChangedEvent.valueInt2 = groupId; 1531 audioConfChangedEvent.valueInt3 = snkAudioLocation; 1532 audioConfChangedEvent.valueInt4 = srcAudioLocation; 1533 audioConfChangedEvent.valueInt5 = availableContexts; 1534 mService.messageFromNative(audioConfChangedEvent); 1535 1536 //Set group and device as active 1537 LeAudioStackEvent groupStatusChangedEvent = 1538 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); 1539 groupStatusChangedEvent.valueInt1 = groupId; 1540 groupStatusChangedEvent.valueInt2 = groupStatus; 1541 mService.messageFromNative(groupStatusChangedEvent); 1542 1543 assertThat(mService.getActiveDevices().contains(leadDevice)).isTrue(); 1544 verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(leadDevice), any(), 1545 any(BluetoothProfileConnectionInfo.class)); 1546 /* Since LeAudioService called AudioManager - assume Audio manager calles properly callback 1547 * mAudioManager.onAudioDeviceAdded 1548 */ 1549 mService.notifyActiveDeviceChanged(leadDevice); 1550 1551 verifyActiveDeviceStateIntent(AUDIO_MANAGER_DEVICE_ADD_TIMEOUT_MS, leadDevice); 1552 /* We don't want to distribute DISCONNECTION event, instead will try to reconnect 1553 * (in native) 1554 */ 1555 injectNoVerifyDeviceDisconnected(leadDevice); 1556 assertThat(mService.getConnectionState(leadDevice)) 1557 .isEqualTo(BluetoothProfile.STATE_CONNECTED); 1558 1559 /* Reconnect device, there should be no intent about that, as device was pretending 1560 * connected 1561 */ 1562 injectNoVerifyDeviceConnected(leadDevice); 1563 1564 injectAndVerifyDeviceDisconnected(memberDevice); 1565 injectAndVerifyDeviceDisconnected(leadDevice); 1566 1567 verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(null), eq(leadDevice), 1568 any(BluetoothProfileConnectionInfo.class)); 1569 1570 verify(mTbsService).setInbandRingtoneSupport(mLeftDevice); 1571 verify(mTbsService).setInbandRingtoneSupport(mRightDevice); 1572 } 1573 1574 /** 1575 * Test volume caching for the group 1576 */ 1577 @Test testVolumeCache()1578 public void testVolumeCache() { 1579 int groupId = 1; 1580 int volume = 100; 1581 /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ 1582 int direction = 1; 1583 int availableContexts = 4; 1584 1585 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1586 connectTestDevice(mLeftDevice, groupId); 1587 connectTestDevice(mRightDevice, groupId); 1588 1589 assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); 1590 1591 ArgumentCaptor<BluetoothProfileConnectionInfo> profileInfo = 1592 ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class); 1593 1594 //Add location support. 1595 injectAudioConfChanged(groupId, availableContexts, direction); 1596 1597 doReturn(-1).when(mVolumeControlService).getAudioDeviceGroupVolume(groupId); 1598 //Set group and device as active. 1599 injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); 1600 1601 verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(any(), eq(null), 1602 profileInfo.capture()); 1603 assertThat(profileInfo.getValue().getVolume()).isEqualTo(-1); 1604 1605 mService.setVolume(volume); 1606 verify(mVolumeControlService, times(1)).setGroupVolume(groupId, volume); 1607 1608 // Set group to inactive. 1609 injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE); 1610 1611 verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(null), any(), 1612 any(BluetoothProfileConnectionInfo.class)); 1613 1614 doReturn(100).when(mVolumeControlService).getAudioDeviceGroupVolume(groupId); 1615 1616 //Set back to active and check if last volume is restored. 1617 injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); 1618 1619 verify(mAudioManager, times(2)).handleBluetoothActiveDeviceChanged(any(), eq(null), 1620 profileInfo.capture()); 1621 1622 assertThat(profileInfo.getValue().getVolume()).isEqualTo(volume); 1623 } 1624 1625 @Test testGetAudioDeviceGroupVolume_whenVolumeControlServiceIsNull()1626 public void testGetAudioDeviceGroupVolume_whenVolumeControlServiceIsNull() { 1627 mService.mVolumeControlService = null; 1628 doReturn(null).when(mServiceFactory).getVolumeControlService(); 1629 1630 int groupId = 1; 1631 assertThat(mService.getAudioDeviceGroupVolume(groupId)).isEqualTo(-1); 1632 } 1633 1634 @Test testGetAudioLocation()1635 public void testGetAudioLocation() { 1636 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1637 connectTestDevice(mSingleDevice, testGroupId); 1638 1639 assertThat(mService.getAudioLocation(null)) 1640 .isEqualTo(BluetoothLeAudio.AUDIO_LOCATION_INVALID); 1641 1642 int sinkAudioLocation = 10; 1643 LeAudioStackEvent stackEvent = 1644 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE); 1645 stackEvent.device = mSingleDevice; 1646 stackEvent.valueInt1 = sinkAudioLocation; 1647 mService.messageFromNative(stackEvent); 1648 1649 assertThat(mService.getAudioLocation(mSingleDevice)).isEqualTo(sinkAudioLocation); 1650 } 1651 1652 @Test testGetConnectedPeerDevices()1653 public void testGetConnectedPeerDevices() { 1654 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1655 connectTestDevice(mLeftDevice, testGroupId); 1656 connectTestDevice(mRightDevice, testGroupId); 1657 1658 List<BluetoothDevice> peerDevices = mService.getConnectedPeerDevices(testGroupId); 1659 assertThat(peerDevices.contains(mLeftDevice)).isTrue(); 1660 assertThat(peerDevices.contains(mRightDevice)).isTrue(); 1661 } 1662 1663 @Test testGetDevicesMatchingConnectionStates()1664 public void testGetDevicesMatchingConnectionStates() { 1665 assertThat(mService.getDevicesMatchingConnectionStates(null)).isEmpty(); 1666 1667 int[] states = new int[] { BluetoothProfile.STATE_CONNECTED }; 1668 doReturn(null).when(mAdapterService).getBondedDevices(); 1669 assertThat(mService.getDevicesMatchingConnectionStates(states)).isEmpty(); 1670 1671 doReturn(new BluetoothDevice[] { mSingleDevice }).when(mAdapterService).getBondedDevices(); 1672 assertThat(mService.getDevicesMatchingConnectionStates(states)).isEmpty(); 1673 } 1674 1675 @Test testDefaultValuesOfSeveralGetters()1676 public void testDefaultValuesOfSeveralGetters() { 1677 assertThat(mService.getMaximumNumberOfBroadcasts()).isEqualTo(1); 1678 assertThat(mService.getMaximumStreamsPerBroadcast()).isEqualTo(1); 1679 assertThat(mService.getMaximumSubgroupsPerBroadcast()).isEqualTo(1); 1680 assertThat(mService.isPlaying(100)).isFalse(); 1681 assertThat(mService.isValidDeviceGroup(LE_AUDIO_GROUP_ID_INVALID)).isFalse(); 1682 } 1683 1684 @Test testHandleGroupIdleDuringCall()1685 public void testHandleGroupIdleDuringCall() { 1686 BluetoothDevice headsetDevice = TestUtils.getTestDevice(mAdapter, 5); 1687 HeadsetService headsetService = Mockito.mock(HeadsetService.class); 1688 when(mServiceFactory.getHeadsetService()).thenReturn(headsetService); 1689 1690 mService.mHfpHandoverDevice = null; 1691 mService.handleGroupIdleDuringCall(); 1692 verify(headsetService, never()).getActiveDevice(); 1693 1694 mService.mHfpHandoverDevice = headsetDevice; 1695 when(headsetService.getActiveDevice()).thenReturn(headsetDevice); 1696 mService.handleGroupIdleDuringCall(); 1697 verify(headsetService).connectAudio(); 1698 assertThat(mService.mHfpHandoverDevice).isNull(); 1699 1700 mService.mHfpHandoverDevice = headsetDevice; 1701 when(headsetService.getActiveDevice()).thenReturn(null); 1702 mService.handleGroupIdleDuringCall(); 1703 verify(headsetService).setActiveDevice(headsetDevice); 1704 assertThat(mService.mHfpHandoverDevice).isNull(); 1705 } 1706 1707 @Test testDump_doesNotCrash()1708 public void testDump_doesNotCrash() { 1709 doReturn(new ParcelUuid[]{BluetoothUuid.LE_AUDIO}).when(mAdapterService) 1710 .getRemoteUuids(any(BluetoothDevice.class)); 1711 doReturn(new BluetoothDevice[]{mSingleDevice}).when(mAdapterService).getBondedDevices(); 1712 when(mDatabaseManager 1713 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) 1714 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED); 1715 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1716 doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); 1717 1718 connectTestDevice(mSingleDevice, testGroupId); 1719 1720 StringBuilder sb = new StringBuilder(); 1721 mService.dump(sb); 1722 } 1723 1724 /** 1725 * Test setting authorization for LeAudio device in the McpService 1726 */ 1727 @Test testAuthorizeMcpServiceWhenDeviceConnecting()1728 public void testAuthorizeMcpServiceWhenDeviceConnecting() { 1729 int groupId = 1; 1730 1731 mService.handleBluetoothEnabled(); 1732 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1733 connectTestDevice(mLeftDevice, groupId); 1734 connectTestDevice(mRightDevice, groupId); 1735 verify(mMcpService, times(1)).setDeviceAuthorized(mLeftDevice, true); 1736 verify(mMcpService, times(1)).setDeviceAuthorized(mRightDevice, true); 1737 } 1738 1739 /** 1740 * Test setting authorization for LeAudio device in the McpService 1741 */ 1742 @Test testAuthorizeMcpServiceOnBluetoothEnableAndNodeRemoval()1743 public void testAuthorizeMcpServiceOnBluetoothEnableAndNodeRemoval() { 1744 int groupId = 1; 1745 1746 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1747 connectTestDevice(mLeftDevice, groupId); 1748 connectTestDevice(mRightDevice, groupId); 1749 1750 generateGroupNodeAdded(mLeftDevice, groupId); 1751 generateGroupNodeAdded(mRightDevice, groupId); 1752 1753 verify(mMcpService, times(0)).setDeviceAuthorized(mLeftDevice, true); 1754 verify(mMcpService, times(0)).setDeviceAuthorized(mRightDevice, true); 1755 1756 mService.handleBluetoothEnabled(); 1757 1758 verify(mMcpService, times(1)).setDeviceAuthorized(mLeftDevice, true); 1759 verify(mMcpService, times(1)).setDeviceAuthorized(mRightDevice, true); 1760 1761 generateGroupNodeRemoved(mLeftDevice, groupId); 1762 verify(mMcpService, times(1)).setDeviceAuthorized(mLeftDevice, false); 1763 1764 generateGroupNodeRemoved(mRightDevice, groupId); 1765 verify(mMcpService, times(1)).setDeviceAuthorized(mRightDevice, false); 1766 } 1767 1768 /** 1769 * Test verifying that when the LE Audio connection policy of a device is set to 1770 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, we unauthorize McpService and 1771 * TbsService. When the LE Audio connection policy is set to 1772 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, we will authorize these services. 1773 */ 1774 @Test testMcsAndTbsAuthorizationWithConnectionPolicy()1775 public void testMcsAndTbsAuthorizationWithConnectionPolicy() { 1776 int groupId = 1; 1777 1778 mService.handleBluetoothEnabled(); 1779 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1780 doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class)); 1781 doReturn(true).when(mDatabaseManager).setProfileConnectionPolicy(any(BluetoothDevice.class), 1782 anyInt(), anyInt()); 1783 when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO)) 1784 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); 1785 1786 // Ensures GATT server services are not authorized when the device does not have a group 1787 assertThat(mService.setConnectionPolicy(mSingleDevice, 1788 BluetoothProfile.CONNECTION_POLICY_ALLOWED)).isTrue(); 1789 verify(mMcpService, never()).setDeviceAuthorized(mSingleDevice, false); 1790 verify(mTbsService, never()).setDeviceAuthorized(mSingleDevice, false); 1791 1792 // Connects the test device and verifies GATT server services are authorized 1793 connectTestDevice(mSingleDevice, groupId); 1794 verify(mMcpService, times(1)).setDeviceAuthorized(mSingleDevice, true); 1795 verify(mTbsService, times(1)).setDeviceAuthorized(mSingleDevice, true); 1796 1797 // Ensure that disconnecting unauthorizes GATT server services 1798 assertThat(mService.setConnectionPolicy(mSingleDevice, 1799 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)).isTrue(); 1800 verify(mMcpService, times(1)).setDeviceAuthorized(mSingleDevice, false); 1801 verify(mTbsService, times(1)).setDeviceAuthorized(mSingleDevice, false); 1802 1803 // Connecting a device that has a group re-authorizes the GATT server services 1804 assertThat(mService.setConnectionPolicy(mSingleDevice, 1805 BluetoothProfile.CONNECTION_POLICY_ALLOWED)).isTrue(); 1806 verify(mMcpService, times(2)).setDeviceAuthorized(mSingleDevice, true); 1807 verify(mTbsService, times(2)).setDeviceAuthorized(mSingleDevice, true); 1808 } 1809 1810 @Test testGetGroupDevices()1811 public void testGetGroupDevices() { 1812 int firstGroupId = 1; 1813 int secondGroupId = 2; 1814 1815 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1816 connectTestDevice(mLeftDevice, firstGroupId); 1817 connectTestDevice(mRightDevice, firstGroupId); 1818 connectTestDevice(mSingleDevice, secondGroupId); 1819 1820 // Checks group device lists for groupId 1 1821 List<BluetoothDevice> firstGroupDevicesById = mService.getGroupDevices(firstGroupId); 1822 List<BluetoothDevice> firstGroupDevicesByLeftDevice = mService.getGroupDevices(mLeftDevice); 1823 List<BluetoothDevice> firstGroupDevicesByRightDevice = mService.getGroupDevices( 1824 mRightDevice); 1825 1826 assertThat(firstGroupDevicesById.size()).isEqualTo(2); 1827 assertThat(firstGroupDevicesById.contains(mLeftDevice)).isTrue(); 1828 assertThat(firstGroupDevicesById.contains(mRightDevice)).isTrue(); 1829 assertThat(firstGroupDevicesById.contains(mSingleDevice)).isFalse(); 1830 assertThat(firstGroupDevicesById.equals(firstGroupDevicesByLeftDevice)).isTrue(); 1831 assertThat(firstGroupDevicesById.equals(firstGroupDevicesByRightDevice)).isTrue(); 1832 1833 // Checks group device lists for groupId 2 1834 List<BluetoothDevice> secondGroupDevicesById = mService.getGroupDevices(secondGroupId); 1835 List<BluetoothDevice> secondGroupDevicesByDevice = mService.getGroupDevices(mSingleDevice); 1836 1837 assertThat(secondGroupDevicesById.size()).isEqualTo(1); 1838 assertThat(secondGroupDevicesById.contains(mSingleDevice)).isTrue(); 1839 assertThat(secondGroupDevicesById.contains(mLeftDevice)).isFalse(); 1840 assertThat(secondGroupDevicesById.contains(mRightDevice)).isFalse(); 1841 assertThat(secondGroupDevicesById.equals(secondGroupDevicesByDevice)).isTrue(); 1842 } 1843 1844 /** 1845 * Tests that {@link LeAudioService#sendPreferredAudioProfileChangeToAudioFramework()} sends 1846 * requests to the audio framework for each active LEA device. 1847 */ 1848 @Test testSendPreferredAudioProfileChangeToAudioFramework()1849 public void testSendPreferredAudioProfileChangeToAudioFramework() { 1850 when(mAdapterService.isAllSupportedClassicAudioProfilesActive(any())).thenReturn(true); 1851 1852 // TEST 1: Verify no requests are sent to the audio framework if there is no active device 1853 assertThat(mService.removeActiveDevice(false)).isTrue(); 1854 List<BluetoothDevice> activeDevices = mService.getActiveDevices(); 1855 assertThat(activeDevices.get(0)).isNull(); 1856 assertThat(activeDevices.get(1)).isNull(); 1857 assertThat(mService.sendPreferredAudioProfileChangeToAudioFramework()).isEqualTo(0); 1858 1859 // TEST 2: Verify we send one request for each active direction 1860 int groupId = 1; 1861 /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 | AUDIO_DIRECTION_INPUT_BIT = 0x02; */ 1862 int direction = 3; 1863 int snkAudioLocation = 3; 1864 int srcAudioLocation = 4; 1865 int availableContexts = 5; 1866 int nodeStatus = LeAudioStackEvent.GROUP_NODE_ADDED; 1867 int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE; 1868 1869 // Single active device 1870 doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); 1871 connectTestDevice(mSingleDevice, testGroupId); 1872 1873 // Add device to group 1874 LeAudioStackEvent nodeStatusChangedEvent = 1875 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED); 1876 nodeStatusChangedEvent.device = mSingleDevice; 1877 nodeStatusChangedEvent.valueInt1 = groupId; 1878 nodeStatusChangedEvent.valueInt2 = nodeStatus; 1879 mService.messageFromNative(nodeStatusChangedEvent); 1880 1881 assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); 1882 1883 // Add location support 1884 LeAudioStackEvent audioConfChangedEvent = 1885 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); 1886 audioConfChangedEvent.device = mSingleDevice; 1887 audioConfChangedEvent.valueInt1 = direction; 1888 audioConfChangedEvent.valueInt2 = groupId; 1889 audioConfChangedEvent.valueInt3 = snkAudioLocation; 1890 audioConfChangedEvent.valueInt4 = srcAudioLocation; 1891 audioConfChangedEvent.valueInt5 = availableContexts; 1892 mService.messageFromNative(audioConfChangedEvent); 1893 1894 // Set group and device as active 1895 LeAudioStackEvent groupStatusChangedEvent = 1896 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED); 1897 groupStatusChangedEvent.device = mSingleDevice; 1898 groupStatusChangedEvent.valueInt1 = groupId; 1899 groupStatusChangedEvent.valueInt2 = groupStatus; 1900 mService.messageFromNative(groupStatusChangedEvent); 1901 1902 assertThat(mService.getActiveDevices().contains(mSingleDevice)).isTrue(); 1903 assertThat(mService.sendPreferredAudioProfileChangeToAudioFramework()).isEqualTo(2); 1904 } 1905 } 1906