1 /* 2 * Copyright 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.bluetooth.BluetoothGatt.GATT_FAILURE; 20 import static android.bluetooth.BluetoothGatt.GATT_SUCCESS; 21 22 import static com.android.bluetooth.bass_client.BassClientStateMachine.ADD_BCAST_SOURCE; 23 import static com.android.bluetooth.bass_client.BassClientStateMachine.CONNECT; 24 import static com.android.bluetooth.bass_client.BassClientStateMachine.CONNECTION_STATE_CHANGED; 25 import static com.android.bluetooth.bass_client.BassClientStateMachine.CONNECT_TIMEOUT; 26 import static com.android.bluetooth.bass_client.BassClientStateMachine.DISCONNECT; 27 import static com.android.bluetooth.bass_client.BassClientStateMachine.GATT_TXN_PROCESSED; 28 import static com.android.bluetooth.bass_client.BassClientStateMachine.GATT_TXN_TIMEOUT; 29 import static com.android.bluetooth.bass_client.BassClientStateMachine.PSYNC_ACTIVE_TIMEOUT; 30 import static com.android.bluetooth.bass_client.BassClientStateMachine.READ_BASS_CHARACTERISTICS; 31 import static com.android.bluetooth.bass_client.BassClientStateMachine.REMOTE_SCAN_START; 32 import static com.android.bluetooth.bass_client.BassClientStateMachine.REMOTE_SCAN_STOP; 33 import static com.android.bluetooth.bass_client.BassClientStateMachine.REMOVE_BCAST_SOURCE; 34 import static com.android.bluetooth.bass_client.BassClientStateMachine.SELECT_BCAST_SOURCE; 35 import static com.android.bluetooth.bass_client.BassClientStateMachine.SET_BCAST_CODE; 36 import static com.android.bluetooth.bass_client.BassClientStateMachine.START_SCAN_OFFLOAD; 37 import static com.android.bluetooth.bass_client.BassClientStateMachine.STOP_SCAN_OFFLOAD; 38 import static com.android.bluetooth.bass_client.BassClientStateMachine.UPDATE_BCAST_SOURCE; 39 import static com.android.bluetooth.bass_client.BassConstants.CLIENT_CHARACTERISTIC_CONFIG; 40 41 import static com.google.common.truth.Truth.assertThat; 42 43 import static org.junit.Assert.assertNotNull; 44 import static org.junit.Assert.assertNull; 45 import static org.mockito.ArgumentMatchers.any; 46 import static org.mockito.ArgumentMatchers.anyBoolean; 47 import static org.mockito.ArgumentMatchers.anyInt; 48 import static org.mockito.ArgumentMatchers.anyString; 49 import static org.mockito.ArgumentMatchers.eq; 50 import static org.mockito.Mockito.after; 51 import static org.mockito.Mockito.atLeast; 52 import static org.mockito.Mockito.doNothing; 53 import static org.mockito.Mockito.never; 54 import static org.mockito.Mockito.timeout; 55 import static org.mockito.Mockito.verify; 56 import static org.mockito.Mockito.when; 57 58 import android.bluetooth.BluetoothAdapter; 59 import android.bluetooth.BluetoothDevice; 60 import android.bluetooth.BluetoothGatt; 61 import android.bluetooth.BluetoothGattCallback; 62 import android.bluetooth.BluetoothGattCharacteristic; 63 import android.bluetooth.BluetoothGattDescriptor; 64 import android.bluetooth.BluetoothGattService; 65 import android.bluetooth.BluetoothLeAudioCodecConfigMetadata; 66 import android.bluetooth.BluetoothLeAudioContentMetadata; 67 import android.bluetooth.BluetoothLeBroadcastChannel; 68 import android.bluetooth.BluetoothLeBroadcastMetadata; 69 import android.bluetooth.BluetoothLeBroadcastReceiveState; 70 import android.bluetooth.BluetoothLeBroadcastSubgroup; 71 import android.bluetooth.BluetoothProfile; 72 import android.bluetooth.le.ScanRecord; 73 import android.bluetooth.le.ScanResult; 74 import android.content.Intent; 75 import android.os.Bundle; 76 import android.os.HandlerThread; 77 import android.os.Looper; 78 import android.os.Message; 79 80 import androidx.test.filters.MediumTest; 81 82 import com.android.bluetooth.BluetoothMethodProxy; 83 import com.android.bluetooth.TestUtils; 84 import com.android.bluetooth.btservice.AdapterService; 85 86 import org.hamcrest.core.IsInstanceOf; 87 import org.junit.After; 88 import org.junit.Assert; 89 import org.junit.Before; 90 import org.junit.Rule; 91 import org.junit.Test; 92 import org.junit.runner.RunWith; 93 import org.junit.runners.JUnit4; 94 import org.mockito.ArgumentCaptor; 95 import org.mockito.Mock; 96 import org.mockito.Mockito; 97 import org.mockito.Spy; 98 import org.mockito.junit.MockitoJUnit; 99 import org.mockito.junit.MockitoRule; 100 101 import java.util.ArrayList; 102 import java.util.Arrays; 103 import java.util.List; 104 import java.util.UUID; 105 106 @MediumTest 107 @RunWith(JUnit4.class) 108 public class BassClientStateMachineTest { 109 @Rule 110 public final MockitoRule mockito = MockitoJUnit.rule(); 111 112 private static final int CONNECTION_TIMEOUT_MS = 1_000; 113 private static final int TIMEOUT_MS = 2_000; 114 private static final int WAIT_MS = 1_200; 115 private static final String TEST_BROADCAST_NAME = "Test"; 116 private BluetoothAdapter mAdapter; 117 private HandlerThread mHandlerThread; 118 private StubBassClientStateMachine mBassClientStateMachine; 119 private BluetoothDevice mTestDevice; 120 121 @Mock private AdapterService mAdapterService; 122 @Mock private BassClientService mBassClientService; 123 @Spy private BluetoothMethodProxy mMethodProxy; 124 125 @Before setUp()126 public void setUp() throws Exception { 127 TestUtils.setAdapterService(mAdapterService); 128 129 mAdapter = BluetoothAdapter.getDefaultAdapter(); 130 BluetoothMethodProxy.setInstanceForTesting(mMethodProxy); 131 doNothing().when(mMethodProxy).periodicAdvertisingManagerTransferSync( 132 any(), any(), anyInt(), anyInt()); 133 134 // Get a device for testing 135 mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); 136 137 // Set up thread and looper 138 mHandlerThread = new HandlerThread("BassClientStateMachineTestHandlerThread"); 139 mHandlerThread.start(); 140 mBassClientStateMachine = new StubBassClientStateMachine(mTestDevice, 141 mBassClientService, mHandlerThread.getLooper(), CONNECTION_TIMEOUT_MS); 142 mBassClientStateMachine.start(); 143 } 144 145 @After tearDown()146 public void tearDown() throws Exception { 147 mBassClientStateMachine.doQuit(); 148 mHandlerThread.quit(); 149 TestUtils.clearAdapterService(mAdapterService); 150 } 151 152 /** 153 * Test that default state is disconnected 154 */ 155 @Test testDefaultDisconnectedState()156 public void testDefaultDisconnectedState() { 157 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 158 mBassClientStateMachine.getConnectionState()); 159 } 160 161 /** 162 * Allow/disallow connection to any device. 163 * 164 * @param allow if true, connection is allowed 165 */ allowConnection(boolean allow)166 private void allowConnection(boolean allow) { 167 when(mBassClientService.okToConnect(any(BluetoothDevice.class))).thenReturn(allow); 168 } 169 allowConnectGatt(boolean allow)170 private void allowConnectGatt(boolean allow) { 171 mBassClientStateMachine.mShouldAllowGatt = allow; 172 } 173 174 /** 175 * Test that an incoming connection with policy forbidding connection is rejected 176 */ 177 @Test testOkToConnectFails()178 public void testOkToConnectFails() { 179 allowConnection(false); 180 allowConnectGatt(true); 181 182 // Inject an event for when incoming connection is requested 183 mBassClientStateMachine.sendMessage(CONNECT); 184 185 // Verify that no connection state broadcast is executed 186 verify(mBassClientService, after(WAIT_MS).never()).sendBroadcast(any(Intent.class), 187 anyString()); 188 189 // Check that we are in Disconnected state 190 Assert.assertThat(mBassClientStateMachine.getCurrentState(), 191 IsInstanceOf.instanceOf(BassClientStateMachine.Disconnected.class)); 192 } 193 194 @Test testFailToConnectGatt()195 public void testFailToConnectGatt() { 196 allowConnection(true); 197 allowConnectGatt(false); 198 199 // Inject an event for when incoming connection is requested 200 mBassClientStateMachine.sendMessage(CONNECT); 201 202 // Verify that no connection state broadcast is executed 203 verify(mBassClientService, after(WAIT_MS).never()).sendBroadcast(any(Intent.class), 204 anyString()); 205 206 // Check that we are in Disconnected state 207 Assert.assertThat(mBassClientStateMachine.getCurrentState(), 208 IsInstanceOf.instanceOf(BassClientStateMachine.Disconnected.class)); 209 assertNull(mBassClientStateMachine.mBluetoothGatt); 210 } 211 212 @Test testSuccessfullyConnected()213 public void testSuccessfullyConnected() { 214 allowConnection(true); 215 allowConnectGatt(true); 216 217 // Inject an event for when incoming connection is requested 218 mBassClientStateMachine.sendMessage(CONNECT); 219 220 // Verify that one connection state broadcast is executed 221 ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class); 222 verify(mBassClientService, timeout(TIMEOUT_MS).times(1)).sendBroadcast( 223 intentArgument1.capture(), anyString(), any(Bundle.class)); 224 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 225 intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); 226 227 Assert.assertThat(mBassClientStateMachine.getCurrentState(), 228 IsInstanceOf.instanceOf(BassClientStateMachine.Connecting.class)); 229 230 assertNotNull(mBassClientStateMachine.mGattCallback); 231 mBassClientStateMachine.notifyConnectionStateChanged( 232 GATT_SUCCESS, BluetoothProfile.STATE_CONNECTED); 233 234 // Verify that the expected number of broadcasts are executed: 235 // - two calls to broadcastConnectionState(): Disconnected -> Connecting -> Connected 236 ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class); 237 verify(mBassClientService, timeout(TIMEOUT_MS).times(2)).sendBroadcast( 238 intentArgument2.capture(), anyString(), any(Bundle.class)); 239 240 Assert.assertThat(mBassClientStateMachine.getCurrentState(), 241 IsInstanceOf.instanceOf(BassClientStateMachine.Connected.class)); 242 } 243 244 @Test testConnectGattTimeout()245 public void testConnectGattTimeout() { 246 allowConnection(true); 247 allowConnectGatt(true); 248 249 // Inject an event for when incoming connection is requested 250 mBassClientStateMachine.sendMessage(CONNECT); 251 252 // Verify that one connection state broadcast is executed 253 ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class); 254 verify(mBassClientService, timeout(TIMEOUT_MS).times(1)).sendBroadcast( 255 intentArgument1.capture(), anyString(), any(Bundle.class)); 256 Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, 257 intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); 258 259 Assert.assertThat(mBassClientStateMachine.getCurrentState(), 260 IsInstanceOf.instanceOf(BassClientStateMachine.Connecting.class)); 261 262 // Verify that one connection state broadcast is executed 263 ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class); 264 verify(mBassClientService, timeout(TIMEOUT_MS).times( 265 2)).sendBroadcast(intentArgument2.capture(), anyString(), any(Bundle.class)); 266 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 267 intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); 268 269 Assert.assertThat(mBassClientStateMachine.getCurrentState(), 270 IsInstanceOf.instanceOf(BassClientStateMachine.Disconnected.class)); 271 } 272 273 @Test testStatesChangesWithMessages()274 public void testStatesChangesWithMessages() { 275 allowConnection(true); 276 allowConnectGatt(true); 277 278 assertThat(mBassClientStateMachine.getCurrentState()) 279 .isInstanceOf(BassClientStateMachine.Disconnected.class); 280 281 // disconnected -> connecting ---timeout---> disconnected 282 sendMessageAndVerifyTransition( 283 mBassClientStateMachine.obtainMessage(CONNECT), 284 BassClientStateMachine.Connecting.class); 285 sendMessageAndVerifyTransition( 286 mBassClientStateMachine.obtainMessage(BassClientStateMachine.CONNECT_TIMEOUT), 287 BassClientStateMachine.Disconnected.class); 288 289 // disconnected -> connecting ---DISCONNECT---> disconnected 290 sendMessageAndVerifyTransition( 291 mBassClientStateMachine.obtainMessage(CONNECT), 292 BassClientStateMachine.Connecting.class); 293 sendMessageAndVerifyTransition( 294 mBassClientStateMachine.obtainMessage(BassClientStateMachine.DISCONNECT), 295 BassClientStateMachine.Disconnected.class); 296 297 // disconnected -> connecting ---CONNECTION_STATE_CHANGED(connected)---> connected --> 298 // disconnected 299 sendMessageAndVerifyTransition( 300 mBassClientStateMachine.obtainMessage(CONNECT), 301 BassClientStateMachine.Connecting.class); 302 sendMessageAndVerifyTransition( 303 mBassClientStateMachine.obtainMessage( 304 CONNECTION_STATE_CHANGED, 305 Integer.valueOf(BluetoothProfile.STATE_CONNECTED)), 306 BassClientStateMachine.Connected.class); 307 sendMessageAndVerifyTransition( 308 mBassClientStateMachine.obtainMessage( 309 CONNECTION_STATE_CHANGED, 310 Integer.valueOf(BluetoothProfile.STATE_DISCONNECTED)), 311 BassClientStateMachine.Disconnected.class); 312 313 // disconnected -> connecting ---CONNECTION_STATE_CHANGED(non-connected) --> disconnected 314 sendMessageAndVerifyTransition( 315 mBassClientStateMachine.obtainMessage(CONNECT), 316 BassClientStateMachine.Connecting.class); 317 sendMessageAndVerifyTransition( 318 mBassClientStateMachine.obtainMessage( 319 CONNECTION_STATE_CHANGED, 320 Integer.valueOf(BluetoothProfile.STATE_DISCONNECTED)), 321 BassClientStateMachine.Disconnected.class); 322 323 // change default state to connected for the next tests 324 sendMessageAndVerifyTransition( 325 mBassClientStateMachine.obtainMessage(CONNECT), 326 BassClientStateMachine.Connecting.class); 327 sendMessageAndVerifyTransition( 328 mBassClientStateMachine.obtainMessage( 329 CONNECTION_STATE_CHANGED, 330 Integer.valueOf(BluetoothProfile.STATE_CONNECTED)), 331 BassClientStateMachine.Connected.class); 332 333 // connected ----READ_BASS_CHARACTERISTICS---> connectedProcessing --GATT_TXN_PROCESSED 334 // --> connected 335 336 // Make bluetoothGatt non-null so state will transit 337 mBassClientStateMachine.mBluetoothGatt = Mockito.mock( 338 BassClientStateMachine.BluetoothGattTestableWrapper.class); 339 mBassClientStateMachine.mBroadcastScanControlPoint = new BluetoothGattCharacteristic( 340 BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT, 341 BluetoothGattCharacteristic.PROPERTY_READ, 342 BluetoothGattCharacteristic.PERMISSION_READ); 343 344 sendMessageAndVerifyTransition( 345 mBassClientStateMachine.obtainMessage( 346 READ_BASS_CHARACTERISTICS, 347 new BluetoothGattCharacteristic(UUID.randomUUID(), 348 BluetoothGattCharacteristic.PROPERTY_READ, 349 BluetoothGattCharacteristic.PERMISSION_READ)), 350 BassClientStateMachine.ConnectedProcessing.class); 351 sendMessageAndVerifyTransition( 352 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED), 353 BassClientStateMachine.Connected.class); 354 355 // connected ----READ_BASS_CHARACTERISTICS---> connectedProcessing --GATT_TXN_TIMEOUT --> 356 // connected 357 sendMessageAndVerifyTransition( 358 mBassClientStateMachine.obtainMessage( 359 READ_BASS_CHARACTERISTICS, 360 new BluetoothGattCharacteristic(UUID.randomUUID(), 361 BluetoothGattCharacteristic.PROPERTY_READ, 362 BluetoothGattCharacteristic.PERMISSION_READ)), 363 BassClientStateMachine.ConnectedProcessing.class); 364 sendMessageAndVerifyTransition( 365 mBassClientStateMachine.obtainMessage(GATT_TXN_TIMEOUT), 366 BassClientStateMachine.Connected.class); 367 368 // connected ----START_SCAN_OFFLOAD---> connectedProcessing --GATT_TXN_PROCESSED--> 369 // connected 370 sendMessageAndVerifyTransition( 371 mBassClientStateMachine.obtainMessage(BassClientStateMachine.START_SCAN_OFFLOAD), 372 BassClientStateMachine.ConnectedProcessing.class); 373 sendMessageAndVerifyTransition( 374 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED), 375 BassClientStateMachine.Connected.class); 376 377 // connected ----STOP_SCAN_OFFLOAD---> connectedProcessing --GATT_TXN_PROCESSED--> connected 378 sendMessageAndVerifyTransition( 379 mBassClientStateMachine.obtainMessage(STOP_SCAN_OFFLOAD), 380 BassClientStateMachine.ConnectedProcessing.class); 381 sendMessageAndVerifyTransition( 382 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED), 383 BassClientStateMachine.Connected.class); 384 } 385 386 @Test acquireAllBassChars()387 public void acquireAllBassChars() { 388 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 389 BassClientStateMachine.BluetoothGattTestableWrapper.class); 390 mBassClientStateMachine.mBluetoothGatt = btGatt; 391 // Do nothing when mBluetoothGatt.getService returns null 392 mBassClientStateMachine.acquireAllBassChars(); 393 394 BluetoothGattService gattService = Mockito.mock(BluetoothGattService.class); 395 when(btGatt.getService(BassConstants.BASS_UUID)).thenReturn(gattService); 396 397 List<BluetoothGattCharacteristic> characteristics = new ArrayList<>(); 398 BluetoothGattCharacteristic scanControlPoint = new BluetoothGattCharacteristic( 399 BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT, 400 BluetoothGattCharacteristic.PROPERTY_READ, 401 BluetoothGattCharacteristic.PERMISSION_READ); 402 characteristics.add(scanControlPoint); 403 404 BluetoothGattCharacteristic bassCharacteristic = new BluetoothGattCharacteristic( 405 UUID.randomUUID(), 406 BluetoothGattCharacteristic.PROPERTY_READ, 407 BluetoothGattCharacteristic.PERMISSION_READ); 408 characteristics.add(bassCharacteristic); 409 410 when(gattService.getCharacteristics()).thenReturn(characteristics); 411 mBassClientStateMachine.acquireAllBassChars(); 412 assertThat(mBassClientStateMachine.mBroadcastScanControlPoint).isEqualTo(scanControlPoint); 413 assertThat(mBassClientStateMachine.mBroadcastCharacteristics).contains(bassCharacteristic); 414 } 415 416 @Test simpleMethods()417 public void simpleMethods() { 418 // dump() shouldn't crash 419 StringBuilder sb = new StringBuilder(); 420 mBassClientStateMachine.dump(sb); 421 422 // log() shouldn't crash 423 String msg = "test-log-message"; 424 mBassClientStateMachine.log(msg); 425 426 // messageWhatToString() shouldn't crash 427 for (int i = CONNECT; i <= CONNECT_TIMEOUT + 1; ++i) { 428 mBassClientStateMachine.messageWhatToString(i); 429 } 430 431 final int invalidSourceId = -100; 432 assertThat(mBassClientStateMachine.getCurrentBroadcastMetadata(invalidSourceId)).isNull(); 433 assertThat(mBassClientStateMachine.getDevice()).isEqualTo(mTestDevice); 434 assertThat(mBassClientStateMachine.hasPendingSourceOperation()).isFalse(); 435 assertThat(mBassClientStateMachine.isEmpty(new byte[] { 0 })).isTrue(); 436 assertThat(mBassClientStateMachine.isEmpty(new byte[] { 1 })).isFalse(); 437 assertThat(mBassClientStateMachine.isPendingRemove(invalidSourceId)).isFalse(); 438 } 439 440 @Test parseScanRecord_withoutBaseData_makesNoStopScanOffloadFalse()441 public void parseScanRecord_withoutBaseData_makesNoStopScanOffloadFalse() { 442 byte[] scanRecord = new byte[]{ 443 0x02, 0x01, 0x1a, // advertising flags 444 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids 445 0x04, 0x09, 0x50, 0x65, 0x64, // name 446 0x02, 0x0A, (byte) 0xec, // tx power level 447 0x05, 0x16, 0x0b, 0x11, 0x50, 0x64, // service data 448 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data 449 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble 450 }; 451 ScanRecord data = ScanRecord.parseFromBytes(scanRecord); 452 mBassClientStateMachine.mNoStopScanOffload = true; 453 mBassClientStateMachine.parseScanRecord(0, data); 454 assertThat(mBassClientStateMachine.mNoStopScanOffload).isFalse(); 455 } 456 457 @Test parseScanRecord_withBaseData_callsUpdateBase()458 public void parseScanRecord_withBaseData_callsUpdateBase() { 459 byte[] scanRecordWithBaseData = new byte[] { 460 0x02, 0x01, 0x1a, // advertising flags 461 0x05, 0x02, 0x51, 0x18, 0x0a, 0x11, // 16 bit service uuids 462 0x04, 0x09, 0x50, 0x65, 0x64, // name 463 0x02, 0x0A, (byte) 0xec, // tx power level 464 0x15, 0x16, 0x51, 0x18, // service data (base data with 18 bytes) 465 // LEVEL 1 466 (byte) 0x01, (byte) 0x02, (byte) 0x03, // presentationDelay 467 (byte) 0x01, // numSubGroups 468 // LEVEL 2 469 (byte) 0x01, // numSubGroups 470 (byte) 0xFE, // UNKNOWN_CODEC 471 (byte) 0x02, // codecConfigLength 472 (byte) 0x01, (byte) 'A', // codecConfigInfo 473 (byte) 0x03, // metaDataLength 474 (byte) 0x06, (byte) 0x07, (byte) 0x08, // metaData 475 // LEVEL 3 476 (byte) 0x04, // index 477 (byte) 0x03, // codecConfigLength 478 (byte) 0x02, (byte) 'B', (byte) 'C', // codecConfigInfo 479 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data 480 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble 481 }; 482 ScanRecord data = ScanRecord.parseFromBytes(scanRecordWithBaseData); 483 assertThat(data.getServiceUuids()).contains(BassConstants.BASIC_AUDIO_UUID); 484 assertThat(data.getServiceData(BassConstants.BASIC_AUDIO_UUID)).isNotNull(); 485 mBassClientStateMachine.parseScanRecord(0, data); 486 verify(mBassClientService).updateBase(anyInt(), any()); 487 } 488 489 @Test gattCallbackOnConnectionStateChange_changedToConnected()490 public void gattCallbackOnConnectionStateChange_changedToConnected() 491 throws InterruptedException { 492 mBassClientStateMachine.connectGatt(true); 493 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 494 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 495 BassClientStateMachine.BluetoothGattTestableWrapper.class); 496 mBassClientStateMachine.mBluetoothGatt = btGatt; 497 498 // disallow connection 499 allowConnection(false); 500 int status = BluetoothProfile.STATE_CONNECTING; 501 int newState = BluetoothProfile.STATE_CONNECTED; 502 cb.onConnectionStateChange(null, status, newState); 503 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 504 505 verify(btGatt).disconnect(); 506 verify(btGatt).close(); 507 assertThat(mBassClientStateMachine.mBluetoothGatt).isNull(); 508 assertThat(mBassClientStateMachine.mMsgWhats).contains(CONNECTION_STATE_CHANGED); 509 mBassClientStateMachine.mMsgWhats.clear(); 510 511 mBassClientStateMachine.mBluetoothGatt = btGatt; 512 allowConnection(true); 513 mBassClientStateMachine.mDiscoveryInitiated = false; 514 status = BluetoothProfile.STATE_DISCONNECTED; 515 newState = BluetoothProfile.STATE_CONNECTED; 516 cb.onConnectionStateChange(null, status, newState); 517 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 518 519 assertThat(mBassClientStateMachine.mDiscoveryInitiated).isTrue(); 520 assertThat(mBassClientStateMachine.mMsgWhats).contains(CONNECTION_STATE_CHANGED); 521 assertThat(mBassClientStateMachine.mMsgObj).isEqualTo(newState); 522 mBassClientStateMachine.mMsgWhats.clear(); 523 } 524 525 @Test gattCallbackOnConnectionStateChanged_changedToDisconnected()526 public void gattCallbackOnConnectionStateChanged_changedToDisconnected() 527 throws InterruptedException { 528 initToConnectingState(); 529 mBassClientStateMachine.connectGatt(true); 530 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 531 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 532 BassClientStateMachine.BluetoothGattTestableWrapper.class); 533 mBassClientStateMachine.mBluetoothGatt = btGatt; 534 535 allowConnection(false); 536 int status = BluetoothProfile.STATE_CONNECTING; 537 int newState = BluetoothProfile.STATE_DISCONNECTED; 538 cb.onConnectionStateChange(null, status, newState); 539 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 540 541 assertThat(mBassClientStateMachine.mMsgWhats).contains(CONNECTION_STATE_CHANGED); 542 assertThat(mBassClientStateMachine.mMsgObj).isEqualTo(newState); 543 mBassClientStateMachine.mMsgWhats.clear(); 544 } 545 546 @Test gattCallbackOnServicesDiscovered()547 public void gattCallbackOnServicesDiscovered() { 548 mBassClientStateMachine.connectGatt(true); 549 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 550 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 551 BassClientStateMachine.BluetoothGattTestableWrapper.class); 552 mBassClientStateMachine.mBluetoothGatt = btGatt; 553 554 // Do nothing if mDiscoveryInitiated is false. 555 mBassClientStateMachine.mDiscoveryInitiated = false; 556 int status = GATT_FAILURE; 557 cb.onServicesDiscovered(null, status); 558 559 verify(btGatt, never()).requestMtu(anyInt()); 560 561 // Do nothing if status is not GATT_SUCCESS. 562 mBassClientStateMachine.mDiscoveryInitiated = true; 563 status = GATT_FAILURE; 564 cb.onServicesDiscovered(null, status); 565 566 verify(btGatt, never()).requestMtu(anyInt()); 567 568 // call requestMtu() if status is GATT_SUCCESS. 569 mBassClientStateMachine.mDiscoveryInitiated = true; 570 status = GATT_SUCCESS; 571 cb.onServicesDiscovered(null, status); 572 573 verify(btGatt).requestMtu(anyInt()); 574 } 575 576 /** 577 * This also tests BassClientStateMachine#processBroadcastReceiverState. 578 */ 579 @Test gattCallbackOnCharacteristicRead()580 public void gattCallbackOnCharacteristicRead() { 581 mBassClientStateMachine.mShouldHandleMessage = false; 582 mBassClientStateMachine.connectGatt(true); 583 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 584 BluetoothGattDescriptor desc = Mockito.mock(BluetoothGattDescriptor.class); 585 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 586 BluetoothGattCharacteristic characteristic = 587 Mockito.mock(BluetoothGattCharacteristic.class); 588 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 589 BassClientStateMachine.BluetoothGattTestableWrapper.class); 590 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 591 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 592 593 // Characteristic read success with null value 594 when(characteristic.getValue()).thenReturn(null); 595 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 596 verify(characteristic, never()).getDescriptor(any()); 597 598 // Characteristic read failed and mBluetoothGatt is null. 599 mBassClientStateMachine.mBluetoothGatt = null; 600 cb.onCharacteristicRead(null, characteristic, GATT_FAILURE); 601 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 602 603 assertThat(mBassClientStateMachine.mMsgWhats).contains(GATT_TXN_PROCESSED); 604 assertThat(mBassClientStateMachine.mMsgAgr1).isEqualTo(GATT_FAILURE); 605 mBassClientStateMachine.mMsgWhats.clear(); 606 607 608 // Characteristic read failed and mBluetoothGatt is not null. 609 mBassClientStateMachine.mBluetoothGatt = btGatt; 610 when(characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG)).thenReturn(desc); 611 cb.onCharacteristicRead(null, characteristic, GATT_FAILURE); 612 613 verify(btGatt).setCharacteristicNotification(any(), anyBoolean()); 614 verify(btGatt).writeDescriptor(desc); 615 verify(desc).setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 616 617 // Tests for processBroadcastReceiverState 618 int sourceId = 1; 619 byte[] value = new byte[] { }; 620 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; 621 mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE; 622 mBassClientStateMachine.mPendingSourceId = (byte) sourceId; 623 when(characteristic.getValue()).thenReturn(value); 624 when(characteristic.getInstanceId()).thenReturn(sourceId); 625 626 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 627 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 628 verify(callbacks).notifyReceiveStateChanged(any(), anyInt(), any()); 629 630 mBassClientStateMachine.mPendingOperation = 0; 631 mBassClientStateMachine.mPendingSourceId = 0; 632 sourceId = 2; // mNextId would become 2 633 when(characteristic.getInstanceId()).thenReturn(sourceId); 634 635 Mockito.clearInvocations(callbacks); 636 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 637 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 638 verify(callbacks).notifyReceiveStateChanged(any(), anyInt(), any()); 639 640 mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata(); 641 sourceId = 1; 642 value = new byte[] { 643 (byte) sourceId, // sourceId 644 0x00, // sourceAddressType 645 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, // sourceAddress 646 0x00, // sourceAdvSid 647 0x00, 0x00, 0x00, // broadcastIdBytes 648 (byte) BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_NO_PAST, 649 (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE, 650 // 16 bytes badBroadcastCode 651 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 652 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 653 0x01, // numSubGroups 654 // SubGroup #1 655 0x00, 0x00, 0x00, 0x00, // audioSyncIndex 656 0x02, // metaDataLength 657 0x00, 0x00, // metadata 658 }; 659 when(characteristic.getValue()).thenReturn(value); 660 when(characteristic.getInstanceId()).thenReturn(sourceId); 661 662 Mockito.clearInvocations(callbacks); 663 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 664 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 665 666 verify(callbacks).notifySourceAdded(any(), any(), anyInt()); 667 verify(callbacks).notifyReceiveStateChanged(any(), anyInt(), any()); 668 assertThat(mBassClientStateMachine.mMsgWhats).contains(STOP_SCAN_OFFLOAD); 669 670 // set some values for covering more lines of processPASyncState() 671 mBassClientStateMachine.mPendingMetadata = null; 672 mBassClientStateMachine.mSetBroadcastCodePending = true; 673 mBassClientStateMachine.mIsPendingRemove = true; 674 value[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX] = 675 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST; 676 value[BassConstants.BCAST_RCVR_STATE_ENC_STATUS_IDX] = 677 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED; 678 value[35] = 0; // set metaDataLength of subgroup #1 0 679 PeriodicAdvertisementResult paResult = Mockito.mock(PeriodicAdvertisementResult.class); 680 when(characteristic.getValue()).thenReturn(value); 681 when(mBassClientService.getPeriodicAdvertisementResult(any())).thenReturn(paResult); 682 when(paResult.getSyncHandle()).thenReturn(100); 683 684 Mockito.clearInvocations(callbacks); 685 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 686 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 687 688 verify(callbacks).notifyReceiveStateChanged(any(), anyInt(), any()); 689 assertThat(mBassClientStateMachine.mMsgWhats).contains(REMOVE_BCAST_SOURCE); 690 691 mBassClientStateMachine.mIsPendingRemove = null; 692 // set some values for covering more lines of processPASyncState() 693 mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata(); 694 for (int i = 0; i < BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE; ++i) { 695 value[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_START_IDX + i] = 0x00; 696 } 697 when(mBassClientService.getPeriodicAdvertisementResult(any())).thenReturn(null); 698 when(mBassClientService.isLocalBroadcast(any())).thenReturn(true); 699 when(characteristic.getValue()).thenReturn(value); 700 701 Mockito.clearInvocations(callbacks); 702 cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS); 703 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 704 705 verify(callbacks).notifySourceRemoved(any(), anyInt(), anyInt()); 706 verify(callbacks).notifyReceiveStateChanged(any(), anyInt(), any()); 707 } 708 709 @Test gattCallbackOnCharacteristicChanged()710 public void gattCallbackOnCharacteristicChanged() { 711 mBassClientStateMachine.connectGatt(true); 712 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 713 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 1; 714 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 715 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 716 717 BluetoothGattCharacteristic characteristic = 718 Mockito.mock(BluetoothGattCharacteristic.class); 719 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 720 when(characteristic.getValue()).thenReturn(null); 721 722 cb.onCharacteristicChanged(null, characteristic); 723 verify(characteristic, atLeast(1)).getUuid(); 724 verify(characteristic).getValue(); 725 verify(callbacks, never()).notifyReceiveStateChanged(any(), anyInt(), any()); 726 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 727 728 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 1; 729 Mockito.clearInvocations(characteristic); 730 when(characteristic.getValue()).thenReturn(new byte[] { }); 731 cb.onCharacteristicChanged(null, characteristic); 732 verify(characteristic, atLeast(1)).getUuid(); 733 verify(characteristic, atLeast(1)).getValue(); 734 verify(callbacks).notifyReceiveStateChanged(any(), anyInt(), any()); 735 } 736 737 @Test gattCharacteristicWrite()738 public void gattCharacteristicWrite() { 739 mBassClientStateMachine.connectGatt(true); 740 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 741 742 BluetoothGattCharacteristic characteristic =Mockito.mock(BluetoothGattCharacteristic.class); 743 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT); 744 745 cb.onCharacteristicWrite(null, characteristic, GATT_SUCCESS); 746 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 747 assertThat(mBassClientStateMachine.mMsgWhats).contains(GATT_TXN_PROCESSED); 748 } 749 750 @Test gattCallbackOnDescriptorWrite()751 public void gattCallbackOnDescriptorWrite() { 752 mBassClientStateMachine.connectGatt(true); 753 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 754 BluetoothGattDescriptor descriptor = Mockito.mock(BluetoothGattDescriptor.class); 755 when(descriptor.getUuid()).thenReturn(BassConstants.CLIENT_CHARACTERISTIC_CONFIG); 756 757 cb.onDescriptorWrite(null, descriptor, GATT_SUCCESS); 758 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 759 assertThat(mBassClientStateMachine.mMsgWhats).contains(GATT_TXN_PROCESSED); 760 } 761 762 @Test gattCallbackOnMtuChanged()763 public void gattCallbackOnMtuChanged() { 764 mBassClientStateMachine.connectGatt(true); 765 BluetoothGattCallback cb = mBassClientStateMachine.mGattCallback; 766 mBassClientStateMachine.mMTUChangeRequested = true; 767 768 cb.onMtuChanged(null, 10, GATT_SUCCESS); 769 assertThat(mBassClientStateMachine.mMTUChangeRequested).isTrue(); 770 771 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 772 BassClientStateMachine.BluetoothGattTestableWrapper.class); 773 mBassClientStateMachine.mBluetoothGatt = btGatt; 774 775 cb.onMtuChanged(null, 10, GATT_SUCCESS); 776 assertThat(mBassClientStateMachine.mMTUChangeRequested).isFalse(); 777 } 778 779 @Test sendConnectMessage_inDisconnectedState()780 public void sendConnectMessage_inDisconnectedState() { 781 initToDisconnectedState(); 782 783 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 784 BassClientStateMachine.BluetoothGattTestableWrapper.class); 785 mBassClientStateMachine.mBluetoothGatt = btGatt; 786 787 sendMessageAndVerifyTransition( 788 mBassClientStateMachine.obtainMessage(CONNECT), 789 BassClientStateMachine.Connecting.class); 790 verify(btGatt).disconnect(); 791 verify(btGatt).close(); 792 } 793 794 @Test sendDisconnectMessage_inDisconnectedState()795 public void sendDisconnectMessage_inDisconnectedState() { 796 initToDisconnectedState(); 797 798 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 799 BassClientStateMachine.BluetoothGattTestableWrapper.class); 800 mBassClientStateMachine.mBluetoothGatt = btGatt; 801 802 mBassClientStateMachine.sendMessage(DISCONNECT); 803 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 804 verify(btGatt).disconnect(); 805 verify(btGatt).close(); 806 } 807 808 @Test sendStateChangedMessage_inDisconnectedState()809 public void sendStateChangedMessage_inDisconnectedState() { 810 initToDisconnectedState(); 811 812 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 813 BassClientStateMachine.BluetoothGattTestableWrapper.class); 814 mBassClientStateMachine.mBluetoothGatt = btGatt; 815 816 Message msgToConnectingState = 817 mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 818 msgToConnectingState.obj = BluetoothProfile.STATE_CONNECTING; 819 820 mBassClientStateMachine.sendMessage(msgToConnectingState); 821 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 822 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 823 824 Message msgToConnectedState = 825 mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 826 msgToConnectedState.obj = BluetoothProfile.STATE_CONNECTED; 827 sendMessageAndVerifyTransition(msgToConnectedState, BassClientStateMachine.Connected.class); 828 } 829 830 @Test sendOtherMessages_inDisconnectedState_doesNotChangeState()831 public void sendOtherMessages_inDisconnectedState_doesNotChangeState() { 832 initToDisconnectedState(); 833 834 mBassClientStateMachine.sendMessage(PSYNC_ACTIVE_TIMEOUT); 835 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 836 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 837 838 mBassClientStateMachine.sendMessage(-1); 839 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 840 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 841 } 842 843 @Test sendConnectMessages_inConnectingState_doesNotChangeState()844 public void sendConnectMessages_inConnectingState_doesNotChangeState() { 845 initToConnectingState(); 846 847 mBassClientStateMachine.sendMessage(CONNECT); 848 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 849 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 850 } 851 852 @Test sendDisconnectMessages_inConnectingState_defersMessage()853 public void sendDisconnectMessages_inConnectingState_defersMessage() { 854 initToConnectingState(); 855 856 mBassClientStateMachine.sendMessage(DISCONNECT); 857 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 858 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(DISCONNECT)).isTrue(); 859 } 860 861 @Test sendReadBassCharacteristicsMessage_inConnectingState_defersMessage()862 public void sendReadBassCharacteristicsMessage_inConnectingState_defersMessage() { 863 initToConnectingState(); 864 865 mBassClientStateMachine.sendMessage(READ_BASS_CHARACTERISTICS); 866 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 867 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(READ_BASS_CHARACTERISTICS)) 868 .isTrue(); 869 } 870 871 @Test sendPsyncActiveTimeoutMessage_inConnectingState_defersMessage()872 public void sendPsyncActiveTimeoutMessage_inConnectingState_defersMessage() { 873 initToConnectingState(); 874 875 mBassClientStateMachine.sendMessage(PSYNC_ACTIVE_TIMEOUT); 876 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 877 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(PSYNC_ACTIVE_TIMEOUT)).isTrue(); 878 } 879 880 @Test sendStateChangedToNonConnectedMessage_inConnectingState_movesToDisconnected()881 public void sendStateChangedToNonConnectedMessage_inConnectingState_movesToDisconnected() { 882 initToConnectingState(); 883 884 Message msg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 885 msg.obj = BluetoothProfile.STATE_CONNECTING; 886 sendMessageAndVerifyTransition(msg, BassClientStateMachine.Disconnected.class); 887 } 888 889 @Test sendStateChangedToConnectedMessage_inConnectingState_movesToConnected()890 public void sendStateChangedToConnectedMessage_inConnectingState_movesToConnected() { 891 initToConnectingState(); 892 893 Message msg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 894 msg.obj = BluetoothProfile.STATE_CONNECTED; 895 sendMessageAndVerifyTransition(msg, BassClientStateMachine.Connected.class); 896 } 897 898 @Test sendConnectTimeMessage_inConnectingState()899 public void sendConnectTimeMessage_inConnectingState() { 900 initToConnectingState(); 901 902 Message timeoutWithDifferentDevice = mBassClientStateMachine.obtainMessage(CONNECT_TIMEOUT, 903 mAdapter.getRemoteDevice("00:00:00:00:00:00")); 904 mBassClientStateMachine.sendMessage(timeoutWithDifferentDevice); 905 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 906 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 907 908 Message msg = mBassClientStateMachine.obtainMessage(CONNECT_TIMEOUT, mTestDevice); 909 sendMessageAndVerifyTransition(msg, BassClientStateMachine.Disconnected.class); 910 } 911 912 @Test sendInvalidMessage_inConnectingState_doesNotChangeState()913 public void sendInvalidMessage_inConnectingState_doesNotChangeState() { 914 initToConnectingState(); 915 mBassClientStateMachine.sendMessage(-1); 916 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 917 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 918 } 919 920 @Test sendConnectMessage_inConnectedState()921 public void sendConnectMessage_inConnectedState() { 922 initToConnectedState(); 923 924 mBassClientStateMachine.sendMessage(CONNECT); 925 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 926 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 927 } 928 929 @Test sendDisconnectMessage_inConnectedState()930 public void sendDisconnectMessage_inConnectedState() { 931 initToConnectedState(); 932 933 mBassClientStateMachine.mBluetoothGatt = null; 934 mBassClientStateMachine.sendMessage(DISCONNECT); 935 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 936 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 937 938 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 939 BassClientStateMachine.BluetoothGattTestableWrapper.class); 940 mBassClientStateMachine.mBluetoothGatt = btGatt; 941 sendMessageAndVerifyTransition( 942 mBassClientStateMachine.obtainMessage(DISCONNECT), 943 BassClientStateMachine.Disconnected.class); 944 verify(btGatt).disconnect(); 945 verify(btGatt).close(); 946 } 947 948 @Test sendStateChangedMessage_inConnectedState()949 public void sendStateChangedMessage_inConnectedState() { 950 initToConnectedState(); 951 952 Message connectedMsg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 953 connectedMsg.obj = BluetoothProfile.STATE_CONNECTED; 954 mBassClientStateMachine.sendMessage(connectedMsg); 955 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 956 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 957 958 Message noneConnectedMsg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 959 noneConnectedMsg.obj = BluetoothProfile.STATE_DISCONNECTING; 960 sendMessageAndVerifyTransition(noneConnectedMsg, BassClientStateMachine.Disconnected.class); 961 } 962 963 @Test sendReadBassCharacteristicsMessage_inConnectedState()964 public void sendReadBassCharacteristicsMessage_inConnectedState() { 965 initToConnectedState(); 966 BluetoothGattCharacteristic gattCharacteristic = Mockito.mock( 967 BluetoothGattCharacteristic.class); 968 969 mBassClientStateMachine.sendMessage(READ_BASS_CHARACTERISTICS, gattCharacteristic); 970 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 971 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 972 973 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 974 BassClientStateMachine.BluetoothGattTestableWrapper.class); 975 mBassClientStateMachine.mBluetoothGatt = btGatt; 976 sendMessageAndVerifyTransition(mBassClientStateMachine.obtainMessage( 977 READ_BASS_CHARACTERISTICS, gattCharacteristic), 978 BassClientStateMachine.ConnectedProcessing.class); 979 } 980 981 @Test sendStartScanOffloadMessage_inConnectedState()982 public void sendStartScanOffloadMessage_inConnectedState() { 983 initToConnectedState(); 984 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 985 BassClientStateMachine.BluetoothGattTestableWrapper.class); 986 mBassClientStateMachine.mBluetoothGatt = btGatt; 987 988 mBassClientStateMachine.sendMessage(START_SCAN_OFFLOAD); 989 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 990 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 991 992 BluetoothGattCharacteristic scanControlPoint = Mockito.mock( 993 BluetoothGattCharacteristic.class); 994 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 995 996 sendMessageAndVerifyTransition( 997 mBassClientStateMachine.obtainMessage(START_SCAN_OFFLOAD), 998 BassClientStateMachine.ConnectedProcessing.class); 999 verify(btGatt).writeCharacteristic(scanControlPoint); 1000 verify(scanControlPoint).setValue(REMOTE_SCAN_START); 1001 } 1002 1003 @Test sendStopScanOffloadMessage_inConnectedState()1004 public void sendStopScanOffloadMessage_inConnectedState() { 1005 initToConnectedState(); 1006 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 1007 BassClientStateMachine.BluetoothGattTestableWrapper.class); 1008 mBassClientStateMachine.mBluetoothGatt = btGatt; 1009 1010 mBassClientStateMachine.sendMessage(STOP_SCAN_OFFLOAD); 1011 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1012 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1013 1014 BluetoothGattCharacteristic scanControlPoint = Mockito.mock( 1015 BluetoothGattCharacteristic.class); 1016 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1017 1018 sendMessageAndVerifyTransition( 1019 mBassClientStateMachine.obtainMessage(STOP_SCAN_OFFLOAD), 1020 BassClientStateMachine.ConnectedProcessing.class); 1021 verify(btGatt).writeCharacteristic(scanControlPoint); 1022 verify(scanControlPoint).setValue(REMOTE_SCAN_STOP); 1023 } 1024 1025 @Test sendPsyncActiveMessage_inConnectedState()1026 public void sendPsyncActiveMessage_inConnectedState() { 1027 initToConnectedState(); 1028 1029 mBassClientStateMachine.mNoStopScanOffload = true; 1030 mBassClientStateMachine.sendMessage(PSYNC_ACTIVE_TIMEOUT); 1031 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1032 assertThat(mBassClientStateMachine.mNoStopScanOffload).isFalse(); 1033 } 1034 1035 @Test sendInvalidMessage_inConnectedState_doesNotChangeState()1036 public void sendInvalidMessage_inConnectedState_doesNotChangeState() { 1037 initToConnectedState(); 1038 1039 mBassClientStateMachine.sendMessage(-1); 1040 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1041 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1042 } 1043 1044 @Test sendSelectBcastSourceMessage_inConnectedState()1045 public void sendSelectBcastSourceMessage_inConnectedState() { 1046 initToConnectedState(); 1047 1048 byte[] scanRecord = new byte[]{ 1049 0x02, 0x01, 0x1a, // advertising flags 1050 0x05, 0x02, 0x52, 0x18, 0x0a, 0x11, // 16 bit service uuids 1051 0x04, 0x09, 0x50, 0x65, 0x64, // name 1052 0x02, 0x0A, (byte) 0xec, // tx power level 1053 0x05, 0x30, 0x54, 0x65, 0x73, 0x74, // broadcast name: Test 1054 0x06, 0x16, 0x52, 0x18, 0x50, 0x64, 0x65, // service data 1055 0x08, 0x16, 0x56, 0x18, 0x07, 0x03, 0x06, 0x07, 0x08, 1056 // service data - public broadcast, 1057 // feature - 0x7, metadata len - 0x3, metadata - 0x6, 0x7, 0x8 1058 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data 1059 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble 1060 }; 1061 ScanRecord record = ScanRecord.parseFromBytes(scanRecord); 1062 1063 doNothing().when(mMethodProxy).periodicAdvertisingManagerRegisterSync( 1064 any(), any(), anyInt(), anyInt(), any(), any()); 1065 ScanResult scanResult = new ScanResult(mTestDevice, 0, 0, 0, 0, 0, 0, 0, record, 0); 1066 mBassClientStateMachine.sendMessage( 1067 SELECT_BCAST_SOURCE, BassConstants.AUTO, 0, scanResult); 1068 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1069 verify(mBassClientService).updatePeriodicAdvertisementResultMap( 1070 any(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), 1071 any(), eq(TEST_BROADCAST_NAME)); 1072 } 1073 1074 @Test sendAddBcastSourceMessage_inConnectedState()1075 public void sendAddBcastSourceMessage_inConnectedState() { 1076 initToConnectedState(); 1077 1078 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1079 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1080 1081 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 1082 mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata); 1083 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1084 1085 verify(mBassClientService).getCallbacks(); 1086 verify(callbacks).notifySourceAddFailed(any(), any(), anyInt()); 1087 1088 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 1089 BassClientStateMachine.BluetoothGattTestableWrapper.class); 1090 mBassClientStateMachine.mBluetoothGatt = btGatt; 1091 BluetoothGattCharacteristic scanControlPoint = Mockito.mock( 1092 BluetoothGattCharacteristic.class); 1093 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1094 1095 sendMessageAndVerifyTransition( 1096 mBassClientStateMachine.obtainMessage(ADD_BCAST_SOURCE, metadata), 1097 BassClientStateMachine.ConnectedProcessing.class); 1098 verify(scanControlPoint).setValue(any(byte[].class)); 1099 verify(btGatt).writeCharacteristic(any()); 1100 } 1101 1102 @Test sendUpdateBcastSourceMessage_inConnectedState()1103 public void sendUpdateBcastSourceMessage_inConnectedState() { 1104 initToConnectedState(); 1105 mBassClientStateMachine.connectGatt(true); 1106 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; 1107 1108 // Prepare mBluetoothLeBroadcastReceiveStates for test 1109 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1110 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1111 int sourceId = 1; 1112 int paSync = BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE; 1113 byte[] value = new byte[] { 1114 (byte) sourceId, // sourceId 1115 0x00, // sourceAddressType 1116 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, // sourceAddress 1117 0x00, // sourceAdvSid 1118 0x00, 0x00, 0x00, // broadcastIdBytes 1119 (byte) paSync, 1120 (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE, 1121 // 16 bytes badBroadcastCode 1122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1123 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1124 0x01, // numSubGroups 1125 // SubGroup #1 1126 0x00, 0x00, 0x00, 0x00, // audioSyncIndex 1127 0x02, // metaDataLength 1128 0x00, 0x00, // metadata 1129 }; 1130 BluetoothGattCharacteristic characteristic = 1131 Mockito.mock(BluetoothGattCharacteristic.class); 1132 when(characteristic.getValue()).thenReturn(value); 1133 when(characteristic.getInstanceId()).thenReturn(sourceId); 1134 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 1135 mBassClientStateMachine.mGattCallback.onCharacteristicRead( 1136 null, characteristic, GATT_SUCCESS); 1137 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1138 1139 BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(); 1140 when(mBassClientService.getPeriodicAdvertisementResult(any())).thenReturn(null); 1141 1142 mBassClientStateMachine.sendMessage(UPDATE_BCAST_SOURCE, sourceId, paSync, metadata); 1143 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1144 verify(callbacks).notifySourceRemoveFailed(any(), anyInt(), anyInt()); 1145 1146 PeriodicAdvertisementResult paResult = Mockito.mock(PeriodicAdvertisementResult.class); 1147 when(mBassClientService.getPeriodicAdvertisementResult(any())).thenReturn(paResult); 1148 when(mBassClientService.getBase(anyInt())).thenReturn(null); 1149 Mockito.clearInvocations(callbacks); 1150 1151 mBassClientStateMachine.sendMessage(UPDATE_BCAST_SOURCE, sourceId, paSync, metadata); 1152 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1153 verify(callbacks).notifySourceRemoveFailed(any(), anyInt(), anyInt()); 1154 1155 BaseData data = Mockito.mock(BaseData.class); 1156 when(mBassClientService.getBase(anyInt())).thenReturn(data); 1157 when(data.getNumberOfSubgroupsofBIG()).thenReturn((byte) 1); 1158 Mockito.clearInvocations(callbacks); 1159 1160 mBassClientStateMachine.sendMessage(UPDATE_BCAST_SOURCE, sourceId, paSync, metadata); 1161 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1162 verify(callbacks).notifySourceModifyFailed(any(), anyInt(), anyInt()); 1163 1164 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 1165 BassClientStateMachine.BluetoothGattTestableWrapper.class); 1166 BluetoothGattCharacteristic scanControlPoint = Mockito.mock( 1167 BluetoothGattCharacteristic.class); 1168 mBassClientStateMachine.mBluetoothGatt = btGatt; 1169 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1170 mBassClientStateMachine.mPendingOperation = 0; 1171 mBassClientStateMachine.mPendingSourceId = 0; 1172 mBassClientStateMachine.mPendingMetadata = null; 1173 Mockito.clearInvocations(callbacks); 1174 1175 sendMessageAndVerifyTransition( 1176 mBassClientStateMachine.obtainMessage( 1177 UPDATE_BCAST_SOURCE, sourceId, paSync, metadata), 1178 BassClientStateMachine.ConnectedProcessing.class); 1179 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(UPDATE_BCAST_SOURCE); 1180 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(sourceId); 1181 assertThat(mBassClientStateMachine.mPendingMetadata).isEqualTo(metadata); 1182 } 1183 1184 @Test sendSetBcastCodeMessage_inConnectedState()1185 public void sendSetBcastCodeMessage_inConnectedState() { 1186 initToConnectedState(); 1187 mBassClientStateMachine.connectGatt(true); 1188 mBassClientStateMachine.mNumOfBroadcastReceiverStates = 2; 1189 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1190 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1191 1192 // Prepare mBluetoothLeBroadcastReceiveStates with metadata for test 1193 mBassClientStateMachine.mShouldHandleMessage = false; 1194 int sourceId = 1; 1195 byte[] value = new byte[] { 1196 (byte) sourceId, // sourceId 1197 0x00, // sourceAddressType 1198 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, // sourceAddress 1199 0x00, // sourceAdvSid 1200 0x00, 0x00, 0x00, // broadcastIdBytes 1201 (byte) BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 1202 (byte) BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED, 1203 // 16 bytes badBroadcastCode 1204 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1205 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1206 0x01, // numSubGroups 1207 // SubGroup #1 1208 0x00, 0x00, 0x00, 0x00, // audioSyncIndex 1209 0x02, // metaDataLength 1210 0x00, 0x00, // metadata 1211 }; 1212 mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE; 1213 mBassClientStateMachine.mPendingSourceId = (byte) sourceId; 1214 BluetoothGattCharacteristic characteristic = 1215 Mockito.mock(BluetoothGattCharacteristic.class); 1216 when(characteristic.getValue()).thenReturn(value); 1217 when(characteristic.getInstanceId()).thenReturn(sourceId); 1218 when(characteristic.getUuid()).thenReturn(BassConstants.BASS_BCAST_RECEIVER_STATE); 1219 1220 mBassClientStateMachine.mGattCallback.onCharacteristicRead( 1221 null, characteristic, GATT_SUCCESS); 1222 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1223 1224 mBassClientStateMachine.mPendingMetadata = createBroadcastMetadata(); 1225 mBassClientStateMachine.mGattCallback.onCharacteristicRead( 1226 null, characteristic, GATT_SUCCESS); 1227 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1228 mBassClientStateMachine.mShouldHandleMessage = true; 1229 1230 BluetoothLeBroadcastReceiveState recvState = new BluetoothLeBroadcastReceiveState( 1231 2, 1232 BluetoothDevice.ADDRESS_TYPE_PUBLIC, 1233 mAdapter.getRemoteLeDevice("00:00:00:00:00:00", 1234 BluetoothDevice.ADDRESS_TYPE_PUBLIC), 1235 0, 1236 0, 1237 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 1238 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED, 1239 null, 1240 0, 1241 Arrays.asList(new Long[0]), 1242 Arrays.asList(new BluetoothLeAudioContentMetadata[0]) 1243 ); 1244 mBassClientStateMachine.mSetBroadcastCodePending = false; 1245 mBassClientStateMachine.sendMessage(SET_BCAST_CODE, recvState); 1246 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1247 assertThat(mBassClientStateMachine.mSetBroadcastCodePending).isTrue(); 1248 1249 recvState = new BluetoothLeBroadcastReceiveState( 1250 sourceId, 1251 BluetoothDevice.ADDRESS_TYPE_PUBLIC, 1252 mAdapter.getRemoteLeDevice("00:00:00:00:00:00", 1253 BluetoothDevice.ADDRESS_TYPE_PUBLIC), 1254 0, 1255 0, 1256 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, 1257 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED, 1258 null, 1259 0, 1260 Arrays.asList(new Long[0]), 1261 Arrays.asList(new BluetoothLeAudioContentMetadata[0]) 1262 ); 1263 mBassClientStateMachine.sendMessage(SET_BCAST_CODE, recvState); 1264 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 1265 BassClientStateMachine.BluetoothGattTestableWrapper.class); 1266 mBassClientStateMachine.mBluetoothGatt = btGatt; 1267 BluetoothGattCharacteristic scanControlPoint = Mockito.mock( 1268 BluetoothGattCharacteristic.class); 1269 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1270 1271 sendMessageAndVerifyTransition( 1272 mBassClientStateMachine.obtainMessage(SET_BCAST_CODE, recvState), 1273 BassClientStateMachine.ConnectedProcessing.class); 1274 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(SET_BCAST_CODE); 1275 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(sourceId); 1276 verify(btGatt).writeCharacteristic(any()); 1277 verify(scanControlPoint).setValue(any(byte[].class)); 1278 } 1279 1280 @Test sendRemoveBcastSourceMessage_inConnectedState()1281 public void sendRemoveBcastSourceMessage_inConnectedState() { 1282 initToConnectedState(); 1283 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1284 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1285 1286 int sid = 10; 1287 mBassClientStateMachine.sendMessage(REMOVE_BCAST_SOURCE, sid); 1288 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1289 verify(callbacks).notifySourceRemoveFailed(any(), anyInt(), anyInt()); 1290 1291 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 1292 BassClientStateMachine.BluetoothGattTestableWrapper.class); 1293 mBassClientStateMachine.mBluetoothGatt = btGatt; 1294 BluetoothGattCharacteristic scanControlPoint = Mockito.mock( 1295 BluetoothGattCharacteristic.class); 1296 mBassClientStateMachine.mBroadcastScanControlPoint = scanControlPoint; 1297 1298 sendMessageAndVerifyTransition( 1299 mBassClientStateMachine.obtainMessage(REMOVE_BCAST_SOURCE, sid), 1300 BassClientStateMachine.ConnectedProcessing.class); 1301 verify(scanControlPoint).setValue(any(byte[].class)); 1302 verify(btGatt).writeCharacteristic(any()); 1303 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(REMOVE_BCAST_SOURCE); 1304 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(sid); 1305 } 1306 1307 @Test sendConnectMessage_inConnectedProcessingState_doesNotChangeState()1308 public void sendConnectMessage_inConnectedProcessingState_doesNotChangeState() { 1309 initToConnectedProcessingState(); 1310 1311 mBassClientStateMachine.sendMessage(CONNECT); 1312 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1313 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1314 } 1315 1316 @Test sendDisconnectMessage_inConnectedProcessingState_doesNotChangeState()1317 public void sendDisconnectMessage_inConnectedProcessingState_doesNotChangeState() { 1318 initToConnectedProcessingState(); 1319 1320 // Mock instance of btGatt was created in initToConnectedProcessingState(). 1321 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = 1322 mBassClientStateMachine.mBluetoothGatt; 1323 mBassClientStateMachine.mBluetoothGatt = null; 1324 mBassClientStateMachine.sendMessage(DISCONNECT); 1325 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1326 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1327 1328 mBassClientStateMachine.mBluetoothGatt = btGatt; 1329 sendMessageAndVerifyTransition( 1330 mBassClientStateMachine.obtainMessage(DISCONNECT), 1331 BassClientStateMachine.Disconnected.class); 1332 verify(btGatt).disconnect(); 1333 verify(btGatt).close(); 1334 } 1335 1336 @Test sendStateChangedMessage_inConnectedProcessingState()1337 public void sendStateChangedMessage_inConnectedProcessingState() { 1338 initToConnectedProcessingState(); 1339 1340 Message msgToConnectedState = 1341 mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1342 msgToConnectedState.obj = BluetoothProfile.STATE_CONNECTED; 1343 1344 mBassClientStateMachine.sendMessage(msgToConnectedState); 1345 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1346 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1347 1348 Message msgToNoneConnectedState = 1349 mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1350 msgToNoneConnectedState.obj = BluetoothProfile.STATE_DISCONNECTING; 1351 sendMessageAndVerifyTransition( 1352 msgToNoneConnectedState, BassClientStateMachine.Disconnected.class); 1353 } 1354 1355 /** 1356 * This also tests BassClientStateMachine#sendPendingCallbacks 1357 */ 1358 @Test sendGattTxnProcessedMessage_inConnectedProcessingState()1359 public void sendGattTxnProcessedMessage_inConnectedProcessingState() { 1360 initToConnectedProcessingState(); 1361 BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class); 1362 when(mBassClientService.getCallbacks()).thenReturn(callbacks); 1363 1364 // Test sendPendingCallbacks(START_SCAN_OFFLOAD, ERROR_UNKNOWN) 1365 mBassClientStateMachine.mPendingOperation = START_SCAN_OFFLOAD; 1366 mBassClientStateMachine.mNoStopScanOffload = true; 1367 mBassClientStateMachine.mAutoTriggered = false; 1368 sendMessageAndVerifyTransition( 1369 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 1370 BassClientStateMachine.Connected.class); 1371 assertThat(mBassClientStateMachine.mNoStopScanOffload).isFalse(); 1372 1373 // Test sendPendingCallbacks(START_SCAN_OFFLOAD, ERROR_UNKNOWN) 1374 moveConnectedStateToConnectedProcessingState(); 1375 mBassClientStateMachine.mPendingOperation = START_SCAN_OFFLOAD; 1376 mBassClientStateMachine.mAutoTriggered = true; 1377 sendMessageAndVerifyTransition( 1378 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 1379 BassClientStateMachine.Connected.class); 1380 assertThat(mBassClientStateMachine.mAutoTriggered).isFalse(); 1381 1382 // Test sendPendingCallbacks(ADD_BCAST_SOURCE, ERROR_UNKNOWN) 1383 moveConnectedStateToConnectedProcessingState(); 1384 mBassClientStateMachine.mPendingOperation = ADD_BCAST_SOURCE; 1385 sendMessageAndVerifyTransition( 1386 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 1387 BassClientStateMachine.Connected.class); 1388 verify(callbacks).notifySourceAddFailed(any(), any(), anyInt()); 1389 1390 // Test sendPendingCallbacks(UPDATE_BCAST_SOURCE, REASON_LOCAL_APP_REQUEST) 1391 moveConnectedStateToConnectedProcessingState(); 1392 mBassClientStateMachine.mPendingOperation = UPDATE_BCAST_SOURCE; 1393 mBassClientStateMachine.mAutoTriggered = true; 1394 sendMessageAndVerifyTransition( 1395 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_SUCCESS), 1396 BassClientStateMachine.Connected.class); 1397 assertThat(mBassClientStateMachine.mAutoTriggered).isFalse(); 1398 1399 // Test sendPendingCallbacks(UPDATE_BCAST_SOURCE, ERROR_UNKNOWN) 1400 moveConnectedStateToConnectedProcessingState(); 1401 mBassClientStateMachine.mPendingOperation = UPDATE_BCAST_SOURCE; 1402 sendMessageAndVerifyTransition( 1403 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 1404 BassClientStateMachine.Connected.class); 1405 verify(callbacks).notifySourceModifyFailed(any(), anyInt(), anyInt()); 1406 1407 // Test sendPendingCallbacks(REMOVE_BCAST_SOURCE, ERROR_UNKNOWN) 1408 moveConnectedStateToConnectedProcessingState(); 1409 mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE; 1410 sendMessageAndVerifyTransition( 1411 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 1412 BassClientStateMachine.Connected.class); 1413 verify(callbacks).notifySourceRemoveFailed(any(), anyInt(), anyInt()); 1414 1415 // Test sendPendingCallbacks(SET_BCAST_CODE, REASON_LOCAL_APP_REQUEST) 1416 moveConnectedStateToConnectedProcessingState(); 1417 mBassClientStateMachine.mPendingOperation = REMOVE_BCAST_SOURCE; 1418 sendMessageAndVerifyTransition( 1419 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 1420 BassClientStateMachine.Connected.class); 1421 // Nothing to verify more 1422 1423 // Test sendPendingCallbacks(SET_BCAST_CODE, REASON_LOCAL_APP_REQUEST) 1424 moveConnectedStateToConnectedProcessingState(); 1425 mBassClientStateMachine.mPendingOperation = -1; 1426 sendMessageAndVerifyTransition( 1427 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE), 1428 BassClientStateMachine.Connected.class); 1429 // Nothing to verify more 1430 } 1431 1432 @Test sendGattTxnTimeoutMessage_inConnectedProcessingState_doesNotChangeState()1433 public void sendGattTxnTimeoutMessage_inConnectedProcessingState_doesNotChangeState() { 1434 initToConnectedProcessingState(); 1435 1436 mBassClientStateMachine.mPendingOperation = SET_BCAST_CODE; 1437 mBassClientStateMachine.mPendingSourceId = 0; 1438 sendMessageAndVerifyTransition( 1439 mBassClientStateMachine.obtainMessage(GATT_TXN_TIMEOUT, GATT_FAILURE), 1440 BassClientStateMachine.Connected.class); 1441 assertThat(mBassClientStateMachine.mPendingOperation).isEqualTo(-1); 1442 assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(-1); 1443 } 1444 1445 @Test sendMessageForDeferring_inConnectedProcessingState_defersMessage()1446 public void sendMessageForDeferring_inConnectedProcessingState_defersMessage() { 1447 initToConnectedProcessingState(); 1448 1449 mBassClientStateMachine.sendMessage(READ_BASS_CHARACTERISTICS); 1450 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1451 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(READ_BASS_CHARACTERISTICS)) 1452 .isTrue(); 1453 1454 mBassClientStateMachine.sendMessage(START_SCAN_OFFLOAD); 1455 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1456 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(START_SCAN_OFFLOAD)) 1457 .isTrue(); 1458 1459 mBassClientStateMachine.sendMessage(STOP_SCAN_OFFLOAD); 1460 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1461 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(STOP_SCAN_OFFLOAD)) 1462 .isTrue(); 1463 1464 mBassClientStateMachine.sendMessage(SELECT_BCAST_SOURCE); 1465 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1466 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(SELECT_BCAST_SOURCE)) 1467 .isTrue(); 1468 1469 mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE); 1470 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1471 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(ADD_BCAST_SOURCE)) 1472 .isTrue(); 1473 1474 mBassClientStateMachine.sendMessage(SET_BCAST_CODE); 1475 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1476 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(SET_BCAST_CODE)) 1477 .isTrue(); 1478 1479 mBassClientStateMachine.sendMessage(REMOVE_BCAST_SOURCE); 1480 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1481 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(REMOVE_BCAST_SOURCE)) 1482 .isTrue(); 1483 1484 mBassClientStateMachine.sendMessage(PSYNC_ACTIVE_TIMEOUT); 1485 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1486 assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(PSYNC_ACTIVE_TIMEOUT)) 1487 .isTrue(); 1488 } 1489 1490 @Test sendInvalidMessage_inConnectedProcessingState_doesNotChangeState()1491 public void sendInvalidMessage_inConnectedProcessingState_doesNotChangeState() { 1492 initToConnectedProcessingState(); 1493 1494 mBassClientStateMachine.sendMessage(-1); 1495 TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); 1496 verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any()); 1497 } 1498 1499 @Test dump_doesNotCrash()1500 public void dump_doesNotCrash() { 1501 mBassClientStateMachine.dump(new StringBuilder()); 1502 } 1503 initToDisconnectedState()1504 private void initToDisconnectedState() { 1505 allowConnection(true); 1506 allowConnectGatt(true); 1507 assertThat(mBassClientStateMachine.getCurrentState()) 1508 .isInstanceOf(BassClientStateMachine.Disconnected.class); 1509 } 1510 initToConnectingState()1511 private void initToConnectingState() { 1512 allowConnection(true); 1513 allowConnectGatt(true); 1514 sendMessageAndVerifyTransition( 1515 mBassClientStateMachine.obtainMessage(CONNECT), 1516 BassClientStateMachine.Connecting.class); 1517 Mockito.clearInvocations(mBassClientService); 1518 } 1519 initToConnectedState()1520 private void initToConnectedState() { 1521 initToConnectingState(); 1522 1523 Message msg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED); 1524 msg.obj = BluetoothProfile.STATE_CONNECTED; 1525 sendMessageAndVerifyTransition(msg, BassClientStateMachine.Connected.class); 1526 Mockito.clearInvocations(mBassClientService); 1527 } 1528 initToConnectedProcessingState()1529 private void initToConnectedProcessingState() { 1530 initToConnectedState(); 1531 moveConnectedStateToConnectedProcessingState(); 1532 } 1533 moveConnectedStateToConnectedProcessingState()1534 private void moveConnectedStateToConnectedProcessingState() { 1535 BluetoothGattCharacteristic gattCharacteristic = Mockito.mock( 1536 BluetoothGattCharacteristic.class); 1537 BassClientStateMachine.BluetoothGattTestableWrapper btGatt = Mockito.mock( 1538 BassClientStateMachine.BluetoothGattTestableWrapper.class); 1539 mBassClientStateMachine.mBluetoothGatt = btGatt; 1540 sendMessageAndVerifyTransition(mBassClientStateMachine.obtainMessage( 1541 READ_BASS_CHARACTERISTICS, gattCharacteristic), 1542 BassClientStateMachine.ConnectedProcessing.class); 1543 Mockito.clearInvocations(mBassClientService); 1544 } 1545 sendMessageAndVerifyTransition(Message msg, Class<T> type)1546 private <T> void sendMessageAndVerifyTransition(Message msg, Class<T> type) { 1547 Mockito.clearInvocations(mBassClientService); 1548 mBassClientStateMachine.sendMessage(msg); 1549 // Verify that one connection state broadcast is executed 1550 verify(mBassClientService, timeout(TIMEOUT_MS) 1551 .times(1)) 1552 .sendBroadcast(any(Intent.class), anyString(), any()); 1553 Assert.assertThat(mBassClientStateMachine.getCurrentState(), IsInstanceOf.instanceOf(type)); 1554 } 1555 createBroadcastMetadata()1556 private BluetoothLeBroadcastMetadata createBroadcastMetadata() { 1557 final String testMacAddress = "00:11:22:33:44:55"; 1558 final int testBroadcastId = 42; 1559 final int testAdvertiserSid = 1234; 1560 final int testPaSyncInterval = 100; 1561 final int testPresentationDelayMs = 345; 1562 1563 BluetoothDevice testDevice = 1564 mAdapter.getRemoteLeDevice(testMacAddress, BluetoothDevice.ADDRESS_TYPE_RANDOM); 1565 1566 BluetoothLeBroadcastMetadata.Builder builder = new BluetoothLeBroadcastMetadata.Builder() 1567 .setEncrypted(false) 1568 .setSourceDevice(testDevice, BluetoothDevice.ADDRESS_TYPE_RANDOM) 1569 .setSourceAdvertisingSid(testAdvertiserSid) 1570 .setBroadcastId(testBroadcastId) 1571 .setBroadcastCode(new byte[] { 0x00 }) 1572 .setPaSyncInterval(testPaSyncInterval) 1573 .setPresentationDelayMicros(testPresentationDelayMs); 1574 // builder expect at least one subgroup 1575 builder.addSubgroup(createBroadcastSubgroup()); 1576 return builder.build(); 1577 } 1578 createBroadcastSubgroup()1579 private BluetoothLeBroadcastSubgroup createBroadcastSubgroup() { 1580 final long testAudioLocationFrontLeft = 0x01; 1581 final long testAudioLocationFrontRight = 0x02; 1582 // For BluetoothLeAudioContentMetadata 1583 final String testProgramInfo = "Test"; 1584 // German language code in ISO 639-3 1585 final String testLanguage = "deu"; 1586 final int testCodecId = 42; 1587 final int testChannelIndex = 56; 1588 1589 BluetoothLeAudioCodecConfigMetadata codecMetadata = 1590 new BluetoothLeAudioCodecConfigMetadata.Builder() 1591 .setAudioLocation(testAudioLocationFrontLeft).build(); 1592 BluetoothLeAudioContentMetadata contentMetadata = 1593 new BluetoothLeAudioContentMetadata.Builder() 1594 .setProgramInfo(testProgramInfo).setLanguage(testLanguage).build(); 1595 BluetoothLeBroadcastSubgroup.Builder builder = new BluetoothLeBroadcastSubgroup.Builder() 1596 .setCodecId(testCodecId) 1597 .setCodecSpecificConfig(codecMetadata) 1598 .setContentMetadata(contentMetadata); 1599 1600 BluetoothLeAudioCodecConfigMetadata channelCodecMetadata = 1601 new BluetoothLeAudioCodecConfigMetadata.Builder() 1602 .setAudioLocation(testAudioLocationFrontRight).build(); 1603 1604 // builder expect at least one channel 1605 BluetoothLeBroadcastChannel channel = 1606 new BluetoothLeBroadcastChannel.Builder() 1607 .setSelected(true) 1608 .setChannelIndex(testChannelIndex) 1609 .setCodecMetadata(channelCodecMetadata) 1610 .build(); 1611 builder.addChannel(channel); 1612 return builder.build(); 1613 } 1614 1615 // It simulates GATT connection for testing. 1616 public static class StubBassClientStateMachine extends BassClientStateMachine { 1617 boolean mShouldAllowGatt = true; 1618 boolean mShouldHandleMessage = true; 1619 Boolean mIsPendingRemove; 1620 List<Integer> mMsgWhats = new ArrayList<>(); 1621 int mMsgWhat; 1622 int mMsgAgr1; 1623 int mMsgArg2; 1624 Object mMsgObj; 1625 StubBassClientStateMachine(BluetoothDevice device, BassClientService service, Looper looper, int connectTimeout)1626 StubBassClientStateMachine(BluetoothDevice device, BassClientService service, Looper looper, 1627 int connectTimeout) { 1628 super(device, service, looper, connectTimeout); 1629 } 1630 1631 @Override connectGatt(Boolean autoConnect)1632 public boolean connectGatt(Boolean autoConnect) { 1633 mGattCallback = new GattCallback(); 1634 return mShouldAllowGatt; 1635 } 1636 1637 @Override sendMessage(Message msg)1638 public void sendMessage(Message msg) { 1639 mMsgWhats.add(msg.what); 1640 mMsgWhat = msg.what; 1641 mMsgAgr1 = msg.arg1; 1642 mMsgArg2 = msg.arg2; 1643 mMsgObj = msg.obj; 1644 if (mShouldHandleMessage) { 1645 super.sendMessage(msg); 1646 } 1647 } 1648 notifyConnectionStateChanged(int status, int newState)1649 public void notifyConnectionStateChanged(int status, int newState) { 1650 if (mGattCallback != null) { 1651 BluetoothGatt gatt = null; 1652 if (mBluetoothGatt != null) { 1653 gatt = mBluetoothGatt.mWrappedBluetoothGatt; 1654 } 1655 mGattCallback.onConnectionStateChange(gatt, status, newState); 1656 } 1657 } 1658 hasDeferredMessagesSuper(int what)1659 public boolean hasDeferredMessagesSuper(int what) { 1660 return super.hasDeferredMessages(what); 1661 } 1662 1663 @Override isPendingRemove(Integer sourceId)1664 boolean isPendingRemove(Integer sourceId) { 1665 if (mIsPendingRemove == null) { 1666 return super.isPendingRemove(sourceId); 1667 } 1668 return mIsPendingRemove; 1669 } 1670 } 1671 } 1672