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