1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.bass_client; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 import static android.bluetooth.BluetoothGatt.GATT_FAILURE; 21 import static android.bluetooth.BluetoothGatt.GATT_SUCCESS; 22 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; 23 import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; 24 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; 25 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING; 26 27 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; 28 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; 29 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasFlag; 30 31 import static com.android.bluetooth.TestUtils.MockitoRule; 32 import static com.android.bluetooth.TestUtils.getTestDevice; 33 import static com.android.bluetooth.Utils.joinUninterruptibly; 34 import static com.android.bluetooth.bass_client.BassClientStateMachine.ADD_BCAST_SOURCE; 35 import static com.android.bluetooth.bass_client.BassClientStateMachine.CANCEL_PENDING_SOURCE_OPERATION; 36 import static com.android.bluetooth.bass_client.BassClientStateMachine.CONNECT; 37 import static com.android.bluetooth.bass_client.BassClientStateMachine.CONNECTION_STATE_CHANGED; 38 import static com.android.bluetooth.bass_client.BassClientStateMachine.CONNECT_TIMEOUT; 39 import static com.android.bluetooth.bass_client.BassClientStateMachine.DISCONNECT; 40 import static com.android.bluetooth.bass_client.BassClientStateMachine.GATT_TXN_PROCESSED; 41 import static com.android.bluetooth.bass_client.BassClientStateMachine.GATT_TXN_TIMEOUT; 42 import static com.android.bluetooth.bass_client.BassClientStateMachine.INITIATE_PA_SYNC_TRANSFER; 43 import static com.android.bluetooth.bass_client.BassClientStateMachine.READ_BASS_CHARACTERISTICS; 44 import static com.android.bluetooth.bass_client.BassClientStateMachine.REMOTE_SCAN_START; 45 import static com.android.bluetooth.bass_client.BassClientStateMachine.REMOTE_SCAN_STOP; 46 import static com.android.bluetooth.bass_client.BassClientStateMachine.REMOVE_BCAST_SOURCE; 47 import static com.android.bluetooth.bass_client.BassClientStateMachine.SET_BCAST_CODE; 48 import static com.android.bluetooth.bass_client.BassClientStateMachine.START_SCAN_OFFLOAD; 49 import static com.android.bluetooth.bass_client.BassClientStateMachine.STOP_SCAN_OFFLOAD; 50 import static com.android.bluetooth.bass_client.BassClientStateMachine.SWITCH_BCAST_SOURCE; 51 import static com.android.bluetooth.bass_client.BassClientStateMachine.UPDATE_BCAST_SOURCE; 52 import static com.android.bluetooth.bass_client.BassConstants.CLIENT_CHARACTERISTIC_CONFIG; 53 54 import static com.google.common.truth.Truth.assertThat; 55 import static com.google.common.truth.Truth.assertWithMessage; 56 57 import static org.mockito.ArgumentMatchers.any; 58 import static org.mockito.ArgumentMatchers.anyBoolean; 59 import static org.mockito.ArgumentMatchers.anyInt; 60 import static org.mockito.ArgumentMatchers.anyString; 61 import static org.mockito.ArgumentMatchers.eq; 62 import static org.mockito.Mockito.after; 63 import static org.mockito.Mockito.anyLong; 64 import static org.mockito.Mockito.atLeast; 65 import static org.mockito.Mockito.doNothing; 66 import static org.mockito.Mockito.doReturn; 67 import static org.mockito.Mockito.inOrder; 68 import static org.mockito.Mockito.never; 69 import static org.mockito.Mockito.timeout; 70 import static org.mockito.Mockito.times; 71 import static org.mockito.Mockito.verify; 72 import static org.mockito.Mockito.when; 73 74 import android.app.BroadcastOptions; 75 import android.bluetooth.BluetoothAdapter; 76 import android.bluetooth.BluetoothDevice; 77 import android.bluetooth.BluetoothGatt; 78 import android.bluetooth.BluetoothGattCallback; 79 import android.bluetooth.BluetoothGattCharacteristic; 80 import android.bluetooth.BluetoothGattDescriptor; 81 import android.bluetooth.BluetoothGattService; 82 import android.bluetooth.BluetoothLeAudioCodecConfigMetadata; 83 import android.bluetooth.BluetoothLeAudioContentMetadata; 84 import android.bluetooth.BluetoothLeBroadcastAssistant; 85 import android.bluetooth.BluetoothLeBroadcastChannel; 86 import android.bluetooth.BluetoothLeBroadcastMetadata; 87 import android.bluetooth.BluetoothLeBroadcastReceiveState; 88 import android.bluetooth.BluetoothLeBroadcastSubgroup; 89 import android.bluetooth.BluetoothManager; 90 import android.bluetooth.BluetoothProfile; 91 import android.bluetooth.BluetoothStatusCodes; 92 import android.content.Context; 93 import android.content.Intent; 94 import android.os.HandlerThread; 95 import android.os.Looper; 96 import android.os.Message; 97 import android.platform.test.annotations.DisableFlags; 98 import android.platform.test.annotations.EnableFlags; 99 import android.platform.test.flag.junit.SetFlagsRule; 100 101 import androidx.test.filters.MediumTest; 102 import androidx.test.platform.app.InstrumentationRegistry; 103 104 import com.android.bluetooth.BluetoothMethodProxy; 105 import com.android.bluetooth.TestUtils; 106 import com.android.bluetooth.Utils; 107 import com.android.bluetooth.btservice.AdapterService; 108 import com.android.bluetooth.btservice.MetricsLogger; 109 import com.android.bluetooth.flags.Flags; 110 111 import com.google.common.primitives.Bytes; 112 113 import org.hamcrest.Matcher; 114 import org.hamcrest.core.AllOf; 115 import org.junit.After; 116 import org.junit.Before; 117 import org.junit.Rule; 118 import org.junit.Test; 119 import org.junit.runner.RunWith; 120 import org.junit.runners.JUnit4; 121 import org.mockito.ArgumentCaptor; 122 import org.mockito.InOrder; 123 import org.mockito.Mock; 124 import org.mockito.Mockito; 125 import org.mockito.hamcrest.MockitoHamcrest; 126 127 import java.util.ArrayList; 128 import java.util.Arrays; 129 import java.util.List; 130 import java.util.Random; 131 import java.util.UUID; 132 133 /** Test cases for {@link BassClientStateMachine}. */ 134 @MediumTest 135 @RunWith(JUnit4.class) 136 public class BassClientStateMachineTest { 137 @Rule public final MockitoRule mMockitoRule = new MockitoRule(); 138 139 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 140 141 @Mock private AdapterService mAdapterService; 142 @Mock private BassClientService mBassClientService; 143 @Mock private BluetoothMethodProxy mMethodProxy; 144 @Mock private MetricsLogger mMetricsLogger; 145 146 private static final int CONNECTION_TIMEOUT_MS = 1_000; 147 private static final int TIMEOUT_MS = 2_000; 148 private static final int NO_TIMEOUT_MS = 0; 149 private static final int WAIT_MS = 1_200; 150 private static final int TEST_BROADCAST_ID = 42; 151 private static final int TEST_SOURCE_ID = 1; 152 private static final int TEST_CHANNEL_INDEX = 1; 153 private static final String EMPTY_BLUETOOTH_DEVICE_ADDRESS = "00:00:00:00:00:00"; 154 private static final byte OPCODE_UPDATE_SOURCE = 0x03; 155 private static final int UPDATE_SOURCE_FIXED_LENGTH = 6; 156 157 private final Context mTargetContext = 158 InstrumentationRegistry.getInstrumentation().getTargetContext(); 159 private final BluetoothAdapter mAdapter = 160 mTargetContext.getSystemService(BluetoothManager.class).getAdapter(); 161 private final BluetoothDevice mTestDevice = getTestDevice(0); 162 private final BluetoothDevice mSourceTestDevice = getTestDevice(1); 163 164 private HandlerThread mHandlerThread; 165 private StubBassClientStateMachine mBassClientStateMachine; 166 private BluetoothDevice mEmptyTestDevice; 167 168 @Before setUp()169 public void setUp() throws Exception { 170 mEmptyTestDevice = mAdapter.getRemoteDevice(EMPTY_BLUETOOTH_DEVICE_ADDRESS); 171 assertThat(mEmptyTestDevice).isNotNull(); 172 173 TestUtils.setAdapterService(mAdapterService); 174 175 BluetoothMethodProxy.setInstanceForTesting(mMethodProxy); 176 doNothing() 177 .when(mMethodProxy) 178 .periodicAdvertisingManagerTransferSync(any(), any(), anyInt(), anyInt()); 179 MetricsLogger.setInstanceForTesting(mMetricsLogger); 180 181 doReturn(mEmptyTestDevice) 182 .when(mAdapterService) 183 .getDeviceFromByte(Utils.getBytesFromAddress(EMPTY_BLUETOOTH_DEVICE_ADDRESS)); 184 doReturn(mBassClientService).when(mBassClientService).getBaseContext(); 185 186 // Set up thread and looper 187 mHandlerThread = new HandlerThread("BassClientStateMachineTestHandlerThread"); 188 mHandlerThread.start(); 189 mBassClientStateMachine = 190 new StubBassClientStateMachine( 191 mTestDevice, 192 mBassClientService, 193 mAdapterService, 194 mHandlerThread.getLooper(), 195 CONNECTION_TIMEOUT_MS); 196 mBassClientStateMachine.start(); 197 } 198 classTypeToConnectionState(Class type)199 private static int classTypeToConnectionState(Class type) { 200 if (type == BassClientStateMachine.Disconnected.class) { 201 return STATE_DISCONNECTED; 202 } else if (type == BassClientStateMachine.Connecting.class) { 203 return STATE_CONNECTING; 204 } else if (type == BassClientStateMachine.Connected.class 205 || type == BassClientStateMachine.ConnectedProcessing.class) { 206 return STATE_CONNECTED; 207 } else { 208 assertWithMessage("Invalid class type given: " + type).fail(); 209 return 0; 210 } 211 } 212 213 @After tearDown()214 public void tearDown() throws Exception { 215 if (mBassClientStateMachine == null) { 216 return; 217 } 218 219 MetricsLogger.setInstanceForTesting(null); 220 mBassClientStateMachine.doQuit(); 221 mHandlerThread.quit(); 222 joinUninterruptibly(mHandlerThread); 223 TestUtils.clearAdapterService(mAdapterService); 224 } 225 226 /** Test that default state is disconnected */ 227 @Test testDefaultDisconnectedState()228 public void testDefaultDisconnectedState() { 229 assertThat(mBassClientStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED); 230 } 231 232 /** 233 * Allow/disallow connection to any device. 234 * 235 * @param allow if true, connection is allowed 236 */ allowConnection(boolean allow)237 private void allowConnection(boolean allow) { 238 when(mBassClientService.okToConnect(any(BluetoothDevice.class))).thenReturn(allow); 239 } 240 allowConnectGatt(boolean allow)241 private void allowConnectGatt(boolean allow) { 242 mBassClientStateMachine.mShouldAllowGatt = allow; 243 } 244 245 /** Test that an incoming connection with policy forbidding connection is rejected */ 246 @Test testOkToConnectFails()247 public void testOkToConnectFails() { 248 allowConnection(false); 249 allowConnectGatt(true); 250 251 // Inject an event for when incoming connection is requested 252 mBassClientStateMachine.sendMessage(CONNECT); 253 254 // Verify that no connection state broadcast is executed 255 verify(mBassClientService, after(WAIT_MS).never()) 256 .sendBroadcast(any(Intent.class), anyString()); 257 258 // Check that we are in Disconnected state 259 assertThat(mBassClientStateMachine.getCurrentState()) 260 .isInstanceOf(BassClientStateMachine.Disconnected.class); 261 } 262 263 @Test testFailToConnectGatt()264 public void testFailToConnectGatt() { 265 allowConnection(true); 266 allowConnectGatt(false); 267 268 // Inject an event for when incoming connection is requested 269 mBassClientStateMachine.sendMessage(CONNECT); 270 271 // Verify that no connection state broadcast is executed 272 verify(mBassClientService, after(WAIT_MS).never()) 273 .sendBroadcast(any(Intent.class), anyString()); 274 275 // Check that we are in Disconnected state 276 assertThat(mBassClientStateMachine.getCurrentState()) 277 .isInstanceOf(BassClientStateMachine.Disconnected.class); 278 assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); 279 } 280 281 @Test testSuccessfullyConnected()282 public void testSuccessfullyConnected() { 283 allowConnection(true); 284 allowConnectGatt(true); 285 286 // Inject an event for when incoming connection is requested 287 mBassClientStateMachine.sendMessage(CONNECT); 288 289 // Verify that one connection state broadcast is executed 290 ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class); 291 verify(mBassClientService, timeout(TIMEOUT_MS)) 292 .sendBroadcastMultiplePermissions( 293 intentArgument1.capture(), 294 any(String[].class), 295 any(BroadcastOptions.class)); 296 assertThat(intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)) 297 .isEqualTo(STATE_CONNECTING); 298 299 assertThat(mBassClientStateMachine.getCurrentState()) 300 .isInstanceOf(BassClientStateMachine.Connecting.class); 301 302 assertThat(mBassClientStateMachine.mGattCallback).isNotNull(); 303 mBassClientStateMachine.notifyConnectionStateChanged(GATT_SUCCESS, STATE_CONNECTED); 304 305 // Verify that the expected number of broadcasts are executed: 306 // - two calls to broadcastConnectionState(): Disconnected -> Connecting -> Connected 307 ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class); 308 verify(mBassClientService, timeout(TIMEOUT_MS).times(2)) 309 .sendBroadcastMultiplePermissions( 310 intentArgument2.capture(), 311 any(String[].class), 312 any(BroadcastOptions.class)); 313 314 assertThat(mBassClientStateMachine.getCurrentState()) 315 .isInstanceOf(BassClientStateMachine.Connected.class); 316 } 317 318 @Test testConnectGattTimeout()319 public void testConnectGattTimeout() { 320 allowConnection(true); 321 allowConnectGatt(true); 322 323 // Inject an event for when incoming connection is requested 324 mBassClientStateMachine.sendMessage(CONNECT); 325 326 // Verify that one connection state broadcast is executed 327 ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class); 328 verify(mBassClientService, timeout(TIMEOUT_MS)) 329 .sendBroadcastMultiplePermissions( 330 intentArgument1.capture(), 331 any(String[].class), 332 any(BroadcastOptions.class)); 333 assertThat(intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)) 334 .isEqualTo(STATE_CONNECTING); 335 336 assertThat(mBassClientStateMachine.getCurrentState()) 337 .isInstanceOf(BassClientStateMachine.Connecting.class); 338 339 // Verify that one connection state broadcast is executed 340 ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class); 341 verify(mBassClientService, timeout(TIMEOUT_MS).times(2)) 342 .sendBroadcastMultiplePermissions( 343 intentArgument2.capture(), 344 any(String[].class), 345 any(BroadcastOptions.class)); 346 assertThat(intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)) 347 .isEqualTo(STATE_DISCONNECTED); 348 349 assertThat(mBassClientStateMachine.getCurrentState()) 350 .isInstanceOf(BassClientStateMachine.Disconnected.class); 351 } 352 353 @Test testStatesChangesWithMessages()354 public void testStatesChangesWithMessages() { 355 allowConnection(true); 356 allowConnectGatt(true); 357 358 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 359 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 360 mBassClientStateMachine.mBluetoothGatt = btGatt; 361 362 assertThat(mBassClientStateMachine.getCurrentState()) 363 .isInstanceOf(BassClientStateMachine.Disconnected.class); 364 365 // disconnected -> connecting ---timeout---> disconnected 366 sendMessageAndVerifyTransition( 367 mBassClientStateMachine.obtainMessage(CONNECT), 368 BassClientStateMachine.Connecting.class); 369 sendMessageAndVerifyTransition( 370 mBassClientStateMachine.obtainMessage( 371 BassClientStateMachine.CONNECT_TIMEOUT, mTestDevice), 372 BassClientStateMachine.Disconnected.class); 373 374 // disconnected -> connecting ---DISCONNECT---> CONNECTION_STATE_CHANGED(connected) 375 // --> connected -> disconnected 376 mBassClientStateMachine.mBluetoothGatt = btGatt; 377 sendMessageAndVerifyTransition( 378 mBassClientStateMachine.obtainMessage(CONNECT), 379 BassClientStateMachine.Connecting.class); 380 sendMessageAndVerifyTransition( 381 mBassClientStateMachine.obtainMessage(BassClientStateMachine.DISCONNECT), 382 BassClientStateMachine.Connecting.class); 383 mBassClientStateMachine.sendMessage( 384 CONNECTION_STATE_CHANGED, Integer.valueOf(STATE_CONNECTED)); 385 386 // disconnected -> connecting ---CONNECTION_STATE_CHANGED(connected)---> connected --> 387 // disconnected 388 mBassClientStateMachine.mBluetoothGatt = btGatt; 389 sendMessageAndVerifyTransition( 390 mBassClientStateMachine.obtainMessage(CONNECT), 391 BassClientStateMachine.Connecting.class); 392 sendMessageAndVerifyTransition( 393 mBassClientStateMachine.obtainMessage( 394 CONNECTION_STATE_CHANGED, Integer.valueOf(STATE_CONNECTED)), 395 BassClientStateMachine.Connected.class); 396 sendMessageAndVerifyTransition( 397 mBassClientStateMachine.obtainMessage( 398 CONNECTION_STATE_CHANGED, Integer.valueOf(STATE_DISCONNECTED)), 399 BassClientStateMachine.Disconnected.class); 400 401 // disconnected -> connecting ---CONNECTION_STATE_CHANGED(non-connected) --> disconnected 402 sendMessageAndVerifyTransition( 403 mBassClientStateMachine.obtainMessage(CONNECT), 404 BassClientStateMachine.Connecting.class); 405 sendMessageAndVerifyTransition( 406 mBassClientStateMachine.obtainMessage( 407 CONNECTION_STATE_CHANGED, Integer.valueOf(STATE_DISCONNECTED)), 408 BassClientStateMachine.Disconnected.class); 409 410 // change default state to connected for the next tests 411 sendMessageAndVerifyTransition( 412 mBassClientStateMachine.obtainMessage(CONNECT), 413 BassClientStateMachine.Connecting.class); 414 sendMessageAndVerifyTransition( 415 mBassClientStateMachine.obtainMessage( 416 CONNECTION_STATE_CHANGED, Integer.valueOf(STATE_CONNECTED)), 417 BassClientStateMachine.Connected.class); 418 419 // connected ----READ_BASS_CHARACTERISTICS---> connectedProcessing --GATT_TXN_PROCESSED 420 // --> connected 421 422 // Make bluetoothGatt non-null so state will transit 423 mBassClientStateMachine.mBluetoothGatt = 424 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 425 mBassClientStateMachine.mBroadcastScanControlPoint = 426 new BluetoothGattCharacteristic( 427 BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT, 428 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE 429 | BluetoothGattCharacteristic.PROPERTY_WRITE, 430 BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED); 431 432 sendMessageAndVerifyTransition( 433 mBassClientStateMachine.obtainMessage( 434 READ_BASS_CHARACTERISTICS, 435 new BluetoothGattCharacteristic( 436 UUID.randomUUID(), 437 BluetoothGattCharacteristic.PROPERTY_READ 438 | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 439 BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED)), 440 BassClientStateMachine.ConnectedProcessing.class); 441 sendMessageAndVerifyTransition( 442 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED), 443 BassClientStateMachine.Connected.class); 444 445 // connected ----READ_BASS_CHARACTERISTICS---> connectedProcessing --GATT_TXN_TIMEOUT --> 446 // connected 447 sendMessageAndVerifyTransition( 448 mBassClientStateMachine.obtainMessage( 449 READ_BASS_CHARACTERISTICS, 450 new BluetoothGattCharacteristic( 451 UUID.randomUUID(), 452 BluetoothGattCharacteristic.PROPERTY_READ 453 | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 454 BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED)), 455 BassClientStateMachine.ConnectedProcessing.class); 456 sendMessageAndVerifyTransition( 457 mBassClientStateMachine.obtainMessage(GATT_TXN_TIMEOUT), 458 BassClientStateMachine.Connected.class); 459 460 // connected ----START_SCAN_OFFLOAD---> connectedProcessing --GATT_TXN_PROCESSED--> 461 // connected 462 sendMessageAndVerifyTransition( 463 mBassClientStateMachine.obtainMessage(BassClientStateMachine.START_SCAN_OFFLOAD), 464 BassClientStateMachine.ConnectedProcessing.class); 465 sendMessageAndVerifyTransition( 466 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED), 467 BassClientStateMachine.Connected.class); 468 469 // connected ----STOP_SCAN_OFFLOAD---> connectedProcessing --GATT_TXN_PROCESSED--> connected 470 sendMessageAndVerifyTransition( 471 mBassClientStateMachine.obtainMessage(STOP_SCAN_OFFLOAD), 472 BassClientStateMachine.ConnectedProcessing.class); 473 sendMessageAndVerifyTransition( 474 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED), 475 BassClientStateMachine.Connected.class); 476 } 477 478 @Test acquireAllBassChars()479 public void acquireAllBassChars() { 480 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 481 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 482 mBassClientStateMachine.mBluetoothGatt = btGatt; 483 // Do nothing when mBluetoothGatt.getService returns null 484 mBassClientStateMachine.acquireAllBassChars(); 485 486 BluetoothGattService gattService = Mockito.mock(BluetoothGattService.class); 487 when(btGatt.getService(BassConstants.BASS_UUID)).thenReturn(gattService); 488 489 List<BluetoothGattCharacteristic> characteristics = new ArrayList<>(); 490 BluetoothGattCharacteristic scanControlPoint = 491 new BluetoothGattCharacteristic( 492 BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT, 493 BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE 494 | BluetoothGattCharacteristic.PROPERTY_WRITE, 495 BluetoothGattCharacteristic.PERMISSION_WRITE_ENCRYPTED); 496 characteristics.add(scanControlPoint); 497 498 BluetoothGattCharacteristic bassCharacteristic = 499 new BluetoothGattCharacteristic( 500 UUID.randomUUID(), 501 BluetoothGattCharacteristic.PROPERTY_READ 502 | BluetoothGattCharacteristic.PROPERTY_NOTIFY, 503 BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED); 504 characteristics.add(bassCharacteristic); 505 506 when(gattService.getCharacteristics()).thenReturn(characteristics); 507 mBassClientStateMachine.acquireAllBassChars(); 508 assertThat(mBassClientStateMachine.mBroadcastScanControlPoint).isEqualTo(scanControlPoint); 509 assertThat(mBassClientStateMachine.mBroadcastCharacteristics).contains(bassCharacteristic); 510 } 511 512 @Test simpleMethods()513 public void simpleMethods() { 514 // dump() shouldn't crash 515 StringBuilder sb = new StringBuilder(); 516 mBassClientStateMachine.dump(sb); 517 518 // log() shouldn't crash 519 String msg = "test-log-message"; 520 mBassClientStateMachine.log(msg); 521 522 // messageWhatToString() shouldn't crash 523 for (int i = CONNECT; i <= CONNECT_TIMEOUT + 1; ++i) { 524 mBassClientStateMachine.messageWhatToString(i); 525 } 526 527 final int invalidSourceId = -100; 528 assertThat(mBassClientStateMachine.getCurrentBroadcastMetadata(invalidSourceId)).isNull(); 529 assertThat(mBassClientStateMachine.getDevice()).isEqualTo(mTestDevice); 530 assertThat(mBassClientStateMachine.hasPendingSourceOperation()).isFalse(); 531 assertThat(mBassClientStateMachine.hasPendingSourceOperation(1)).isFalse(); 532 assertThat(mBassClientStateMachine.isEmpty(new byte[] {0})).isTrue(); 533 assertThat(mBassClientStateMachine.isEmpty(new byte[] {1})).isFalse(); 534 assertThat(mBassClientStateMachine.isPendingRemove(invalidSourceId)).isFalse(); 535 } 536 537 @Test gattCallbackOnConnectionStateChange_changedToConnected()538 public void gattCallbackOnConnectionStateChange_changedToConnected() 539 throws InterruptedException { 540 mBassClientStateMachine.connectGatt(true); 541 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 542 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 543 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 544 mBassClientStateMachine.mBluetoothGatt = btGatt; 545 546 // disallow connection 547 allowConnection(false); 548 int status = STATE_CONNECTING; 549 int newState = STATE_CONNECTED; 550 cb.onConnectionStateChange(null, status, newState); 551 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 552 553 verify(btGatt).disconnect(); 554 verify(btGatt).close(); 555 assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); 556 assertThat(mBassClientStateMachine.mMsgWhats).contains(CONNECTION_STATE_CHANGED); 557 mBassClientStateMachine.mMsgWhats.clear(); 558 559 mBassClientStateMachine.mBluetoothGatt = btGatt; 560 allowConnection(true); 561 mBassClientStateMachine.mDiscoveryInitiated = false; 562 status = STATE_DISCONNECTED; 563 newState = STATE_CONNECTED; 564 cb.onConnectionStateChange(null, status, newState); 565 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 566 567 assertThat(mBassClientStateMachine.mDiscoveryInitiated).isTrue(); 568 assertThat(mBassClientStateMachine.mMsgWhats).contains(CONNECTION_STATE_CHANGED); 569 assertThat(mBassClientStateMachine.mMsgObj).isEqualTo(newState); 570 mBassClientStateMachine.mMsgWhats.clear(); 571 } 572 573 @Test gattCallbackOnConnectionStateChanged_changedToDisconnected()574 public void gattCallbackOnConnectionStateChanged_changedToDisconnected() 575 throws InterruptedException { 576 initToConnectingState(); 577 mBassClientStateMachine.connectGatt(true); 578 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 579 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 580 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 581 mBassClientStateMachine.mBluetoothGatt = btGatt; 582 583 allowConnection(false); 584 int status = STATE_CONNECTING; 585 int newState = STATE_DISCONNECTED; 586 cb.onConnectionStateChange(null, status, newState); 587 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 588 589 assertThat(mBassClientStateMachine.mMsgWhats).contains(CONNECTION_STATE_CHANGED); 590 assertThat(mBassClientStateMachine.mMsgObj).isEqualTo(newState); 591 mBassClientStateMachine.mMsgWhats.clear(); 592 } 593 594 @Test gattCallbackOnServicesDiscovered()595 public void gattCallbackOnServicesDiscovered() { 596 mBassClientStateMachine.connectGatt(true); 597 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 598 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 599 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 600 mBassClientStateMachine.mBluetoothGatt = btGatt; 601 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 602 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 603 604 // Do nothing if mDiscoveryInitiated is false. 605 mBassClientStateMachine.mDiscoveryInitiated = false; 606 int status = GATT_FAILURE; 607 cb.onServicesDiscovered(null, status); 608 609 verify(btGatt, never()).requestMtu(anyInt()); 610 611 // Do nothing if status is not GATT_SUCCESS. 612 mBassClientStateMachine.mDiscoveryInitiated = true; 613 status = GATT_FAILURE; 614 cb.onServicesDiscovered(null, status); 615 616 verify(btGatt, never()).requestMtu(anyInt()); 617 verify(callbacks).notifyBassStateSetupFailed(eq(mBassClientStateMachine.getDevice())); 618 assertThat(mBassClientStateMachine.isBassStateReady()).isEqualTo(false); 619 620 // call requestMtu() if status is GATT_SUCCESS. 621 mBassClientStateMachine.mDiscoveryInitiated = true; 622 status = GATT_SUCCESS; 623 cb.onServicesDiscovered(null, status); 624 625 verify(btGatt).requestMtu(anyInt()); 626 } 627 628 /** This also tests BassClientStateMachine#processBroadcastReceiverState. */ 629 @Test 630 @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RECEIVE_STATE_PROCESSING_REFACTOR) gattCallbackOnCharacteristicReadObsolete()631 public void gattCallbackOnCharacteristicReadObsolete() { 632 mBassClientStateMachine.mShouldHandleMessage = false; 633 mBassClientStateMachine.connectGatt(true); 634 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 635 BluetoothGattDescriptor desc = Mockito.mock(BluetoothGattDescriptor.class); 636 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 637 BluetoothGattCharacteristic characteristic = 638 Mockito.mock(BluetoothGattCharacteristic.class); 639 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 640 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 641 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 642 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 643 644 // Characteristic read success with null value 645 when(characteristic.getValue()).thenReturn(null); 646 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 647 verify(characteristic, never()).getDescriptor(any()); 648 649 // Characteristic read failed and mBluetoothGatt is null. 650 mBassClientStateMachine.mBluetoothGatt = null; 651 cb.onCharacteristicRead(null, characteristic, GATT_FAILURE); 652 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 653 654 assertThat(mBassClientStateMachine.mMsgWhats).contains(GATT_TXN_PROCESSED); 655 assertThat(mBassClientStateMachine.mMsgAgr1).isEqualTo(GATT_FAILURE); 656 mBassClientStateMachine.mMsgWhats.clear(); 657 658 // Characteristic read failed and mBluetoothGatt is not null. 659 mBassClientStateMachine.mBluetoothGatt = btGatt; 660 when(characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG)).thenReturn(desc); 661 cb.onCharacteristicRead(null, characteristic, GATT_FAILURE); 662 663 verify(btGatt).setCharacteristicNotification(any(), anyBoolean()); 664 verify(btGatt).writeDescriptor(desc); 665 verify(desc).setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 666 667 // Tests for processBroadcastReceiverState 668 int sourceId = 1; 669 byte[] value = new byte[] {}; 670 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; 671 mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE; 672 mBassClientStateMachine.mPendingSourceId = (byte) sourceId; 673 when(characteristic.getValue()).thenReturn(value); 674 when(characteristic.getInstanceId()).thenReturn(sourceId); 675 676 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 677 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 678 679 ArgumentCaptor<BluetoothLeBroadcastReceiveState> receiveStateCaptor = 680 ArgumentCaptor.forClass(BluetoothLeBroadcastReceiveState.class); 681 verify(callbacks) 682 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 683 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice); 684 685 mBassClientStateMachine.mPendingOperation = 0; 686 mBassClientStateMachine.mPendingSourceId = 0; 687 sourceId = 2; // mNextId would become 2 688 when(characteristic.getInstanceId()).thenReturn(sourceId); 689 690 Mockito.clearInvocations(callbacks); 691 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 692 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 693 verify(callbacks) 694 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 695 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice); 696 697 mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata(); 698 sourceId = 1; 699 value = 700 new byte[] { 701 (byte) sourceId, // sourceId 702 (byte) (mSourceTestDevice.getAddressType() & 0xFF), // sourceAddressType 703 Utils.getByteAddress(mSourceTestDevice)[5], 704 Utils.getByteAddress(mSourceTestDevice)[4], 705 Utils.getByteAddress(mSourceTestDevice)[3], 706 Utils.getByteAddress(mSourceTestDevice)[2], 707 Utils.getByteAddress(mSourceTestDevice)[1], 708 Utils.getByteAddress(mSourceTestDevice)[0], // sourceAddress 709 0x00, // sourceAdvSid 710 0x00, 711 0x00, 712 0x00, // broadcastIdBytes 713 (byte) BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_NO_PAST, 714 (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE, 715 // 16 bytes badBroadcastCode 716 0x00, 717 0x00, 718 0x00, 719 0x00, 720 0x00, 721 0x00, 722 0x00, 723 0x00, 724 0x00, 725 0x00, 726 0x00, 727 0x00, 728 0x00, 729 0x00, 730 0x00, 731 0x00, 732 0x01, // numSubGroups 733 // SubGroup #1 734 0x00, 735 0x00, 736 0x00, 737 0x00, // audioSyncIndex 738 0x02, // metaDataLength 739 0x00, 740 0x00, // metadata 741 }; 742 when(characteristic.getValue()).thenReturn(value); 743 when(characteristic.getInstanceId()).thenReturn(sourceId); 744 745 Mockito.clearInvocations(callbacks); 746 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 747 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 748 749 verify(callbacks).notifySourceAdded(any(), any(), anyInt()); 750 verify(callbacks) 751 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 752 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 753 754 // set some values for covering more lines of processPASyncState() 755 mBassClientStateMachine.mPendingMetadata = null; 756 mBassClientStateMachine.mSetBroadcastCodePending = true; 757 mBassClientStateMachine.mIsPendingRemove = true; 758 value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] = 759 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST; 760 value[BassConstants.BCAST_RCVR_STATE_ENC_STATUS_IDX] = 761 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED; 762 value[35] = 0; // set metaDataLength of subgroup #1 0 763 PeriodicAdvertisementResult paResult = Mockito.mock(PeriodicAdvertisementResult.class); 764 when(characteristic.getValue()).thenReturn(value); 765 when(mBassClientService.getPeriodicAdvertisementResult(any(), anyInt())) 766 .thenReturn(paResult); 767 int syncHandle = 100; 768 when(paResult.getSyncHandle()).thenReturn(syncHandle); 769 770 Mockito.clearInvocations(callbacks); 771 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 772 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 773 774 int serviceData = 0x000000FF & sourceId; 775 serviceData = serviceData << 8; 776 // advA matches EXT_ADV_ADDRESS 777 // also matches source address (as we would have written) 778 serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS); 779 serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS); 780 verify(mMethodProxy) 781 .periodicAdvertisingManagerTransferSync( 782 any(), any(), eq(serviceData), eq(syncHandle)); 783 784 verify(callbacks) 785 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 786 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 787 assertThat(mBassClientStateMachine.mMsgWhats).contains(REMOVE_BCAST_SOURCE); 788 789 mBassClientStateMachine.mIsPendingRemove = null; 790 // set some values for covering more lines of processPASyncState() 791 mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata(); 792 for (int i = 0; i < BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE; ++i) { 793 value[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_START_IDX + i] = 0x00; 794 } 795 when(mBassClientService.getPeriodicAdvertisementResult(any(), anyInt())).thenReturn(null); 796 when(mBassClientService.isLocalBroadcast(any(BluetoothLeBroadcastMetadata.class))) 797 .thenReturn(true); 798 when(characteristic.getValue()).thenReturn(value); 799 mBassClientStateMachine.mPendingSourceToSwitch = mBassClientStateMachine.mPendingMetadata; 800 801 Mockito.clearInvocations(callbacks); 802 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 803 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 804 805 verify(callbacks) 806 .notifySourceRemoved( 807 any(), anyInt(), eq(BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST)); 808 verify(callbacks) 809 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 810 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice); 811 } 812 813 @Test 814 @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RECEIVE_STATE_PROCESSING_REFACTOR) gattCallbackOnCharacteristicChangedObsolete()815 public void gattCallbackOnCharacteristicChangedObsolete() { 816 mBassClientStateMachine.connectGatt(true); 817 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 818 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 1; 819 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 820 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 821 822 BluetoothGattCharacteristic characteristic = 823 Mockito.mock(BluetoothGattCharacteristic.class); 824 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 825 when(characteristic.getValue()).thenReturn(null); 826 827 cb.onCharacteristicChanged(null, characteristic); 828 verify(characteristic, atLeast(1)).getUuid(); 829 verify(characteristic).getValue(); 830 verify(callbacks, never()).notifyReceiveStateChanged(any(), anyInt(), any()); 831 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 832 833 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 1; 834 Mockito.clearInvocations(characteristic); 835 when(characteristic.getValue()).thenReturn(new byte[] {}); 836 cb.onCharacteristicChanged(null, characteristic); 837 verify(characteristic, atLeast(1)).getUuid(); 838 verify(characteristic, atLeast(1)).getValue(); 839 840 ArgumentCaptor<BluetoothLeBroadcastReceiveState> receiveStateCaptor = 841 ArgumentCaptor.forClass(BluetoothLeBroadcastReceiveState.class); 842 verify(callbacks).notifyReceiveStateChanged(any(), anyInt(), receiveStateCaptor.capture()); 843 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice); 844 } 845 846 @Test 847 @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RECEIVE_STATE_PROCESSING_REFACTOR) gattCallbackOnCharacteristicRead()848 public void gattCallbackOnCharacteristicRead() { 849 mBassClientStateMachine.mShouldHandleMessage = false; 850 mBassClientStateMachine.connectGatt(true); 851 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 852 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 853 BluetoothGattDescriptor desc = Mockito.mock(BluetoothGattDescriptor.class); 854 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 855 BluetoothGattCharacteristic characteristic = 856 Mockito.mock(BluetoothGattCharacteristic.class); 857 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 858 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 859 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 860 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 861 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; 862 863 // Characteristic read success with null value 864 when(characteristic.getValue()).thenReturn(null); 865 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 866 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 867 InOrder inOrderCharacteristic = inOrder(characteristic); 868 inOrderCharacteristic.verify(characteristic).getUuid(); 869 inOrderCharacteristic.verify(characteristic).getValue(); 870 InOrder inOrderCallbacks = inOrder(callbacks); 871 inOrderCallbacks 872 .verify(callbacks, never()) 873 .notifyReceiveStateChanged(any(), anyInt(), any()); 874 verify(characteristic, never()).getDescriptor(any()); 875 876 // Characteristic read failed and mBluetoothGatt is null. 877 mBassClientStateMachine.mBluetoothGatt = null; 878 cb.onCharacteristicRead(null, characteristic, GATT_FAILURE); 879 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 880 inOrderCharacteristic.verify(characteristic, never()).getUuid(); 881 inOrderCharacteristic.verify(characteristic, never()).getValue(); 882 inOrderCallbacks 883 .verify(callbacks, never()) 884 .notifyReceiveStateChanged(any(), anyInt(), any()); 885 assertThat(mBassClientStateMachine.mMsgWhats).contains(GATT_TXN_PROCESSED); 886 assertThat(mBassClientStateMachine.mMsgAgr1).isEqualTo(GATT_FAILURE); 887 mBassClientStateMachine.mMsgWhats.clear(); 888 889 // Characteristic read failed and mBluetoothGatt is not null. 890 mBassClientStateMachine.mBluetoothGatt = btGatt; 891 when(characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG)).thenReturn(desc); 892 cb.onCharacteristicRead(null, characteristic, GATT_FAILURE); 893 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 894 inOrderCharacteristic.verify(characteristic, never()).getUuid(); 895 inOrderCharacteristic.verify(characteristic, never()).getValue(); 896 inOrderCallbacks 897 .verify(callbacks, never()) 898 .notifyReceiveStateChanged(any(), anyInt(), any()); 899 verify(btGatt).setCharacteristicNotification(any(), anyBoolean()); 900 verify(btGatt).writeDescriptor(desc); 901 verify(desc).setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 902 903 // Tests for processBroadcastReceiverState 904 905 // Empty value without any previous read/change 906 when(characteristic.getValue()).thenReturn(new byte[] {}); 907 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 908 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 909 inOrderCharacteristic.verify(characteristic).getUuid(); 910 inOrderCharacteristic.verify(characteristic, times(4)).getValue(); 911 inOrderCallbacks 912 .verify(callbacks, never()) 913 .notifyReceiveStateChanged(any(), anyInt(), any()); 914 915 // Read first time first characteristic 916 int sourceId = 1; 917 int instanceId = 1234; 918 byte[] value = 919 new byte[] { 920 (byte) sourceId, // sourceId 921 (byte) (mSourceTestDevice.getAddressType() & 0xFF), // sourceAddressType 922 Utils.getByteAddress(mSourceTestDevice)[5], 923 Utils.getByteAddress(mSourceTestDevice)[4], 924 Utils.getByteAddress(mSourceTestDevice)[3], 925 Utils.getByteAddress(mSourceTestDevice)[2], 926 Utils.getByteAddress(mSourceTestDevice)[1], 927 Utils.getByteAddress(mSourceTestDevice)[0], // sourceAddress 928 0x00, // sourceAdvSid 929 0x00, 930 0x00, 931 0x00, // broadcastIdBytes 932 (byte) BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 933 (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE, 934 // 16 bytes badBroadcastCode 935 0x00, 936 0x00, 937 0x00, 938 0x00, 939 0x00, 940 0x00, 941 0x00, 942 0x00, 943 0x00, 944 0x00, 945 0x00, 946 0x00, 947 0x00, 948 0x00, 949 0x00, 950 0x00, 951 0x01, // numSubGroups 952 // SubGroup #1 953 0x00, 954 0x00, 955 0x00, 956 0x00, // audioSyncIndex 957 0x02, // metaDataLength 958 0x00, 959 0x00, // metadata 960 }; 961 when(characteristic.getValue()).thenReturn(value); 962 when(characteristic.getInstanceId()).thenReturn(instanceId); 963 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 964 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 965 inOrderCharacteristic.verify(characteristic).getUuid(); 966 inOrderCharacteristic.verify(characteristic, times(4)).getValue(); 967 ArgumentCaptor<BluetoothLeBroadcastReceiveState> receiveStateCaptor = 968 ArgumentCaptor.forClass(BluetoothLeBroadcastReceiveState.class); 969 inOrderCallbacks 970 .verify(callbacks) 971 .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_REMOTE_REQUEST)); 972 inOrderCallbacks 973 .verify(callbacks) 974 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 975 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 976 977 // Read first time second (last) characteristic 978 int sourceId2 = 2; 979 int instanceId2 = 4321; 980 value[BassConstants.BCAST_RCVR_STATE_SRC_ID_IDX] = (byte) sourceId2; 981 when(characteristic.getInstanceId()).thenReturn(instanceId2); 982 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 983 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 984 inOrderCharacteristic.verify(characteristic).getUuid(); 985 inOrderCharacteristic.verify(characteristic, times(4)).getValue(); 986 inOrderCallbacks 987 .verify(callbacks) 988 .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_REMOTE_REQUEST)); 989 inOrderCallbacks 990 .verify(callbacks) 991 .notifyReceiveStateChanged(any(), eq(sourceId2), receiveStateCaptor.capture()); 992 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 993 } 994 995 /** This also tests BassClientStateMachine#processBroadcastReceiverState. */ 996 @Test 997 @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RECEIVE_STATE_PROCESSING_REFACTOR) gattCallbackOnCharacteristicChanged()998 public void gattCallbackOnCharacteristicChanged() { 999 mBassClientStateMachine.connectGatt(true); 1000 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1001 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 1002 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 1; 1003 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1004 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1005 1006 BluetoothGattCharacteristic characteristic = 1007 Mockito.mock(BluetoothGattCharacteristic.class); 1008 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 1009 1010 // Null value 1011 when(characteristic.getValue()).thenReturn(null); 1012 cb.onCharacteristicChanged(null, characteristic); 1013 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1014 InOrder inOrderCharacteristic = inOrder(characteristic); 1015 inOrderCharacteristic.verify(characteristic).getUuid(); 1016 inOrderCharacteristic.verify(characteristic).getValue(); 1017 InOrder inOrderCallbacks = inOrder(callbacks); 1018 inOrderCallbacks 1019 .verify(callbacks, never()) 1020 .notifyReceiveStateChanged(any(), anyInt(), any()); 1021 1022 // Empty value without any previous read/change 1023 when(characteristic.getValue()).thenReturn(new byte[] {}); 1024 cb.onCharacteristicChanged(null, characteristic); 1025 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1026 inOrderCharacteristic.verify(characteristic).getUuid(); 1027 inOrderCharacteristic.verify(characteristic, times(2)).getValue(); 1028 inOrderCallbacks 1029 .verify(callbacks, never()) 1030 .notifyReceiveStateChanged(any(), anyInt(), any()); 1031 1032 // Sync value, first time 1033 int sourceId = 1; 1034 byte[] value = 1035 new byte[] { 1036 (byte) sourceId, // sourceId 1037 (byte) (mSourceTestDevice.getAddressType() & 0xFF), // sourceAddressType 1038 Utils.getByteAddress(mSourceTestDevice)[5], 1039 Utils.getByteAddress(mSourceTestDevice)[4], 1040 Utils.getByteAddress(mSourceTestDevice)[3], 1041 Utils.getByteAddress(mSourceTestDevice)[2], 1042 Utils.getByteAddress(mSourceTestDevice)[1], 1043 Utils.getByteAddress(mSourceTestDevice)[0], // sourceAddress 1044 0x00, // sourceAdvSid 1045 0x00, 1046 0x00, 1047 0x00, // broadcastIdBytes 1048 (byte) BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 1049 (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE, 1050 // 16 bytes badBroadcastCode 1051 0x00, 1052 0x00, 1053 0x00, 1054 0x00, 1055 0x00, 1056 0x00, 1057 0x00, 1058 0x00, 1059 0x00, 1060 0x00, 1061 0x00, 1062 0x00, 1063 0x00, 1064 0x00, 1065 0x00, 1066 0x00, 1067 0x01, // numSubGroups 1068 // SubGroup #1 1069 0x00, 1070 0x00, 1071 0x00, 1072 0x00, // audioSyncIndex 1073 0x02, // metaDataLength 1074 0x00, 1075 0x00, // metadata 1076 }; 1077 when(characteristic.getValue()).thenReturn(value); 1078 cb.onCharacteristicChanged(null, characteristic); 1079 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1080 inOrderCharacteristic.verify(characteristic).getUuid(); 1081 inOrderCharacteristic.verify(characteristic, times(2)).getValue(); 1082 ArgumentCaptor<BluetoothLeBroadcastReceiveState> receiveStateCaptor = 1083 ArgumentCaptor.forClass(BluetoothLeBroadcastReceiveState.class); 1084 inOrderCallbacks 1085 .verify(callbacks) 1086 .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_REMOTE_REQUEST)); 1087 inOrderCallbacks 1088 .verify(callbacks) 1089 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1090 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 1091 1092 // Empty value to indicates removing source from device by remote 1093 when(characteristic.getValue()).thenReturn(new byte[] {}); 1094 cb.onCharacteristicChanged(null, characteristic); 1095 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1096 inOrderCharacteristic.verify(characteristic).getUuid(); 1097 inOrderCharacteristic.verify(characteristic, times(2)).getValue(); 1098 inOrderCallbacks 1099 .verify(callbacks) 1100 .notifySourceRemoved( 1101 any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_REMOTE_REQUEST)); 1102 inOrderCallbacks 1103 .verify(callbacks) 1104 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1105 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice); 1106 1107 // Sync value again 1108 mBassClientStateMachine.mPendingOperation = ADD_BCAST_SOURCE; 1109 when(characteristic.getValue()).thenReturn(value); 1110 cb.onCharacteristicChanged(null, characteristic); 1111 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1112 inOrderCharacteristic.verify(characteristic).getUuid(); 1113 inOrderCharacteristic.verify(characteristic, times(2)).getValue(); 1114 inOrderCallbacks 1115 .verify(callbacks) 1116 .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 1117 inOrderCallbacks 1118 .verify(callbacks) 1119 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1120 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 1121 1122 // Empty value to indicates removing source from device by local app 1123 mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE; 1124 when(characteristic.getValue()).thenReturn(new byte[] {}); 1125 cb.onCharacteristicChanged(null, characteristic); 1126 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1127 inOrderCharacteristic.verify(characteristic).getUuid(); 1128 inOrderCharacteristic.verify(characteristic, times(2)).getValue(); 1129 inOrderCallbacks 1130 .verify(callbacks) 1131 .notifySourceRemoved( 1132 any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 1133 inOrderCallbacks 1134 .verify(callbacks) 1135 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1136 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice); 1137 1138 // Sync value again 1139 mBassClientStateMachine.mPendingOperation = ADD_BCAST_SOURCE; 1140 when(characteristic.getValue()).thenReturn(value); 1141 cb.onCharacteristicChanged(null, characteristic); 1142 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1143 inOrderCharacteristic.verify(characteristic).getUuid(); 1144 inOrderCharacteristic.verify(characteristic, times(2)).getValue(); 1145 inOrderCallbacks 1146 .verify(callbacks) 1147 .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 1148 inOrderCallbacks 1149 .verify(callbacks) 1150 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1151 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 1152 1153 // Empty value to indicates removing source from device by stack (source switch) 1154 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 1155 mBassClientStateMachine.mPendingSourceToSwitch = metadata; 1156 when(characteristic.getValue()).thenReturn(new byte[] {}); 1157 cb.onCharacteristicChanged(null, characteristic); 1158 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1159 inOrderCharacteristic.verify(characteristic).getUuid(); 1160 inOrderCharacteristic.verify(characteristic, times(2)).getValue(); 1161 inOrderCallbacks 1162 .verify(callbacks) 1163 .notifySourceRemoved( 1164 any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST)); 1165 inOrderCallbacks 1166 .verify(callbacks) 1167 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1168 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mEmptyTestDevice); 1169 assertThat(mBassClientStateMachine.mMsgWhats).contains(ADD_BCAST_SOURCE); 1170 assertThat(mBassClientStateMachine.mMsgObj).isEqualTo(metadata); 1171 1172 // Sync value again 1173 mBassClientStateMachine.mPendingOperation = ADD_BCAST_SOURCE; 1174 when(characteristic.getValue()).thenReturn(value); 1175 cb.onCharacteristicChanged(null, characteristic); 1176 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1177 inOrderCharacteristic.verify(characteristic).getUuid(); 1178 inOrderCharacteristic.verify(characteristic, times(2)).getValue(); 1179 inOrderCallbacks 1180 .verify(callbacks) 1181 .notifySourceAdded(any(), any(), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 1182 inOrderCallbacks 1183 .verify(callbacks) 1184 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1185 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 1186 1187 // Update value - PA SyncInfo Request 1188 value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] = 1189 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST; 1190 PeriodicAdvertisementResult paResult = Mockito.mock(PeriodicAdvertisementResult.class); 1191 when(mBassClientService.getPeriodicAdvertisementResult(any(), anyInt())) 1192 .thenReturn(paResult); 1193 int syncHandle = 100; 1194 when(paResult.getSyncHandle()).thenReturn(syncHandle); 1195 cb.onCharacteristicChanged(null, characteristic); 1196 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1197 inOrderCallbacks 1198 .verify(callbacks) 1199 .notifySourceModified( 1200 any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 1201 int serviceData = 0x000000FF & sourceId; 1202 serviceData = serviceData << 8; 1203 // advA matches EXT_ADV_ADDRESS 1204 // also matches source address (as we would have written) 1205 serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS); 1206 serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS); 1207 verify(mMethodProxy) 1208 .periodicAdvertisingManagerTransferSync( 1209 any(), any(), eq(serviceData), eq(syncHandle)); 1210 inOrderCallbacks 1211 .verify(callbacks) 1212 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1213 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 1214 1215 // Update value - PA SyncInfo Request, local broadcast 1216 mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata(); 1217 when(mBassClientService.isLocalBroadcast(any(BluetoothLeBroadcastReceiveState.class))) 1218 .thenReturn(true); 1219 cb.onCharacteristicChanged(null, characteristic); 1220 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1221 inOrderCallbacks 1222 .verify(callbacks) 1223 .notifySourceModified( 1224 any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 1225 serviceData = 0x000000FF & sourceId; 1226 serviceData = serviceData << 8; 1227 // Address we set in the Source Address can differ from the address in the air 1228 serviceData = serviceData | BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS; 1229 verify(mMethodProxy) 1230 .periodicAdvertisingManagerTransferSetInfo( 1231 any(), any(), eq(serviceData), anyInt(), any()); 1232 inOrderCallbacks 1233 .verify(callbacks) 1234 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1235 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 1236 1237 // Update value - Broadcast Code 1238 value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] = 1239 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED; 1240 value[BassConstants.BCAST_RCVR_STATE_ENC_STATUS_IDX] = 1241 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED; 1242 mBassClientStateMachine.mSetBroadcastCodePending = true; 1243 cb.onCharacteristicChanged(null, characteristic); 1244 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1245 inOrderCallbacks 1246 .verify(callbacks) 1247 .notifySourceModified( 1248 any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 1249 assertThat(mBassClientStateMachine.mMsgWhats).contains(SET_BCAST_CODE); 1250 assertThat(mBassClientStateMachine.mMsgAgr1) 1251 .isEqualTo(BassClientStateMachine.ARGTYPE_RCVSTATE); 1252 inOrderCallbacks 1253 .verify(callbacks) 1254 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1255 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 1256 1257 // Update value - Pending Remove 1258 value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] = 1259 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE; 1260 mBassClientStateMachine.mIsPendingRemove = true; 1261 cb.onCharacteristicChanged(null, characteristic); 1262 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1263 inOrderCallbacks 1264 .verify(callbacks) 1265 .notifySourceModified( 1266 any(), eq(sourceId), eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 1267 assertThat(mBassClientStateMachine.mMsgWhats).contains(REMOVE_BCAST_SOURCE); 1268 assertThat(mBassClientStateMachine.mMsgAgr1).isEqualTo(sourceId); 1269 inOrderCallbacks 1270 .verify(callbacks) 1271 .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture()); 1272 assertThat(receiveStateCaptor.getValue().getSourceDevice()).isEqualTo(mSourceTestDevice); 1273 } 1274 1275 @Test gattCharacteristicWrite()1276 public void gattCharacteristicWrite() { 1277 mBassClientStateMachine.connectGatt(true); 1278 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 1279 1280 BluetoothGattCharacteristic characteristic = 1281 Mockito.mock(BluetoothGattCharacteristic.class); 1282 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT); 1283 1284 cb.onCharacteristicWrite(null, characteristic, GATT_SUCCESS); 1285 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1286 assertThat(mBassClientStateMachine.mMsgWhats).contains(GATT_TXN_PROCESSED); 1287 } 1288 1289 @Test gattCallbackOnDescriptorWrite()1290 public void gattCallbackOnDescriptorWrite() { 1291 mBassClientStateMachine.connectGatt(true); 1292 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 1293 BluetoothGattDescriptor descriptor = Mockito.mock(BluetoothGattDescriptor.class); 1294 when(descriptor.getUuid()).thenReturn(BassConstants.CLIENT_CHARACTERISTIC_CONFIG); 1295 1296 cb.onDescriptorWrite(null, descriptor, GATT_SUCCESS); 1297 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1298 assertThat(mBassClientStateMachine.mMsgWhats).contains(GATT_TXN_PROCESSED); 1299 } 1300 1301 @Test gattCallbackOnMtuChanged()1302 public void gattCallbackOnMtuChanged() { 1303 mBassClientStateMachine.connectGatt(true); 1304 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 1305 mBassClientStateMachine.mMTUChangeRequested = true; 1306 1307 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1308 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1309 1310 // Verify notifyBassStateSetupFailed is called 1311 cb.onMtuChanged(null, 10, GATT_FAILURE); 1312 verify(callbacks).notifyBassStateSetupFailed(eq(mBassClientStateMachine.getDevice())); 1313 assertThat(mBassClientStateMachine.mMTUChangeRequested).isTrue(); 1314 assertThat(mBassClientStateMachine.isBassStateReady()).isEqualTo(false); 1315 1316 cb.onMtuChanged(null, 10, GATT_SUCCESS); 1317 assertThat(mBassClientStateMachine.mMTUChangeRequested).isTrue(); 1318 1319 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1320 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1321 mBassClientStateMachine.mBluetoothGatt = btGatt; 1322 1323 cb.onMtuChanged(null, 10, GATT_SUCCESS); 1324 assertThat(mBassClientStateMachine.mMTUChangeRequested).isFalse(); 1325 } 1326 1327 @Test sendConnectMessage_inDisconnectedState()1328 public void sendConnectMessage_inDisconnectedState() { 1329 initToDisconnectedState(); 1330 1331 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1332 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1333 mBassClientStateMachine.mBluetoothGatt = btGatt; 1334 1335 sendMessageAndVerifyTransition( 1336 mBassClientStateMachine.obtainMessage(CONNECT), 1337 BassClientStateMachine.Connecting.class); 1338 verify(btGatt).disconnect(); 1339 verify(btGatt).close(); 1340 } 1341 1342 @Test sendDisconnectMessage_inDisconnectedState()1343 public void sendDisconnectMessage_inDisconnectedState() { 1344 initToDisconnectedState(); 1345 1346 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1347 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1348 mBassClientStateMachine.mBluetoothGatt = btGatt; 1349 1350 mBassClientStateMachine.sendMessage(DISCONNECT); 1351 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1352 verify(btGatt).disconnect(); 1353 verify(btGatt).close(); 1354 } 1355 1356 @Test sendStateChangedMessage_inDisconnectedState()1357 public void sendStateChangedMessage_inDisconnectedState() { 1358 initToDisconnectedState(); 1359 1360 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1361 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1362 mBassClientStateMachine.mBluetoothGatt = btGatt; 1363 1364 Message msgToConnectingState = 1365 mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1366 msgToConnectingState.obj = STATE_CONNECTING; 1367 1368 mBassClientStateMachine.sendMessage(msgToConnectingState); 1369 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1370 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1371 1372 Message msgToConnectedState = 1373 mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1374 msgToConnectedState.obj = STATE_CONNECTED; 1375 sendMessageAndVerifyTransition(msgToConnectedState, BassClientStateMachine.Connected.class); 1376 } 1377 1378 @Test sendOtherMessages_inDisconnectedState_doesNotChangeState()1379 public void sendOtherMessages_inDisconnectedState_doesNotChangeState() { 1380 initToDisconnectedState(); 1381 1382 mBassClientStateMachine.sendMessage(STOP_SCAN_OFFLOAD); 1383 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1384 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1385 1386 mBassClientStateMachine.sendMessage(-1); 1387 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1388 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1389 } 1390 1391 @Test sendConnectMessages_inConnectingState_doesNotChangeState()1392 public void sendConnectMessages_inConnectingState_doesNotChangeState() { 1393 initToConnectingState(); 1394 1395 mBassClientStateMachine.sendMessage(CONNECT); 1396 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1397 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1398 } 1399 1400 @Test sendDisconnectMessages_inConnectingState_defersMessage()1401 public void sendDisconnectMessages_inConnectingState_defersMessage() { 1402 initToConnectingState(); 1403 1404 mBassClientStateMachine.sendMessage(DISCONNECT); 1405 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1406 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(DISCONNECT)).isTrue(); 1407 } 1408 1409 @Test sendReadBassCharacteristicsMessage_inConnectingState_defersMessage()1410 public void sendReadBassCharacteristicsMessage_inConnectingState_defersMessage() { 1411 initToConnectingState(); 1412 1413 mBassClientStateMachine.sendMessage(READ_BASS_CHARACTERISTICS); 1414 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1415 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(READ_BASS_CHARACTERISTICS)) 1416 .isTrue(); 1417 } 1418 1419 @Test sendStateChangedToNonConnectedMessage_inConnectingState_movesToDisconnected()1420 public void sendStateChangedToNonConnectedMessage_inConnectingState_movesToDisconnected() { 1421 initToConnectingState(); 1422 1423 Message msg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1424 msg.obj = STATE_CONNECTING; 1425 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1426 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1427 mBassClientStateMachine.mBluetoothGatt = btGatt; 1428 sendMessageAndVerifyTransition(msg, BassClientStateMachine.Disconnected.class); 1429 verify(btGatt).close(); 1430 assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); 1431 } 1432 1433 @Test sendStateChangedToConnectedMessage_inConnectingState_movesToConnected()1434 public void sendStateChangedToConnectedMessage_inConnectingState_movesToConnected() { 1435 initToConnectingState(); 1436 1437 Message msg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1438 msg.obj = STATE_CONNECTED; 1439 sendMessageAndVerifyTransition(msg, BassClientStateMachine.Connected.class); 1440 } 1441 1442 @Test sendConnectTimeMessage_inConnectingState()1443 public void sendConnectTimeMessage_inConnectingState() { 1444 initToConnectingState(); 1445 1446 Message timeoutWithDifferentDevice = 1447 mBassClientStateMachine.obtainMessage(CONNECT_TIMEOUT, getTestDevice(230)); 1448 mBassClientStateMachine.sendMessage(timeoutWithDifferentDevice); 1449 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1450 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1451 1452 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1453 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1454 mBassClientStateMachine.mBluetoothGatt = btGatt; 1455 Message msg = mBassClientStateMachine.obtainMessage(CONNECT_TIMEOUT, mTestDevice); 1456 sendMessageAndVerifyTransition(msg, BassClientStateMachine.Disconnected.class); 1457 verify(btGatt).close(); 1458 assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); 1459 } 1460 1461 @Test sendInvalidMessage_inConnectingState_doesNotChangeState()1462 public void sendInvalidMessage_inConnectingState_doesNotChangeState() { 1463 initToConnectingState(); 1464 mBassClientStateMachine.sendMessage(-1); 1465 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1466 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1467 } 1468 1469 @Test sendConnectMessage_inConnectedState()1470 public void sendConnectMessage_inConnectedState() { 1471 initToConnectedState(); 1472 1473 mBassClientStateMachine.sendMessage(CONNECT); 1474 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1475 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1476 } 1477 1478 @Test sendDisconnectMessage_inConnectedState()1479 public void sendDisconnectMessage_inConnectedState() { 1480 initToConnectedState(); 1481 1482 mBassClientStateMachine.mBluetoothGatt = null; 1483 1484 mBassClientStateMachine.sendMessage(DISCONNECT); 1485 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1486 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1487 1488 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1489 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1490 mBassClientStateMachine.mBluetoothGatt = btGatt; 1491 sendMessageAndVerifyTransition( 1492 mBassClientStateMachine.obtainMessage(DISCONNECT), 1493 BassClientStateMachine.Disconnected.class); 1494 verify(btGatt).disconnect(); 1495 verify(btGatt).close(); 1496 } 1497 1498 @Test sendStateChangedMessage_inConnectedState()1499 public void sendStateChangedMessage_inConnectedState() { 1500 initToConnectedState(); 1501 1502 Message connectedMsg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1503 connectedMsg.obj = STATE_CONNECTED; 1504 1505 mBassClientStateMachine.sendMessage(connectedMsg); 1506 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1507 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1508 1509 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1510 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1511 mBassClientStateMachine.mBluetoothGatt = btGatt; 1512 Message noneConnectedMsg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1513 noneConnectedMsg.obj = STATE_DISCONNECTING; 1514 sendMessageAndVerifyTransition(noneConnectedMsg, BassClientStateMachine.Disconnected.class); 1515 verify(btGatt).close(); 1516 assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); 1517 } 1518 1519 @Test sendReadBassCharacteristicsMessage_inConnectedState()1520 public void sendReadBassCharacteristicsMessage_inConnectedState() { 1521 initToConnectedState(); 1522 BluetoothGattCharacteristic gattCharacteristic = 1523 Mockito.mock(BluetoothGattCharacteristic.class); 1524 1525 mBassClientStateMachine.sendMessage(READ_BASS_CHARACTERISTICS, gattCharacteristic); 1526 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1527 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1528 1529 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1530 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1531 mBassClientStateMachine.mBluetoothGatt = btGatt; 1532 sendMessageAndVerifyTransition( 1533 mBassClientStateMachine.obtainMessage( 1534 READ_BASS_CHARACTERISTICS, gattCharacteristic), 1535 BassClientStateMachine.ConnectedProcessing.class); 1536 } 1537 1538 @Test sendStartScanOffloadMessage_inConnectedState()1539 public void sendStartScanOffloadMessage_inConnectedState() { 1540 initToConnectedState(); 1541 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1542 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1543 mBassClientStateMachine.mBluetoothGatt = btGatt; 1544 1545 mBassClientStateMachine.sendMessage(START_SCAN_OFFLOAD); 1546 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1547 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1548 1549 BluetoothGattCharacteristic scanControlPoint = 1550 Mockito.mock(BluetoothGattCharacteristic.class); 1551 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1552 1553 sendMessageAndVerifyTransition( 1554 mBassClientStateMachine.obtainMessage(START_SCAN_OFFLOAD), 1555 BassClientStateMachine.ConnectedProcessing.class); 1556 verify(btGatt).writeCharacteristic(scanControlPoint); 1557 verify(scanControlPoint).setValue(REMOTE_SCAN_START); 1558 } 1559 1560 @Test sendStopScanOffloadMessage_inConnectedState()1561 public void sendStopScanOffloadMessage_inConnectedState() { 1562 initToConnectedState(); 1563 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1564 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1565 mBassClientStateMachine.mBluetoothGatt = btGatt; 1566 1567 mBassClientStateMachine.sendMessage(STOP_SCAN_OFFLOAD); 1568 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1569 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1570 1571 BluetoothGattCharacteristic scanControlPoint = 1572 Mockito.mock(BluetoothGattCharacteristic.class); 1573 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1574 1575 sendMessageAndVerifyTransition( 1576 mBassClientStateMachine.obtainMessage(STOP_SCAN_OFFLOAD), 1577 BassClientStateMachine.ConnectedProcessing.class); 1578 verify(btGatt).writeCharacteristic(scanControlPoint); 1579 verify(scanControlPoint).setValue(REMOTE_SCAN_STOP); 1580 } 1581 1582 @Test sendInvalidMessage_inConnectedState_doesNotChangeState()1583 public void sendInvalidMessage_inConnectedState_doesNotChangeState() { 1584 initToConnectedState(); 1585 1586 mBassClientStateMachine.sendMessage(-1); 1587 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1588 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1589 } 1590 1591 @Test sendAddBcastSourceMessage_inConnectedState()1592 public void sendAddBcastSourceMessage_inConnectedState() { 1593 initToConnectedState(); 1594 1595 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1596 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1597 1598 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 1599 // verify local broadcast doesn't require active synced source 1600 when(mBassClientService.isLocalBroadcast(any(BluetoothLeBroadcastMetadata.class))) 1601 .thenReturn(true); 1602 mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata); 1603 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1604 1605 verify(mBassClientService).getCallbacks(); 1606 verify(callbacks).notifySourceAddFailed(any(), any(), anyInt()); 1607 1608 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1609 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1610 mBassClientStateMachine.mBluetoothGatt = btGatt; 1611 BluetoothGattCharacteristic scanControlPoint = 1612 Mockito.mock(BluetoothGattCharacteristic.class); 1613 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1614 1615 mBassClientStateMachine.mPendingSourceToSwitch = metadata; 1616 sendMessageAndVerifyTransition( 1617 mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata), 1618 BassClientStateMachine.ConnectedProcessing.class); 1619 verify(scanControlPoint).setValue(any(byte[].class)); 1620 verify(btGatt).writeCharacteristic(any()); 1621 assertThat(mBassClientStateMachine.mPendingSourceToSwitch).isNull(); 1622 } 1623 1624 @Test sendSwitchSourceMessage_inConnectedState()1625 public void sendSwitchSourceMessage_inConnectedState() { 1626 initToConnectedState(); 1627 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 1628 Integer sourceId = 1; 1629 1630 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1631 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1632 mBassClientStateMachine.mBluetoothGatt = btGatt; 1633 BluetoothGattCharacteristic scanControlPoint = 1634 Mockito.mock(BluetoothGattCharacteristic.class); 1635 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1636 1637 mBassClientStateMachine.sendMessage(SWITCH_BCAST_SOURCE, sourceId, 0, metadata); 1638 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1639 assertThat(mBassClientStateMachine.mPendingSourceToSwitch).isEqualTo(metadata); 1640 } 1641 1642 @Test sendUpdateBcastSourceMessage_inConnectedState()1643 public void sendUpdateBcastSourceMessage_inConnectedState() { 1644 initToConnectedState(); 1645 mBassClientStateMachine.connectGatt(true); 1646 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; 1647 1648 // Prepare mBluetoothLeBroadcastReceiveStates for test 1649 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1650 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1651 int sourceId = 1; 1652 int paSync = BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE; 1653 byte[] value = 1654 new byte[] { 1655 (byte) sourceId, // sourceId 1656 (byte) (mSourceTestDevice.getAddressType() & 0xFF), // sourceAddressType 1657 Utils.getByteAddress(mSourceTestDevice)[5], 1658 Utils.getByteAddress(mSourceTestDevice)[4], 1659 Utils.getByteAddress(mSourceTestDevice)[3], 1660 Utils.getByteAddress(mSourceTestDevice)[2], 1661 Utils.getByteAddress(mSourceTestDevice)[1], 1662 Utils.getByteAddress(mSourceTestDevice)[0], // sourceAddress 1663 0x00, // sourceAdvSid 1664 0x00, 1665 0x00, 1666 0x00, // broadcastIdBytes 1667 (byte) paSync, 1668 (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE, 1669 // 16 bytes badBroadcastCode 1670 0x00, 1671 0x00, 1672 0x00, 1673 0x00, 1674 0x00, 1675 0x00, 1676 0x00, 1677 0x00, 1678 0x00, 1679 0x00, 1680 0x00, 1681 0x00, 1682 0x00, 1683 0x00, 1684 0x00, 1685 0x00, 1686 0x01, // numSubGroups 1687 // SubGroup #1 1688 0x00, 1689 0x00, 1690 0x00, 1691 0x00, // audioSyncIndex 1692 0x02, // metaDataLength 1693 0x00, 1694 0x00, // metadata 1695 }; 1696 BluetoothGattCharacteristic characteristic = 1697 Mockito.mock(BluetoothGattCharacteristic.class); 1698 when(characteristic.getValue()).thenReturn(value); 1699 when(characteristic.getInstanceId()).thenReturn(sourceId); 1700 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 1701 mBassClientStateMachine.mGattCallback.onCharacteristicRead( 1702 null, characteristic, GATT_SUCCESS); 1703 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1704 1705 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 1706 when(mBassClientService.getPeriodicAdvertisementResult(any(), anyInt())).thenReturn(null); 1707 1708 mBassClientStateMachine.sendMessage(UPDATE_BCAST_SOURCE, sourceId, paSync, metadata); 1709 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1710 1711 PeriodicAdvertisementResult paResult = Mockito.mock(PeriodicAdvertisementResult.class); 1712 when(mBassClientService.getPeriodicAdvertisementResult(any(), anyInt())) 1713 .thenReturn(paResult); 1714 when(mBassClientService.getBase(anyInt())).thenReturn(null); 1715 Mockito.clearInvocations(callbacks); 1716 1717 mBassClientStateMachine.sendMessage(UPDATE_BCAST_SOURCE, sourceId, paSync, metadata); 1718 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1719 1720 byte[] serviceData = 1721 new byte[] { 1722 // LEVEL 1 1723 (byte) 0x01, 1724 (byte) 0x02, 1725 (byte) 0x03, // mPresentationDelay 1726 (byte) 0x01, // mNumSubGroups 1727 // LEVEL 2 1728 (byte) 0x01, // numBIS 1729 (byte) 0xFF, // VENDOR_CODEC 1730 (byte) 0x0A, 1731 (byte) 0xAB, 1732 (byte) 0xBC, 1733 (byte) 0xCD, 1734 (byte) 0x00, // mCodecConfigLength 1735 (byte) 0x00, // mMetaDataLength 1736 // LEVEL 3 1737 (byte) 0x04, // mIndex 1738 (byte) 0x00, // mCodecConfigLength 1739 }; 1740 1741 BaseData data = BaseData.parseBaseData(serviceData); 1742 when(mBassClientService.getBase(anyInt())).thenReturn(data); 1743 Mockito.clearInvocations(callbacks); 1744 1745 mBassClientStateMachine.sendMessage(UPDATE_BCAST_SOURCE, sourceId, paSync, metadata); 1746 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1747 verify(callbacks).notifySourceModifyFailed(any(), anyInt(), anyInt()); 1748 1749 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1750 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1751 BluetoothGattCharacteristic scanControlPoint = 1752 Mockito.mock(BluetoothGattCharacteristic.class); 1753 mBassClientStateMachine.mBluetoothGatt = btGatt; 1754 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1755 mBassClientStateMachine.mPendingOperation = 0; 1756 mBassClientStateMachine.mPendingSourceId = 0; 1757 mBassClientStateMachine.mPendingMetadata = null; 1758 Mockito.clearInvocations(callbacks); 1759 1760 sendMessageAndVerifyTransition( 1761 mBassClientStateMachine.obtainMessage( 1762 UPDATE_BCAST_SOURCE, sourceId, paSync, metadata), 1763 BassClientStateMachine.ConnectedProcessing.class); 1764 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE); 1765 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(sourceId); 1766 assertThat(mBassClientStateMachine.mPendingMetadata).isEqualTo(metadata); 1767 } 1768 1769 @Test sendSetBcastCodeMessage_inConnectedState()1770 public void sendSetBcastCodeMessage_inConnectedState() { 1771 initToConnectedState(); 1772 mBassClientStateMachine.connectGatt(true); 1773 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; 1774 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1775 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1776 1777 // Prepare mBluetoothLeBroadcastReceiveStates with metadata for test 1778 mBassClientStateMachine.mShouldHandleMessage = false; 1779 int sourceId = 1; 1780 byte[] value = 1781 new byte[] { 1782 (byte) sourceId, // sourceId 1783 (byte) (mSourceTestDevice.getAddressType() & 0xFF), // sourceAddressType 1784 Utils.getByteAddress(mSourceTestDevice)[5], 1785 Utils.getByteAddress(mSourceTestDevice)[4], 1786 Utils.getByteAddress(mSourceTestDevice)[3], 1787 Utils.getByteAddress(mSourceTestDevice)[2], 1788 Utils.getByteAddress(mSourceTestDevice)[1], 1789 Utils.getByteAddress(mSourceTestDevice)[0], // sourceAddress 1790 0x00, // sourceAdvSid 1791 0x00, 1792 0x00, 1793 0x00, // broadcastIdBytes 1794 (byte) BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 1795 (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED, 1796 0x01, // numSubGroups 1797 // SubGroup #1 1798 0x00, 1799 0x00, 1800 0x00, 1801 0x00, // audioSyncIndex 1802 0x02, // metaDataLength 1803 0x00, 1804 0x00, // metadata 1805 }; 1806 mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE; 1807 mBassClientStateMachine.mPendingSourceId = (byte) sourceId; 1808 BluetoothGattCharacteristic characteristic = 1809 Mockito.mock(BluetoothGattCharacteristic.class); 1810 when(characteristic.getValue()).thenReturn(value); 1811 when(characteristic.getInstanceId()).thenReturn(sourceId); 1812 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 1813 1814 mBassClientStateMachine.mGattCallback.onCharacteristicRead( 1815 null, characteristic, GATT_SUCCESS); 1816 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1817 1818 mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata(); 1819 mBassClientStateMachine.mGattCallback.onCharacteristicRead( 1820 null, characteristic, GATT_SUCCESS); 1821 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1822 mBassClientStateMachine.mShouldHandleMessage = true; 1823 1824 BluetoothLeBroadcastReceiveState recvState = 1825 new BluetoothLeBroadcastReceiveState( 1826 2, 1827 BluetoothDevice.ADDRESS_TYPE_PUBLIC, 1828 mAdapter.getRemoteLeDevice( 1829 "00:00:00:00:00:00", BluetoothDevice.ADDRESS_TYPE_PUBLIC), 1830 0, 1831 0, 1832 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 1833 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED, 1834 null, 1835 0, 1836 Arrays.asList(new Long[0]), 1837 Arrays.asList(new BluetoothLeAudioContentMetadata[0])); 1838 mBassClientStateMachine.mSetBroadcastCodePending = false; 1839 mBassClientStateMachine.sendMessage(SET_BCAST_CODE, recvState); 1840 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1841 assertThat(mBassClientStateMachine.mSetBroadcastCodePending).isTrue(); 1842 1843 recvState = 1844 new BluetoothLeBroadcastReceiveState( 1845 sourceId, 1846 BluetoothDevice.ADDRESS_TYPE_PUBLIC, 1847 mAdapter.getRemoteLeDevice( 1848 "00:00:00:00:00:00", BluetoothDevice.ADDRESS_TYPE_PUBLIC), 1849 0, 1850 0, 1851 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 1852 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED, 1853 null, 1854 0, 1855 Arrays.asList(new Long[0]), 1856 Arrays.asList(new BluetoothLeAudioContentMetadata[0])); 1857 mBassClientStateMachine.sendMessage(SET_BCAST_CODE, recvState); 1858 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1859 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1860 mBassClientStateMachine.mBluetoothGatt = btGatt; 1861 BluetoothGattCharacteristic scanControlPoint = 1862 Mockito.mock(BluetoothGattCharacteristic.class); 1863 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1864 1865 sendMessageAndVerifyTransition( 1866 mBassClientStateMachine.obtainMessage(SET_BCAST_CODE, recvState), 1867 BassClientStateMachine.ConnectedProcessing.class); 1868 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(SET_BCAST_CODE); 1869 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(sourceId); 1870 verify(btGatt).writeCharacteristic(any()); 1871 verify(scanControlPoint).setValue(any(byte[].class)); 1872 } 1873 1874 @Test receiveSinkReceiveState_inConnectedState()1875 public void receiveSinkReceiveState_inConnectedState() { 1876 int sourceId = 1; 1877 1878 initToConnectedState(); 1879 mBassClientStateMachine.connectGatt(true); 1880 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; 1881 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1882 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1883 1884 generateBroadcastReceiveStatesAndVerify( 1885 mSourceTestDevice, 1886 sourceId, 1887 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 1888 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED, 1889 0x0L); 1890 } 1891 1892 @Test sendRemoveBcastSourceMessage_inConnectedState()1893 public void sendRemoveBcastSourceMessage_inConnectedState() { 1894 initToConnectedState(); 1895 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1896 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1897 1898 int sid = 10; 1899 mBassClientStateMachine.sendMessage(REMOVE_BCAST_SOURCE, sid); 1900 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1901 verify(callbacks).notifySourceRemoveFailed(any(), anyInt(), anyInt()); 1902 1903 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1904 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1905 mBassClientStateMachine.mBluetoothGatt = btGatt; 1906 BluetoothGattCharacteristic scanControlPoint = 1907 Mockito.mock(BluetoothGattCharacteristic.class); 1908 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1909 1910 sendMessageAndVerifyTransition( 1911 mBassClientStateMachine.obtainMessage(REMOVE_BCAST_SOURCE, sid), 1912 BassClientStateMachine.ConnectedProcessing.class); 1913 verify(scanControlPoint).setValue(any(byte[].class)); 1914 verify(btGatt).writeCharacteristic(any()); 1915 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(REMOVE_BCAST_SOURCE); 1916 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(sid); 1917 } 1918 1919 @Test sendInitiatePaSyncTransferMessage_inConnectedState()1920 public void sendInitiatePaSyncTransferMessage_inConnectedState() { 1921 initToConnectedState(); 1922 int syncHandle = 1234; 1923 int sourceId = 4321; 1924 1925 mBassClientStateMachine.sendMessage(INITIATE_PA_SYNC_TRANSFER, syncHandle, sourceId); 1926 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1927 1928 int serviceData = 0x000000FF & sourceId; 1929 serviceData = serviceData << 8; 1930 // advA matches EXT_ADV_ADDRESS 1931 // also matches source address (as we would have written) 1932 serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS); 1933 serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS); 1934 verify(mMethodProxy) 1935 .periodicAdvertisingManagerTransferSync( 1936 any(), any(), eq(serviceData), eq(syncHandle)); 1937 } 1938 1939 @Test sendConnectMessage_inConnectedProcessingState_doesNotChangeState()1940 public void sendConnectMessage_inConnectedProcessingState_doesNotChangeState() { 1941 initToConnectedProcessingState(); 1942 1943 mBassClientStateMachine.sendMessage(CONNECT); 1944 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1945 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1946 } 1947 1948 @Test sendDisconnectMessage_inConnectedProcessingState_doesNotChangeState()1949 public void sendDisconnectMessage_inConnectedProcessingState_doesNotChangeState() { 1950 initToConnectedProcessingState(); 1951 1952 // Mock instance of btGatt was created in initToConnectedProcessingState(). 1953 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1954 mBassClientStateMachine.mBluetoothGatt; 1955 1956 mBassClientStateMachine.mBluetoothGatt = null; 1957 mBassClientStateMachine.sendMessage(DISCONNECT); 1958 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1959 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1960 1961 mBassClientStateMachine.mBluetoothGatt = btGatt; 1962 sendMessageAndVerifyTransition( 1963 mBassClientStateMachine.obtainMessage(DISCONNECT), 1964 BassClientStateMachine.Disconnected.class); 1965 verify(btGatt).disconnect(); 1966 verify(btGatt).close(); 1967 } 1968 1969 @Test sendStateChangedMessage_inConnectedProcessingState()1970 public void sendStateChangedMessage_inConnectedProcessingState() { 1971 initToConnectedProcessingState(); 1972 1973 Message msgToConnectedState = 1974 mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1975 msgToConnectedState.obj = STATE_CONNECTED; 1976 1977 mBassClientStateMachine.sendMessage(msgToConnectedState); 1978 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1979 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1980 1981 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1982 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 1983 mBassClientStateMachine.mBluetoothGatt = btGatt; 1984 Message msgToNoneConnectedState = 1985 mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1986 msgToNoneConnectedState.obj = STATE_DISCONNECTING; 1987 sendMessageAndVerifyTransition( 1988 msgToNoneConnectedState, BassClientStateMachine.Disconnected.class); 1989 verify(btGatt).close(); 1990 assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); 1991 } 1992 1993 /** This also tests BassClientStateMachine#sendPendingCallbacks */ 1994 @Test sendGattTxnProcessedMessage_inConnectedProcessingState()1995 public void sendGattTxnProcessedMessage_inConnectedProcessingState() { 1996 initToConnectedProcessingState(); 1997 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1998 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1999 2000 // Test sendPendingCallbacks(START_SCAN_OFFLOAD, ERROR_UNKNOWN) 2001 mBassClientStateMachine.mPendingOperation = START_SCAN_OFFLOAD; 2002 sendMessageAndVerifyTransition( 2003 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 2004 BassClientStateMachine.Connected.class); 2005 2006 // Test sendPendingCallbacks(ADD_BCAST_SOURCE, ERROR_UNKNOWN) 2007 moveConnectedStateToConnectedProcessingState(); 2008 mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata(); 2009 mBassClientStateMachine.mPendingOperation = ADD_BCAST_SOURCE; 2010 sendMessageAndVerifyTransition( 2011 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 2012 BassClientStateMachine.Connected.class); 2013 verify(callbacks).notifySourceAddFailed(any(), any(), anyInt()); 2014 2015 // Test sendPendingCallbacks(UPDATE_BCAST_SOURCE, ERROR_UNKNOWN) 2016 moveConnectedStateToConnectedProcessingState(); 2017 mBassClientStateMachine.mPendingOperation = UPDATE_BCAST_SOURCE; 2018 sendMessageAndVerifyTransition( 2019 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 2020 BassClientStateMachine.Connected.class); 2021 verify(callbacks).notifySourceModifyFailed(any(), anyInt(), anyInt()); 2022 2023 // Test sendPendingCallbacks(REMOVE_BCAST_SOURCE, ERROR_UNKNOWN) 2024 moveConnectedStateToConnectedProcessingState(); 2025 mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE; 2026 sendMessageAndVerifyTransition( 2027 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 2028 BassClientStateMachine.Connected.class); 2029 verify(callbacks).notifySourceRemoveFailed(any(), anyInt(), anyInt()); 2030 2031 // Test sendPendingCallbacks(SET_BCAST_CODE, REASON_LOCAL_APP_REQUEST) 2032 moveConnectedStateToConnectedProcessingState(); 2033 mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE; 2034 sendMessageAndVerifyTransition( 2035 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 2036 BassClientStateMachine.Connected.class); 2037 // Nothing to verify more 2038 2039 // Test sendPendingCallbacks(SET_BCAST_CODE, REASON_LOCAL_APP_REQUEST) 2040 moveConnectedStateToConnectedProcessingState(); 2041 mBassClientStateMachine.mPendingOperation = -1; 2042 sendMessageAndVerifyTransition( 2043 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 2044 BassClientStateMachine.Connected.class); 2045 // Nothing to verify more 2046 } 2047 2048 @Test sendGattTxnTimeoutMessage_inConnectedProcessingState_doesNotChangeState()2049 public void sendGattTxnTimeoutMessage_inConnectedProcessingState_doesNotChangeState() { 2050 initToConnectedProcessingState(); 2051 2052 mBassClientStateMachine.mPendingOperation = SET_BCAST_CODE; 2053 mBassClientStateMachine.mPendingSourceId = 0; 2054 sendMessageAndVerifyTransition( 2055 mBassClientStateMachine.obtainMessage(GATT_TXN_TIMEOUT, GATT_FAILURE), 2056 BassClientStateMachine.Connected.class); 2057 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(-1); 2058 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(-1); 2059 } 2060 2061 @Test sendMessageForDeferring_inConnectedProcessingState_defersMessage()2062 public void sendMessageForDeferring_inConnectedProcessingState_defersMessage() { 2063 initToConnectedProcessingState(); 2064 2065 mBassClientStateMachine.sendMessage(READ_BASS_CHARACTERISTICS); 2066 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2067 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(READ_BASS_CHARACTERISTICS)) 2068 .isTrue(); 2069 2070 mBassClientStateMachine.sendMessage(START_SCAN_OFFLOAD); 2071 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2072 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(START_SCAN_OFFLOAD)).isTrue(); 2073 2074 mBassClientStateMachine.sendMessage(STOP_SCAN_OFFLOAD); 2075 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2076 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(STOP_SCAN_OFFLOAD)).isTrue(); 2077 2078 mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE); 2079 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2080 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(ADD_BCAST_SOURCE)).isTrue(); 2081 2082 mBassClientStateMachine.sendMessage(SET_BCAST_CODE); 2083 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2084 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(SET_BCAST_CODE)).isTrue(); 2085 2086 mBassClientStateMachine.sendMessage(REMOVE_BCAST_SOURCE); 2087 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2088 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(REMOVE_BCAST_SOURCE)).isTrue(); 2089 2090 mBassClientStateMachine.sendMessage(SWITCH_BCAST_SOURCE); 2091 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2092 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(SWITCH_BCAST_SOURCE)).isTrue(); 2093 2094 mBassClientStateMachine.sendMessage(INITIATE_PA_SYNC_TRANSFER); 2095 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2096 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(INITIATE_PA_SYNC_TRANSFER)) 2097 .isTrue(); 2098 } 2099 2100 @Test sendInvalidMessage_inConnectedProcessingState_doesNotChangeState()2101 public void sendInvalidMessage_inConnectedProcessingState_doesNotChangeState() { 2102 initToConnectedProcessingState(); 2103 2104 mBassClientStateMachine.sendMessage(-1); 2105 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2106 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 2107 } 2108 2109 @Test dump_doesNotCrash()2110 public void dump_doesNotCrash() { 2111 mBassClientStateMachine.dump(new StringBuilder()); 2112 } 2113 2114 @Test sendAddBcastSourceMessage_NoResponseWrite()2115 public void sendAddBcastSourceMessage_NoResponseWrite() { 2116 mBassClientStateMachine.connectGatt(true); 2117 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 2118 cb.onMtuChanged(null, 250, GATT_SUCCESS); 2119 initToConnectedState(); 2120 2121 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 2122 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 2123 2124 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 2125 // verify local broadcast doesn't require active synced source 2126 when(mBassClientService.isLocalBroadcast(any(BluetoothLeBroadcastMetadata.class))) 2127 .thenReturn(true); 2128 mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata); 2129 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2130 2131 verify(mBassClientService).getCallbacks(); 2132 verify(callbacks).notifySourceAddFailed(any(), any(), anyInt()); 2133 2134 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 2135 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 2136 mBassClientStateMachine.mBluetoothGatt = btGatt; 2137 BluetoothGattCharacteristic scanControlPoint = 2138 Mockito.mock(BluetoothGattCharacteristic.class); 2139 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 2140 2141 sendMessageAndVerifyTransition( 2142 mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata), 2143 BassClientStateMachine.ConnectedProcessing.class); 2144 verify(scanControlPoint).setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); 2145 verify(scanControlPoint).setValue(any(byte[].class)); 2146 verify(btGatt).writeCharacteristic(any()); 2147 } 2148 2149 @Test sendAddBcastSourceMessage_LongWrite()2150 public void sendAddBcastSourceMessage_LongWrite() { 2151 mBassClientStateMachine.connectGatt(true); 2152 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 2153 cb.onMtuChanged(null, 23, GATT_SUCCESS); 2154 initToConnectedState(); 2155 2156 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 2157 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 2158 2159 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 2160 // verify local broadcast doesn't require active synced source 2161 when(mBassClientService.isLocalBroadcast(any(BluetoothLeBroadcastMetadata.class))) 2162 .thenReturn(true); 2163 mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata); 2164 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2165 2166 verify(mBassClientService).getCallbacks(); 2167 verify(callbacks).notifySourceAddFailed(any(), any(), anyInt()); 2168 2169 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 2170 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 2171 mBassClientStateMachine.mBluetoothGatt = btGatt; 2172 BluetoothGattCharacteristic scanControlPoint = 2173 Mockito.mock(BluetoothGattCharacteristic.class); 2174 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 2175 2176 sendMessageAndVerifyTransition( 2177 mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata), 2178 BassClientStateMachine.ConnectedProcessing.class); 2179 verify(scanControlPoint).setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); 2180 verify(scanControlPoint).setValue(any(byte[].class)); 2181 verify(btGatt).writeCharacteristic(any()); 2182 } 2183 initToDisconnectedState()2184 private void initToDisconnectedState() { 2185 allowConnection(true); 2186 allowConnectGatt(true); 2187 assertThat(mBassClientStateMachine.getCurrentState()) 2188 .isInstanceOf(BassClientStateMachine.Disconnected.class); 2189 } 2190 2191 @Test cancelPendingAddBcastSourceMessage_inConnectedState()2192 public void cancelPendingAddBcastSourceMessage_inConnectedState() { 2193 initToConnectedState(); 2194 2195 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 2196 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 2197 2198 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 2199 // verify local broadcast doesn't require active synced source 2200 when(mBassClientService.isLocalBroadcast(any(BluetoothLeBroadcastMetadata.class))) 2201 .thenReturn(true); 2202 2203 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 2204 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 2205 mBassClientStateMachine.mBluetoothGatt = btGatt; 2206 BluetoothGattCharacteristic scanControlPoint = 2207 Mockito.mock(BluetoothGattCharacteristic.class); 2208 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 2209 2210 sendMessageAndVerifyTransition( 2211 mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata), 2212 BassClientStateMachine.ConnectedProcessing.class); 2213 verify(scanControlPoint).setValue(any(byte[].class)); 2214 verify(btGatt).writeCharacteristic(any()); 2215 2216 /* Verify if there is pending add source operation */ 2217 assertThat(mBassClientStateMachine.hasPendingSourceOperation(metadata.getBroadcastId())) 2218 .isTrue(); 2219 2220 assertThat(mBassClientStateMachine.mMsgWhats).contains(CANCEL_PENDING_SOURCE_OPERATION); 2221 assertThat(mBassClientStateMachine.mMsgAgr1).isEqualTo(TEST_BROADCAST_ID); 2222 2223 /* Inject a cancel pending source operation event */ 2224 Message msg = mBassClientStateMachine.obtainMessage(CANCEL_PENDING_SOURCE_OPERATION); 2225 msg.arg1 = metadata.getBroadcastId(); 2226 mBassClientStateMachine.sendMessage(msg); 2227 2228 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2229 2230 /* Verify if pending add source operation is canceled */ 2231 assertThat(mBassClientStateMachine.hasPendingSourceOperation(metadata.getBroadcastId())) 2232 .isFalse(); 2233 } 2234 2235 @Test cancelPendingUpdateBcastSourceMessage_inConnectedState()2236 public void cancelPendingUpdateBcastSourceMessage_inConnectedState() { 2237 initToConnectedState(); 2238 mBassClientStateMachine.connectGatt(true); 2239 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; 2240 2241 // Prepare mBluetoothLeBroadcastReceiveStates for test 2242 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 2243 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 2244 int sourceId = 1; 2245 int paSync = BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE; 2246 byte[] value = 2247 new byte[] { 2248 (byte) sourceId, // sourceId 2249 (byte) (mSourceTestDevice.getAddressType() & 0xFF), // sourceAddressType 2250 Utils.getByteAddress(mSourceTestDevice)[5], 2251 Utils.getByteAddress(mSourceTestDevice)[4], 2252 Utils.getByteAddress(mSourceTestDevice)[3], 2253 Utils.getByteAddress(mSourceTestDevice)[2], 2254 Utils.getByteAddress(mSourceTestDevice)[1], 2255 Utils.getByteAddress(mSourceTestDevice)[0], // sourceAddress 2256 0x00, // sourceAdvSid 2257 (byte) (TEST_BROADCAST_ID & 0xFF), 2258 0x00, 2259 0x00, // broadcastIdBytes 2260 (byte) paSync, 2261 (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE, 2262 // 16 bytes badBroadcastCode 2263 0x00, 2264 0x00, 2265 0x00, 2266 0x00, 2267 0x00, 2268 0x00, 2269 0x00, 2270 0x00, 2271 0x00, 2272 0x00, 2273 0x00, 2274 0x00, 2275 0x00, 2276 0x00, 2277 0x00, 2278 0x00, 2279 0x01, // numSubGroups 2280 // SubGroup #1 2281 0x00, 2282 0x00, 2283 0x00, 2284 0x00, // audioSyncIndex 2285 0x02, // metaDataLength 2286 0x00, 2287 0x00, // metadata 2288 }; 2289 BluetoothGattCharacteristic characteristic = 2290 Mockito.mock(BluetoothGattCharacteristic.class); 2291 when(characteristic.getValue()).thenReturn(value); 2292 when(characteristic.getInstanceId()).thenReturn(sourceId); 2293 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 2294 mBassClientStateMachine.mGattCallback.onCharacteristicRead( 2295 null, characteristic, GATT_SUCCESS); 2296 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2297 2298 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 2299 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 2300 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 2301 mBassClientStateMachine.mBluetoothGatt = btGatt; 2302 BluetoothGattCharacteristic scanControlPoint = 2303 Mockito.mock(BluetoothGattCharacteristic.class); 2304 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 2305 2306 sendMessageAndVerifyTransition( 2307 mBassClientStateMachine.obtainMessage( 2308 UPDATE_BCAST_SOURCE, sourceId, paSync, metadata), 2309 BassClientStateMachine.ConnectedProcessing.class); 2310 verify(scanControlPoint).setValue(any(byte[].class)); 2311 verify(btGatt).writeCharacteristic(any()); 2312 2313 /* Verify if there is pending add source operation */ 2314 assertThat(mBassClientStateMachine.hasPendingSourceOperation(metadata.getBroadcastId())) 2315 .isTrue(); 2316 2317 assertThat(mBassClientStateMachine.mMsgWhats).contains(CANCEL_PENDING_SOURCE_OPERATION); 2318 assertThat(mBassClientStateMachine.mMsgAgr1).isEqualTo(TEST_BROADCAST_ID); 2319 2320 /* Inject a cancel pending source operation event */ 2321 Message msg = mBassClientStateMachine.obtainMessage(CANCEL_PENDING_SOURCE_OPERATION); 2322 msg.arg1 = metadata.getBroadcastId(); 2323 mBassClientStateMachine.sendMessage(msg); 2324 2325 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2326 2327 /* Verify if pending add source operation is canceled */ 2328 assertThat(mBassClientStateMachine.hasPendingSourceOperation(metadata.getBroadcastId())) 2329 .isFalse(); 2330 } 2331 2332 @Test receiveSinkReceiveStateChange_logSyncMetricsWhenSyncNoPast()2333 public void receiveSinkReceiveStateChange_logSyncMetricsWhenSyncNoPast() { 2334 prepareInitialReceiveStateForGatt(); 2335 2336 generateBroadcastReceiveStatesAndVerify( 2337 mSourceTestDevice, 2338 TEST_SOURCE_ID, 2339 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_NO_PAST, 2340 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED, 2341 0x0L); 2342 // Verify broadcast audio session is logged when pa no past 2343 verify(mMetricsLogger) 2344 .logLeAudioBroadcastAudioSync( 2345 eq(mTestDevice), 2346 eq(TEST_BROADCAST_ID), 2347 eq(false), 2348 anyLong(), 2349 anyLong(), 2350 anyLong(), 2351 eq(0x5)); // STATS_SYNC_PA_NO_PAST 2352 } 2353 2354 @Test receiveSinkReceiveStateChange_logSyncMetricsWhenBigEncryptFailed()2355 public void receiveSinkReceiveStateChange_logSyncMetricsWhenBigEncryptFailed() { 2356 prepareInitialReceiveStateForGatt(); 2357 2358 generateBroadcastReceiveStatesAndVerify( 2359 mSourceTestDevice, 2360 TEST_SOURCE_ID, 2361 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED, 2362 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE, 2363 0x0L); 2364 // Verify broadcast audio session is logged when big encryption failed 2365 verify(mMetricsLogger) 2366 .logLeAudioBroadcastAudioSync( 2367 eq(mTestDevice), 2368 eq(TEST_BROADCAST_ID), 2369 eq(false), 2370 anyLong(), 2371 anyLong(), 2372 anyLong(), 2373 eq(0x6)); // STATS_SYNC_BIG_DECRYPT_FAILED 2374 } 2375 2376 @Test receiveSinkReceiveStateChange_logSyncMetricsWhenAudioSyncFailed()2377 public void receiveSinkReceiveStateChange_logSyncMetricsWhenAudioSyncFailed() { 2378 prepareInitialReceiveStateForGatt(); 2379 2380 generateBroadcastReceiveStatesAndVerify( 2381 mSourceTestDevice, 2382 TEST_SOURCE_ID, 2383 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED, 2384 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING, 2385 BassConstants.BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG); 2386 // Verify broadcast audio session is logged when bis sync failed 2387 verify(mMetricsLogger) 2388 .logLeAudioBroadcastAudioSync( 2389 eq(mTestDevice), 2390 eq(TEST_BROADCAST_ID), 2391 eq(false), 2392 anyLong(), 2393 anyLong(), 2394 anyLong(), 2395 eq(0x7)); // STATS_SYNC_AUDIO_SYNC_FAILED 2396 } 2397 2398 @Test receiveSinkReceiveStateChange_logSyncMetricsWhenSourceRemoved()2399 public void receiveSinkReceiveStateChange_logSyncMetricsWhenSourceRemoved() { 2400 prepareInitialReceiveStateForGatt(); 2401 2402 generateBroadcastReceiveStatesAndVerify( 2403 mSourceTestDevice, 2404 TEST_SOURCE_ID, 2405 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED, 2406 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING, 2407 0x1L); 2408 2409 // Verify broadcast audio session is not reported, only update the status 2410 verify(mMetricsLogger, never()) 2411 .logLeAudioBroadcastAudioSync( 2412 any(), anyInt(), anyBoolean(), anyLong(), anyLong(), anyLong(), anyInt()); 2413 2414 // Update receive state to source removed 2415 generateBroadcastReceiveStatesAndVerify( 2416 mEmptyTestDevice, 2417 TEST_SOURCE_ID, 2418 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 2419 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, 2420 0x0L); 2421 2422 // Verify broadcast audio session is logged when source removed 2423 verify(mMetricsLogger) 2424 .logLeAudioBroadcastAudioSync( 2425 eq(mTestDevice), 2426 eq(TEST_BROADCAST_ID), 2427 eq(false), 2428 anyLong(), 2429 anyLong(), 2430 anyLong(), 2431 eq(0x3)); // STATS_SYNC_AUDIO_SYNC_SUCCESS 2432 } 2433 2434 @Test sinkDisconnected_logSyncMetricsWhenSourceRemoved()2435 public void sinkDisconnected_logSyncMetricsWhenSourceRemoved() { 2436 prepareInitialReceiveStateForGatt(); 2437 2438 generateBroadcastReceiveStatesAndVerify( 2439 mSourceTestDevice, 2440 TEST_SOURCE_ID, 2441 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED, 2442 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING, 2443 0x1L); 2444 2445 sendMessageAndVerifyTransition( 2446 mBassClientStateMachine.obtainMessage(DISCONNECT), 2447 BassClientStateMachine.Disconnected.class); 2448 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2449 2450 // Verify broadcast audio session is logged when source removed 2451 verify(mMetricsLogger) 2452 .logLeAudioBroadcastAudioSync( 2453 eq(mTestDevice), 2454 eq(TEST_BROADCAST_ID), 2455 eq(false), 2456 anyLong(), 2457 anyLong(), 2458 anyLong(), 2459 eq(0x3)); // STATS_SYNC_AUDIO_SYNC_SUCCESS 2460 } 2461 2462 @Test 2463 @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER) sinkConnected_queueAddingSourceForReceiveStateReady()2464 public void sinkConnected_queueAddingSourceForReceiveStateReady() { 2465 mBassClientStateMachine.connectGatt(true); 2466 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 2467 cb.onMtuChanged(null, 23, GATT_SUCCESS); 2468 initToConnectedState(); 2469 2470 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 1; 2471 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 2472 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 2473 2474 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 2475 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 2476 mBassClientStateMachine.mBluetoothGatt = btGatt; 2477 BluetoothGattCharacteristic scanControlPoint = 2478 Mockito.mock(BluetoothGattCharacteristic.class); 2479 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 2480 2481 // Initial receive state with empty source device 2482 generateBroadcastReceiveStatesAndVerify( 2483 mEmptyTestDevice, 2484 TEST_SOURCE_ID, 2485 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 2486 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, 2487 0x0L); 2488 // Verify notifyBassStateReady is called 2489 verify(callbacks).notifyBassStateReady(eq(mTestDevice)); 2490 assertThat(mBassClientStateMachine.isBassStateReady()).isEqualTo(true); 2491 } 2492 2493 @Test updateBroadcastSource_withoutMetadata()2494 public void updateBroadcastSource_withoutMetadata() { 2495 int sourceId = 1; 2496 int paSync = BassConstants.PA_SYNC_DO_NOT_SYNC; 2497 2498 prepareInitialReceiveStateForGatt(); 2499 2500 generateBroadcastReceiveStatesAndVerify( 2501 mSourceTestDevice, 2502 TEST_SOURCE_ID, 2503 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED, 2504 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING, 2505 0x1L); 2506 2507 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 2508 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 2509 mBassClientStateMachine.mBluetoothGatt = btGatt; 2510 BluetoothGattCharacteristic scanControlPoint = 2511 Mockito.mock(BluetoothGattCharacteristic.class); 2512 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 2513 2514 mBassClientStateMachine.mPendingOperation = 0; 2515 mBassClientStateMachine.mPendingSourceId = 0; 2516 mBassClientStateMachine.mPendingMetadata = null; 2517 2518 // update source without metadata 2519 sendMessageAndVerifyTransition( 2520 mBassClientStateMachine.obtainMessage(UPDATE_BCAST_SOURCE, sourceId, paSync, null), 2521 BassClientStateMachine.ConnectedProcessing.class); 2522 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE); 2523 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(sourceId); 2524 } 2525 2526 @Test updateBroadcastSource_pendingSourceToRemove()2527 public void updateBroadcastSource_pendingSourceToRemove() { 2528 prepareInitialReceiveStateForGatt(); 2529 2530 generateBroadcastReceiveStatesAndVerify( 2531 mSourceTestDevice, 2532 TEST_SOURCE_ID, 2533 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED, 2534 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING, 2535 0x1L); 2536 2537 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 2538 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 2539 mBassClientStateMachine.mBluetoothGatt = btGatt; 2540 BluetoothGattCharacteristic scanControlPoint = 2541 Mockito.mock(BluetoothGattCharacteristic.class); 2542 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 2543 2544 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 2545 mBassClientStateMachine.mPendingMetadata = metadata; 2546 2547 sendMessageAndVerifyTransition( 2548 mBassClientStateMachine.obtainMessage( 2549 UPDATE_BCAST_SOURCE, 2550 TEST_SOURCE_ID, 2551 BassConstants.PA_SYNC_DO_NOT_SYNC, 2552 metadata), 2553 BassClientStateMachine.ConnectedProcessing.class); 2554 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE); 2555 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(TEST_SOURCE_ID); 2556 2557 mBassClientStateMachine.mPendingOperation = 0; 2558 mBassClientStateMachine.mPendingSourceId = 0; 2559 // Verify not removing source when PA is still synced 2560 generateBroadcastReceiveStatesAndVerify( 2561 mSourceTestDevice, 2562 TEST_SOURCE_ID, 2563 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED, 2564 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, 2565 0x0L); 2566 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(0); 2567 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(0); 2568 2569 // Verify removing source when PA is unsynced 2570 generateBroadcastReceiveStatesAndVerify( 2571 mSourceTestDevice, 2572 TEST_SOURCE_ID, 2573 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 2574 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, 2575 0x0L); 2576 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(REMOVE_BCAST_SOURCE); 2577 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(TEST_SOURCE_ID); 2578 } 2579 2580 @Test updateBroadcastSource_withMetadataChanged()2581 public void updateBroadcastSource_withMetadataChanged() { 2582 prepareInitialReceiveStateForGatt(); 2583 2584 generateBroadcastReceiveStatesAndVerify( 2585 mSourceTestDevice, 2586 TEST_SOURCE_ID, 2587 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED, 2588 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING, 2589 0x1L); 2590 2591 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 2592 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 2593 mBassClientStateMachine.mBluetoothGatt = btGatt; 2594 BluetoothGattCharacteristic scanControlPoint = 2595 Mockito.mock(BluetoothGattCharacteristic.class); 2596 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 2597 2598 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 2599 mBassClientStateMachine.mPendingMetadata = metadata; 2600 2601 // Verify pausing broadcast stream with updated metadata 2602 BluetoothLeBroadcastMetadata updatedMetadataPaused = getMetadataToPauseStream(metadata); 2603 byte[] valueBisPaused = convertMetadataToUpdateSourceByteArray(updatedMetadataPaused); 2604 2605 sendMessageAndVerifyTransition( 2606 mBassClientStateMachine.obtainMessage( 2607 UPDATE_BCAST_SOURCE, 2608 TEST_SOURCE_ID, 2609 BassConstants.INVALID_PA_SYNC_VALUE, 2610 updatedMetadataPaused), 2611 BassClientStateMachine.ConnectedProcessing.class); 2612 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE); 2613 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(TEST_SOURCE_ID); 2614 verify(scanControlPoint).setValue(eq(valueBisPaused)); 2615 2616 sendMessageAndVerifyTransition( 2617 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED), 2618 BassClientStateMachine.Connected.class); 2619 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2620 Mockito.clearInvocations(scanControlPoint); 2621 2622 // Verify resuming broadcast stream with the original metadata 2623 byte[] valueBisResumed = convertMetadataToUpdateSourceByteArray(metadata); 2624 sendMessageAndVerifyTransition( 2625 mBassClientStateMachine.obtainMessage( 2626 UPDATE_BCAST_SOURCE, 2627 TEST_SOURCE_ID, 2628 BassConstants.INVALID_PA_SYNC_VALUE, 2629 metadata), 2630 BassClientStateMachine.ConnectedProcessing.class); 2631 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE); 2632 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(TEST_SOURCE_ID); 2633 verify(scanControlPoint).setValue(eq(valueBisResumed)); 2634 2635 sendMessageAndVerifyTransition( 2636 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED), 2637 BassClientStateMachine.Connected.class); 2638 2639 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2640 Mockito.clearInvocations(scanControlPoint); 2641 } 2642 initToConnectingState()2643 private void initToConnectingState() { 2644 allowConnection(true); 2645 allowConnectGatt(true); 2646 sendMessageAndVerifyTransition( 2647 mBassClientStateMachine.obtainMessage(CONNECT), 2648 BassClientStateMachine.Connecting.class); 2649 Mockito.clearInvocations(mBassClientService); 2650 } 2651 initToConnectedState()2652 private void initToConnectedState() { 2653 initToConnectingState(); 2654 2655 Message msg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 2656 msg.obj = STATE_CONNECTED; 2657 sendMessageAndVerifyTransition(msg, BassClientStateMachine.Connected.class); 2658 Mockito.clearInvocations(mBassClientService); 2659 } 2660 initToConnectedProcessingState()2661 private void initToConnectedProcessingState() { 2662 initToConnectedState(); 2663 moveConnectedStateToConnectedProcessingState(); 2664 } 2665 moveConnectedStateToConnectedProcessingState()2666 private void moveConnectedStateToConnectedProcessingState() { 2667 BluetoothGattCharacteristic gattCharacteristic = 2668 Mockito.mock(BluetoothGattCharacteristic.class); 2669 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 2670 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 2671 mBassClientStateMachine.mBluetoothGatt = btGatt; 2672 sendMessageAndVerifyTransition( 2673 mBassClientStateMachine.obtainMessage( 2674 READ_BASS_CHARACTERISTICS, gattCharacteristic), 2675 BassClientStateMachine.ConnectedProcessing.class); 2676 Mockito.clearInvocations(mBassClientService); 2677 } 2678 isConnectionIntentExpected(Class currentType, Class nextType)2679 private static boolean isConnectionIntentExpected(Class currentType, Class nextType) { 2680 if (currentType == nextType) { 2681 return false; // Same state, no intent expected 2682 } 2683 2684 if ((currentType == BassClientStateMachine.ConnectedProcessing.class) 2685 || (nextType == BassClientStateMachine.ConnectedProcessing.class)) { 2686 return false; // ConnectedProcessing is an internal state that doesn't generate a 2687 // broadcast 2688 } else { 2689 return true; // All other state are generating broadcast 2690 } 2691 } 2692 2693 @SafeVarargs verifyIntentSent(int timeout_ms, Matcher<Intent>... matchers)2694 private void verifyIntentSent(int timeout_ms, Matcher<Intent>... matchers) { 2695 verify(mBassClientService, timeout(timeout_ms)) 2696 .sendBroadcast( 2697 MockitoHamcrest.argThat(AllOf.allOf(matchers)), 2698 eq(BLUETOOTH_CONNECT), 2699 any()); 2700 } 2701 sendMessageAndVerifyTransition(Message msg, Class<T> type)2702 private <T> void sendMessageAndVerifyTransition(Message msg, Class<T> type) { 2703 Mockito.clearInvocations(mBassClientService); 2704 2705 mBassClientStateMachine.sendMessage(msg); 2706 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2707 Class currentStateClass = mBassClientStateMachine.getCurrentState().getClass(); 2708 if (isConnectionIntentExpected(currentStateClass, type)) { 2709 verifyIntentSent( 2710 NO_TIMEOUT_MS, 2711 hasAction(BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED), 2712 hasExtra( 2713 BluetoothProfile.EXTRA_STATE, 2714 classTypeToConnectionState(currentStateClass)), 2715 hasExtra( 2716 BluetoothProfile.EXTRA_PREVIOUS_STATE, 2717 classTypeToConnectionState(type)), 2718 hasFlag( 2719 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 2720 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND)); 2721 } 2722 assertThat(mBassClientStateMachine.getCurrentState()).isInstanceOf(type); 2723 } 2724 createBroadcastMetadata()2725 private BluetoothLeBroadcastMetadata createBroadcastMetadata() { 2726 final String testMacAddress = "00:11:22:33:44:55"; 2727 final int testAdvertiserSid = 1234; 2728 final int testPaSyncInterval = 100; 2729 final int testPresentationDelayMs = 345; 2730 2731 BluetoothDevice testDevice = 2732 mAdapter.getRemoteLeDevice(testMacAddress, BluetoothDevice.ADDRESS_TYPE_RANDOM); 2733 2734 BluetoothLeBroadcastMetadata.Builder builder = 2735 new BluetoothLeBroadcastMetadata.Builder() 2736 .setEncrypted(false) 2737 .setSourceDevice(testDevice, BluetoothDevice.ADDRESS_TYPE_RANDOM) 2738 .setSourceAdvertisingSid(testAdvertiserSid) 2739 .setBroadcastId(TEST_BROADCAST_ID) 2740 .setBroadcastCode(new byte[] {0x00, 0x01, 0x00, 0x02}) 2741 .setPaSyncInterval(testPaSyncInterval) 2742 .setPresentationDelayMicros(testPresentationDelayMs); 2743 // builder expect at least one subgroup 2744 builder.addSubgroup(createBroadcastSubgroup()); 2745 return builder.build(); 2746 } 2747 convertMetadataToUpdateSourceByteArray( BluetoothLeBroadcastMetadata metaData)2748 private static byte[] convertMetadataToUpdateSourceByteArray( 2749 BluetoothLeBroadcastMetadata metaData) { 2750 int numSubGroups = metaData.getSubgroups().size(); 2751 2752 byte[] res = new byte[UPDATE_SOURCE_FIXED_LENGTH + numSubGroups * 5]; 2753 int offset = 0; 2754 // Opcode 2755 res[offset++] = OPCODE_UPDATE_SOURCE; 2756 // Source_ID 2757 res[offset++] = (byte) TEST_SOURCE_ID; 2758 // PA_Sync 2759 res[offset++] = (byte) (0x01); 2760 // PA_Interval 2761 res[offset++] = (byte) 0xFF; 2762 res[offset++] = (byte) 0xFF; 2763 // Num_Subgroups 2764 res[offset++] = (byte) numSubGroups; 2765 2766 for (int i = 0; i < numSubGroups; i++) { 2767 int bisIndexValue = 0; 2768 for (BluetoothLeBroadcastChannel channel : 2769 metaData.getSubgroups().get(i).getChannels()) { 2770 if (channel.isSelected()) { 2771 if (channel.getChannelIndex() == 0) { 2772 continue; 2773 } 2774 bisIndexValue |= 1 << (channel.getChannelIndex() - 1); 2775 } 2776 } 2777 // BIS_Sync 2778 res[offset++] = (byte) (bisIndexValue & 0x00000000000000FF); 2779 res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00) >>> 8); 2780 res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000) >>> 16); 2781 res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000) >>> 24); 2782 // Metadata_Length; On Modify source, don't update any Metadata 2783 res[offset++] = 0; 2784 } 2785 return res; 2786 } 2787 getMetadataToPauseStream( BluetoothLeBroadcastMetadata metadata)2788 private static BluetoothLeBroadcastMetadata getMetadataToPauseStream( 2789 BluetoothLeBroadcastMetadata metadata) { 2790 BluetoothLeBroadcastMetadata.Builder metadataToUpdateBuilder = 2791 new BluetoothLeBroadcastMetadata.Builder(metadata); 2792 2793 List<BluetoothLeBroadcastSubgroup> updatedSubgroups = new ArrayList<>(); 2794 for (BluetoothLeBroadcastSubgroup subgroup : metadata.getSubgroups()) { 2795 BluetoothLeBroadcastSubgroup.Builder subgroupBuilder = 2796 new BluetoothLeBroadcastSubgroup.Builder(subgroup); 2797 2798 List<BluetoothLeBroadcastChannel> updatedChannels = new ArrayList<>(); 2799 for (BluetoothLeBroadcastChannel channel : subgroup.getChannels()) { 2800 BluetoothLeBroadcastChannel updatedChannel = 2801 new BluetoothLeBroadcastChannel.Builder(channel).setSelected(false).build(); 2802 updatedChannels.add(updatedChannel); 2803 } 2804 2805 subgroupBuilder.clearChannel(); 2806 for (BluetoothLeBroadcastChannel channel : updatedChannels) { 2807 subgroupBuilder.addChannel(channel); 2808 } 2809 2810 updatedSubgroups.add(subgroupBuilder.build()); 2811 } 2812 2813 metadataToUpdateBuilder.clearSubgroup(); 2814 for (BluetoothLeBroadcastSubgroup subgroup : updatedSubgroups) { 2815 metadataToUpdateBuilder.addSubgroup(subgroup); 2816 } 2817 2818 return metadataToUpdateBuilder.build(); 2819 } 2820 prepareInitialReceiveStateForGatt()2821 private void prepareInitialReceiveStateForGatt() { 2822 initToConnectedState(); 2823 mBassClientStateMachine.connectGatt(true); 2824 2825 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; 2826 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 2827 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 2828 2829 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 2830 when(mBassClientService.isLocalBroadcast(any(BluetoothLeBroadcastMetadata.class))) 2831 .thenReturn(false); 2832 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 2833 Mockito.mock(BassClientStateMachine.BluetoothGattTestableWrapper.class); 2834 mBassClientStateMachine.mBluetoothGatt = btGatt; 2835 BluetoothGattCharacteristic scanControlPoint = 2836 Mockito.mock(BluetoothGattCharacteristic.class); 2837 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 2838 2839 sendMessageAndVerifyTransition( 2840 mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata), 2841 BassClientStateMachine.ConnectedProcessing.class); 2842 verify(scanControlPoint).setValue(any(byte[].class)); 2843 verify(btGatt).writeCharacteristic(any()); 2844 // Initial receive state 2845 generateBroadcastReceiveStatesAndVerify( 2846 mSourceTestDevice, 2847 TEST_SOURCE_ID, 2848 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 2849 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED, 2850 0x0L); 2851 } 2852 generateBroadcastReceiveStatesAndVerify( BluetoothDevice sourceDevice, int sourceId, int paSyncState, int bigEncryptState, long bisSyncState)2853 private void generateBroadcastReceiveStatesAndVerify( 2854 BluetoothDevice sourceDevice, 2855 int sourceId, 2856 int paSyncState, 2857 int bigEncryptState, 2858 long bisSyncState) { 2859 final int sourceAdvSid = 0; 2860 final int numOfSubgroups = 1; 2861 final int metaDataLength = 142; 2862 2863 // Prepare mBluetoothLeBroadcastReceiveStates with metadata for test 2864 byte[] value_base = 2865 new byte[] { 2866 (byte) sourceId, // sourceId 2867 (byte) (sourceDevice.getAddressType() & 0xFF), // sourceAddressType 2868 Utils.getByteAddress(sourceDevice)[5], 2869 Utils.getByteAddress(sourceDevice)[4], 2870 Utils.getByteAddress(sourceDevice)[3], 2871 Utils.getByteAddress(sourceDevice)[2], 2872 Utils.getByteAddress(sourceDevice)[1], 2873 Utils.getByteAddress(sourceDevice)[0], // sourceAddress 2874 (byte) sourceAdvSid, // sourceAdvSid 2875 (byte) (TEST_BROADCAST_ID & 0xFF), 2876 (byte) 0x00, 2877 (byte) 0x00, // broadcastIdBytes 2878 (byte) paSyncState, 2879 (byte) bigEncryptState, 2880 }; 2881 2882 byte[] value_subgroup = 2883 new byte[] { 2884 (byte) numOfSubgroups, // numSubGroups 2885 (byte) (bisSyncState & 0xFF), 2886 (byte) ((bisSyncState >> 8) & 0xFF), 2887 (byte) ((bisSyncState >> 16) & 0xFF), 2888 (byte) ((bisSyncState >> 24) & 0xFF), // audioSyncIndex 2889 (byte) metaDataLength, // metaDataLength 2890 }; 2891 2892 byte[] badBroadcastCode = new byte[16]; 2893 Arrays.fill(badBroadcastCode, (byte) 0xFF); 2894 2895 byte[] metadataHeader = 2896 new byte[] { 2897 (byte) (metaDataLength - 1), // length 141 2898 (byte) 0xFF 2899 }; 2900 2901 byte[] metadataPayload = new byte[140]; 2902 new Random().nextBytes(metadataPayload); 2903 2904 BluetoothGattCharacteristic characteristic = 2905 Mockito.mock(BluetoothGattCharacteristic.class); 2906 when(characteristic.getValue()) 2907 .thenReturn( 2908 Bytes.concat( 2909 bigEncryptState 2910 == BluetoothLeBroadcastReceiveState 2911 .BIG_ENCRYPTION_STATE_BAD_CODE 2912 ? Bytes.concat(value_base, badBroadcastCode, value_subgroup) 2913 : Bytes.concat(value_base, value_subgroup), 2914 metadataHeader, 2915 metadataPayload)); 2916 when(characteristic.getInstanceId()).thenReturn(sourceId); 2917 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 2918 2919 mBassClientStateMachine.mGattCallback.onCharacteristicRead( 2920 null, characteristic, GATT_SUCCESS); 2921 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 2922 2923 assertThat(mBassClientStateMachine.getAllSources()).hasSize(1); 2924 BluetoothLeBroadcastReceiveState recvState = mBassClientStateMachine.getAllSources().get(0); 2925 2926 assertThat(recvState.getSourceId()).isEqualTo(sourceId); 2927 assertThat(recvState.getSourceAddressType()).isEqualTo(sourceDevice.getAddressType()); 2928 assertThat(recvState.getSourceDevice()).isEqualTo(sourceDevice); 2929 assertThat(recvState.getSourceAdvertisingSid()).isEqualTo(sourceAdvSid); 2930 assertThat(recvState.getBroadcastId()).isEqualTo(TEST_BROADCAST_ID); 2931 assertThat(recvState.getPaSyncState()).isEqualTo(paSyncState); 2932 assertThat(recvState.getBigEncryptionState()).isEqualTo(bigEncryptState); 2933 assertThat(recvState.getNumSubgroups()).isEqualTo(numOfSubgroups); 2934 2935 assertThat(recvState.getBisSyncState()).hasSize(numOfSubgroups); 2936 assertThat(recvState.getBisSyncState().get(0)).isEqualTo(bisSyncState); 2937 2938 assertThat(recvState.getSubgroupMetadata()).hasSize(numOfSubgroups); 2939 BluetoothLeAudioContentMetadata metaData = recvState.getSubgroupMetadata().get(0); 2940 assertThat(metaData.getRawMetadata().length).isEqualTo(metaDataLength); 2941 assertThat(metaData.getRawMetadata()) 2942 .isEqualTo(Bytes.concat(metadataHeader, metadataPayload)); 2943 } 2944 createBroadcastSubgroup()2945 private static BluetoothLeBroadcastSubgroup createBroadcastSubgroup() { 2946 final long testAudioLocationFrontLeft = 0x01; 2947 final long testAudioLocationFrontRight = 0x02; 2948 // For BluetoothLeAudioContentMetadata 2949 final String testProgramInfo = "Test"; 2950 // German language code in ISO 639-3 2951 final String testLanguage = "deu"; 2952 final int testCodecId = 42; 2953 2954 BluetoothLeAudioCodecConfigMetadata codecMetadata = 2955 new BluetoothLeAudioCodecConfigMetadata.Builder() 2956 .setAudioLocation(testAudioLocationFrontLeft) 2957 .build(); 2958 BluetoothLeAudioContentMetadata contentMetadata = 2959 new BluetoothLeAudioContentMetadata.Builder() 2960 .setProgramInfo(testProgramInfo) 2961 .setLanguage(testLanguage) 2962 .build(); 2963 BluetoothLeBroadcastSubgroup.Builder builder = 2964 new BluetoothLeBroadcastSubgroup.Builder() 2965 .setCodecId(testCodecId) 2966 .setCodecSpecificConfig(codecMetadata) 2967 .setContentMetadata(contentMetadata); 2968 2969 BluetoothLeAudioCodecConfigMetadata channelCodecMetadata = 2970 new BluetoothLeAudioCodecConfigMetadata.Builder() 2971 .setAudioLocation(testAudioLocationFrontRight) 2972 .build(); 2973 2974 // builder expect at least one channel 2975 BluetoothLeBroadcastChannel channel = 2976 new BluetoothLeBroadcastChannel.Builder() 2977 .setSelected(true) 2978 .setChannelIndex(TEST_CHANNEL_INDEX) 2979 .setCodecMetadata(channelCodecMetadata) 2980 .build(); 2981 builder.addChannel(channel); 2982 return builder.build(); 2983 } 2984 2985 // It simulates GATT connection for testing. 2986 public static class StubBassClientStateMachine extends BassClientStateMachine { 2987 boolean mShouldAllowGatt = true; 2988 boolean mShouldHandleMessage = true; 2989 Boolean mIsPendingRemove; 2990 List<Integer> mMsgWhats = new ArrayList<>(); 2991 int mMsgWhat; 2992 int mMsgAgr1; 2993 int mMsgArg2; 2994 Object mMsgObj; 2995 long mMsgDelay; 2996 StubBassClientStateMachine( BluetoothDevice device, BassClientService service, AdapterService adapterService, Looper looper, int connectTimeout)2997 StubBassClientStateMachine( 2998 BluetoothDevice device, 2999 BassClientService service, 3000 AdapterService adapterService, 3001 Looper looper, 3002 int connectTimeout) { 3003 super(device, service, adapterService, looper, connectTimeout); 3004 } 3005 3006 @Override connectGatt(Boolean autoConnect)3007 public boolean connectGatt(Boolean autoConnect) { 3008 mGattCallback = new GattCallback(); 3009 return mShouldAllowGatt; 3010 } 3011 3012 @Override sendMessage(Message msg)3013 public void sendMessage(Message msg) { 3014 mMsgWhats.add(msg.what); 3015 mMsgWhat = msg.what; 3016 mMsgAgr1 = msg.arg1; 3017 mMsgArg2 = msg.arg2; 3018 mMsgObj = msg.obj; 3019 if (mShouldHandleMessage) { 3020 super.sendMessage(msg); 3021 } 3022 } 3023 3024 @Override sendMessageDelayed(int what, Object obj, long delayMillis)3025 public void sendMessageDelayed(int what, Object obj, long delayMillis) { 3026 mMsgWhats.add(what); 3027 mMsgWhat = what; 3028 mMsgObj = obj; 3029 mMsgDelay = delayMillis; 3030 if (mShouldHandleMessage) { 3031 super.sendMessageDelayed(what, obj, delayMillis); 3032 } 3033 } 3034 3035 @Override sendMessageDelayed(int what, int arg1, long delayMillis)3036 public void sendMessageDelayed(int what, int arg1, long delayMillis) { 3037 mMsgWhats.add(what); 3038 mMsgWhat = what; 3039 mMsgAgr1 = arg1; 3040 mMsgDelay = delayMillis; 3041 if (mShouldHandleMessage) { 3042 super.sendMessageDelayed(what, arg1, delayMillis); 3043 } 3044 } 3045 notifyConnectionStateChanged(int status, int newState)3046 public void notifyConnectionStateChanged(int status, int newState) { 3047 if (mGattCallback != null) { 3048 BluetoothGatt gatt = null; 3049 if (mBluetoothGatt != null) { 3050 gatt = mBluetoothGatt.mWrappedBluetoothGatt; 3051 } 3052 mGattCallback.onConnectionStateChange(gatt, status, newState); 3053 } 3054 } 3055 hasDeferredMessagesSuper(int what)3056 public boolean hasDeferredMessagesSuper(int what) { 3057 return super.hasDeferredMessages(what); 3058 } 3059 3060 @Override isPendingRemove(Integer sourceId)3061 boolean isPendingRemove(Integer sourceId) { 3062 if (mIsPendingRemove == null) { 3063 return super.isPendingRemove(sourceId); 3064 } 3065 return mIsPendingRemove; 3066 } 3067 } 3068 } 3069