• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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