• 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.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
20 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
21 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
22 import static android.bluetooth.BluetoothProfile.EXTRA_PREVIOUS_STATE;
23 import static android.bluetooth.BluetoothProfile.EXTRA_STATE;
24 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
25 import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
26 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
27 
28 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
29 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
30 
31 import static com.android.bluetooth.TestUtils.MockitoRule;
32 import static com.android.bluetooth.TestUtils.getTestDevice;
33 
34 import static com.google.common.truth.Truth.assertThat;
35 
36 import static org.mockito.ArgumentMatchers.anyInt;
37 import static org.mockito.ArgumentMatchers.eq;
38 import static org.mockito.Mockito.any;
39 import static org.mockito.Mockito.anyInt;
40 import static org.mockito.Mockito.atLeast;
41 import static org.mockito.Mockito.clearInvocations;
42 import static org.mockito.Mockito.doAnswer;
43 import static org.mockito.Mockito.doCallRealMethod;
44 import static org.mockito.Mockito.doNothing;
45 import static org.mockito.Mockito.doReturn;
46 import static org.mockito.Mockito.eq;
47 import static org.mockito.Mockito.inOrder;
48 import static org.mockito.Mockito.mock;
49 import static org.mockito.Mockito.never;
50 import static org.mockito.Mockito.notNull;
51 import static org.mockito.Mockito.timeout;
52 import static org.mockito.Mockito.times;
53 import static org.mockito.Mockito.verify;
54 import static org.mockito.Mockito.when;
55 
56 import android.app.BroadcastOptions;
57 import android.bluetooth.BluetoothAdapter;
58 import android.bluetooth.BluetoothDevice;
59 import android.bluetooth.BluetoothGatt;
60 import android.bluetooth.BluetoothLeAudio;
61 import android.bluetooth.BluetoothLeAudioCodecConfigMetadata;
62 import android.bluetooth.BluetoothLeAudioContentMetadata;
63 import android.bluetooth.BluetoothLeBroadcastAssistant;
64 import android.bluetooth.BluetoothLeBroadcastChannel;
65 import android.bluetooth.BluetoothLeBroadcastMetadata;
66 import android.bluetooth.BluetoothLeBroadcastReceiveState;
67 import android.bluetooth.BluetoothLeBroadcastSubgroup;
68 import android.bluetooth.BluetoothProfile;
69 import android.bluetooth.BluetoothStatusCodes;
70 import android.bluetooth.BluetoothUuid;
71 import android.bluetooth.IBluetoothLeBroadcastAssistantCallback;
72 import android.bluetooth.le.IScannerCallback;
73 import android.bluetooth.le.PeriodicAdvertisingReport;
74 import android.bluetooth.le.ScanCallback;
75 import android.bluetooth.le.ScanFilter;
76 import android.bluetooth.le.ScanRecord;
77 import android.bluetooth.le.ScanResult;
78 import android.bluetooth.le.ScanSettings;
79 import android.content.Intent;
80 import android.os.Binder;
81 import android.os.Handler;
82 import android.os.Message;
83 import android.os.ParcelUuid;
84 import android.os.RemoteException;
85 import android.platform.test.annotations.DisableFlags;
86 import android.platform.test.annotations.EnableFlags;
87 import android.platform.test.flag.junit.SetFlagsRule;
88 
89 import androidx.test.filters.MediumTest;
90 import androidx.test.runner.AndroidJUnit4;
91 
92 import com.android.bluetooth.BluetoothMethodProxy;
93 import com.android.bluetooth.TestUtils;
94 import com.android.bluetooth.btservice.AdapterService;
95 import com.android.bluetooth.btservice.ServiceFactory;
96 import com.android.bluetooth.btservice.storage.DatabaseManager;
97 import com.android.bluetooth.csip.CsipSetCoordinatorService;
98 import com.android.bluetooth.flags.Flags;
99 import com.android.bluetooth.le_audio.LeAudioService;
100 import com.android.bluetooth.le_scan.ScanController;
101 
102 import com.google.common.truth.Expect;
103 
104 import org.hamcrest.Matcher;
105 import org.hamcrest.core.AllOf;
106 import org.junit.After;
107 import org.junit.Before;
108 import org.junit.Rule;
109 import org.junit.Test;
110 import org.junit.runner.RunWith;
111 import org.mockito.ArgumentCaptor;
112 import org.mockito.InOrder;
113 import org.mockito.Mock;
114 import org.mockito.Mockito;
115 import org.mockito.Spy;
116 import org.mockito.hamcrest.MockitoHamcrest;
117 
118 import java.util.ArrayList;
119 import java.util.Arrays;
120 import java.util.HashMap;
121 import java.util.List;
122 import java.util.Optional;
123 import java.util.Set;
124 import java.util.stream.Collectors;
125 
126 /** Test cases for {@link BassClientService}. */
127 @MediumTest
128 @RunWith(AndroidJUnit4.class)
129 public class BassClientServiceTest {
130     @Rule public final MockitoRule mMockitoRule = new MockitoRule();
131     @Rule public Expect expect = Expect.create();
132     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
133 
134     @Spy private BassObjectsFactory mObjectsFactory = BassObjectsFactory.getInstance();
135     @Mock private AdapterService mAdapterService;
136     @Mock private DatabaseManager mDatabaseManager;
137     @Mock private BluetoothLeScannerWrapper mBluetoothLeScannerWrapper;
138     @Mock private ServiceFactory mServiceFactory;
139     @Mock private ScanController mScanController;
140     @Mock private CsipSetCoordinatorService mCsipService;
141     @Mock private LeAudioService mLeAudioService;
142     @Mock private IBluetoothLeBroadcastAssistantCallback mCallback;
143     @Mock private Binder mBinder;
144     @Mock private BluetoothMethodProxy mMethodProxy;
145 
146     private static final int TIMEOUT_MS = 1000;
147 
148     private static final ParcelUuid[] FAKE_SERVICE_UUIDS = {BluetoothUuid.BASS};
149 
150     private static final int TEST_BROADCAST_ID = 42;
151     private static final int TEST_ADVERTISER_SID = 1234;
152     private static final int TEST_PA_SYNC_INTERVAL = 100;
153     private static final int TEST_PRESENTATION_DELAY_MS = 345;
154     private static final int TEST_RSSI = -40;
155 
156     private static final int TEST_SYNC_HANDLE = 0;
157 
158     private static final int TEST_CODEC_ID = 42;
159     private static final int TEST_CHANNEL_INDEX = 56;
160 
161     // For BluetoothLeAudioCodecConfigMetadata
162     private static final long TEST_AUDIO_LOCATION_FRONT_LEFT = 0x01;
163     private static final long TEST_AUDIO_LOCATION_FRONT_RIGHT = 0x02;
164 
165     // For BluetoothLeAudioContentMetadata
166     private static final String TEST_PROGRAM_INFO = "Test";
167     // German language code in ISO 639-3
168     private static final String TEST_LANGUAGE = "deu";
169     private static final int TEST_SOURCE_ID = 10;
170     private static final int TEST_NUM_SOURCES = 1;
171 
172     private final HashMap<BluetoothDevice, BassClientStateMachine> mStateMachines = new HashMap<>();
173 
174     private final BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
175     private final BluetoothDevice mCurrentDevice = getTestDevice(0);
176     private final BluetoothDevice mCurrentDevice1 = getTestDevice(1);
177 
178     private BassClientService mBassClientService;
179 
180     private final BluetoothDevice mSourceDevice =
181             mBluetoothAdapter.getRemoteLeDevice(
182                     "00:11:22:33:44:55", BluetoothDevice.ADDRESS_TYPE_RANDOM);
183     private final BluetoothDevice mSourceDevice2 =
184             mBluetoothAdapter.getRemoteLeDevice(
185                     "00:11:22:33:44:66", BluetoothDevice.ADDRESS_TYPE_RANDOM);
186     private ArgumentCaptor<ScanCallback> mCallbackCaptor;
187     private ArgumentCaptor<IScannerCallback> mBassScanCallbackCaptor;
188 
189     private InOrder mInOrderMethodProxy;
190     private InOrder mInOrder;
191 
createBroadcastSubgroup()192     BluetoothLeBroadcastSubgroup createBroadcastSubgroup() {
193         BluetoothLeAudioCodecConfigMetadata codecMetadata =
194                 new BluetoothLeAudioCodecConfigMetadata.Builder()
195                         .setAudioLocation(TEST_AUDIO_LOCATION_FRONT_LEFT)
196                         .build();
197         BluetoothLeAudioContentMetadata contentMetadata =
198                 new BluetoothLeAudioContentMetadata.Builder()
199                         .setProgramInfo(TEST_PROGRAM_INFO)
200                         .setLanguage(TEST_LANGUAGE)
201                         .build();
202         BluetoothLeBroadcastSubgroup.Builder builder =
203                 new BluetoothLeBroadcastSubgroup.Builder()
204                         .setCodecId(TEST_CODEC_ID)
205                         .setCodecSpecificConfig(codecMetadata)
206                         .setContentMetadata(contentMetadata);
207 
208         BluetoothLeAudioCodecConfigMetadata channelCodecMetadata =
209                 new BluetoothLeAudioCodecConfigMetadata.Builder()
210                         .setAudioLocation(TEST_AUDIO_LOCATION_FRONT_RIGHT)
211                         .build();
212 
213         // builder expect at least one channel
214         BluetoothLeBroadcastChannel channel =
215                 new BluetoothLeBroadcastChannel.Builder()
216                         .setSelected(true)
217                         .setChannelIndex(TEST_CHANNEL_INDEX)
218                         .setCodecMetadata(channelCodecMetadata)
219                         .build();
220         builder.addChannel(channel);
221         return builder.build();
222     }
223 
createBroadcastMetadata(int broadcastId)224     BluetoothLeBroadcastMetadata createBroadcastMetadata(int broadcastId) {
225         BluetoothLeBroadcastMetadata.Builder builder =
226                 new BluetoothLeBroadcastMetadata.Builder()
227                         .setEncrypted(false)
228                         .setSourceDevice(mSourceDevice, BluetoothDevice.ADDRESS_TYPE_RANDOM)
229                         .setSourceAdvertisingSid(TEST_ADVERTISER_SID)
230                         .setBroadcastId(broadcastId)
231                         .setBroadcastCode(null)
232                         .setPaSyncInterval(TEST_PA_SYNC_INTERVAL)
233                         .setPresentationDelayMicros(TEST_PRESENTATION_DELAY_MS);
234         // builder expect at least one subgroup
235         builder.addSubgroup(createBroadcastSubgroup());
236         return builder.build();
237     }
238 
createEmptyBroadcastMetadata()239     BluetoothLeBroadcastMetadata createEmptyBroadcastMetadata() {
240         BluetoothLeBroadcastMetadata.Builder builder =
241                 new BluetoothLeBroadcastMetadata.Builder()
242                         .setEncrypted(false)
243                         .setSourceDevice(
244                                 mBluetoothAdapter.getRemoteLeDevice(
245                                         "00:00:00:00:00:00", BluetoothDevice.ADDRESS_TYPE_RANDOM),
246                                 BluetoothDevice.ADDRESS_TYPE_RANDOM)
247                         .setSourceAdvertisingSid(TEST_ADVERTISER_SID)
248                         .setBroadcastId(0)
249                         .setBroadcastCode(null)
250                         .setPaSyncInterval(TEST_PA_SYNC_INTERVAL)
251                         .setPresentationDelayMicros(TEST_PRESENTATION_DELAY_MS);
252         // builder expect at least one subgroup
253         builder.addSubgroup(createBroadcastSubgroup());
254         return builder.build();
255     }
256 
257     @Before
setUp()258     public void setUp() throws Exception {
259         mInOrderMethodProxy = inOrder(mMethodProxy);
260         mInOrder = inOrder(mAdapterService);
261 
262         BassObjectsFactory.setInstanceForTesting(mObjectsFactory);
263         BluetoothMethodProxy.setInstanceForTesting(mMethodProxy);
264 
265         doReturn(true).when(mMethodProxy).initializePeriodicAdvertisingManagerOnDefaultAdapter();
266         doNothing()
267                 .when(mMethodProxy)
268                 .periodicAdvertisingManagerRegisterSync(
269                         any(), any(), anyInt(), anyInt(), any(), any());
270         doNothing().when(mMethodProxy).periodicAdvertisingManagerUnregisterSync(any(), any());
271 
272         doReturn(mAdapterService).when(mAdapterService).getBaseContext();
273         doReturn(new ParcelUuid[] {BluetoothUuid.BASS})
274                 .when(mAdapterService)
275                 .getRemoteUuids(any(BluetoothDevice.class));
276 
277         // Mock methods in AdapterService
278         doReturn(FAKE_SERVICE_UUIDS)
279                 .when(mAdapterService)
280                 .getRemoteUuids(any(BluetoothDevice.class));
281         doReturn(BluetoothDevice.BOND_BONDED)
282                 .when(mAdapterService)
283                 .getBondState(any(BluetoothDevice.class));
284         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
285         doAnswer(
286                         invocation -> {
287                             Set<BluetoothDevice> keys = mStateMachines.keySet();
288                             return keys.toArray(new BluetoothDevice[keys.size()]);
289                         })
290                 .when(mAdapterService)
291                 .getBondedDevices();
292         doReturn(mScanController).when(mAdapterService).getBluetoothScanController();
293 
294         // Mock methods in BassObjectsFactory
295         doAnswer(
296                         invocation -> {
297                             assertThat(mCurrentDevice).isNotNull();
298                             final BassClientStateMachine stateMachine =
299                                     mock(BassClientStateMachine.class);
300                             doReturn(new ArrayList<>()).when(stateMachine).getAllSources();
301                             doReturn(TEST_NUM_SOURCES)
302                                     .when(stateMachine)
303                                     .getMaximumSourceCapacity();
304                             doReturn((BluetoothDevice) invocation.getArgument(0))
305                                     .when(stateMachine)
306                                     .getDevice();
307                             doReturn(true).when(stateMachine).isBassStateReady();
308                             mStateMachines.put(
309                                     (BluetoothDevice) invocation.getArgument(0), stateMachine);
310                             doAnswer(
311                                             inv -> {
312                                                 return Message.obtain(
313                                                         (Handler) null, (int) inv.getArgument(0));
314                                             })
315                                     .when(stateMachine)
316                                     .obtainMessage(anyInt());
317                             return stateMachine;
318                         })
319                 .when(mObjectsFactory)
320                 .makeStateMachine(any(), any(), any(), any());
321         doReturn(mBluetoothLeScannerWrapper)
322                 .when(mObjectsFactory)
323                 .getBluetoothLeScannerWrapper(any());
324 
325         mBassClientService = new BassClientService(mAdapterService);
326         mBassClientService.setAvailable(true);
327 
328         mBassClientService.mServiceFactory = mServiceFactory;
329         doReturn(mCsipService).when(mServiceFactory).getCsipSetCoordinatorService();
330         doReturn(mLeAudioService).when(mServiceFactory).getLeAudioService();
331 
332         when(mCallback.asBinder()).thenReturn(mBinder);
333         mBassClientService.registerCallback(mCallback);
334     }
335 
336     @After
tearDown()337     public void tearDown() throws Exception {
338         mBassClientService.unregisterCallback(mCallback);
339 
340         mBassClientService.cleanup();
341         assertThat(BassClientService.getBassClientService()).isNull();
342         mStateMachines.clear();
343         BassObjectsFactory.setInstanceForTesting(null);
344     }
345 
346     /** Test to verify that BassClientService can be successfully started */
347     @Test
testGetBassClientService()348     public void testGetBassClientService() {
349         assertThat(mBassClientService).isEqualTo(BassClientService.getBassClientService());
350         // Verify default connection and audio states
351         assertThat(mBassClientService.getConnectionState(mCurrentDevice))
352                 .isEqualTo(STATE_DISCONNECTED);
353     }
354 
355     /** Test if getProfileConnectionPolicy works after the service is stopped. */
356     @Test
testGetPolicyAfterStopped()357     public void testGetPolicyAfterStopped() {
358         mBassClientService.cleanup();
359         when(mDatabaseManager.getProfileConnectionPolicy(
360                         mCurrentDevice, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT))
361                 .thenReturn(CONNECTION_POLICY_UNKNOWN);
362         assertThat(mBassClientService.getConnectionPolicy(mCurrentDevice))
363                 .isEqualTo(CONNECTION_POLICY_UNKNOWN);
364     }
365 
366     /**
367      * Test connecting to a test device. - service.connect() should return false -
368      * bassClientStateMachine.sendMessage(CONNECT) should be called.
369      */
370     @Test
testConnect()371     public void testConnect() {
372         when(mDatabaseManager.getProfileConnectionPolicy(
373                         any(BluetoothDevice.class),
374                         eq(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)))
375                 .thenReturn(CONNECTION_POLICY_ALLOWED);
376 
377         assertThat(mBassClientService.connect(mCurrentDevice)).isTrue();
378         verify(mObjectsFactory)
379                 .makeStateMachine(
380                         eq(mCurrentDevice), eq(mBassClientService), eq(mAdapterService), any());
381         BassClientStateMachine stateMachine = mStateMachines.get(mCurrentDevice);
382         assertThat(stateMachine).isNotNull();
383         verify(stateMachine).sendMessage(BassClientStateMachine.CONNECT);
384     }
385 
386     /** Test connecting to a null device. - service.connect() should return false. */
387     @Test
testConnect_nullDevice()388     public void testConnect_nullDevice() {
389         when(mDatabaseManager.getProfileConnectionPolicy(
390                         any(BluetoothDevice.class),
391                         eq(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)))
392                 .thenReturn(CONNECTION_POLICY_ALLOWED);
393         BluetoothDevice nullDevice = null;
394 
395         assertThat(mBassClientService.connect(nullDevice)).isFalse();
396     }
397 
398     /**
399      * Test connecting to a device when the connection policy is forbidden. - service.connect()
400      * should return false.
401      */
402     @Test
testConnect_whenConnectionPolicyIsForbidden()403     public void testConnect_whenConnectionPolicyIsForbidden() {
404         when(mDatabaseManager.getProfileConnectionPolicy(
405                         any(BluetoothDevice.class),
406                         eq(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)))
407                 .thenReturn(CONNECTION_POLICY_FORBIDDEN);
408         assertThat(mCurrentDevice).isNotNull();
409 
410         assertThat(mBassClientService.connect(mCurrentDevice)).isFalse();
411     }
412 
413     /**
414      * Test whether service.startSearchingForSources() calls BluetoothLeScannerWrapper.startScan().
415      */
416     @Test
testStartSearchingForSources()417     public void testStartSearchingForSources() {
418         prepareConnectedDeviceGroup();
419         List<ScanFilter> scanFilters = new ArrayList<>();
420 
421         assertThat(mStateMachines).hasSize(2);
422         for (BassClientStateMachine sm : mStateMachines.values()) {
423             Mockito.clearInvocations(sm);
424         }
425 
426         mBassClientService.startSearchingForSources(scanFilters);
427 
428         if (Flags.leaudioBassScanWithInternalScanController()) {
429             verify(mScanController).registerScannerInternal(any(), any(), any());
430         } else {
431             verify(mBluetoothLeScannerWrapper).startScan(notNull(), notNull(), notNull());
432         }
433         for (BassClientStateMachine sm : mStateMachines.values()) {
434             verify(sm).sendMessage(BassClientStateMachine.START_SCAN_OFFLOAD);
435         }
436     }
437 
438     /**
439      * Test whether service.startSearchingForSources() does not call
440      * BluetoothLeScannerWrapper.startScan() when the scanner instance cannot be achieved.
441      */
442     @Test
443     @DisableFlags(Flags.FLAG_LEAUDIO_BASS_SCAN_WITH_INTERNAL_SCAN_CONTROLLER)
testStartSearchingForSources_whenScannerIsNull()444     public void testStartSearchingForSources_whenScannerIsNull() {
445         doReturn(null).when(mObjectsFactory).getBluetoothLeScannerWrapper(any());
446         List<ScanFilter> scanFilters = new ArrayList<>();
447 
448         mBassClientService.startSearchingForSources(scanFilters);
449 
450         verify(mBluetoothLeScannerWrapper, never()).startScan(any(), any(), any());
451     }
452 
prepareConnectedDeviceGroup()453     private void prepareConnectedDeviceGroup() {
454         when(mDatabaseManager.getProfileConnectionPolicy(
455                         any(BluetoothDevice.class),
456                         eq(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)))
457                 .thenReturn(CONNECTION_POLICY_ALLOWED);
458 
459         // Mock the CSIP group
460         List<BluetoothDevice> groupDevices = new ArrayList<>();
461         groupDevices.add(mCurrentDevice);
462         groupDevices.add(mCurrentDevice1);
463         doReturn(groupDevices)
464                 .when(mCsipService)
465                 .getGroupDevicesOrdered(mCurrentDevice, BluetoothUuid.CAP);
466         doReturn(groupDevices)
467                 .when(mCsipService)
468                 .getGroupDevicesOrdered(mCurrentDevice1, BluetoothUuid.CAP);
469 
470         // Prepare connected devices
471         assertThat(mBassClientService.connect(mCurrentDevice)).isTrue();
472         assertThat(mBassClientService.connect(mCurrentDevice1)).isTrue();
473 
474         assertThat(mStateMachines).hasSize(2);
475         for (BassClientStateMachine sm : mStateMachines.values()) {
476             // Verify the call
477             verify(sm).sendMessage(eq(BassClientStateMachine.CONNECT));
478 
479             // Notify the service about the connection event
480             BluetoothDevice dev = sm.getDevice();
481             doCallRealMethod()
482                     .when(sm)
483                     .broadcastConnectionState(eq(dev), any(Integer.class), any(Integer.class));
484             sm.mService = mBassClientService;
485             sm.mDevice = dev;
486             sm.broadcastConnectionState(dev, STATE_CONNECTING, STATE_CONNECTED);
487 
488             doReturn(STATE_CONNECTED).when(sm).getConnectionState();
489             doReturn(true).when(sm).isConnected();
490 
491             // Inject initial broadcast source state
492             BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
493             if (sm.getDevice().equals(mCurrentDevice)) {
494                 injectRemoteSourceStateSourceAdded(
495                         sm,
496                         meta,
497                         TEST_SOURCE_ID,
498                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
499                         meta.isEncrypted()
500                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
501                                 : BluetoothLeBroadcastReceiveState
502                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
503                         null);
504                 injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID);
505             } else if (sm.getDevice().equals(mCurrentDevice1)) {
506                 injectRemoteSourceStateSourceAdded(
507                         sm,
508                         meta,
509                         TEST_SOURCE_ID + 1,
510                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
511                         meta.isEncrypted()
512                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
513                                 : BluetoothLeBroadcastReceiveState
514                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
515                         null);
516                 injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID + 1);
517             }
518         }
519 
520         doReturn(true).when(mLeAudioService).isPrimaryDevice(mCurrentDevice);
521         doReturn(true).when(mLeAudioService).isPrimaryDevice(mCurrentDevice1);
522     }
523 
startSearchingForSources()524     private void startSearchingForSources() {
525         List<ScanFilter> scanFilters = new ArrayList<>();
526         int scannerId = 1;
527 
528         assertThat(mStateMachines).hasSize(2);
529         for (BassClientStateMachine sm : mStateMachines.values()) {
530             Mockito.clearInvocations(sm);
531         }
532 
533         clearInvocations(mBluetoothLeScannerWrapper);
534         clearInvocations(mScanController);
535 
536         mBassClientService.startSearchingForSources(scanFilters);
537 
538         if (Flags.leaudioBassScanWithInternalScanController()) {
539             mBassScanCallbackCaptor = ArgumentCaptor.forClass(IScannerCallback.class);
540             verify(mScanController)
541                     .registerScannerInternal(mBassScanCallbackCaptor.capture(), any(), any());
542 
543             try {
544                 mBassScanCallbackCaptor.getValue().onScannerRegistered(0, scannerId);
545             } catch (RemoteException e) {
546                 // the mocked onScannerRegistered doesn't throw RemoteException
547             }
548             verify(mScanController).startScanInternal(eq(scannerId), any(), any());
549         } else {
550             mCallbackCaptor = ArgumentCaptor.forClass(ScanCallback.class);
551             verify(mBluetoothLeScannerWrapper)
552                     .startScan(notNull(), notNull(), mCallbackCaptor.capture());
553         }
554         for (BassClientStateMachine sm : mStateMachines.values()) {
555             verify(sm).sendMessage(BassClientStateMachine.START_SCAN_OFFLOAD);
556         }
557     }
558 
559     @Test
testStopSearchingForSources()560     public void testStopSearchingForSources() {
561         prepareConnectedDeviceGroup();
562         startSearchingForSources();
563 
564         // Scan and sync 1
565         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
566         mInOrderMethodProxy
567                 .verify(mMethodProxy)
568                 .periodicAdvertisingManagerRegisterSync(
569                         any(), any(), anyInt(), anyInt(), any(), any());
570         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
571         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
572         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
573                 .isEqualTo(mSourceDevice);
574         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
575                 .isEqualTo(TEST_BROADCAST_ID);
576 
577         // Stop searching
578         mBassClientService.stopSearchingForSources();
579         if (Flags.leaudioBassScanWithInternalScanController()) {
580             verify(mScanController).stopScanInternal(anyInt());
581 
582         } else {
583             verify(mBluetoothLeScannerWrapper).stopScan(mCallbackCaptor.getValue());
584         }
585         for (BassClientStateMachine sm : mStateMachines.values()) {
586             verify(sm).sendMessage(BassClientStateMachine.STOP_SCAN_OFFLOAD);
587         }
588 
589         // Check if unsyced
590         mInOrderMethodProxy
591                 .verify(mMethodProxy)
592                 .periodicAdvertisingManagerUnregisterSync(any(), any());
593         expect.that(mBassClientService.getActiveSyncedSources()).isEmpty();
594         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
595         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
596                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
597     }
598 
599     @Test
testStop()600     public void testStop() {
601         prepareConnectedDeviceGroup();
602         startSearchingForSources();
603 
604         // Scan and sync 1
605         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
606         mInOrderMethodProxy
607                 .verify(mMethodProxy)
608                 .periodicAdvertisingManagerRegisterSync(
609                         any(), any(), anyInt(), anyInt(), any(), any());
610         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
611         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
612         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
613                 .isEqualTo(mSourceDevice);
614         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
615                 .isEqualTo(TEST_BROADCAST_ID);
616 
617         // Stop
618         mBassClientService.cleanup();
619         if (Flags.leaudioBassScanWithInternalScanController()) {
620             verify(mScanController).stopScanInternal(anyInt());
621         } else {
622             verify(mBluetoothLeScannerWrapper).stopScan(mCallbackCaptor.getValue());
623         }
624 
625         // Check if unsyced
626         mInOrderMethodProxy
627                 .verify(mMethodProxy)
628                 .periodicAdvertisingManagerUnregisterSync(any(), any());
629         expect.that(mBassClientService.getActiveSyncedSources()).isEmpty();
630         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
631         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
632                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
633     }
634 
635     @Test
testStopSearchingForSources_startAndSyncAgain()636     public void testStopSearchingForSources_startAndSyncAgain() {
637         prepareConnectedDeviceGroup();
638         startSearchingForSources();
639 
640         // Scan and sync 1
641         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
642         mInOrderMethodProxy
643                 .verify(mMethodProxy)
644                 .periodicAdvertisingManagerRegisterSync(
645                         any(), any(), anyInt(), anyInt(), any(), any());
646         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
647 
648         // Stop searching
649         mBassClientService.stopSearchingForSources();
650 
651         // Start searching again
652         startSearchingForSources();
653 
654         // Sync the same device again
655         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
656         mInOrderMethodProxy
657                 .verify(mMethodProxy)
658                 .periodicAdvertisingManagerRegisterSync(
659                         any(), any(), anyInt(), anyInt(), any(), any());
660         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
661         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
662         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
663                 .isEqualTo(mSourceDevice);
664         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
665                 .isEqualTo(TEST_BROADCAST_ID);
666     }
667 
668     @Test
testStop_startAndSyncAgain()669     public void testStop_startAndSyncAgain() {
670         prepareConnectedDeviceGroup();
671         startSearchingForSources();
672 
673         // Scan and sync 1
674         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
675         mInOrderMethodProxy
676                 .verify(mMethodProxy)
677                 .periodicAdvertisingManagerRegisterSync(
678                         any(), any(), anyInt(), anyInt(), any(), any());
679         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
680 
681         // Stop
682         mBassClientService.cleanup();
683 
684         // Start again
685         mBassClientService = new BassClientService(mAdapterService);
686 
687         // Start searching again
688         prepareConnectedDeviceGroup();
689         startSearchingForSources();
690 
691         // Sync the same device again
692         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
693         mInOrderMethodProxy
694                 .verify(mMethodProxy)
695                 .periodicAdvertisingManagerRegisterSync(
696                         any(), any(), anyInt(), anyInt(), any(), any());
697         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
698         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
699         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
700                 .isEqualTo(mSourceDevice);
701         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
702                 .isEqualTo(TEST_BROADCAST_ID);
703     }
704 
705     @Test
testStopSearchingForSources_addSourceCauseSyncEvenWithoutScanning()706     public void testStopSearchingForSources_addSourceCauseSyncEvenWithoutScanning() {
707         prepareConnectedDeviceGroup();
708         startSearchingForSources();
709 
710         // Scan and sync 1
711         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
712         mInOrderMethodProxy
713                 .verify(mMethodProxy)
714                 .periodicAdvertisingManagerRegisterSync(
715                         any(), any(), anyInt(), anyInt(), any(), any());
716         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
717 
718         // Stop searching
719         mBassClientService.stopSearchingForSources();
720 
721         // Add source to unsynced broadcast, causes synchronization first
722         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
723         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
724         handleHandoverSupport();
725         mInOrderMethodProxy
726                 .verify(mMethodProxy)
727                 .periodicAdvertisingManagerRegisterSync(
728                         any(), any(), anyInt(), anyInt(), any(), any());
729 
730         // Verify not getting ADD_BCAST_SOURCE message before source sync
731         assertThat(mStateMachines).hasSize(2);
732         for (BassClientStateMachine sm : mStateMachines.values()) {
733             verify(sm, never()).sendMessage(any());
734         }
735 
736         // Source synced which cause execute pending add source
737         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
738         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
739         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
740                 .isEqualTo(mSourceDevice);
741         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
742                 .isEqualTo(TEST_BROADCAST_ID);
743 
744         // Verify all group members getting ADD_BCAST_SOURCE message
745         expect.that(mStateMachines.size()).isEqualTo(2);
746         for (BassClientStateMachine sm : mStateMachines.values()) {
747             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
748             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
749 
750             Message msg =
751                     messageCaptor.getAllValues().stream()
752                             .filter(
753                                     m ->
754                                             (m.what == BassClientStateMachine.ADD_BCAST_SOURCE)
755                                                     && (m.obj == meta))
756                             .findFirst()
757                             .orElse(null);
758             expect.that(msg).isNotNull();
759         }
760     }
761 
762     @Test
763     @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
testNotRemovingCachedBroadcastOnLostWithoutScanning_noResyncFlag()764     public void testNotRemovingCachedBroadcastOnLostWithoutScanning_noResyncFlag()
765             throws RemoteException {
766         prepareConnectedDeviceGroup();
767         startSearchingForSources();
768 
769         // Scan and sync 1
770         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
771         mInOrderMethodProxy
772                 .verify(mMethodProxy)
773                 .periodicAdvertisingManagerRegisterSync(
774                         any(), any(), anyInt(), anyInt(), any(), any());
775         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
776 
777         // Sync lost during scanning removes cached broadcast
778         onSyncLost();
779 
780         // Add source to not cached broadcast cause addFailed notification
781         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
782         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
783         handleHandoverSupport();
784         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
785         verify(mCallback)
786                 .onSourceAddFailed(
787                         eq(mCurrentDevice),
788                         eq(meta),
789                         eq(BluetoothStatusCodes.ERROR_BAD_PARAMETERS));
790 
791         // Add broadcast to cache
792         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
793         mInOrderMethodProxy
794                 .verify(mMethodProxy)
795                 .periodicAdvertisingManagerRegisterSync(
796                         any(), any(), anyInt(), anyInt(), any(), any());
797         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
798 
799         // Stop searching
800         mBassClientService.stopSearchingForSources();
801 
802         // Add sync handle by add source
803         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
804         handleHandoverSupport();
805         mInOrderMethodProxy
806                 .verify(mMethodProxy)
807                 .periodicAdvertisingManagerRegisterSync(
808                         any(), any(), anyInt(), anyInt(), any(), any());
809         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
810 
811         // Sync lost without active scanning should not remove broadcast cache
812         onSyncLost();
813 
814         // Add source to unsynced broadcast, causes synchronization first
815         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
816         handleHandoverSupport();
817         mInOrderMethodProxy
818                 .verify(mMethodProxy)
819                 .periodicAdvertisingManagerRegisterSync(
820                         any(), any(), anyInt(), anyInt(), any(), any());
821     }
822 
823     @Test
824     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
testNotRemovingCachedBroadcastOnLostWithoutScanning()825     public void testNotRemovingCachedBroadcastOnLostWithoutScanning() throws RemoteException {
826         prepareConnectedDeviceGroup();
827         startSearchingForSources();
828 
829         // Scan and sync 1
830         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
831         mInOrderMethodProxy
832                 .verify(mMethodProxy)
833                 .periodicAdvertisingManagerRegisterSync(
834                         any(), any(), anyInt(), anyInt(), any(), any());
835         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
836 
837         // Sync lost during scanning removes cached broadcast
838         onSyncLost();
839         checkAndDispatchTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_SYNC_LOST_TIMEOUT);
840 
841         // Add source to not cached broadcast cause addFailed notification
842         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
843         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
844         handleHandoverSupport();
845         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
846         verify(mCallback)
847                 .onSourceAddFailed(
848                         eq(mCurrentDevice),
849                         eq(meta),
850                         eq(BluetoothStatusCodes.ERROR_BAD_PARAMETERS));
851 
852         // Add broadcast to cache
853         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
854         mInOrderMethodProxy
855                 .verify(mMethodProxy)
856                 .periodicAdvertisingManagerRegisterSync(
857                         any(), any(), anyInt(), anyInt(), any(), any());
858 
859         // Stop searching
860         mBassClientService.stopSearchingForSources();
861 
862         // Add sync handle by add source
863         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
864         handleHandoverSupport();
865         mInOrderMethodProxy
866                 .verify(mMethodProxy)
867                 .periodicAdvertisingManagerRegisterSync(
868                         any(), any(), anyInt(), anyInt(), any(), any());
869         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
870 
871         // Sync lost without active scanning should not remove broadcast cache
872         onSyncLost();
873         checkAndDispatchTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_SYNC_LOST_TIMEOUT);
874 
875         // Add source to unsynced broadcast, causes synchronization first
876         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
877         handleHandoverSupport();
878         mInOrderMethodProxy
879                 .verify(mMethodProxy)
880                 .periodicAdvertisingManagerRegisterSync(
881                         any(), any(), anyInt(), anyInt(), any(), any());
882     }
883 
884     @Test
testNotRemovingCachedBroadcastOnFailEstablishWithoutScanning()885     public void testNotRemovingCachedBroadcastOnFailEstablishWithoutScanning()
886             throws RemoteException {
887         final BluetoothDevice device1 =
888                 mBluetoothAdapter.getRemoteLeDevice(
889                         "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
890         final BluetoothDevice device2 =
891                 mBluetoothAdapter.getRemoteLeDevice(
892                         "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM);
893         final BluetoothDevice device3 =
894                 mBluetoothAdapter.getRemoteLeDevice(
895                         "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM);
896         final BluetoothDevice device4 =
897                 mBluetoothAdapter.getRemoteLeDevice(
898                         "00:11:22:33:44:44", BluetoothDevice.ADDRESS_TYPE_RANDOM);
899         final BluetoothDevice device5 =
900                 mBluetoothAdapter.getRemoteLeDevice(
901                         "00:11:22:33:44:55", BluetoothDevice.ADDRESS_TYPE_RANDOM);
902         final int handle1 = 0;
903         final int handle2 = 1;
904         final int handle3 = 2;
905         final int handle4 = 3;
906         final int handle5 = 4;
907         final int broadcastId1 = 1111;
908         final int broadcastId2 = 2222;
909         final int broadcastId3 = 3333;
910         final int broadcastId4 = 4444;
911         final int broadcastId5 = 5555;
912 
913         prepareConnectedDeviceGroup();
914         startSearchingForSources();
915 
916         // Scan and sync 5 sources cause removing 1 synced element
917         onScanResult(device1, broadcastId1);
918         onSyncEstablished(device1, handle1);
919         onScanResult(device2, broadcastId2);
920         onSyncEstablished(device2, handle2);
921         onScanResult(device3, broadcastId3);
922         onSyncEstablished(device3, handle3);
923         onScanResult(device4, broadcastId4);
924         onSyncEstablished(device4, handle4);
925         onScanResult(device5, broadcastId5);
926         onSyncEstablished(device5, handle5);
927         mInOrderMethodProxy
928                 .verify(mMethodProxy, times(5))
929                 .periodicAdvertisingManagerRegisterSync(
930                         any(), any(), anyInt(), anyInt(), any(), any());
931 
932         BluetoothLeBroadcastMetadata.Builder builder =
933                 new BluetoothLeBroadcastMetadata.Builder()
934                         .setEncrypted(false)
935                         .setSourceDevice(device1, BluetoothDevice.ADDRESS_TYPE_RANDOM)
936                         .setSourceAdvertisingSid(TEST_ADVERTISER_SID)
937                         .setBroadcastId(broadcastId1)
938                         .setBroadcastCode(null)
939                         .setPaSyncInterval(TEST_PA_SYNC_INTERVAL)
940                         .setPresentationDelayMicros(TEST_PRESENTATION_DELAY_MS);
941         // builder expect at least one subgroup
942         builder.addSubgroup(createBroadcastSubgroup());
943         BluetoothLeBroadcastMetadata meta = builder.build();
944 
945         // Add source to unsynced broadcast, causes synchronization first
946         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
947         handleHandoverSupport();
948         mInOrderMethodProxy
949                 .verify(mMethodProxy)
950                 .periodicAdvertisingManagerRegisterSync(
951                         any(), any(), anyInt(), anyInt(), any(), any());
952 
953         // Error in syncEstablished causes sourceLost, sourceAddFailed notification
954         // and removing cache because scanning is active
955         onSyncEstablishedFailed(device1, handle1);
956         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
957         InOrder inOrderCallback = inOrder(mCallback);
958         inOrderCallback.verify(mCallback).onSourceLost(eq(broadcastId1));
959         inOrderCallback
960                 .verify(mCallback)
961                 .onSourceAddFailed(
962                         eq(mCurrentDevice),
963                         eq(meta),
964                         eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
965 
966         // Add source to not cached broadcast causes addFailed notification
967         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
968         handleHandoverSupport();
969         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
970         inOrderCallback
971                 .verify(mCallback)
972                 .onSourceAddFailed(
973                         eq(mCurrentDevice),
974                         eq(meta),
975                         eq(BluetoothStatusCodes.ERROR_BAD_PARAMETERS));
976 
977         // Scan and sync again
978         onScanResult(device1, broadcastId1);
979         mInOrderMethodProxy
980                 .verify(mMethodProxy)
981                 .periodicAdvertisingManagerRegisterSync(
982                         any(), any(), anyInt(), anyInt(), any(), any());
983         onSyncEstablished(device1, handle1);
984 
985         // Stop searching
986         mBassClientService.stopSearchingForSources();
987 
988         // Add source to unsynced broadcast, causes synchronization first
989         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
990         handleHandoverSupport();
991         mInOrderMethodProxy
992                 .verify(mMethodProxy)
993                 .periodicAdvertisingManagerRegisterSync(
994                         any(), any(), anyInt(), anyInt(), any(), any());
995 
996         // Error in syncEstablished causes sourceLost, sourceAddFailed notification
997         // and not removing cache because scanning is inactive
998         onSyncEstablishedFailed(device1, handle1);
999         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
1000         inOrderCallback.verify(mCallback).onSourceLost(eq(broadcastId1));
1001         inOrderCallback
1002                 .verify(mCallback)
1003                 .onSourceAddFailed(
1004                         eq(mCurrentDevice),
1005                         eq(meta),
1006                         eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
1007 
1008         // Add source to unsynced broadcast, causes synchronization first
1009         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
1010         handleHandoverSupport();
1011         mInOrderMethodProxy
1012                 .verify(mMethodProxy)
1013                 .periodicAdvertisingManagerRegisterSync(
1014                         any(), any(), anyInt(), anyInt(), any(), any());
1015     }
1016 
1017     @Test
testMultipleAddSourceToUnsyncedBroadcaster()1018     public void testMultipleAddSourceToUnsyncedBroadcaster() {
1019         prepareConnectedDeviceGroup();
1020         startSearchingForSources();
1021 
1022         // Scan and sync 1
1023         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
1024         mInOrderMethodProxy
1025                 .verify(mMethodProxy)
1026                 .periodicAdvertisingManagerRegisterSync(
1027                         any(), any(), anyInt(), anyInt(), any(), any());
1028         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1029 
1030         // Stop searching to unsync broadcaster
1031         mBassClientService.stopSearchingForSources();
1032 
1033         // Sink1 aAdd source to unsynced broadcast, causes synchronization first
1034         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
1035         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ false);
1036         handleHandoverSupport();
1037         mInOrderMethodProxy
1038                 .verify(mMethodProxy)
1039                 .periodicAdvertisingManagerRegisterSync(
1040                         any(), any(), anyInt(), anyInt(), any(), any());
1041 
1042         // Sink2 add source to unsynced broadcast
1043         mBassClientService.addSource(mCurrentDevice1, meta, /* isGroupOp */ false);
1044         handleHandoverSupport();
1045 
1046         // Sync established
1047         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1048         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
1049 
1050         // Both add sources should be called to state machines
1051         expect.that(mStateMachines.size()).isEqualTo(2);
1052         for (BassClientStateMachine sm : mStateMachines.values()) {
1053             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
1054             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
1055 
1056             Message msg =
1057                     messageCaptor.getAllValues().stream()
1058                             .filter(
1059                                     m ->
1060                                             (m.what == BassClientStateMachine.ADD_BCAST_SOURCE)
1061                                                     && (m.obj == meta))
1062                             .findFirst()
1063                             .orElse(null);
1064             expect.that(msg).isNotNull();
1065         }
1066 
1067         // There should be no second selectSource call
1068         mInOrderMethodProxy
1069                 .verify(mMethodProxy, never())
1070                 .periodicAdvertisingManagerRegisterSync(
1071                         any(), any(), anyInt(), anyInt(), any(), any());
1072     }
1073 
1074     @Test
testMultipleAddSourceToUnsyncedInactiveBroadcaster()1075     public void testMultipleAddSourceToUnsyncedInactiveBroadcaster() throws RemoteException {
1076         prepareConnectedDeviceGroup();
1077         startSearchingForSources();
1078 
1079         // Scan and sync 1
1080         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
1081         mInOrderMethodProxy
1082                 .verify(mMethodProxy)
1083                 .periodicAdvertisingManagerRegisterSync(
1084                         any(), any(), anyInt(), anyInt(), any(), any());
1085         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1086 
1087         // Stop searching to unsync broadcaster
1088         mBassClientService.stopSearchingForSources();
1089 
1090         // Sink1 aAdd source to unsynced broadcast, causes synchronization first
1091         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
1092         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ false);
1093         handleHandoverSupport();
1094         mInOrderMethodProxy
1095                 .verify(mMethodProxy)
1096                 .periodicAdvertisingManagerRegisterSync(
1097                         any(), any(), anyInt(), anyInt(), any(), any());
1098 
1099         // Sink2 add source to unsynced broadcast
1100         mBassClientService.addSource(mCurrentDevice1, meta, /* isGroupOp */ false);
1101         handleHandoverSupport();
1102 
1103         // Error in syncEstablished causes sourceLost, sourceAddFailed notification for both sinks
1104         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
1105         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
1106         InOrder inOrderCallback = inOrder(mCallback);
1107         inOrderCallback.verify(mCallback).onSourceLost(eq(TEST_BROADCAST_ID));
1108         inOrderCallback
1109                 .verify(mCallback)
1110                 .onSourceAddFailed(
1111                         eq(mCurrentDevice),
1112                         eq(meta),
1113                         eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
1114         inOrderCallback
1115                 .verify(mCallback)
1116                 .onSourceAddFailed(
1117                         eq(mCurrentDevice1),
1118                         eq(meta),
1119                         eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
1120 
1121         // There should be no second selectSource call
1122         mInOrderMethodProxy
1123                 .verify(mMethodProxy, never())
1124                 .periodicAdvertisingManagerRegisterSync(
1125                         any(), any(), anyInt(), anyInt(), any(), any());
1126     }
1127 
checkNoTimeout(int broadcastId, int message)1128     private void checkNoTimeout(int broadcastId, int message) {
1129         assertThat(mBassClientService.mTimeoutHandler.isStarted(broadcastId, message)).isFalse();
1130     }
1131 
checkTimeout(int broadcastId, int message)1132     private void checkTimeout(int broadcastId, int message) {
1133         assertThat(mBassClientService.mTimeoutHandler.isStarted(broadcastId, message)).isTrue();
1134     }
1135 
checkAndDispatchTimeout(int broadcastId, int message)1136     private void checkAndDispatchTimeout(int broadcastId, int message) {
1137         checkTimeout(broadcastId, message);
1138         mBassClientService.mTimeoutHandler.stop(broadcastId, message);
1139         Handler handler = mBassClientService.mTimeoutHandler.getOrCreateHandler(broadcastId);
1140         Message newMsg = handler.obtainMessage(message);
1141         handler.dispatchMessage(newMsg);
1142     }
1143 
1144     @Test
1145     @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
testStopSearchingForSources_timeoutForActiveSync()1146     public void testStopSearchingForSources_timeoutForActiveSync() {
1147         prepareConnectedDeviceGroup();
1148         startSearchingForSources();
1149 
1150         // Scan and sync 1
1151         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
1152         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1153 
1154         // Stop searching
1155         mBassClientService.stopSearchingForSources();
1156         mInOrderMethodProxy
1157                 .verify(mMethodProxy)
1158                 .periodicAdvertisingManagerUnregisterSync(any(), any());
1159 
1160         // Add source to unsynced broadcast, causes synchronization first
1161         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
1162         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
1163         handleHandoverSupport();
1164 
1165         // Source synced which cause start timeout event
1166         assertThat(mBassClientService.mHandler.hasMessages(BassClientService.MESSAGE_SYNC_TIMEOUT))
1167                 .isFalse();
1168         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1169 
1170         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
1171         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
1172                 .isEqualTo(mSourceDevice);
1173         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
1174                 .isEqualTo(TEST_BROADCAST_ID);
1175 
1176         assertThat(mBassClientService.mHandler.hasMessages(BassClientService.MESSAGE_SYNC_TIMEOUT))
1177                 .isTrue();
1178         mBassClientService.mHandler.removeMessages(BassClientService.MESSAGE_SYNC_TIMEOUT);
1179         Message newMsg =
1180                 mBassClientService.mHandler.obtainMessage(BassClientService.MESSAGE_SYNC_TIMEOUT);
1181         newMsg.arg1 = TEST_BROADCAST_ID;
1182         mBassClientService.mHandler.dispatchMessage(newMsg);
1183 
1184         // Check if unsyced
1185         mInOrderMethodProxy
1186                 .verify(mMethodProxy)
1187                 .periodicAdvertisingManagerUnregisterSync(any(), any());
1188     }
1189 
1190     @Test
1191     @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
testStopSearchingForSources_clearTimeoutForActiveSync()1192     public void testStopSearchingForSources_clearTimeoutForActiveSync() {
1193         prepareConnectedDeviceGroup();
1194         startSearchingForSources();
1195 
1196         // Scan and sync 1
1197         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
1198         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1199 
1200         // Stop searching
1201         mBassClientService.stopSearchingForSources();
1202         mInOrderMethodProxy
1203                 .verify(mMethodProxy)
1204                 .periodicAdvertisingManagerUnregisterSync(any(), any());
1205 
1206         // Add source to unsynced broadcast, causes synchronization first
1207         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
1208         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
1209         handleHandoverSupport();
1210 
1211         // Source synced which cause start timeout event
1212         assertThat(mBassClientService.mHandler.hasMessages(BassClientService.MESSAGE_SYNC_TIMEOUT))
1213                 .isFalse();
1214         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1215         assertThat(mBassClientService.mHandler.hasMessages(BassClientService.MESSAGE_SYNC_TIMEOUT))
1216                 .isTrue();
1217 
1218         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
1219         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
1220                 .isEqualTo(mSourceDevice);
1221         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
1222                 .isEqualTo(TEST_BROADCAST_ID);
1223 
1224         // Start searching again should clear timeout
1225         startSearchingForSources();
1226         assertThat(mBassClientService.mHandler.hasMessages(BassClientService.MESSAGE_SYNC_TIMEOUT))
1227                 .isFalse();
1228 
1229         mInOrderMethodProxy
1230                 .verify(mMethodProxy, never())
1231                 .periodicAdvertisingManagerUnregisterSync(any(), any());
1232         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
1233         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
1234                 .isEqualTo(mSourceDevice);
1235         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
1236                 .isEqualTo(TEST_BROADCAST_ID);
1237     }
1238 
getScanRecord(int broadcastId)1239     private static byte[] getScanRecord(int broadcastId) {
1240         return new byte[] {
1241             0x02,
1242             0x01,
1243             0x1a, // advertising flags
1244             0x05,
1245             0x02,
1246             0x52,
1247             0x18,
1248             0x0a,
1249             0x11, // 16 bit service uuids
1250             0x04,
1251             0x09,
1252             0x50,
1253             0x65,
1254             0x64, // name
1255             0x02,
1256             0x0A,
1257             (byte) 0xec, // tx power level
1258             0x05,
1259             0x30,
1260             0x54,
1261             0x65,
1262             0x73,
1263             0x74, // broadcast name: Test
1264             0x06,
1265             0x16,
1266             0x52,
1267             0x18,
1268             (byte) broadcastId,
1269             (byte) (broadcastId >> 8),
1270             (byte) (broadcastId >> 16), // service data, broadcast id
1271             0x08,
1272             0x16,
1273             0x56,
1274             0x18,
1275             0x07,
1276             0x03,
1277             0x06,
1278             0x07,
1279             0x08,
1280             // service data - public broadcast,
1281             // feature - 0x7, metadata len - 0x3, metadata - 0x6, 0x7, 0x8
1282             0x05,
1283             (byte) 0xff,
1284             (byte) 0xe0,
1285             0x00,
1286             0x02,
1287             0x15, // manufacturer specific data
1288             0x03,
1289             0x50,
1290             0x01,
1291             0x02, // an unknown data type won't cause trouble
1292         };
1293     }
1294 
generateScanResult(ScanResult result)1295     private void generateScanResult(ScanResult result) {
1296         if (Flags.leaudioBassScanWithInternalScanController()) {
1297             try {
1298                 mBassScanCallbackCaptor.getValue().onScanResult(result);
1299             } catch (RemoteException e) {
1300                 // the mocked onScanResult doesn't throw RemoteException
1301             }
1302         } else {
1303             mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result);
1304         }
1305     }
1306 
onScanResult(BluetoothDevice testDevice, int broadcastId)1307     private void onScanResult(BluetoothDevice testDevice, int broadcastId) {
1308         byte[] scanRecord = getScanRecord(broadcastId);
1309         ScanResult scanResult =
1310                 new ScanResult(
1311                         testDevice,
1312                         0,
1313                         0,
1314                         0,
1315                         0,
1316                         0,
1317                         TEST_RSSI,
1318                         0,
1319                         ScanRecord.parseFromBytes(scanRecord),
1320                         0);
1321         generateScanResult(scanResult);
1322     }
1323 
getPAScanRecord()1324     private static byte[] getPAScanRecord() {
1325         return new byte[] {
1326             (byte) 0x02,
1327             (byte) 0x01,
1328             (byte) 0x1a, // advertising flags
1329             (byte) 0x05,
1330             (byte) 0x02,
1331             (byte) 0x51,
1332             (byte) 0x18,
1333             (byte) 0x0a,
1334             (byte) 0x11, // 16 bit service uuids
1335             (byte) 0x04,
1336             (byte) 0x09,
1337             (byte) 0x50,
1338             (byte) 0x65,
1339             (byte) 0x64, // name
1340             (byte) 0x02,
1341             (byte) 0x0A,
1342             (byte) 0xec, // tx power level
1343             (byte) 0x19,
1344             (byte) 0x16,
1345             (byte) 0x51,
1346             (byte) 0x18, // service data (base data with 18 bytes)
1347             // LEVEL 1
1348             (byte) 0x01,
1349             (byte) 0x02,
1350             (byte) 0x03, // mPresentationDelay
1351             (byte) 0x01, // mNumSubGroups
1352             // LEVEL 2
1353             (byte) 0x01, // mNumSubGroups
1354             (byte) 0x00,
1355             (byte) 0x00,
1356             (byte) 0x00,
1357             (byte) 0x00,
1358             (byte) 0x00, // UNKNOWN_CODEC
1359             (byte) 0x02, // mCodecConfigLength
1360             (byte) 0x01,
1361             (byte) 'A', // mCodecConfigInfo
1362             (byte) 0x03, // mMetaDataLength
1363             (byte) 0x06,
1364             (byte) 0x07,
1365             (byte) 0x08, // mMetaData
1366             // LEVEL 3
1367             (byte) 0x04, // mIndex
1368             (byte) 0x03, // mCodecConfigLength
1369             (byte) 0x02,
1370             (byte) 'B',
1371             (byte) 'C', // mCodecConfigInfo
1372             (byte) 0x05,
1373             (byte) 0xff,
1374             (byte) 0xe0,
1375             (byte) 0x00,
1376             (byte) 0x02,
1377             (byte) 0x15, // manufacturer specific data
1378             (byte) 0x03,
1379             (byte) 0x50,
1380             (byte) 0x01,
1381             (byte) 0x02, // an unknown data type won't cause trouble
1382         };
1383     }
1384 
onPeriodicAdvertisingReport()1385     private void onPeriodicAdvertisingReport() {
1386         byte[] scanRecord = getPAScanRecord();
1387         ScanRecord record = ScanRecord.parseFromBytes(scanRecord);
1388         PeriodicAdvertisingReport report =
1389                 new PeriodicAdvertisingReport(TEST_SYNC_HANDLE, 0, 0, 0, record);
1390         BassClientService.PACallback callback = mBassClientService.new PACallback();
1391         callback.onPeriodicAdvertisingReport(report);
1392     }
1393 
onBigInfoAdvertisingReport()1394     private void onBigInfoAdvertisingReport() {
1395         BassClientService.PACallback callback = mBassClientService.new PACallback();
1396         callback.onBigInfoAdvertisingReport(TEST_SYNC_HANDLE, true);
1397     }
1398 
onSyncLost()1399     private void onSyncLost() {
1400         BassClientService.PACallback callback = mBassClientService.new PACallback();
1401         callback.onSyncLost(TEST_SYNC_HANDLE);
1402     }
1403 
onSyncEstablished(BluetoothDevice testDevice, int syncHandle)1404     private void onSyncEstablished(BluetoothDevice testDevice, int syncHandle) {
1405         BassClientService.PACallback callback = mBassClientService.new PACallback();
1406         callback.onSyncEstablished(
1407                 syncHandle, testDevice, TEST_ADVERTISER_SID, 0, 200, BluetoothGatt.GATT_SUCCESS);
1408     }
1409 
onSyncEstablishedFailed(BluetoothDevice testDevice, int syncHandle)1410     private void onSyncEstablishedFailed(BluetoothDevice testDevice, int syncHandle) {
1411         BassClientService.PACallback callback = mBassClientService.new PACallback();
1412         callback.onSyncEstablished(
1413                 syncHandle, testDevice, TEST_ADVERTISER_SID, 0, 200, BluetoothGatt.GATT_FAILURE);
1414     }
1415 
handleHandoverSupport()1416     private void handleHandoverSupport() {
1417         /* Unicast finished streaming */
1418         mBassClientService.handleUnicastSourceStreamStatusChange(
1419                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
1420     }
1421 
verifyAddSourceForGroup(BluetoothLeBroadcastMetadata meta)1422     private void verifyAddSourceForGroup(BluetoothLeBroadcastMetadata meta) {
1423         // Add broadcast source
1424         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
1425 
1426         /* In case if device supports handover, Source stream status needs to be updated */
1427         handleHandoverSupport();
1428 
1429         // Verify all group members getting ADD_BCAST_SOURCE message
1430         assertThat(mStateMachines).hasSize(2);
1431         for (BassClientStateMachine sm : mStateMachines.values()) {
1432             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
1433             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
1434 
1435             Message msg =
1436                     messageCaptor.getAllValues().stream()
1437                             .filter(
1438                                     m ->
1439                                             (m.what == BassClientStateMachine.ADD_BCAST_SOURCE)
1440                                                     && (m.obj == meta))
1441                             .findFirst()
1442                             .orElse(null);
1443             assertThat(msg).isNotNull();
1444             clearInvocations(sm);
1445         }
1446     }
1447 
injectRemoteSourceState( BassClientStateMachine sm, BluetoothLeBroadcastMetadata meta, int sourceId, int paSynState, int encryptionState, byte[] badCode, long bisSyncState)1448     private static BluetoothLeBroadcastReceiveState injectRemoteSourceState(
1449             BassClientStateMachine sm,
1450             BluetoothLeBroadcastMetadata meta,
1451             int sourceId,
1452             int paSynState,
1453             int encryptionState,
1454             byte[] badCode,
1455             long bisSyncState) {
1456         BluetoothLeBroadcastReceiveState recvState =
1457                 new BluetoothLeBroadcastReceiveState(
1458                         sourceId,
1459                         meta.getSourceAddressType(),
1460                         meta.getSourceDevice(),
1461                         meta.getSourceAdvertisingSid(),
1462                         meta.getBroadcastId(),
1463                         paSynState,
1464                         encryptionState,
1465                         badCode,
1466                         meta.getSubgroups().size(),
1467                         // Bis sync states
1468                         meta.getSubgroups().stream()
1469                                 .map(e -> bisSyncState)
1470                                 .collect(Collectors.toList()),
1471                         meta.getSubgroups().stream()
1472                                 .map(e -> e.getContentMetadata())
1473                                 .collect(Collectors.toList()));
1474         doReturn(meta).when(sm).getCurrentBroadcastMetadata(eq(sourceId));
1475 
1476         List<BluetoothLeBroadcastReceiveState> stateList = sm.getAllSources();
1477         if (stateList == null) {
1478             stateList = new ArrayList<BluetoothLeBroadcastReceiveState>();
1479         } else {
1480             stateList.removeIf(e -> e.getSourceId() == sourceId);
1481         }
1482         stateList.add(recvState);
1483         doReturn(stateList).when(sm).getAllSources();
1484 
1485         return recvState;
1486     }
1487 
injectRemoteSourceStateSourceAdded( BassClientStateMachine sm, BluetoothLeBroadcastMetadata meta, int sourceId, int paSynState, int encryptionState, byte[] badCode)1488     private BluetoothLeBroadcastReceiveState injectRemoteSourceStateSourceAdded(
1489             BassClientStateMachine sm,
1490             BluetoothLeBroadcastMetadata meta,
1491             int sourceId,
1492             int paSynState,
1493             int encryptionState,
1494             byte[] badCode) {
1495         BluetoothLeBroadcastReceiveState recvState =
1496                 injectRemoteSourceState(
1497                         sm,
1498                         meta,
1499                         sourceId,
1500                         paSynState,
1501                         encryptionState,
1502                         badCode,
1503                         (long) 0x00000000);
1504 
1505         mBassClientService
1506                 .getCallbacks()
1507                 .notifySourceAdded(
1508                         sm.getDevice(), recvState, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
1509         mBassClientService
1510                 .getCallbacks()
1511                 .notifyReceiveStateChanged(sm.getDevice(), recvState.getSourceId(), recvState);
1512         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
1513 
1514         return recvState;
1515     }
1516 
injectRemoteSourceStateChanged( BassClientStateMachine sm, BluetoothLeBroadcastMetadata meta, int sourceId, int paSynState, int encryptionState, byte[] badCode, long bisSyncState)1517     private BluetoothLeBroadcastReceiveState injectRemoteSourceStateChanged(
1518             BassClientStateMachine sm,
1519             BluetoothLeBroadcastMetadata meta,
1520             int sourceId,
1521             int paSynState,
1522             int encryptionState,
1523             byte[] badCode,
1524             long bisSyncState) {
1525         BluetoothLeBroadcastReceiveState recvState =
1526                 injectRemoteSourceState(
1527                         sm, meta, sourceId, paSynState, encryptionState, badCode, bisSyncState);
1528 
1529         mBassClientService
1530                 .getCallbacks()
1531                 .notifyReceiveStateChanged(sm.getDevice(), recvState.getSourceId(), recvState);
1532         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
1533 
1534         return recvState;
1535     }
1536 
injectRemoteSourceStateChanged( BassClientStateMachine sm, BluetoothLeBroadcastMetadata meta, boolean isPaSynced, boolean isBisSynced)1537     private void injectRemoteSourceStateChanged(
1538             BassClientStateMachine sm,
1539             BluetoothLeBroadcastMetadata meta,
1540             boolean isPaSynced,
1541             boolean isBisSynced) {
1542         // Update receiver state
1543         if (sm.getDevice().equals(mCurrentDevice)) {
1544             injectRemoteSourceStateChanged(
1545                     sm,
1546                     meta,
1547                     TEST_SOURCE_ID,
1548                     isPaSynced
1549                             ? BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED
1550                             : BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
1551                     meta.isEncrypted()
1552                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1553                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1554                     null,
1555                     isBisSynced ? (long) 0x00000001 : (long) 0x00000000);
1556         } else if (sm.getDevice().equals(mCurrentDevice1)) {
1557             injectRemoteSourceStateChanged(
1558                     sm,
1559                     meta,
1560                     TEST_SOURCE_ID + 1,
1561                     isPaSynced
1562                             ? BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED
1563                             : BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
1564                     meta.isEncrypted()
1565                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1566                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1567                     null,
1568                     isBisSynced ? (long) 0x00000002 : (long) 0x00000000);
1569         }
1570     }
1571 
injectRemoteSourceStateChanged( BluetoothLeBroadcastMetadata meta, boolean isPaSynced, boolean isBisSynced)1572     private void injectRemoteSourceStateChanged(
1573             BluetoothLeBroadcastMetadata meta, boolean isPaSynced, boolean isBisSynced) {
1574         for (BassClientStateMachine sm : mStateMachines.values()) {
1575             injectRemoteSourceStateChanged(sm, meta, isPaSynced, isBisSynced);
1576         }
1577     }
1578 
injectRemoteSourceStateChanged( BluetoothLeBroadcastMetadata meta, int paSynState, boolean isBisSynced)1579     private void injectRemoteSourceStateChanged(
1580             BluetoothLeBroadcastMetadata meta, int paSynState, boolean isBisSynced) {
1581         for (BassClientStateMachine sm : mStateMachines.values()) {
1582             // Update receiver state
1583             if (sm.getDevice().equals(mCurrentDevice)) {
1584                 injectRemoteSourceStateChanged(
1585                         sm,
1586                         meta,
1587                         TEST_SOURCE_ID,
1588                         paSynState,
1589                         meta.isEncrypted()
1590                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1591                                 : BluetoothLeBroadcastReceiveState
1592                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1593                         null,
1594                         isBisSynced ? (long) 0x00000001 : (long) 0x00000000);
1595             } else if (sm.getDevice().equals(mCurrentDevice1)) {
1596                 injectRemoteSourceStateChanged(
1597                         sm,
1598                         meta,
1599                         TEST_SOURCE_ID + 1,
1600                         paSynState,
1601                         meta.isEncrypted()
1602                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1603                                 : BluetoothLeBroadcastReceiveState
1604                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1605                         null,
1606                         isBisSynced ? (long) 0x00000002 : (long) 0x00000000);
1607             }
1608         }
1609     }
1610 
injectRemoteSourceStateRemoval(BassClientStateMachine sm, int sourceId)1611     private void injectRemoteSourceStateRemoval(BassClientStateMachine sm, int sourceId) {
1612         List<BluetoothLeBroadcastReceiveState> stateList = sm.getAllSources();
1613         if (stateList == null) {
1614             stateList = new ArrayList<BluetoothLeBroadcastReceiveState>();
1615         }
1616         stateList.replaceAll(
1617                 e -> {
1618                     if (e.getSourceId() != sourceId) return e;
1619                     return new BluetoothLeBroadcastReceiveState(
1620                             sourceId,
1621                             BluetoothDevice.ADDRESS_TYPE_PUBLIC,
1622                             mBluetoothAdapter.getRemoteLeDevice(
1623                                     "00:00:00:00:00:00", BluetoothDevice.ADDRESS_TYPE_PUBLIC),
1624                             0,
1625                             0,
1626                             BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
1627                             BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1628                             null,
1629                             0,
1630                             Arrays.asList(new Long[0]),
1631                             Arrays.asList(new BluetoothLeAudioContentMetadata[0]));
1632                 });
1633         doReturn(stateList).when(sm).getAllSources();
1634 
1635         Optional<BluetoothLeBroadcastReceiveState> receiveState =
1636                 stateList.stream().filter(e -> e.getSourceId() == sourceId).findFirst();
1637 
1638         mBassClientService
1639                 .getCallbacks()
1640                 .notifySourceRemoved(
1641                         sm.getDevice(), sourceId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
1642         mBassClientService
1643                 .getCallbacks()
1644                 .notifyReceiveStateChanged(sm.getDevice(), sourceId, receiveState.get());
1645         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
1646     }
1647 
prepareRemoteSourceState( BluetoothLeBroadcastMetadata meta, boolean isPaSynced, boolean isBisSynced)1648     private void prepareRemoteSourceState(
1649             BluetoothLeBroadcastMetadata meta, boolean isPaSynced, boolean isBisSynced) {
1650         for (BassClientStateMachine sm : mStateMachines.values()) {
1651             if (sm.getDevice().equals(mCurrentDevice)) {
1652                 injectRemoteSourceStateSourceAdded(
1653                         sm,
1654                         meta,
1655                         TEST_SOURCE_ID,
1656                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
1657                         meta.isEncrypted()
1658                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1659                                 : BluetoothLeBroadcastReceiveState
1660                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1661                         null);
1662             } else if (sm.getDevice().equals(mCurrentDevice1)) {
1663                 injectRemoteSourceStateSourceAdded(
1664                         sm,
1665                         meta,
1666                         TEST_SOURCE_ID + 1,
1667                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
1668                         meta.isEncrypted()
1669                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1670                                 : BluetoothLeBroadcastReceiveState
1671                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1672                         null);
1673             }
1674         }
1675         injectRemoteSourceStateChanged(meta, isPaSynced, isBisSynced);
1676     }
1677 
1678     /**
1679      * Test whether service.addSource() does send proper messages to all the state machines within
1680      * the Csip coordinated group
1681      */
1682     @Test
testAddSourceForGroup()1683     public void testAddSourceForGroup() {
1684         prepareConnectedDeviceGroup();
1685         startSearchingForSources();
1686         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
1687         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1688         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
1689         verifyAddSourceForGroup(meta);
1690     }
1691 
1692     /** Test whether service.addSource() source id can be propagated through callback correctly */
1693     @Test
testAddSourceCallbackForGroup()1694     public void testAddSourceCallbackForGroup() throws RemoteException {
1695         prepareConnectedDeviceGroup();
1696         startSearchingForSources();
1697         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
1698         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1699         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
1700         verifyAddSourceForGroup(meta);
1701         for (BassClientStateMachine sm : mStateMachines.values()) {
1702             if (sm.getDevice().equals(mCurrentDevice)) {
1703                 injectRemoteSourceStateSourceAdded(
1704                         sm,
1705                         meta,
1706                         TEST_SOURCE_ID,
1707                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
1708                         meta.isEncrypted()
1709                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1710                                 : BluetoothLeBroadcastReceiveState
1711                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1712                         null);
1713                 // verify source id
1714                 verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce())
1715                         .onSourceAdded(
1716                                 eq(mCurrentDevice),
1717                                 eq(TEST_SOURCE_ID),
1718                                 eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST));
1719             } else if (sm.getDevice().equals(mCurrentDevice1)) {
1720                 injectRemoteSourceStateSourceAdded(
1721                         sm,
1722                         meta,
1723                         TEST_SOURCE_ID + 1,
1724                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
1725                         meta.isEncrypted()
1726                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1727                                 : BluetoothLeBroadcastReceiveState
1728                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1729                         null);
1730                 // verify source id
1731                 verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce())
1732                         .onSourceAdded(
1733                                 eq(mCurrentDevice1),
1734                                 eq(TEST_SOURCE_ID + 1),
1735                                 eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST));
1736             }
1737         }
1738     }
1739 
1740     /**
1741      * Test whether service.modifySource() does send proper messages to all the state machines
1742      * within the Csip coordinated group
1743      */
1744     @Test
testModifySourceForGroup()1745     public void testModifySourceForGroup() {
1746         prepareConnectedDeviceGroup();
1747         startSearchingForSources();
1748         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
1749         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1750         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
1751         verifyAddSourceForGroup(meta);
1752         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ false);
1753 
1754         // Update broadcast source using other member of the same group
1755         BluetoothLeBroadcastMetadata metaUpdate =
1756                 new BluetoothLeBroadcastMetadata.Builder(meta)
1757                         .setBroadcastId(TEST_BROADCAST_ID + 1)
1758                         .build();
1759         mBassClientService.modifySource(mCurrentDevice1, TEST_SOURCE_ID + 1, metaUpdate);
1760 
1761         // Verify all group members getting UPDATE_BCAST_SOURCE message on proper sources
1762         expect.that(mStateMachines.size()).isEqualTo(2);
1763         for (BassClientStateMachine sm : mStateMachines.values()) {
1764             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
1765             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
1766 
1767             Optional<Message> msg =
1768                     messageCaptor.getAllValues().stream()
1769                             .filter(m -> m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
1770                             .findFirst();
1771             expect.that(msg.isPresent()).isEqualTo(true);
1772             expect.that(msg.get().obj).isEqualTo(metaUpdate);
1773 
1774             // Verify using the right sourceId on each device
1775             if (sm.getDevice().equals(mCurrentDevice)) {
1776                 expect.that(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
1777             } else if (sm.getDevice().equals(mCurrentDevice1)) {
1778                 expect.that(msg.get().arg1).isEqualTo(TEST_SOURCE_ID + 1);
1779             }
1780         }
1781     }
1782 
1783     /**
1784      * Test whether service.removeSource() does send proper messages to all the state machines
1785      * within the Csip coordinated group
1786      */
1787     @Test
testRemoveSourceForGroup()1788     public void testRemoveSourceForGroup() {
1789         prepareConnectedDeviceGroup();
1790         startSearchingForSources();
1791         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
1792         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1793         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
1794         verifyAddSourceForGroup(meta);
1795         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ false);
1796 
1797         // Remove broadcast source using other member of the same group
1798         mBassClientService.removeSource(mCurrentDevice1, TEST_SOURCE_ID + 1);
1799 
1800         // Verify all group members getting REMOVE_BCAST_SOURCE message
1801         expect.that(mStateMachines.size()).isEqualTo(2);
1802         for (BassClientStateMachine sm : mStateMachines.values()) {
1803             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
1804             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
1805 
1806             Optional<Message> msg =
1807                     messageCaptor.getAllValues().stream()
1808                             .filter(m -> m.what == BassClientStateMachine.REMOVE_BCAST_SOURCE)
1809                             .findFirst();
1810             expect.that(msg.isPresent()).isEqualTo(true);
1811 
1812             // Verify using the right sourceId on each device
1813             if (sm.getDevice().equals(mCurrentDevice)) {
1814                 expect.that(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
1815             } else if (sm.getDevice().equals(mCurrentDevice1)) {
1816                 expect.that(msg.get().arg1).isEqualTo(TEST_SOURCE_ID + 1);
1817             }
1818         }
1819     }
1820 
1821     /**
1822      * Test whether service.removeSource() does send modify source to all the state machines if
1823      * either PA or BIS is synced
1824      */
1825     @Test
testRemoveSourceForGroupAndTriggerModifySource()1826     public void testRemoveSourceForGroupAndTriggerModifySource() {
1827         prepareConnectedDeviceGroup();
1828         startSearchingForSources();
1829         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
1830         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1831         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
1832         verifyAddSourceForGroup(meta);
1833         for (BassClientStateMachine sm : mStateMachines.values()) {
1834             injectRemoteSourceStateSourceAdded(
1835                     sm,
1836                     meta,
1837                     TEST_SOURCE_ID,
1838                     BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
1839                     meta.isEncrypted()
1840                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1841                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1842                     null);
1843             doReturn(meta).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
1844             doReturn(true).when(sm).isSyncedToTheSource(eq(TEST_SOURCE_ID));
1845         }
1846 
1847         // Remove broadcast source
1848         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
1849 
1850         // Verify all group members getting UPDATE_BCAST_SOURCE message
1851         // because PA state is synced
1852         assertThat(mStateMachines).hasSize(2);
1853         for (BassClientStateMachine sm : mStateMachines.values()) {
1854             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
1855             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
1856 
1857             Optional<Message> msg =
1858                     messageCaptor.getAllValues().stream()
1859                             .filter(m -> m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
1860                             .findFirst();
1861             assertThat(msg.isPresent()).isEqualTo(true);
1862 
1863             // Verify using the right sourceId on each device
1864             assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
1865         }
1866 
1867         for (BassClientStateMachine sm : mStateMachines.values()) {
1868             // Update receiver state
1869             injectRemoteSourceStateChanged(
1870                     sm,
1871                     meta,
1872                     TEST_SOURCE_ID,
1873                     BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
1874                     meta.isEncrypted()
1875                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1876                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1877                     null,
1878                     (long) 0x00000001);
1879         }
1880 
1881         // Remove broadcast source
1882         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
1883 
1884         // Verify all group members getting UPDATE_BCAST_SOURCE message if
1885         // bis sync state is non-zero and pa sync state is not synced
1886         assertThat(mStateMachines).hasSize(2);
1887         for (BassClientStateMachine sm : mStateMachines.values()) {
1888             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
1889             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
1890 
1891             Optional<Message> msg =
1892                     messageCaptor.getAllValues().stream()
1893                             .filter(m -> m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
1894                             .findFirst();
1895             assertThat(msg.isPresent()).isEqualTo(true);
1896 
1897             // Verify using the right sourceId on each device
1898             assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
1899         }
1900 
1901         for (BassClientStateMachine sm : mStateMachines.values()) {
1902             injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID);
1903         }
1904 
1905         verify(mLeAudioService).activeBroadcastAssistantNotification(eq(false));
1906     }
1907 
verifyRemoveMessageAndInjectSourceRemoval()1908     private void verifyRemoveMessageAndInjectSourceRemoval() {
1909         for (BassClientStateMachine sm : mStateMachines.values()) {
1910             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
1911             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
1912 
1913             Optional<Message> msg =
1914                     messageCaptor.getAllValues().stream()
1915                             .filter(m -> m.what == BassClientStateMachine.REMOVE_BCAST_SOURCE)
1916                             .findFirst();
1917             assertThat(msg.isPresent()).isEqualTo(true);
1918 
1919             if (sm.getDevice().equals(mCurrentDevice)) {
1920                 assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
1921                 injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID);
1922             } else if (sm.getDevice().equals(mCurrentDevice1)) {
1923                 assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID + 1);
1924                 injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID + 1);
1925             }
1926         }
1927     }
1928 
verifyModifyMessageAndInjectSourceModfified()1929     private void verifyModifyMessageAndInjectSourceModfified() {
1930         for (BassClientStateMachine sm : mStateMachines.values()) {
1931             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
1932             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
1933 
1934             Optional<Message> msg =
1935                     messageCaptor.getAllValues().stream()
1936                             .filter(m -> m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
1937                             .findFirst();
1938             assertThat(msg.isPresent()).isEqualTo(true);
1939 
1940             if (sm.getDevice().equals(mCurrentDevice)) {
1941                 assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
1942                 injectRemoteSourceStateChanged(
1943                         sm, createBroadcastMetadata(TEST_BROADCAST_ID), false, false);
1944             } else if (sm.getDevice().equals(mCurrentDevice1)) {
1945                 assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID + 1);
1946                 injectRemoteSourceStateChanged(
1947                         sm, createBroadcastMetadata(TEST_BROADCAST_ID), false, false);
1948             }
1949         }
1950     }
1951 
1952     /**
1953      * Test whether service.removeSource() does send modify source if source is from remote receive
1954      * state. In this case, assistant should be able to remove source which was not managed by BASS
1955      * service (external manager/no source metadata)
1956      */
1957     @Test
testRemoveSourceForGroupAndTriggerModifySourceWithoutMetadata()1958     public void testRemoveSourceForGroupAndTriggerModifySourceWithoutMetadata() {
1959         prepareConnectedDeviceGroup();
1960         startSearchingForSources();
1961         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
1962         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
1963         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
1964 
1965         for (BassClientStateMachine sm : mStateMachines.values()) {
1966             injectRemoteSourceStateSourceAdded(
1967                     sm,
1968                     meta,
1969                     TEST_SOURCE_ID,
1970                     BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
1971                     meta.isEncrypted()
1972                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
1973                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
1974                     null);
1975             // no current broadcast metadata for external broadcast source
1976             doReturn(null).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
1977             doReturn(true).when(sm).isSyncedToTheSource(eq(TEST_SOURCE_ID));
1978         }
1979 
1980         for (BassClientStateMachine sm : mStateMachines.values()) {
1981             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
1982             mBassClientService.removeSource(sm.getDevice(), TEST_SOURCE_ID);
1983             // Verify device get update source
1984             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
1985 
1986             Optional<Message> msg =
1987                     messageCaptor.getAllValues().stream()
1988                             .filter(m -> m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
1989                             .findFirst();
1990             assertThat(msg.isPresent()).isEqualTo(true);
1991 
1992             assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID);
1993             assertThat(msg.get().arg2).isEqualTo(BassConstants.PA_SYNC_DO_NOT_SYNC);
1994             // Verify metadata is null
1995             assertThat(msg.get().obj).isNull();
1996         }
1997 
1998         for (BassClientStateMachine sm : mStateMachines.values()) {
1999             injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID);
2000         }
2001     }
2002 
2003     @Test
2004     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_API_GET_LOCAL_METADATA)
testGetSourceMetadata()2005     public void testGetSourceMetadata() {
2006         prepareConnectedDeviceGroup();
2007         startSearchingForSources();
2008         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
2009         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
2010         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
2011 
2012         for (BassClientStateMachine sm : mStateMachines.values()) {
2013             injectRemoteSourceStateSourceAdded(
2014                     sm,
2015                     meta,
2016                     TEST_SOURCE_ID,
2017                     BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
2018                     meta.isEncrypted()
2019                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
2020                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
2021                     null);
2022             doReturn(null).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
2023             assertThat(mBassClientService.getSourceMetadata(sm.getDevice(), TEST_SOURCE_ID))
2024                     .isNull();
2025 
2026             doReturn(meta).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
2027             doReturn(true).when(sm).isSyncedToTheSource(eq(TEST_SOURCE_ID));
2028             assertThat(mBassClientService.getSourceMetadata(sm.getDevice(), TEST_SOURCE_ID))
2029                     .isEqualTo(meta);
2030         }
2031 
2032         for (BassClientStateMachine sm : mStateMachines.values()) {
2033             injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID);
2034         }
2035     }
2036 
2037     /** Test whether the group operation flag is set on addSource() and removed on removeSource */
2038     @Test
testGroupStickyFlagSetUnset()2039     public void testGroupStickyFlagSetUnset() {
2040         prepareConnectedDeviceGroup();
2041         startSearchingForSources();
2042         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
2043         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
2044         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
2045         verifyAddSourceForGroup(meta);
2046         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ false);
2047 
2048         // Remove broadcast source
2049         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
2050         verifyRemoveMessageAndInjectSourceRemoval();
2051 
2052         // Update broadcast source
2053         BluetoothLeBroadcastMetadata metaUpdate = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
2054         mBassClientService.modifySource(mCurrentDevice, TEST_SOURCE_ID, metaUpdate);
2055 
2056         ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
2057         Optional<Message> msg;
2058 
2059         // Verify that one device got the message...
2060         verify(mStateMachines.get(mCurrentDevice), atLeast(1)).sendMessage(messageCaptor.capture());
2061         msg =
2062                 messageCaptor.getAllValues().stream()
2063                         .filter(m -> m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
2064                         .findFirst();
2065         expect.that(msg.isPresent()).isTrue();
2066         expect.that(msg.orElse(null)).isNotNull();
2067 
2068         // ... but not the other one, since the sticky group flag should have been removed
2069         messageCaptor = ArgumentCaptor.forClass(Message.class);
2070         verify(mStateMachines.get(mCurrentDevice1), atLeast(1))
2071                 .sendMessage(messageCaptor.capture());
2072         msg =
2073                 messageCaptor.getAllValues().stream()
2074                         .filter(m -> m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
2075                         .findFirst();
2076         expect.that(msg.isPresent()).isFalse();
2077     }
2078 
2079     /** Test switch source will be triggered if adding new source when sink has source */
2080     @Test
testSwitchSourceAfterSourceAdded()2081     public void testSwitchSourceAfterSourceAdded() {
2082         prepareConnectedDeviceGroup();
2083         startSearchingForSources();
2084         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
2085         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
2086         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
2087         BluetoothLeBroadcastMetadata newMeta = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
2088         verifyAddSourceForGroup(meta);
2089         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ true);
2090 
2091         // Add another new broadcast source
2092         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
2093         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
2094         mBassClientService.addSource(mCurrentDevice, newMeta, /* isGroupOp */ true);
2095 
2096         // Verify all group members getting SWITCH_BCAST_SOURCE message and first source got
2097         // selected to remove
2098         expect.that(mStateMachines.size()).isEqualTo(2);
2099         for (BassClientStateMachine sm : mStateMachines.values()) {
2100             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
2101             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
2102             if (sm.getDevice().equals(mCurrentDevice)) {
2103                 Optional<Message> msg =
2104                         messageCaptor.getAllValues().stream()
2105                                 .filter(
2106                                         m ->
2107                                                 (m.what
2108                                                                 == BassClientStateMachine
2109                                                                         .SWITCH_BCAST_SOURCE)
2110                                                         && (m.obj == newMeta)
2111                                                         && (m.arg1 == TEST_SOURCE_ID))
2112                                 .findFirst();
2113                 expect.that(msg.isPresent()).isTrue();
2114                 expect.that(msg.orElse(null)).isNotNull();
2115             } else if (sm.getDevice().equals(mCurrentDevice1)) {
2116                 Optional<Message> msg =
2117                         messageCaptor.getAllValues().stream()
2118                                 .filter(
2119                                         m ->
2120                                                 (m.what
2121                                                                 == BassClientStateMachine
2122                                                                         .SWITCH_BCAST_SOURCE)
2123                                                         && (m.obj == newMeta)
2124                                                         && (m.arg1 == TEST_SOURCE_ID + 1))
2125                                 .findFirst();
2126                 expect.that(msg.isPresent()).isTrue();
2127                 expect.that(msg.orElse(null)).isNotNull();
2128             } else {
2129                 throw new AssertionError("Unexpected device");
2130             }
2131         }
2132     }
2133 
2134     @Test
testSecondAddSourceWithCapacityGreaterThanOne()2135     public void testSecondAddSourceWithCapacityGreaterThanOne() {
2136         prepareConnectedDeviceGroup();
2137 
2138         // Set maximum source capacity to 2
2139         for (BassClientStateMachine sm : mStateMachines.values()) {
2140             doReturn(2).when(sm).getMaximumSourceCapacity();
2141         }
2142 
2143         startSearchingForSources();
2144         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
2145         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
2146         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
2147         verifyAddSourceForGroup(meta);
2148         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ true);
2149 
2150         // Add another new broadcast source
2151         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
2152         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
2153         BluetoothLeBroadcastMetadata newMeta = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
2154         verifyAddSourceForGroup(newMeta);
2155     }
2156 
2157     /**
2158      * Test that after multiple calls to service.addSource() with a group operation flag set, there
2159      * are two call to service.removeSource() needed to clear the flag
2160      */
2161     @Test
testAddRemoveMultipleSourcesForGroup()2162     public void testAddRemoveMultipleSourcesForGroup() {
2163         prepareConnectedDeviceGroup();
2164         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
2165 
2166         // Add more room for the source broadcasts
2167         for (BassClientStateMachine sm : mStateMachines.values()) {
2168             if (sm.getDevice().equals(mCurrentDevice)) {
2169                 injectRemoteSourceStateSourceAdded(
2170                         sm,
2171                         meta,
2172                         TEST_SOURCE_ID + 1,
2173                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
2174                         meta.isEncrypted()
2175                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
2176                                 : BluetoothLeBroadcastReceiveState
2177                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
2178                         null);
2179                 injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID + 1);
2180             } else if (sm.getDevice().equals(mCurrentDevice1)) {
2181                 injectRemoteSourceStateSourceAdded(
2182                         sm,
2183                         meta,
2184                         TEST_SOURCE_ID,
2185                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
2186                         meta.isEncrypted()
2187                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
2188                                 : BluetoothLeBroadcastReceiveState
2189                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
2190                         null);
2191                 injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID);
2192             }
2193         }
2194 
2195         startSearchingForSources();
2196         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
2197         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
2198         verifyAddSourceForGroup(meta);
2199         assertThat(mStateMachines).hasSize(2);
2200         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ true);
2201 
2202         // Add another broadcast source
2203         BluetoothLeBroadcastMetadata meta1 =
2204                 new BluetoothLeBroadcastMetadata.Builder(meta)
2205                         .setBroadcastId(TEST_BROADCAST_ID + 1)
2206                         .build();
2207         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
2208         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
2209         verifyAddSourceForGroup(meta1);
2210         assertThat(mStateMachines).hasSize(2);
2211         for (BassClientStateMachine sm : mStateMachines.values()) {
2212             if (sm.getDevice().equals(mCurrentDevice)) {
2213                 injectRemoteSourceStateSourceAdded(
2214                         sm,
2215                         meta1,
2216                         TEST_SOURCE_ID + 2,
2217                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
2218                         meta1.isEncrypted()
2219                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
2220                                 : BluetoothLeBroadcastReceiveState
2221                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
2222                         null);
2223             } else if (sm.getDevice().equals(mCurrentDevice1)) {
2224                 injectRemoteSourceStateSourceAdded(
2225                         sm,
2226                         meta1,
2227                         TEST_SOURCE_ID + 3,
2228                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
2229                         meta1.isEncrypted()
2230                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
2231                                 : BluetoothLeBroadcastReceiveState
2232                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
2233                         null);
2234             } else {
2235                 throw new AssertionError("Unexpected device");
2236             }
2237         }
2238 
2239         // Remove the first broadcast source
2240         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
2241         assertThat(mStateMachines).hasSize(2);
2242         verifyRemoveMessageAndInjectSourceRemoval();
2243 
2244         // Modify the second one and verify all group members getting UPDATE_BCAST_SOURCE
2245         BluetoothLeBroadcastMetadata metaUpdate = createBroadcastMetadata(TEST_BROADCAST_ID + 3);
2246         mBassClientService.modifySource(mCurrentDevice1, TEST_SOURCE_ID + 3, metaUpdate);
2247         assertThat(mStateMachines).hasSize(2);
2248         for (BassClientStateMachine sm : mStateMachines.values()) {
2249             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
2250             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
2251 
2252             Optional<Message> msg =
2253                     messageCaptor.getAllValues().stream()
2254                             .filter(m -> m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
2255                             .findFirst();
2256             assertThat(msg.isPresent()).isEqualTo(true);
2257             assertThat(msg.get().obj).isEqualTo(metaUpdate);
2258 
2259             // Verify using the right sourceId on each device
2260             if (sm.getDevice().equals(mCurrentDevice)) {
2261                 assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID + 2);
2262             } else if (sm.getDevice().equals(mCurrentDevice1)) {
2263                 assertThat(msg.get().arg1).isEqualTo(TEST_SOURCE_ID + 3);
2264             } else {
2265                 throw new AssertionError("Unexpected device");
2266             }
2267         }
2268 
2269         // Remove the second broadcast source and verify all group members getting
2270         // REMOVE_BCAST_SOURCE message for the second source
2271         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID + 2);
2272         assertThat(mStateMachines).hasSize(2);
2273         for (BassClientStateMachine sm : mStateMachines.values()) {
2274             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
2275             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
2276 
2277             if (sm.getDevice().equals(mCurrentDevice)) {
2278                 Optional<Message> msg =
2279                         messageCaptor.getAllValues().stream()
2280                                 .filter(
2281                                         m ->
2282                                                 (m.what
2283                                                                 == BassClientStateMachine
2284                                                                         .REMOVE_BCAST_SOURCE)
2285                                                         && (m.arg1 == TEST_SOURCE_ID + 2))
2286                                 .findFirst();
2287                 assertThat(msg.isPresent()).isEqualTo(true);
2288                 injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID + 2);
2289             } else if (sm.getDevice().equals(mCurrentDevice1)) {
2290                 Optional<Message> msg =
2291                         messageCaptor.getAllValues().stream()
2292                                 .filter(
2293                                         m ->
2294                                                 (m.what
2295                                                                 == BassClientStateMachine
2296                                                                         .REMOVE_BCAST_SOURCE)
2297                                                         && (m.arg1 == TEST_SOURCE_ID + 3))
2298                                 .findFirst();
2299                 assertThat(msg.isPresent()).isEqualTo(true);
2300                 injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID + 3);
2301             } else {
2302                 throw new AssertionError("Unexpected device");
2303             }
2304         }
2305 
2306         // Fake the autonomous source change - or other client setting the source
2307         for (BassClientStateMachine sm : mStateMachines.values()) {
2308             clearInvocations(sm);
2309 
2310             BluetoothLeBroadcastMetadata metaOther =
2311                     createBroadcastMetadata(TEST_BROADCAST_ID + 20);
2312             injectRemoteSourceStateSourceAdded(
2313                     sm,
2314                     metaOther,
2315                     TEST_SOURCE_ID + 20,
2316                     BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
2317                     meta.isEncrypted()
2318                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
2319                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
2320                     null);
2321         }
2322 
2323         // Modify this source and verify it is not group managed
2324         BluetoothLeBroadcastMetadata metaUpdate2 = createBroadcastMetadata(TEST_BROADCAST_ID + 30);
2325         mBassClientService.modifySource(mCurrentDevice1, TEST_SOURCE_ID + 20, metaUpdate2);
2326         for (BassClientStateMachine sm : mStateMachines.values()) {
2327             if (sm.getDevice().equals(mCurrentDevice)) {
2328                 verify(sm, never()).sendMessage(any());
2329             } else if (sm.getDevice().equals(mCurrentDevice1)) {
2330                 ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
2331                 verify(sm).sendMessage(messageCaptor.capture());
2332                 List<Message> msgs =
2333                         messageCaptor.getAllValues().stream()
2334                                 .filter(
2335                                         m ->
2336                                                 (m.what
2337                                                                 == BassClientStateMachine
2338                                                                         .UPDATE_BCAST_SOURCE)
2339                                                         && (m.arg1 == TEST_SOURCE_ID + 20))
2340                                 .collect(Collectors.toList());
2341                 assertThat(msgs).hasSize(1);
2342             } else {
2343                 throw new AssertionError("Unexpected device");
2344             }
2345         }
2346     }
2347 
2348     @Test
testInvalidRequestForGroup()2349     public void testInvalidRequestForGroup() throws RemoteException {
2350         // Prepare the initial state
2351         prepareConnectedDeviceGroup();
2352 
2353         // Verify errors are reported for the entire group
2354         mBassClientService.addSource(mCurrentDevice1, null, /* isGroupOp */ true);
2355         assertThat(mStateMachines).hasSize(2);
2356         for (BassClientStateMachine sm : mStateMachines.values()) {
2357             verify(sm, never()).sendMessage(any());
2358         }
2359         startSearchingForSources();
2360         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
2361         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
2362         // Prepare valid source for group
2363         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
2364         verifyAddSourceForGroup(meta);
2365         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ false);
2366 
2367         // Verify errors are reported for the entire group
2368         mBassClientService.modifySource(mCurrentDevice, TEST_SOURCE_ID, null);
2369         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
2370         assertThat(mStateMachines).hasSize(2);
2371         for (BassClientStateMachine sm : mStateMachines.values()) {
2372             if (sm.getDevice().equals(mCurrentDevice)) {
2373                 verify(mCallback)
2374                         .onSourceModifyFailed(
2375                                 eq(sm.getDevice()),
2376                                 eq(TEST_SOURCE_ID),
2377                                 eq(BluetoothStatusCodes.ERROR_BAD_PARAMETERS));
2378             } else if (sm.getDevice().equals(mCurrentDevice1)) {
2379                 verify(mCallback)
2380                         .onSourceModifyFailed(
2381                                 eq(sm.getDevice()),
2382                                 eq(TEST_SOURCE_ID + 1),
2383                                 eq(BluetoothStatusCodes.ERROR_BAD_PARAMETERS));
2384             }
2385         }
2386 
2387         assertThat(mStateMachines).hasSize(2);
2388         for (BassClientStateMachine sm : mStateMachines.values()) {
2389             doReturn(STATE_DISCONNECTED).when(sm).getConnectionState();
2390         }
2391 
2392         // Verify errors are reported for the entire group
2393         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
2394         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
2395         assertThat(mStateMachines).hasSize(2);
2396         for (BassClientStateMachine sm : mStateMachines.values()) {
2397             if (sm.getDevice().equals(mCurrentDevice)) {
2398                 verify(mCallback)
2399                         .onSourceRemoveFailed(
2400                                 eq(sm.getDevice()),
2401                                 eq(TEST_SOURCE_ID),
2402                                 eq(BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR));
2403             } else if (sm.getDevice().equals(mCurrentDevice1)) {
2404                 verify(mCallback)
2405                         .onSourceRemoveFailed(
2406                                 eq(sm.getDevice()),
2407                                 eq(TEST_SOURCE_ID + 1),
2408                                 eq(BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR));
2409             }
2410         }
2411     }
2412 
2413     /**
2414      * Test that an outgoing connection to two device that have BASS UUID is successful and a
2415      * connection state change intent is sent
2416      */
2417     @Test
testConnectedIntent()2418     public void testConnectedIntent() {
2419         prepareConnectedDeviceGroup();
2420 
2421         expect.that(mStateMachines.size()).isEqualTo(2);
2422         for (BassClientStateMachine sm : mStateMachines.values()) {
2423             BluetoothDevice dev = sm.getDevice();
2424             verifyConnectionStateIntent(dev, STATE_CONNECTED, STATE_CONNECTING);
2425         }
2426 
2427         List<BluetoothDevice> devices = mBassClientService.getConnectedDevices();
2428         expect.that(devices.contains(mCurrentDevice)).isTrue();
2429         expect.that(devices.contains(mCurrentDevice1)).isTrue();
2430     }
2431 
2432     @Test
testActiveSyncedSource_AddRemoveGet()2433     public void testActiveSyncedSource_AddRemoveGet() {
2434         final int handle1 = 1;
2435         final int handle2 = 2;
2436         final int handle3 = 3;
2437 
2438         // Check if empty
2439         assertThat(mBassClientService.getActiveSyncedSources()).isEmpty();
2440 
2441         // Check adding first handle
2442         mBassClientService.addActiveSyncedSource(handle1);
2443         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
2444         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(handle1);
2445 
2446         // Check if cannot add duplicate element
2447         mBassClientService.addActiveSyncedSource(handle1);
2448         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
2449         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(handle1);
2450 
2451         // Check adding second element
2452         mBassClientService.addActiveSyncedSource(handle2);
2453         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(2);
2454         assertThat(mBassClientService.getActiveSyncedSources())
2455                 .containsExactly(handle1, handle2)
2456                 .inOrder();
2457 
2458         // Check removing non existing element
2459         mBassClientService.removeActiveSyncedSource(handle3);
2460         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(2);
2461         assertThat(mBassClientService.getActiveSyncedSources())
2462                 .containsExactly(handle1, handle2)
2463                 .inOrder();
2464         // Check removing second element
2465         mBassClientService.removeActiveSyncedSource(handle1);
2466         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
2467         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(handle2);
2468 
2469         // Check removing first element
2470         mBassClientService.removeActiveSyncedSource(handle2);
2471         assertThat(mBassClientService.getActiveSyncedSources()).isEmpty();
2472 
2473         // Add 2 elements
2474         mBassClientService.addActiveSyncedSource(handle1);
2475         mBassClientService.addActiveSyncedSource(handle2);
2476         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(2);
2477 
2478         // Check removing all at once
2479         mBassClientService.removeActiveSyncedSource(null);
2480         assertThat(mBassClientService.getActiveSyncedSources()).isEmpty();
2481     }
2482 
2483     @Test
testScanResult_withSameBroadcastId()2484     public void testScanResult_withSameBroadcastId() {
2485         prepareConnectedDeviceGroup();
2486         startSearchingForSources();
2487 
2488         // First scanResult
2489         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
2490         mInOrderMethodProxy
2491                 .verify(mMethodProxy)
2492                 .periodicAdvertisingManagerRegisterSync(
2493                         any(), any(), anyInt(), anyInt(), any(), any());
2494         // Finish select
2495         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
2496 
2497         // Second scanResult with the same broadcast id
2498         onScanResult(mSourceDevice2, TEST_BROADCAST_ID);
2499         mInOrderMethodProxy
2500                 .verify(mMethodProxy, never())
2501                 .periodicAdvertisingManagerRegisterSync(
2502                         any(), any(), anyInt(), anyInt(), any(), any());
2503 
2504         // Third scanResult with new broadcast id
2505         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
2506         mInOrderMethodProxy
2507                 .verify(mMethodProxy)
2508                 .periodicAdvertisingManagerRegisterSync(
2509                         any(), any(), anyInt(), anyInt(), any(), any());
2510     }
2511 
2512     @Test
testSelectSource_withSameBroadcastId()2513     public void testSelectSource_withSameBroadcastId() {
2514         prepareConnectedDeviceGroup();
2515         startSearchingForSources();
2516 
2517         // First selectSource
2518         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
2519         mInOrderMethodProxy
2520                 .verify(mMethodProxy)
2521                 .periodicAdvertisingManagerRegisterSync(
2522                         any(), any(), anyInt(), anyInt(), any(), any());
2523         // Finish select
2524         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
2525 
2526         // Second selectSource with the same broadcast id
2527         onScanResult(mSourceDevice2, TEST_BROADCAST_ID);
2528         mInOrderMethodProxy
2529                 .verify(mMethodProxy, never())
2530                 .periodicAdvertisingManagerRegisterSync(
2531                         any(), any(), anyInt(), anyInt(), any(), any());
2532     }
2533 
2534     @Test
testSelectSource_wrongBassUUID()2535     public void testSelectSource_wrongBassUUID() {
2536         byte[] scanRecord =
2537                 new byte[] {
2538                     0x02,
2539                     0x01,
2540                     0x1a, // advertising flags
2541                     0x05,
2542                     0x02,
2543                     0x52,
2544                     0x18,
2545                     0x0a,
2546                     0x11, // 16 bit service uuids
2547                     0x04,
2548                     0x09,
2549                     0x50,
2550                     0x65,
2551                     0x64, // name
2552                     0x02,
2553                     0x0A,
2554                     (byte) 0xec, // tx power level
2555                     0x05,
2556                     0x30,
2557                     0x54,
2558                     0x65,
2559                     0x73,
2560                     0x74, // broadcast name: Test
2561                     0x06,
2562                     0x16,
2563                     0x00, // WRONG BAAS_UUID UUID
2564                     0x18,
2565                     (byte) TEST_BROADCAST_ID,
2566                     (byte) (TEST_BROADCAST_ID >> 8),
2567                     (byte) (TEST_BROADCAST_ID >> 16), // service data, broadcast id
2568                     0x08,
2569                     0x16,
2570                     0x56,
2571                     0x18,
2572                     0x07,
2573                     0x03,
2574                     0x06,
2575                     0x07,
2576                     0x08,
2577                     // service data - public broadcast,
2578                     // feature - 0x7, metadata len - 0x3, metadata - 0x6, 0x7, 0x8
2579                     0x05,
2580                     (byte) 0xff,
2581                     (byte) 0xe0,
2582                     0x00,
2583                     0x02,
2584                     0x15, // manufacturer specific data
2585                     0x03,
2586                     0x50,
2587                     0x01,
2588                     0x02, // an unknown data type won't cause trouble
2589                 };
2590         ScanResult scanResult =
2591                 new ScanResult(
2592                         mSourceDevice,
2593                         0,
2594                         0,
2595                         0,
2596                         0,
2597                         0,
2598                         TEST_RSSI,
2599                         0,
2600                         ScanRecord.parseFromBytes(scanRecord),
2601                         0);
2602 
2603         prepareConnectedDeviceGroup();
2604         startSearchingForSources();
2605         generateScanResult(scanResult);
2606         verify(mMethodProxy, never())
2607                 .periodicAdvertisingManagerRegisterSync(
2608                         any(), any(), anyInt(), anyInt(), any(), any());
2609     }
2610 
2611     @Test
testSyncEstablished_statusFailed()2612     public void testSyncEstablished_statusFailed() {
2613         prepareConnectedDeviceGroup();
2614         startSearchingForSources();
2615 
2616         // First scanResult
2617         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
2618         mInOrderMethodProxy
2619                 .verify(mMethodProxy)
2620                 .periodicAdvertisingManagerRegisterSync(
2621                         any(), any(), anyInt(), anyInt(), any(), any());
2622 
2623         // Finish select with failed status
2624         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
2625 
2626         // Could try to sync again
2627         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
2628         mInOrderMethodProxy
2629                 .verify(mMethodProxy)
2630                 .periodicAdvertisingManagerRegisterSync(
2631                         any(), any(), anyInt(), anyInt(), any(), any());
2632     }
2633 
2634     @Test
testSelectSource_wrongPublicBroadcastUUID()2635     public void testSelectSource_wrongPublicBroadcastUUID() {
2636         byte[] scanRecord =
2637                 new byte[] {
2638                     0x02,
2639                     0x01,
2640                     0x1a, // advertising flags
2641                     0x05,
2642                     0x02,
2643                     0x52,
2644                     0x18,
2645                     0x0a,
2646                     0x11, // 16 bit service uuids
2647                     0x04,
2648                     0x09,
2649                     0x50,
2650                     0x65,
2651                     0x64, // name
2652                     0x02,
2653                     0x0A,
2654                     (byte) 0xec, // tx power level
2655                     0x05,
2656                     0x30,
2657                     0x54,
2658                     0x65,
2659                     0x73,
2660                     0x74, // broadcast name: Test
2661                     0x06,
2662                     0x16,
2663                     0x52,
2664                     0x18,
2665                     (byte) TEST_BROADCAST_ID,
2666                     (byte) (TEST_BROADCAST_ID >> 8),
2667                     (byte) (TEST_BROADCAST_ID >> 16), // service data, broadcast id
2668                     0x08,
2669                     0x16,
2670                     0x00, // WRONG PUBLIC_BROADCAST_UUID
2671                     0x18,
2672                     0x07,
2673                     0x03,
2674                     0x06,
2675                     0x07,
2676                     0x08,
2677                     // service data - public broadcast,
2678                     // feature - 0x7, metadata len - 0x3, metadata - 0x6, 0x7, 0x8
2679                     0x05,
2680                     (byte) 0xff,
2681                     (byte) 0xe0,
2682                     0x00,
2683                     0x02,
2684                     0x15, // manufacturer specific data
2685                     0x03,
2686                     0x50,
2687                     0x01,
2688                     0x02, // an unknown data type won't cause trouble
2689                 };
2690         ScanResult scanResult =
2691                 new ScanResult(
2692                         mSourceDevice,
2693                         0,
2694                         0,
2695                         0,
2696                         0,
2697                         0,
2698                         TEST_RSSI,
2699                         0,
2700                         ScanRecord.parseFromBytes(scanRecord),
2701                         0);
2702 
2703         prepareConnectedDeviceGroup();
2704         startSearchingForSources();
2705         generateScanResult(scanResult);
2706         verify(mMethodProxy)
2707                 .periodicAdvertisingManagerRegisterSync(
2708                         any(), any(), anyInt(), anyInt(), any(), any());
2709     }
2710 
2711     @Test
testSelectSource_wrongPublicBroadcastData()2712     public void testSelectSource_wrongPublicBroadcastData() {
2713         byte[] scanRecord =
2714                 new byte[] {
2715                     0x02,
2716                     0x01,
2717                     0x1a, // advertising flags
2718                     0x05,
2719                     0x02,
2720                     0x52,
2721                     0x18,
2722                     0x0a,
2723                     0x11, // 16 bit service uuids
2724                     0x04,
2725                     0x09,
2726                     0x50,
2727                     0x65,
2728                     0x64, // name
2729                     0x02,
2730                     0x0A,
2731                     (byte) 0xec, // tx power level
2732                     0x05,
2733                     0x30,
2734                     0x54,
2735                     0x65,
2736                     0x73,
2737                     0x74, // broadcast name: Test
2738                     0x06,
2739                     0x16,
2740                     0x52,
2741                     0x18,
2742                     (byte) TEST_BROADCAST_ID,
2743                     (byte) (TEST_BROADCAST_ID >> 8),
2744                     (byte) (TEST_BROADCAST_ID >> 16), // service data, broadcast id
2745                     0x08,
2746                     0x16,
2747                     0x56,
2748                     0x18,
2749                     0x07,
2750                     0x04, // WRONG PUBLIC_BROADCAST data (metadata size)
2751                     0x06,
2752                     0x07,
2753                     0x08,
2754                     // service data - public broadcast,
2755                     // feature - 0x7, metadata len - 0x3, metadata - 0x6, 0x7, 0x8
2756                     0x05,
2757                     (byte) 0xff,
2758                     (byte) 0xe0,
2759                     0x00,
2760                     0x02,
2761                     0x15, // manufacturer specific data
2762                     0x03,
2763                     0x50,
2764                     0x01,
2765                     0x02, // an unknown data type won't cause trouble
2766                 };
2767         ScanResult scanResult =
2768                 new ScanResult(
2769                         mSourceDevice,
2770                         0,
2771                         0,
2772                         0,
2773                         0,
2774                         0,
2775                         TEST_RSSI,
2776                         0,
2777                         ScanRecord.parseFromBytes(scanRecord),
2778                         0);
2779 
2780         prepareConnectedDeviceGroup();
2781         startSearchingForSources();
2782         generateScanResult(scanResult);
2783         verify(mMethodProxy)
2784                 .periodicAdvertisingManagerRegisterSync(
2785                         any(), any(), anyInt(), anyInt(), any(), any());
2786     }
2787 
2788     @Test
testSelectSource_queueAndRemoveAfterMaxLimit()2789     public void testSelectSource_queueAndRemoveAfterMaxLimit() {
2790         final BluetoothDevice device1 =
2791                 mBluetoothAdapter.getRemoteLeDevice(
2792                         "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
2793         final BluetoothDevice device2 =
2794                 mBluetoothAdapter.getRemoteLeDevice(
2795                         "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM);
2796         final BluetoothDevice device3 =
2797                 mBluetoothAdapter.getRemoteLeDevice(
2798                         "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM);
2799         final BluetoothDevice device4 =
2800                 mBluetoothAdapter.getRemoteLeDevice(
2801                         "00:11:22:33:44:44", BluetoothDevice.ADDRESS_TYPE_RANDOM);
2802         final BluetoothDevice device5 =
2803                 mBluetoothAdapter.getRemoteLeDevice(
2804                         "00:11:22:33:44:55", BluetoothDevice.ADDRESS_TYPE_RANDOM);
2805         final int handle1 = 0;
2806         final int handle2 = 1;
2807         final int handle3 = 2;
2808         final int handle4 = 3;
2809         final int handle5 = 4;
2810         final int broadcastId1 = 1111;
2811         final int broadcastId2 = 2222;
2812         final int broadcastId3 = 3333;
2813         final int broadcastId4 = 4444;
2814         final int broadcastId5 = 5555;
2815 
2816         prepareConnectedDeviceGroup();
2817         startSearchingForSources();
2818 
2819         // Queue two scan requests
2820         onScanResult(device1, broadcastId1);
2821         onScanResult(device2, broadcastId2);
2822         mInOrderMethodProxy
2823                 .verify(mMethodProxy)
2824                 .periodicAdvertisingManagerRegisterSync(
2825                         any(), any(), anyInt(), anyInt(), any(), any());
2826 
2827         // Two SyncRequest queued but not synced yet
2828         assertThat(mBassClientService.getActiveSyncedSources()).isEmpty();
2829         assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isNull();
2830         assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isNull();
2831         assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isNull();
2832         assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isNull();
2833         assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
2834         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1))
2835                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2836         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2))
2837                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2838         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3))
2839                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2840         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle4))
2841                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2842         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle5))
2843                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2844 
2845         // Sync 1
2846         onSyncEstablished(device1, handle1);
2847         mInOrderMethodProxy
2848                 .verify(mMethodProxy)
2849                 .periodicAdvertisingManagerRegisterSync(
2850                         any(), any(), anyInt(), anyInt(), any(), any());
2851         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
2852         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(handle1);
2853         assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
2854         assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isNull();
2855         assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isNull();
2856         assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isNull();
2857         assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
2858         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
2859         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2))
2860                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2861         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3))
2862                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2863         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle4))
2864                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2865         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle5))
2866                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2867 
2868         // Sync 2
2869         onSyncEstablished(device2, handle2);
2870         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(2);
2871         assertThat(mBassClientService.getActiveSyncedSources())
2872                 .containsExactly(handle1, handle2)
2873                 .inOrder();
2874         assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
2875         assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
2876         assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isNull();
2877         assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isNull();
2878         assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
2879         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
2880         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
2881         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3))
2882                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2883         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle4))
2884                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2885         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle5))
2886                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2887 
2888         // Scan and sync 3
2889         onScanResult(device3, broadcastId3);
2890         mInOrderMethodProxy
2891                 .verify(mMethodProxy)
2892                 .periodicAdvertisingManagerRegisterSync(
2893                         any(), any(), anyInt(), anyInt(), any(), any());
2894         onSyncEstablished(device3, handle3);
2895         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(3);
2896         assertThat(mBassClientService.getActiveSyncedSources())
2897                 .containsExactly(handle1, handle2, handle3)
2898                 .inOrder();
2899         assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
2900         assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
2901         assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isNull();
2902         assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
2903         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
2904         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
2905         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3)).isEqualTo(broadcastId3);
2906         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle4))
2907                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2908         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle5))
2909                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2910 
2911         // Scan and sync 4
2912         onScanResult(device4, broadcastId4);
2913         mInOrderMethodProxy
2914                 .verify(mMethodProxy)
2915                 .periodicAdvertisingManagerRegisterSync(
2916                         any(), any(), anyInt(), anyInt(), any(), any());
2917         onSyncEstablished(device4, handle4);
2918         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(4);
2919         assertThat(mBassClientService.getActiveSyncedSources())
2920                 .containsExactly(handle1, handle2, handle3, handle4)
2921                 .inOrder();
2922         assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
2923         assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
2924         assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
2925         assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
2926         assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
2927         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
2928         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
2929         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3)).isEqualTo(broadcastId3);
2930         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle4)).isEqualTo(broadcastId4);
2931         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle5))
2932                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2933 
2934         // Scan 5 cause removing first element
2935         onScanResult(device5, broadcastId5);
2936         mInOrderMethodProxy
2937                 .verify(mMethodProxy)
2938                 .periodicAdvertisingManagerUnregisterSync(any(), any());
2939         mInOrderMethodProxy
2940                 .verify(mMethodProxy)
2941                 .periodicAdvertisingManagerRegisterSync(
2942                         any(), any(), anyInt(), anyInt(), any(), any());
2943         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(3);
2944         assertThat(mBassClientService.getActiveSyncedSources())
2945                 .containsExactly(handle2, handle3, handle4)
2946                 .inOrder();
2947         assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isNull();
2948         assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
2949         assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
2950         assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
2951         assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
2952         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1))
2953                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2954         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
2955         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3)).isEqualTo(broadcastId3);
2956         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle4)).isEqualTo(broadcastId4);
2957         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle5))
2958                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2959 
2960         // Sync 5
2961         onSyncEstablished(device5, handle5);
2962         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(4);
2963         expect.that(mBassClientService.getActiveSyncedSources())
2964                 .containsExactly(handle2, handle3, handle4, handle5)
2965                 .inOrder();
2966         expect.that(mBassClientService.getDeviceForSyncHandle(handle1)).isNull();
2967         expect.that(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
2968         expect.that(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
2969         expect.that(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
2970         expect.that(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(device5);
2971         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle1))
2972                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
2973         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle2))
2974                 .isEqualTo(broadcastId2);
2975         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle3))
2976                 .isEqualTo(broadcastId3);
2977         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle4))
2978                 .isEqualTo(broadcastId4);
2979         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle5))
2980                 .isEqualTo(broadcastId5);
2981     }
2982 
2983     @Test
2984     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
testSelectSource_removeAfterMaxLimit_notSyncedToAnySink()2985     public void testSelectSource_removeAfterMaxLimit_notSyncedToAnySink() {
2986         final BluetoothDevice device1 =
2987                 mBluetoothAdapter.getRemoteLeDevice(
2988                         "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
2989         final BluetoothDevice device2 =
2990                 mBluetoothAdapter.getRemoteLeDevice(
2991                         "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM);
2992         final BluetoothDevice device3 =
2993                 mBluetoothAdapter.getRemoteLeDevice(
2994                         "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM);
2995         final BluetoothDevice device4 =
2996                 mBluetoothAdapter.getRemoteLeDevice(
2997                         "00:11:22:33:44:44", BluetoothDevice.ADDRESS_TYPE_RANDOM);
2998         final BluetoothDevice device5 =
2999                 mBluetoothAdapter.getRemoteLeDevice(
3000                         "00:11:22:33:44:55", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3001         final int handle1 = 0;
3002         final int handle2 = 1;
3003         final int handle3 = 2;
3004         final int handle4 = 3;
3005         final int handle5 = 4;
3006         final int broadcastId1 = 1111;
3007         final int broadcastId2 = 2222;
3008         final int broadcastId3 = 3333;
3009         final int broadcastId4 = 4444;
3010         final int broadcastId5 = 5555;
3011 
3012         prepareConnectedDeviceGroup();
3013         startSearchingForSources();
3014 
3015         // Scan and sync 4 sources
3016         onScanResult(device1, broadcastId1);
3017         onSyncEstablished(device1, handle1);
3018         onScanResult(device2, broadcastId2);
3019         onSyncEstablished(device2, handle2);
3020         onScanResult(device3, broadcastId3);
3021         onSyncEstablished(device3, handle3);
3022         onScanResult(device4, broadcastId4);
3023         onSyncEstablished(device4, handle4);
3024         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(4);
3025         assertThat(mBassClientService.getActiveSyncedSources())
3026                 .containsExactly(handle1, handle2, handle3, handle4)
3027                 .inOrder();
3028         assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
3029         assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
3030         assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
3031         assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
3032         assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
3033         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
3034         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
3035         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3)).isEqualTo(broadcastId3);
3036         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle4)).isEqualTo(broadcastId4);
3037         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle5))
3038                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
3039 
3040         // Add source 1
3041         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(broadcastId1);
3042         verifyAddSourceForGroup(meta);
3043         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ true);
3044 
3045         // Scan 5 cause removing first element which is not synced to any sink
3046         onScanResult(device5, broadcastId5);
3047         mInOrderMethodProxy
3048                 .verify(mMethodProxy)
3049                 .periodicAdvertisingManagerUnregisterSync(any(), any());
3050         mInOrderMethodProxy
3051                 .verify(mMethodProxy)
3052                 .periodicAdvertisingManagerRegisterSync(
3053                         any(), any(), anyInt(), anyInt(), any(), any());
3054         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(3);
3055         expect.that(mBassClientService.getActiveSyncedSources())
3056                 .containsExactly(handle1, handle3, handle4)
3057                 .inOrder();
3058         expect.that(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
3059         expect.that(mBassClientService.getDeviceForSyncHandle(handle2)).isNull();
3060         expect.that(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
3061         expect.that(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
3062         expect.that(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
3063         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle1))
3064                 .isEqualTo(broadcastId1);
3065         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle2))
3066                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
3067         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle3))
3068                 .isEqualTo(broadcastId3);
3069         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle4))
3070                 .isEqualTo(broadcastId4);
3071         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle5))
3072                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
3073     }
3074 
3075     @Test
3076     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
testSelectSource_removeAfterMaxLimit_firstIfAllSyncedToSinks()3077     public void testSelectSource_removeAfterMaxLimit_firstIfAllSyncedToSinks() {
3078         final BluetoothDevice device1 =
3079                 mBluetoothAdapter.getRemoteLeDevice(
3080                         "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3081         final BluetoothDevice device2 =
3082                 mBluetoothAdapter.getRemoteLeDevice(
3083                         "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3084         final BluetoothDevice device3 =
3085                 mBluetoothAdapter.getRemoteLeDevice(
3086                         "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3087         final BluetoothDevice device4 =
3088                 mBluetoothAdapter.getRemoteLeDevice(
3089                         "00:11:22:33:44:44", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3090         final BluetoothDevice device5 =
3091                 mBluetoothAdapter.getRemoteLeDevice(
3092                         "00:11:22:33:44:55", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3093         final int handle1 = 0;
3094         final int handle2 = 1;
3095         final int handle3 = 2;
3096         final int handle4 = 3;
3097         final int handle5 = 4;
3098         final int broadcastId1 = 1111;
3099         final int broadcastId2 = 2222;
3100         final int broadcastId3 = 3333;
3101         final int broadcastId4 = 4444;
3102         final int broadcastId5 = 5555;
3103 
3104         prepareConnectedDeviceGroup();
3105         startSearchingForSources();
3106 
3107         // Scan and sync 4 sources
3108         onScanResult(device1, broadcastId1);
3109         onSyncEstablished(device1, handle1);
3110         onScanResult(device2, broadcastId2);
3111         onSyncEstablished(device2, handle2);
3112         onScanResult(device3, broadcastId3);
3113         onSyncEstablished(device3, handle3);
3114         onScanResult(device4, broadcastId4);
3115         onSyncEstablished(device4, handle4);
3116         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(4);
3117         assertThat(mBassClientService.getActiveSyncedSources())
3118                 .containsExactly(handle1, handle2, handle3, handle4)
3119                 .inOrder();
3120         assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
3121         assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
3122         assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
3123         assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
3124         assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
3125         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1)).isEqualTo(broadcastId1);
3126         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
3127         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3)).isEqualTo(broadcastId3);
3128         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle4)).isEqualTo(broadcastId4);
3129         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle5))
3130                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
3131 
3132         // Fake add 4 sources
3133         BluetoothLeBroadcastMetadata meta1 = createBroadcastMetadata(broadcastId1);
3134         BluetoothLeBroadcastMetadata meta2 = createBroadcastMetadata(broadcastId2);
3135         BluetoothLeBroadcastMetadata meta3 = createBroadcastMetadata(broadcastId3);
3136         BluetoothLeBroadcastMetadata meta4 = createBroadcastMetadata(broadcastId4);
3137         for (BassClientStateMachine sm : mStateMachines.values()) {
3138             injectRemoteSourceStateSourceAdded(
3139                     sm,
3140                     meta1,
3141                     TEST_SOURCE_ID + 1,
3142                     BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
3143                     meta1.isEncrypted()
3144                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
3145                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
3146                     null);
3147             injectRemoteSourceStateSourceAdded(
3148                     sm,
3149                     meta2,
3150                     TEST_SOURCE_ID + 2,
3151                     BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
3152                     meta2.isEncrypted()
3153                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
3154                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
3155                     null);
3156             injectRemoteSourceStateSourceAdded(
3157                     sm,
3158                     meta3,
3159                     TEST_SOURCE_ID + 3,
3160                     BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
3161                     meta3.isEncrypted()
3162                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
3163                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
3164                     null);
3165             injectRemoteSourceStateSourceAdded(
3166                     sm,
3167                     meta4,
3168                     TEST_SOURCE_ID + 4,
3169                     BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
3170                     meta4.isEncrypted()
3171                             ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
3172                             : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
3173                     null);
3174         }
3175 
3176         // Scan 5 cause removing first element which is not synced to any sink or first at all
3177         onScanResult(device5, broadcastId5);
3178         mInOrderMethodProxy
3179                 .verify(mMethodProxy)
3180                 .periodicAdvertisingManagerUnregisterSync(any(), any());
3181         mInOrderMethodProxy
3182                 .verify(mMethodProxy)
3183                 .periodicAdvertisingManagerRegisterSync(
3184                         any(), any(), anyInt(), anyInt(), any(), any());
3185         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(3);
3186         expect.that(mBassClientService.getActiveSyncedSources())
3187                 .containsExactly(handle2, handle3, handle4)
3188                 .inOrder();
3189         expect.that(mBassClientService.getDeviceForSyncHandle(handle1)).isNull();
3190         expect.that(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
3191         expect.that(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
3192         expect.that(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
3193         expect.that(mBassClientService.getDeviceForSyncHandle(handle5)).isNull();
3194         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle1))
3195                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
3196         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle2))
3197                 .isEqualTo(broadcastId2);
3198         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle3))
3199                 .isEqualTo(broadcastId3);
3200         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle4))
3201                 .isEqualTo(broadcastId4);
3202         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle5))
3203                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
3204     }
3205 
3206     @Test
testAddSourceToUnsyncedSource_causesSyncBeforeAddingSource()3207     public void testAddSourceToUnsyncedSource_causesSyncBeforeAddingSource() {
3208         final BluetoothDevice device1 =
3209                 mBluetoothAdapter.getRemoteLeDevice(
3210                         "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3211         final BluetoothDevice device2 =
3212                 mBluetoothAdapter.getRemoteLeDevice(
3213                         "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3214         final BluetoothDevice device3 =
3215                 mBluetoothAdapter.getRemoteLeDevice(
3216                         "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3217         final BluetoothDevice device4 =
3218                 mBluetoothAdapter.getRemoteLeDevice(
3219                         "00:11:22:33:44:44", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3220         final BluetoothDevice device5 =
3221                 mBluetoothAdapter.getRemoteLeDevice(
3222                         "00:11:22:33:44:55", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3223         final int handle1 = 0;
3224         final int handle2 = 1;
3225         final int handle3 = 2;
3226         final int handle4 = 3;
3227         final int handle5 = 4;
3228         final int broadcastId1 = 1111;
3229         final int broadcastId2 = 2222;
3230         final int broadcastId3 = 3333;
3231         final int broadcastId4 = 4444;
3232         final int broadcastId5 = 5555;
3233 
3234         prepareConnectedDeviceGroup();
3235         startSearchingForSources();
3236 
3237         // Scan and sync 5 sources cause removing 1 synced element
3238         onScanResult(device1, broadcastId1);
3239         onSyncEstablished(device1, handle1);
3240         onScanResult(device2, broadcastId2);
3241         onSyncEstablished(device2, handle2);
3242         onScanResult(device3, broadcastId3);
3243         onSyncEstablished(device3, handle3);
3244         onScanResult(device4, broadcastId4);
3245         onSyncEstablished(device4, handle4);
3246         onScanResult(device5, broadcastId5);
3247         mInOrderMethodProxy
3248                 .verify(mMethodProxy, times(4))
3249                 .periodicAdvertisingManagerRegisterSync(
3250                         any(), any(), anyInt(), anyInt(), any(), any());
3251         mInOrderMethodProxy
3252                 .verify(mMethodProxy)
3253                 .periodicAdvertisingManagerUnregisterSync(any(), any());
3254         mInOrderMethodProxy
3255                 .verify(mMethodProxy)
3256                 .periodicAdvertisingManagerRegisterSync(
3257                         any(), any(), anyInt(), anyInt(), any(), any());
3258         onSyncEstablished(device5, handle5);
3259         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(4);
3260         assertThat(mBassClientService.getActiveSyncedSources())
3261                 .containsExactly(handle2, handle3, handle4, handle5)
3262                 .inOrder();
3263         assertThat(mBassClientService.getDeviceForSyncHandle(handle1)).isNull();
3264         assertThat(mBassClientService.getDeviceForSyncHandle(handle2)).isEqualTo(device2);
3265         assertThat(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
3266         assertThat(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
3267         assertThat(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(device5);
3268         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle1))
3269                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
3270         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle2)).isEqualTo(broadcastId2);
3271         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle3)).isEqualTo(broadcastId3);
3272         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle4)).isEqualTo(broadcastId4);
3273         assertThat(mBassClientService.getBroadcastIdForSyncHandle(handle5)).isEqualTo(broadcastId5);
3274 
3275         BluetoothLeBroadcastMetadata.Builder builder =
3276                 new BluetoothLeBroadcastMetadata.Builder()
3277                         .setEncrypted(false)
3278                         .setSourceDevice(device1, BluetoothDevice.ADDRESS_TYPE_RANDOM)
3279                         .setSourceAdvertisingSid(TEST_ADVERTISER_SID)
3280                         .setBroadcastId(broadcastId1)
3281                         .setBroadcastCode(null)
3282                         .setPaSyncInterval(TEST_PA_SYNC_INTERVAL)
3283                         .setPresentationDelayMicros(TEST_PRESENTATION_DELAY_MS);
3284         // builder expect at least one subgroup
3285         builder.addSubgroup(createBroadcastSubgroup());
3286         BluetoothLeBroadcastMetadata meta = builder.build();
3287         ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class);
3288 
3289         // Add source to unsynced broadcast, causes synchronization first
3290         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
3291         handleHandoverSupport();
3292         mInOrderMethodProxy
3293                 .verify(mMethodProxy)
3294                 .periodicAdvertisingManagerRegisterSync(
3295                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3296         assertThat(
3297                         BassUtils.parseBroadcastId(
3298                                 resultCaptor
3299                                         .getValue()
3300                                         .getScanRecord()
3301                                         .getServiceData()
3302                                         .get(BassConstants.BAAS_UUID)))
3303                 .isEqualTo(broadcastId1);
3304 
3305         // Verify not getting ADD_BCAST_SOURCE message before source sync
3306         assertThat(mStateMachines).hasSize(2);
3307         for (BassClientStateMachine sm : mStateMachines.values()) {
3308             verify(sm, never()).sendMessage(any());
3309         }
3310 
3311         // Source synced which cause execute pending add source
3312         onSyncEstablished(device1, handle1);
3313 
3314         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(4);
3315         expect.that(mBassClientService.getActiveSyncedSources())
3316                 .containsExactly(handle3, handle4, handle5, handle1)
3317                 .inOrder();
3318         expect.that(mBassClientService.getDeviceForSyncHandle(handle1)).isEqualTo(device1);
3319         expect.that(mBassClientService.getDeviceForSyncHandle(handle2)).isNull();
3320         expect.that(mBassClientService.getDeviceForSyncHandle(handle3)).isEqualTo(device3);
3321         expect.that(mBassClientService.getDeviceForSyncHandle(handle4)).isEqualTo(device4);
3322         expect.that(mBassClientService.getDeviceForSyncHandle(handle5)).isEqualTo(device5);
3323         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle1))
3324                 .isEqualTo(broadcastId1);
3325         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle2))
3326                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
3327         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle3))
3328                 .isEqualTo(broadcastId3);
3329         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle4))
3330                 .isEqualTo(broadcastId4);
3331         expect.that(mBassClientService.getBroadcastIdForSyncHandle(handle5))
3332                 .isEqualTo(broadcastId5);
3333 
3334         // Verify all group members getting ADD_BCAST_SOURCE message
3335         expect.that(mStateMachines.size()).isEqualTo(2);
3336         for (BassClientStateMachine sm : mStateMachines.values()) {
3337             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
3338             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
3339 
3340             Message msg =
3341                     messageCaptor.getAllValues().stream()
3342                             .filter(
3343                                     m ->
3344                                             (m.what == BassClientStateMachine.ADD_BCAST_SOURCE)
3345                                                     && (m.obj == meta))
3346                             .findFirst()
3347                             .orElse(null);
3348             expect.that(msg).isNotNull();
3349         }
3350     }
3351 
3352     @Test
testAddSourceForExternalBroadcast_triggerSetContextMask()3353     public void testAddSourceForExternalBroadcast_triggerSetContextMask() {
3354         final int testGroupId = 1;
3355         prepareConnectedDeviceGroup();
3356         startSearchingForSources();
3357         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
3358         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
3359 
3360         /* Fake external broadcast - no Broadcast Metadata from LE Audio service */
3361         doReturn(new ArrayList<BluetoothLeBroadcastMetadata>())
3362                 .when(mLeAudioService)
3363                 .getAllBroadcastMetadata();
3364         doReturn(testGroupId).when(mLeAudioService).getActiveGroupId();
3365         doReturn(new ArrayList<BluetoothDevice>(Arrays.asList(mCurrentDevice)))
3366                 .when(mLeAudioService)
3367                 .getActiveDevices();
3368 
3369         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
3370         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
3371         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
3372                 .isEqualTo(mSourceDevice);
3373         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
3374                 .isEqualTo(TEST_BROADCAST_ID);
3375 
3376         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
3377 
3378         // Add source to unsynced broadcast, causes synchronization first
3379         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
3380 
3381         // Verify setting allowed context mask is triggered
3382         verify(mLeAudioService)
3383                 .setActiveGroupAllowedContextMask(
3384                         eq(
3385                                 BluetoothLeAudio.CONTEXTS_ALL
3386                                         & ~BluetoothLeAudio.CONTEXT_TYPE_SOUND_EFFECTS),
3387                         eq(BluetoothLeAudio.CONTEXTS_ALL));
3388         handleHandoverSupport();
3389 
3390         // Verify all group members getting ADD_BCAST_SOURCE message
3391         assertThat(mStateMachines).hasSize(2);
3392         for (BassClientStateMachine sm : mStateMachines.values()) {
3393             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
3394             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
3395 
3396             Message msg =
3397                     messageCaptor.getAllValues().stream()
3398                             .filter(
3399                                     m ->
3400                                             (m.what == BassClientStateMachine.ADD_BCAST_SOURCE)
3401                                                     && (m.obj == meta))
3402                             .findFirst()
3403                             .orElse(null);
3404             assertThat(msg).isNotNull();
3405         }
3406 
3407         mBassClientService
3408                 .getCallbacks()
3409                 .notifySourceAddFailed(mCurrentDevice, meta, BluetoothStatusCodes.ERROR_UNKNOWN);
3410         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
3411 
3412         // Verify resetting allowed context mask is triggered when switching source failed
3413         verify(mLeAudioService)
3414                 .setActiveGroupAllowedContextMask(
3415                         eq(BluetoothLeAudio.CONTEXTS_ALL), eq(BluetoothLeAudio.CONTEXTS_ALL));
3416     }
3417 
3418     @Test
testSelectSource_orderOfSyncRegisteringByPriorityAndRssi()3419     public void testSelectSource_orderOfSyncRegisteringByPriorityAndRssi() {
3420         final BluetoothDevice device1 =
3421                 mBluetoothAdapter.getRemoteLeDevice(
3422                         "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3423         final BluetoothDevice device2 =
3424                 mBluetoothAdapter.getRemoteLeDevice(
3425                         "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3426         final BluetoothDevice device3 =
3427                 mBluetoothAdapter.getRemoteLeDevice(
3428                         "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3429         final BluetoothDevice device4 =
3430                 mBluetoothAdapter.getRemoteLeDevice(
3431                         "00:11:22:33:44:44", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3432         final BluetoothDevice device5 =
3433                 mBluetoothAdapter.getRemoteLeDevice(
3434                         "00:11:22:33:44:55", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3435         final BluetoothDevice device6 =
3436                 mBluetoothAdapter.getRemoteLeDevice(
3437                         "00:11:22:33:44:66", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3438         final BluetoothDevice device7 =
3439                 mBluetoothAdapter.getRemoteLeDevice(
3440                         "00:11:22:33:44:77", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3441         final int broadcastId1 = 1111;
3442         final int broadcastId2 = 2222;
3443         final int broadcastId3 = 3333;
3444         final int broadcastId4 = 4444;
3445         final int broadcastId5 = 5555;
3446         final int broadcastId6 = 6666;
3447         final int broadcastId7 = 7777;
3448 
3449         byte[] scanRecord1 = getScanRecord(broadcastId1);
3450         byte[] scanRecord2 = getScanRecord(broadcastId2);
3451         byte[] scanRecord3 = getScanRecord(broadcastId3);
3452         byte[] scanRecord4 = getScanRecord(broadcastId4);
3453         byte[] scanRecord5 = getScanRecord(broadcastId5);
3454         byte[] scanRecord6 = getScanRecord(broadcastId6);
3455         byte[] scanRecord7 = getScanRecord(broadcastId7);
3456 
3457         ScanResult scanResult1 =
3458                 new ScanResult(
3459                         device1,
3460                         0,
3461                         0,
3462                         0,
3463                         0,
3464                         0,
3465                         TEST_RSSI,
3466                         0,
3467                         ScanRecord.parseFromBytes(scanRecord1),
3468                         0);
3469         ScanResult scanResult2 =
3470                 new ScanResult(
3471                         device2,
3472                         0,
3473                         0,
3474                         0,
3475                         0,
3476                         0,
3477                         TEST_RSSI + 3,
3478                         0,
3479                         ScanRecord.parseFromBytes(scanRecord2),
3480                         0);
3481         ScanResult scanResult3 =
3482                 new ScanResult(
3483                         device3,
3484                         0,
3485                         0,
3486                         0,
3487                         0,
3488                         0,
3489                         TEST_RSSI + 7,
3490                         0,
3491                         ScanRecord.parseFromBytes(scanRecord3),
3492                         0);
3493         ScanResult scanResult4 =
3494                 new ScanResult(
3495                         device4,
3496                         0,
3497                         0,
3498                         0,
3499                         0,
3500                         0,
3501                         TEST_RSSI + 5,
3502                         0,
3503                         ScanRecord.parseFromBytes(scanRecord4),
3504                         0);
3505         ScanResult scanResult5 =
3506                 new ScanResult(
3507                         device5,
3508                         0,
3509                         0,
3510                         0,
3511                         0,
3512                         0,
3513                         TEST_RSSI + 2,
3514                         0,
3515                         ScanRecord.parseFromBytes(scanRecord5),
3516                         0);
3517         ScanResult scanResult6 =
3518                 new ScanResult(
3519                         device6,
3520                         0,
3521                         0,
3522                         0,
3523                         0,
3524                         0,
3525                         TEST_RSSI + 6,
3526                         0,
3527                         ScanRecord.parseFromBytes(scanRecord6),
3528                         0);
3529         ScanResult scanResult7 =
3530                 new ScanResult(
3531                         device7,
3532                         0,
3533                         0,
3534                         0,
3535                         0,
3536                         0,
3537                         TEST_RSSI + 4,
3538                         0,
3539                         ScanRecord.parseFromBytes(scanRecord7),
3540                         0);
3541 
3542         prepareConnectedDeviceGroup();
3543         startSearchingForSources();
3544 
3545         // Added and executed immediately as no other in queue
3546         generateScanResult(scanResult1);
3547         // Added to queue with worst rssi
3548         generateScanResult(scanResult2);
3549         // Added to queue with best rssi
3550         generateScanResult(scanResult3);
3551         // Added to queue with medium rssi
3552         generateScanResult(scanResult4);
3553         // Added to queue with worst rssi (increase priority after all)
3554         generateScanResult(scanResult5);
3555         // Added to queue with best rssi (increase priority after all)
3556         generateScanResult(scanResult6);
3557         // Added to queue with medium rssi (increase priority after all)
3558         generateScanResult(scanResult7);
3559 
3560         // Increase priority of last 3 of them
3561         mBassClientService.addSelectSourceRequest(broadcastId5, /* hasPriority */ true);
3562         mBassClientService.addSelectSourceRequest(broadcastId6, /* hasPriority */ true);
3563         mBassClientService.addSelectSourceRequest(broadcastId7, /* hasPriority */ true);
3564 
3565         ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class);
3566         mInOrderMethodProxy
3567                 .verify(mMethodProxy)
3568                 .periodicAdvertisingManagerRegisterSync(
3569                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3570         assertThat(
3571                         BassUtils.parseBroadcastId(
3572                                 resultCaptor
3573                                         .getValue()
3574                                         .getScanRecord()
3575                                         .getServiceData()
3576                                         .get(BassConstants.BAAS_UUID)))
3577                 .isEqualTo(broadcastId1);
3578 
3579         onSyncEstablished(device1, TEST_SYNC_HANDLE);
3580         mInOrderMethodProxy
3581                 .verify(mMethodProxy)
3582                 .periodicAdvertisingManagerRegisterSync(
3583                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3584         assertThat(
3585                         BassUtils.parseBroadcastId(
3586                                 resultCaptor
3587                                         .getValue()
3588                                         .getScanRecord()
3589                                         .getServiceData()
3590                                         .get(BassConstants.BAAS_UUID)))
3591                 .isEqualTo(broadcastId6);
3592 
3593         onSyncEstablished(device6, TEST_SYNC_HANDLE + 1);
3594         mInOrderMethodProxy
3595                 .verify(mMethodProxy)
3596                 .periodicAdvertisingManagerRegisterSync(
3597                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3598         assertThat(
3599                         BassUtils.parseBroadcastId(
3600                                 resultCaptor
3601                                         .getValue()
3602                                         .getScanRecord()
3603                                         .getServiceData()
3604                                         .get(BassConstants.BAAS_UUID)))
3605                 .isEqualTo(broadcastId7);
3606 
3607         onSyncEstablished(device7, TEST_SYNC_HANDLE + 2);
3608         mInOrderMethodProxy
3609                 .verify(mMethodProxy)
3610                 .periodicAdvertisingManagerRegisterSync(
3611                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3612         assertThat(
3613                         BassUtils.parseBroadcastId(
3614                                 resultCaptor
3615                                         .getValue()
3616                                         .getScanRecord()
3617                                         .getServiceData()
3618                                         .get(BassConstants.BAAS_UUID)))
3619                 .isEqualTo(broadcastId5);
3620 
3621         onSyncEstablished(device5, TEST_SYNC_HANDLE + 3);
3622         mInOrderMethodProxy
3623                 .verify(mMethodProxy)
3624                 .periodicAdvertisingManagerRegisterSync(
3625                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3626         assertThat(
3627                         BassUtils.parseBroadcastId(
3628                                 resultCaptor
3629                                         .getValue()
3630                                         .getScanRecord()
3631                                         .getServiceData()
3632                                         .get(BassConstants.BAAS_UUID)))
3633                 .isEqualTo(broadcastId3);
3634 
3635         onSyncEstablished(device3, TEST_SYNC_HANDLE + 4);
3636         mInOrderMethodProxy
3637                 .verify(mMethodProxy)
3638                 .periodicAdvertisingManagerRegisterSync(
3639                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3640         assertThat(
3641                         BassUtils.parseBroadcastId(
3642                                 resultCaptor
3643                                         .getValue()
3644                                         .getScanRecord()
3645                                         .getServiceData()
3646                                         .get(BassConstants.BAAS_UUID)))
3647                 .isEqualTo(broadcastId4);
3648 
3649         onSyncEstablished(device4, TEST_SYNC_HANDLE + 5);
3650         mInOrderMethodProxy
3651                 .verify(mMethodProxy)
3652                 .periodicAdvertisingManagerRegisterSync(
3653                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3654         assertThat(
3655                         BassUtils.parseBroadcastId(
3656                                 resultCaptor
3657                                         .getValue()
3658                                         .getScanRecord()
3659                                         .getServiceData()
3660                                         .get(BassConstants.BAAS_UUID)))
3661                 .isEqualTo(broadcastId2);
3662     }
3663 
3664     @Test
3665     @EnableFlags(Flags.FLAG_LEAUDIO_SORT_SCANS_TO_SYNC_BY_FAILS)
testSelectSource_orderOfSyncRegisteringByRssiAndFailsCounter()3666     public void testSelectSource_orderOfSyncRegisteringByRssiAndFailsCounter() {
3667         final BluetoothDevice device1 =
3668                 mBluetoothAdapter.getRemoteLeDevice(
3669                         "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3670         final BluetoothDevice device2 =
3671                 mBluetoothAdapter.getRemoteLeDevice(
3672                         "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3673         final BluetoothDevice device3 =
3674                 mBluetoothAdapter.getRemoteLeDevice(
3675                         "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM);
3676         final int broadcastId1 = 1111;
3677         final int broadcastId2 = 2222;
3678         final int broadcastId3 = 3333;
3679 
3680         byte[] scanRecord1 = getScanRecord(broadcastId1);
3681         byte[] scanRecord2 = getScanRecord(broadcastId2);
3682         byte[] scanRecord3 = getScanRecord(broadcastId3);
3683 
3684         ScanResult scanResult1 =
3685                 new ScanResult(
3686                         device1,
3687                         0,
3688                         0,
3689                         0,
3690                         0,
3691                         0,
3692                         TEST_RSSI + 10,
3693                         0,
3694                         ScanRecord.parseFromBytes(scanRecord1),
3695                         0);
3696         ScanResult scanResult2 =
3697                 new ScanResult(
3698                         device2,
3699                         0,
3700                         0,
3701                         0,
3702                         0,
3703                         0,
3704                         TEST_RSSI + 9,
3705                         0,
3706                         ScanRecord.parseFromBytes(scanRecord2),
3707                         0);
3708         ScanResult scanResult3 =
3709                 new ScanResult(
3710                         device3,
3711                         0,
3712                         0,
3713                         0,
3714                         0,
3715                         0,
3716                         TEST_RSSI,
3717                         0,
3718                         ScanRecord.parseFromBytes(scanRecord3),
3719                         0);
3720 
3721         prepareConnectedDeviceGroup();
3722         startSearchingForSources();
3723 
3724         // Test using onSyncEstablishedFailed
3725         // Added and executed immediately as no other in queue, high rssi
3726         generateScanResult(scanResult1);
3727         // Added to queue, medium rssi
3728         generateScanResult(scanResult2);
3729         // Added to queue, low rssi
3730         generateScanResult(scanResult3);
3731 
3732         ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class);
3733         mInOrderMethodProxy
3734                 .verify(mMethodProxy)
3735                 .periodicAdvertisingManagerRegisterSync(
3736                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3737         assertThat(
3738                         BassUtils.parseBroadcastId(
3739                                 resultCaptor
3740                                         .getValue()
3741                                         .getScanRecord()
3742                                         .getServiceData()
3743                                         .get(BassConstants.BAAS_UUID)))
3744                 .isEqualTo(broadcastId1);
3745 
3746         onSyncEstablishedFailed(device1, TEST_SYNC_HANDLE);
3747         mInOrderMethodProxy
3748                 .verify(mMethodProxy)
3749                 .periodicAdvertisingManagerRegisterSync(
3750                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3751         assertThat(
3752                         BassUtils.parseBroadcastId(
3753                                 resultCaptor
3754                                         .getValue()
3755                                         .getScanRecord()
3756                                         .getServiceData()
3757                                         .get(BassConstants.BAAS_UUID)))
3758                 .isEqualTo(broadcastId2);
3759 
3760         // Added to queue again, high rssi
3761         generateScanResult(scanResult1);
3762 
3763         onSyncEstablishedFailed(device2, TEST_SYNC_HANDLE + 1);
3764         mInOrderMethodProxy
3765                 .verify(mMethodProxy)
3766                 .periodicAdvertisingManagerRegisterSync(
3767                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3768         assertThat(
3769                         BassUtils.parseBroadcastId(
3770                                 resultCaptor
3771                                         .getValue()
3772                                         .getScanRecord()
3773                                         .getServiceData()
3774                                         .get(BassConstants.BAAS_UUID)))
3775                 .isEqualTo(broadcastId3);
3776 
3777         onSyncEstablished(device3, TEST_SYNC_HANDLE + 2);
3778         mInOrderMethodProxy
3779                 .verify(mMethodProxy)
3780                 .periodicAdvertisingManagerRegisterSync(
3781                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3782         assertThat(
3783                         BassUtils.parseBroadcastId(
3784                                 resultCaptor
3785                                         .getValue()
3786                                         .getScanRecord()
3787                                         .getServiceData()
3788                                         .get(BassConstants.BAAS_UUID)))
3789                 .isEqualTo(broadcastId1);
3790 
3791         // Restart searching clears the mSyncFailureCounter
3792         mBassClientService.stopSearchingForSources();
3793         mInOrderMethodProxy
3794                 .verify(mMethodProxy, times(2))
3795                 .periodicAdvertisingManagerUnregisterSync(any(), any());
3796         startSearchingForSources();
3797 
3798         // Test using onSyncLost
3799         // Added and executed immediately as no other in queue, high rssi
3800         generateScanResult(scanResult1);
3801         // Added to queue, medium rssi
3802         generateScanResult(scanResult2);
3803         // Added to queue, low rssi
3804         generateScanResult(scanResult3);
3805 
3806         mInOrderMethodProxy
3807                 .verify(mMethodProxy)
3808                 .periodicAdvertisingManagerRegisterSync(
3809                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3810         assertThat(
3811                         BassUtils.parseBroadcastId(
3812                                 resultCaptor
3813                                         .getValue()
3814                                         .getScanRecord()
3815                                         .getServiceData()
3816                                         .get(BassConstants.BAAS_UUID)))
3817                 .isEqualTo(broadcastId1);
3818 
3819         onSyncEstablished(device1, TEST_SYNC_HANDLE);
3820         mInOrderMethodProxy
3821                 .verify(mMethodProxy)
3822                 .periodicAdvertisingManagerRegisterSync(
3823                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3824         assertThat(
3825                         BassUtils.parseBroadcastId(
3826                                 resultCaptor
3827                                         .getValue()
3828                                         .getScanRecord()
3829                                         .getServiceData()
3830                                         .get(BassConstants.BAAS_UUID)))
3831                 .isEqualTo(broadcastId2);
3832         onSyncLost();
3833         if (Flags.leaudioBroadcastResyncHelper()) {
3834             checkAndDispatchTimeout(broadcastId1, BassClientService.MESSAGE_SYNC_LOST_TIMEOUT);
3835         }
3836 
3837         // Added to queue again, high rssi
3838         generateScanResult(scanResult1);
3839 
3840         onSyncEstablished(device2, TEST_SYNC_HANDLE + 1);
3841         mInOrderMethodProxy
3842                 .verify(mMethodProxy)
3843                 .periodicAdvertisingManagerRegisterSync(
3844                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
3845         assertThat(
3846                         BassUtils.parseBroadcastId(
3847                                 resultCaptor
3848                                         .getValue()
3849                                         .getScanRecord()
3850                                         .getServiceData()
3851                                         .get(BassConstants.BAAS_UUID)))
3852                 .isEqualTo(broadcastId3);
3853     }
3854 
3855     @Test
testPeriodicAdvertisementResultMap_updateGetAndModifyNotifiedFlag()3856     public void testPeriodicAdvertisementResultMap_updateGetAndModifyNotifiedFlag() {
3857         final String testBroadcastName = "Test";
3858         final int testSyncHandle = 1;
3859         final int testBroadcastId = 42;
3860         final int testBroadcastIdInvalid = 43;
3861         final int testAdvertiserSid = 1234;
3862         final int testAdvInterval = 100;
3863 
3864         // mock the update in selectSource
3865         mBassClientService.updateSyncHandleForBroadcastId(
3866                 BassConstants.PENDING_SYNC_HANDLE, testBroadcastId);
3867         mBassClientService.updatePeriodicAdvertisementResultMap(
3868                 mSourceDevice,
3869                 mSourceDevice.getAddressType(),
3870                 BassConstants.PENDING_SYNC_HANDLE,
3871                 BassConstants.INVALID_ADV_SID,
3872                 testAdvInterval,
3873                 testBroadcastId,
3874                 null,
3875                 testBroadcastName);
3876 
3877         // mock the update in onSyncEstablished
3878         mBassClientService.updatePeriodicAdvertisementResultMap(
3879                 mSourceDevice,
3880                 BassConstants.INVALID_ADV_ADDRESS_TYPE,
3881                 testSyncHandle,
3882                 testAdvertiserSid,
3883                 BassConstants.INVALID_ADV_INTERVAL,
3884                 BassConstants.INVALID_BROADCAST_ID,
3885                 null,
3886                 null);
3887 
3888         assertThat(
3889                         mBassClientService.getPeriodicAdvertisementResult(
3890                                 mSourceDevice, testBroadcastIdInvalid))
3891                 .isNull();
3892         PeriodicAdvertisementResult paResult =
3893                 mBassClientService.getPeriodicAdvertisementResult(mSourceDevice, testBroadcastId);
3894         assertThat(paResult.getAddressType()).isEqualTo(BluetoothDevice.ADDRESS_TYPE_RANDOM);
3895         assertThat(paResult.getSyncHandle()).isEqualTo(testSyncHandle);
3896         assertThat(paResult.getAdvSid()).isEqualTo(testAdvertiserSid);
3897         assertThat(paResult.getAdvInterval()).isEqualTo(testAdvInterval);
3898         assertThat(paResult.getBroadcastName()).isEqualTo(testBroadcastName);
3899 
3900         // validate modify notified flag
3901         paResult.setNotified(true);
3902         assertThat(paResult.isNotified()).isEqualTo(true);
3903         mBassClientService.clearNotifiedFlags();
3904         assertThat(paResult.isNotified()).isEqualTo(false);
3905     }
3906 
3907     @Test
testPeriodicAdvertisementResultMap_syncEstablishedOnTheSameSyncHandle()3908     public void testPeriodicAdvertisementResultMap_syncEstablishedOnTheSameSyncHandle() {
3909         final String testBroadcastName1 = "Test1";
3910         final String testBroadcastName2 = "Test2";
3911         final int testSyncHandle = 1;
3912         final int testBroadcastId1 = 42;
3913         final int testBroadcastId2 = 43;
3914         final int testAdvertiserSid1 = 1234;
3915         final int testAdvertiserSid2 = 2345;
3916         final int testAdvInterval1 = 100;
3917         final int testAdvInterval2 = 200;
3918 
3919         // mock the update in selectSource
3920         mBassClientService.updateSyncHandleForBroadcastId(
3921                 BassConstants.PENDING_SYNC_HANDLE, testBroadcastId1);
3922         mBassClientService.updatePeriodicAdvertisementResultMap(
3923                 mSourceDevice,
3924                 mSourceDevice.getAddressType(),
3925                 BassConstants.PENDING_SYNC_HANDLE,
3926                 BassConstants.INVALID_ADV_SID,
3927                 testAdvInterval1,
3928                 testBroadcastId1,
3929                 null,
3930                 testBroadcastName1);
3931 
3932         // mock the update in onSyncEstablished
3933         mBassClientService.updatePeriodicAdvertisementResultMap(
3934                 mSourceDevice,
3935                 BassConstants.INVALID_ADV_ADDRESS_TYPE,
3936                 testSyncHandle,
3937                 testAdvertiserSid1,
3938                 BassConstants.INVALID_ADV_INTERVAL,
3939                 BassConstants.INVALID_BROADCAST_ID,
3940                 null,
3941                 null);
3942 
3943         assertThat(
3944                         mBassClientService.getPeriodicAdvertisementResult(
3945                                 mSourceDevice, testBroadcastId2))
3946                 .isNull();
3947         PeriodicAdvertisementResult paResult =
3948                 mBassClientService.getPeriodicAdvertisementResult(mSourceDevice, testBroadcastId1);
3949         assertThat(paResult.getAddressType()).isEqualTo(BluetoothDevice.ADDRESS_TYPE_RANDOM);
3950         assertThat(paResult.getSyncHandle()).isEqualTo(testSyncHandle);
3951         assertThat(paResult.getAdvSid()).isEqualTo(testAdvertiserSid1);
3952         assertThat(paResult.getAdvInterval()).isEqualTo(testAdvInterval1);
3953         assertThat(paResult.getBroadcastName()).isEqualTo(testBroadcastName1);
3954 
3955         // mock the update in selectSource
3956         mBassClientService.updateSyncHandleForBroadcastId(
3957                 BassConstants.PENDING_SYNC_HANDLE, testBroadcastId2);
3958         mBassClientService.updatePeriodicAdvertisementResultMap(
3959                 mSourceDevice,
3960                 mSourceDevice.getAddressType(),
3961                 BassConstants.PENDING_SYNC_HANDLE,
3962                 BassConstants.INVALID_ADV_SID,
3963                 testAdvInterval2,
3964                 testBroadcastId2,
3965                 null,
3966                 testBroadcastName2);
3967 
3968         // mock the update in onSyncEstablished
3969         mBassClientService.updatePeriodicAdvertisementResultMap(
3970                 mSourceDevice,
3971                 BassConstants.INVALID_ADV_ADDRESS_TYPE,
3972                 testSyncHandle,
3973                 testAdvertiserSid2,
3974                 BassConstants.INVALID_ADV_INTERVAL,
3975                 BassConstants.INVALID_BROADCAST_ID,
3976                 null,
3977                 null);
3978 
3979         expect.that(
3980                         mBassClientService.getPeriodicAdvertisementResult(
3981                                 mSourceDevice, testBroadcastId1))
3982                 .isNull();
3983         paResult =
3984                 mBassClientService.getPeriodicAdvertisementResult(mSourceDevice, testBroadcastId2);
3985         expect.that(paResult.getAddressType()).isEqualTo(BluetoothDevice.ADDRESS_TYPE_RANDOM);
3986         expect.that(paResult.getSyncHandle()).isEqualTo(testSyncHandle);
3987         expect.that(paResult.getAdvSid()).isEqualTo(testAdvertiserSid2);
3988         expect.that(paResult.getAdvInterval()).isEqualTo(testAdvInterval2);
3989         expect.that(paResult.getBroadcastName()).isEqualTo(testBroadcastName2);
3990     }
3991 
3992     @Test
testSyncHandleToBroadcastIdMap_getSyncHandleAndGetBroadcastId()3993     public void testSyncHandleToBroadcastIdMap_getSyncHandleAndGetBroadcastId() {
3994         final int testSyncHandle = 1;
3995         final int testSyncHandleInvalid = 2;
3996         final int testBroadcastId = 42;
3997         final int testBroadcastIdInvalid = 43;
3998 
3999         prepareConnectedDeviceGroup();
4000         startSearchingForSources();
4001         onScanResult(mSourceDevice, testBroadcastId);
4002         onSyncEstablished(mSourceDevice, testSyncHandle);
4003 
4004         assertThat(mBassClientService.getSyncHandleForBroadcastId(testBroadcastIdInvalid))
4005                 .isEqualTo(BassConstants.INVALID_SYNC_HANDLE);
4006         assertThat(mBassClientService.getBroadcastIdForSyncHandle(testSyncHandleInvalid))
4007                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
4008         assertThat(mBassClientService.getSyncHandleForBroadcastId(testBroadcastId))
4009                 .isEqualTo(testSyncHandle);
4010         assertThat(mBassClientService.getBroadcastIdForSyncHandle(testSyncHandle))
4011                 .isEqualTo(testBroadcastId);
4012     }
4013 
verifyAllGroupMembersGettingUpdateOrAddSource(BluetoothLeBroadcastMetadata meta)4014     private void verifyAllGroupMembersGettingUpdateOrAddSource(BluetoothLeBroadcastMetadata meta) {
4015         assertThat(mStateMachines).hasSize(2);
4016         for (BassClientStateMachine sm : mStateMachines.values()) {
4017             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
4018             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
4019             long count;
4020 
4021             if (sm.getDevice().equals(mCurrentDevice)) {
4022                 count =
4023                         messageCaptor.getAllValues().stream()
4024                                 .filter(
4025                                         m ->
4026                                                 ((m.what
4027                                                                         == BassClientStateMachine
4028                                                                                 .UPDATE_BCAST_SOURCE)
4029                                                                 && (m.obj.equals(meta))
4030                                                                 && (m.arg1 == TEST_SOURCE_ID)
4031                                                                 && (m.arg2
4032                                                                         == BassConstants
4033                                                                                 .PA_SYNC_PAST_AVAILABLE))
4034                                                         || ((m.what
4035                                                                         == BassClientStateMachine
4036                                                                                 .ADD_BCAST_SOURCE)
4037                                                                 && (m.obj.equals(meta)))
4038                                                         || ((m.what
4039                                                                         == BassClientStateMachine
4040                                                                                 .SWITCH_BCAST_SOURCE)
4041                                                                 && (m.obj.equals(meta))
4042                                                                 && (m.arg1 == TEST_SOURCE_ID)))
4043                                 .count();
4044                 assertThat(count).isEqualTo(1);
4045             } else if (sm.getDevice().equals(mCurrentDevice1)) {
4046                 count =
4047                         messageCaptor.getAllValues().stream()
4048                                 .filter(
4049                                         m ->
4050                                                 ((m.what
4051                                                                         == BassClientStateMachine
4052                                                                                 .UPDATE_BCAST_SOURCE)
4053                                                                 && (m.obj.equals(meta))
4054                                                                 && (m.arg1 == TEST_SOURCE_ID + 1)
4055                                                                 && (m.arg2
4056                                                                         == BassConstants
4057                                                                                 .PA_SYNC_PAST_AVAILABLE))
4058                                                         || ((m.what
4059                                                                         == BassClientStateMachine
4060                                                                                 .ADD_BCAST_SOURCE)
4061                                                                 && (m.obj.equals(meta)))
4062                                                         || ((m.what
4063                                                                         == BassClientStateMachine
4064                                                                                 .SWITCH_BCAST_SOURCE)
4065                                                                 && (m.obj.equals(meta))
4066                                                                 && (m.arg1 == TEST_SOURCE_ID + 1)))
4067                                 .count();
4068                 assertThat(count).isEqualTo(1);
4069             }
4070         }
4071     }
4072 
4073     @Test
testSuspendResumeSourceSynchronization()4074     public void testSuspendResumeSourceSynchronization() {
4075         prepareConnectedDeviceGroup();
4076         startSearchingForSources();
4077         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
4078         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
4079         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
4080         verifyAddSourceForGroup(meta);
4081         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ false);
4082 
4083         injectRemoteSourceStateChanged(meta, /* isPaSynced */ true, /* isBisSynced */ true);
4084         verify(mLeAudioService).activeBroadcastAssistantNotification(eq(true));
4085         Mockito.clearInvocations(mLeAudioService);
4086 
4087         /* Imitate broadcast source stop, sink notify about loosing PA and BIS sync */
4088         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
4089 
4090         /* Unicast would like to stream */
4091         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
4092 
4093         mBassClientService.resumeReceiversSourceSynchronization();
4094         handleHandoverSupport();
4095         verifyAllGroupMembersGettingUpdateOrAddSource(meta);
4096     }
4097 
4098     @Test
testHandleUnicastSourceStreamStatusChange()4099     public void testHandleUnicastSourceStreamStatusChange() {
4100         prepareConnectedDeviceGroup();
4101         startSearchingForSources();
4102         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
4103         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
4104         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
4105 
4106         /* Fake external broadcast - no Broadcast Metadata from LE Audio service */
4107         doReturn(new ArrayList<BluetoothLeBroadcastMetadata>())
4108                 .when(mLeAudioService)
4109                 .getAllBroadcastMetadata();
4110 
4111         verifyAddSourceForGroup(meta);
4112         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ true);
4113 
4114         verify(mLeAudioService).activeBroadcastAssistantNotification(eq(true));
4115 
4116         /* Unicast would like to stream */
4117         mBassClientService.handleUnicastSourceStreamStatusChange(
4118                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
4119 
4120         /* Imitate broadcast source stop, sink notify about loosing PA and BIS sync */
4121         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
4122 
4123         /* Unicast finished streaming */
4124         mBassClientService.handleUnicastSourceStreamStatusChange(
4125                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
4126 
4127         verifyAllGroupMembersGettingUpdateOrAddSource(meta);
4128 
4129         // Update receiver state with lost BIS sync
4130         injectRemoteSourceStateChanged(meta, /* isPaSynced */ true, /* isBisSynced */ false);
4131         if (!Flags.leaudioBroadcastResyncHelper()
4132                 && !Flags.leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()) {
4133             verify(mLeAudioService).activeBroadcastAssistantNotification(eq(false));
4134         }
4135     }
4136 
4137     @Test
testHandleUnicastSourceStreamStatusChange_MultipleRequests()4138     public void testHandleUnicastSourceStreamStatusChange_MultipleRequests() {
4139         prepareConnectedDeviceGroup();
4140         startSearchingForSources();
4141         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
4142         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
4143         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
4144 
4145         /* Fake external broadcast - no Broadcast Metadata from LE Audio service */
4146         doReturn(new ArrayList<BluetoothLeBroadcastMetadata>())
4147                 .when(mLeAudioService)
4148                 .getAllBroadcastMetadata();
4149 
4150         verifyAddSourceForGroup(meta);
4151         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ true);
4152 
4153         verify(mLeAudioService).activeBroadcastAssistantNotification(eq(true));
4154 
4155         /* Unicast would like to stream */
4156         mBassClientService.handleUnicastSourceStreamStatusChange(
4157                 3 /* STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE */);
4158 
4159         /* Imitate broadcast source stop, sink notify about loosing BIS sync */
4160         verifyModifyMessageAndInjectSourceModfified();
4161 
4162         assertThat(mStateMachines).hasSize(2);
4163         for (BassClientStateMachine sm : mStateMachines.values()) {
4164             Mockito.clearInvocations(sm);
4165         }
4166         // Make another stream request with no context validate
4167         mBassClientService.handleUnicastSourceStreamStatusChange(
4168                 3 /* STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE */);
4169 
4170         // Make another stream request
4171         mBassClientService.handleUnicastSourceStreamStatusChange(
4172                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
4173 
4174         /* Unicast finished streaming */
4175         mBassClientService.handleUnicastSourceStreamStatusChange(
4176                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
4177 
4178         // Verify all group members resume with the previous cached source
4179         for (BassClientStateMachine sm : mStateMachines.values()) {
4180             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
4181             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
4182 
4183             Message msg =
4184                     messageCaptor.getAllValues().stream()
4185                             .filter(
4186                                     m ->
4187                                             (m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE)
4188                                                     && (m.obj == meta))
4189                             .findFirst()
4190                             .orElse(null);
4191             expect.that(msg).isNotNull();
4192         }
4193     }
4194 
4195     @Test
testIsAnyReceiverActive()4196     public void testIsAnyReceiverActive() {
4197         prepareConnectedDeviceGroup();
4198         startSearchingForSources();
4199         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
4200         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
4201         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
4202         verifyAddSourceForGroup(meta);
4203         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
4204 
4205         List<BluetoothDevice> devices = mBassClientService.getConnectedDevices();
4206         // Verify isAnyReceiverActive returns false if no PA and no BIS synced
4207         assertThat(mBassClientService.isAnyReceiverActive(devices)).isFalse();
4208 
4209         // Update receiver state with PA sync
4210         injectRemoteSourceStateChanged(meta, /* isPaSynced */ true, /* isBisSynced */ false);
4211         BluetoothDevice invalidDevice = getTestDevice(2);
4212         // Verify isAnyReceiverActive returns false if invalid device
4213         expect.that(mBassClientService.isAnyReceiverActive(List.of(invalidDevice))).isFalse();
4214         // Verify isAnyReceiverActive returns true if PA synced
4215         expect.that(mBassClientService.isAnyReceiverActive(devices)).isTrue();
4216 
4217         // Update receiver state with PA and BIS sync
4218         injectRemoteSourceStateChanged(meta, /* isPaSynced */ true, /* isBisSynced */ true);
4219         // Verify isAnyReceiverActive returns true if PA and BIS synced
4220         expect.that(mBassClientService.isAnyReceiverActive(devices)).isTrue();
4221 
4222         // Update receiver state with BIS only sync
4223         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ true);
4224         // Verify isAnyReceiverActive returns true if BIS only synced
4225         expect.that(mBassClientService.isAnyReceiverActive(devices)).isTrue();
4226     }
4227 
4228     @Test
testGetSyncedBroadcastSinks()4229     public void testGetSyncedBroadcastSinks() {
4230         prepareConnectedDeviceGroup();
4231         startSearchingForSources();
4232         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
4233         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
4234         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
4235         BluetoothLeBroadcastMetadata metaNoBroadcast = createEmptyBroadcastMetadata();
4236 
4237         verifyAddSourceForGroup(meta);
4238         prepareRemoteSourceState(metaNoBroadcast, /* isPaSynced */ true, /* isBisSynced */ false);
4239 
4240         // Verify getSyncedBroadcastSinks returns empty device list if no broadcast ID
4241         assertThat(mBassClientService.getSyncedBroadcastSinks().isEmpty()).isTrue();
4242         assertThat(mBassClientService.getSyncedBroadcastSinks(TEST_BROADCAST_ID).isEmpty())
4243                 .isTrue();
4244 
4245         // Update receiver state with broadcast ID
4246         injectRemoteSourceStateChanged(meta, /* isPaSynced */ true, /* isBisSynced */ false);
4247 
4248         List<BluetoothDevice> activeSinks = mBassClientService.getSyncedBroadcastSinks();
4249         if (Flags.leaudioBigDependsOnAudioState()) {
4250             // Verify getSyncedBroadcastSinks returns correct device list if no BIS synced
4251             assertThat(activeSinks).hasSize(2);
4252             assertThat(activeSinks.contains(mCurrentDevice)).isTrue();
4253             assertThat(activeSinks.contains(mCurrentDevice1)).isTrue();
4254         } else {
4255             // Verify getSyncedBroadcastSinks returns empty device list if no BIS synced
4256             assertThat(mBassClientService.getSyncedBroadcastSinks().isEmpty()).isTrue();
4257         }
4258 
4259         activeSinks.clear();
4260         // Verify getSyncedBroadcastSinks by broadcast id
4261         activeSinks = mBassClientService.getSyncedBroadcastSinks(TEST_BROADCAST_ID);
4262         if (Flags.leaudioBigDependsOnAudioState()) {
4263             // Verify getSyncedBroadcastSinks returns correct device list if no BIS synced
4264             assertThat(activeSinks.size()).isEqualTo(2);
4265             assertThat(activeSinks.contains(mCurrentDevice)).isTrue();
4266             assertThat(activeSinks.contains(mCurrentDevice1)).isTrue();
4267         }
4268 
4269         // Update receiver state with BIS sync
4270         injectRemoteSourceStateChanged(meta, /* isPaSynced */ true, /* isBisSynced */ true);
4271 
4272         // Verify getSyncedBroadcastSinks returns correct device list if BIS synced
4273         activeSinks = mBassClientService.getSyncedBroadcastSinks();
4274         expect.that(activeSinks.size()).isEqualTo(2);
4275         expect.that(activeSinks.contains(mCurrentDevice)).isTrue();
4276         expect.that(activeSinks.contains(mCurrentDevice1)).isTrue();
4277     }
4278 
prepareTwoSynchronizedDevicesForLocalBroadcast()4279     private void prepareTwoSynchronizedDevicesForLocalBroadcast() throws RemoteException {
4280         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
4281 
4282         doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta)))
4283                 .when(mLeAudioService)
4284                 .getAllBroadcastMetadata();
4285         prepareConnectedDeviceGroup();
4286         verifyAddSourceForGroup(meta);
4287         for (BassClientStateMachine sm : mStateMachines.values()) {
4288             if (sm.getDevice().equals(mCurrentDevice)) {
4289                 injectRemoteSourceStateSourceAdded(
4290                         sm,
4291                         meta,
4292                         TEST_SOURCE_ID,
4293                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
4294                         meta.isEncrypted()
4295                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
4296                                 : BluetoothLeBroadcastReceiveState
4297                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
4298                         null);
4299                 // verify source id
4300                 verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce())
4301                         .onSourceAdded(
4302                                 eq(mCurrentDevice),
4303                                 eq(TEST_SOURCE_ID),
4304                                 eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST));
4305             } else if (sm.getDevice().equals(mCurrentDevice1)) {
4306                 injectRemoteSourceStateSourceAdded(
4307                         sm,
4308                         meta,
4309                         TEST_SOURCE_ID + 1,
4310                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
4311                         meta.isEncrypted()
4312                                 ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
4313                                 : BluetoothLeBroadcastReceiveState
4314                                         .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
4315                         null);
4316                 // verify source id
4317                 verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce())
4318                         .onSourceAdded(
4319                                 eq(mCurrentDevice1),
4320                                 eq(TEST_SOURCE_ID + 1),
4321                                 eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST));
4322             }
4323         }
4324     }
4325 
4326     @Test
testLocalAddSourceWhenBroadcastIsPlaying()4327     public void testLocalAddSourceWhenBroadcastIsPlaying() throws RemoteException {
4328         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4329         if (Flags.leaudioBigDependsOnAudioState()) {
4330             doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);
4331         }
4332 
4333         prepareTwoSynchronizedDevicesForLocalBroadcast();
4334     }
4335 
4336     @Test
4337     @EnableFlags({Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE})
testLocalAddSourceWhenBroadcastIsPaused()4338     public void testLocalAddSourceWhenBroadcastIsPaused() throws RemoteException {
4339         doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4340         doReturn(true).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);
4341 
4342         prepareTwoSynchronizedDevicesForLocalBroadcast();
4343     }
4344 
4345     @Test
testLocalAddSourceWhenBroadcastIsStopped()4346     public void testLocalAddSourceWhenBroadcastIsStopped() throws RemoteException {
4347         doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4348         if (Flags.leaudioBigDependsOnAudioState()) {
4349             doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);
4350         }
4351 
4352         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
4353 
4354         doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta)))
4355                 .when(mLeAudioService)
4356                 .getAllBroadcastMetadata();
4357         prepareConnectedDeviceGroup();
4358         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
4359         verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce())
4360                 .onSourceAddFailed(
4361                         eq(mCurrentDevice),
4362                         eq(meta),
4363                         eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
4364     }
4365 
4366     @Test
testSinksDisconnectionWhenBroadcastIsPlaying()4367     public void testSinksDisconnectionWhenBroadcastIsPlaying() throws RemoteException {
4368         /* Imitate broadcast being active */
4369         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4370         if (Flags.leaudioBigDependsOnAudioState()) {
4371             doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);
4372         }
4373 
4374         prepareTwoSynchronizedDevicesForLocalBroadcast();
4375 
4376         /* Imitate scenario when if there would be broadcast - stop would be called */
4377         mBassClientService.handleDeviceDisconnection(mCurrentDevice, true);
4378         mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true);
4379 
4380         verify(mLeAudioService).stopBroadcast(eq(TEST_BROADCAST_ID));
4381     }
4382 
4383     @Test
4384     @EnableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE)
testSinksDisconnectionWhenBroadcastIsPaused()4385     public void testSinksDisconnectionWhenBroadcastIsPaused() throws RemoteException {
4386         /* Imitate broadcast being active */
4387         doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4388         doReturn(true).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);
4389 
4390         prepareTwoSynchronizedDevicesForLocalBroadcast();
4391 
4392         /* Imitate scenario when if there would be broadcast - stop would be called */
4393         mBassClientService.handleDeviceDisconnection(mCurrentDevice, true);
4394         mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true);
4395 
4396         verify(mLeAudioService).stopBroadcast(eq(TEST_BROADCAST_ID));
4397     }
4398 
4399     @Test
testSinksDisconnectionWhenBroadcastIsStopped()4400     public void testSinksDisconnectionWhenBroadcastIsStopped() throws RemoteException {
4401         /* Imitate broadcast being active */
4402         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4403         if (Flags.leaudioBigDependsOnAudioState()) {
4404             doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);
4405         }
4406 
4407         prepareTwoSynchronizedDevicesForLocalBroadcast();
4408 
4409         doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4410 
4411         /* Imitate scenario when if there would be broadcast - stop would be called */
4412         mBassClientService.handleDeviceDisconnection(mCurrentDevice, true);
4413         mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true);
4414 
4415         verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID));
4416     }
4417 
4418     @Test
testPrivateBroadcastIntentionalDisconnection()4419     public void testPrivateBroadcastIntentionalDisconnection() throws RemoteException {
4420         /* Imitate broadcast being active */
4421         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4422 
4423         prepareTwoSynchronizedDevicesForLocalBroadcast();
4424 
4425         /* Imitate devices being primary */
4426         doReturn(true).when(mLeAudioService).isPrimaryDevice(mCurrentDevice);
4427         doReturn(true).when(mLeAudioService).isPrimaryDevice(mCurrentDevice1);
4428 
4429         /* Imitate device 1/2 disconnection from StateMachine context */
4430         mBassClientService.handleDeviceDisconnection(mCurrentDevice, true);
4431 
4432         /* After first device disconnection and de-synchronization expect not stopping broadcast */
4433         verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID));
4434 
4435         /* Imitate first device being in disconnected state */
4436         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
4437 
4438         /* Imitate device 2/2 disconnection from StateMachine context */
4439         mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true);
4440 
4441         /* After second device disconnection and de-synchronization expect stopping broadcast */
4442         verify(mLeAudioService).stopBroadcast(eq(TEST_BROADCAST_ID));
4443     }
4444 
4445     @Test
testPrivateBroadcastUnintentionalDisconnection()4446     public void testPrivateBroadcastUnintentionalDisconnection() throws RemoteException {
4447         /* Imitate broadcast being active */
4448         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4449 
4450         prepareTwoSynchronizedDevicesForLocalBroadcast();
4451 
4452         /* Imitate devices being primary */
4453         doReturn(true).when(mLeAudioService).isPrimaryDevice(mCurrentDevice);
4454         doReturn(true).when(mLeAudioService).isPrimaryDevice(mCurrentDevice1);
4455 
4456         /* Imitate device 1/2 disconnection from StateMachine context */
4457         mBassClientService.handleDeviceDisconnection(mCurrentDevice, false);
4458 
4459         /* After first device disconnection and de-synchronization expect not stopping broadcast */
4460         verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID));
4461 
4462         /* Imitate first device being in disconnected state */
4463         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
4464 
4465         /* Imitate device 2/2 disconnection from StateMachine context */
4466         mBassClientService.handleDeviceDisconnection(mCurrentDevice1, false);
4467 
4468         /* After second device disconnection and de-synchronization expect stopping broadcast */
4469         verify(mLeAudioService).stopBroadcast(eq(TEST_BROADCAST_ID));
4470     }
4471 
4472     @Test
testAudioSharingIntentionalDisconnection()4473     public void testAudioSharingIntentionalDisconnection() throws RemoteException {
4474         /* Imitate broadcast being active */
4475         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4476 
4477         prepareTwoSynchronizedDevicesForLocalBroadcast();
4478 
4479         /* Imitate devices being primary */
4480         doReturn(true).when(mLeAudioService).isPrimaryDevice(mCurrentDevice);
4481         doReturn(false).when(mLeAudioService).isPrimaryDevice(mCurrentDevice1);
4482 
4483         /* Imitate device 1/2 disconnection from StateMachine context */
4484         mBassClientService.handleDeviceDisconnection(mCurrentDevice, true);
4485 
4486         /* After first device disconnection and de-synchronization expect stopping broadcast */
4487         verify(mLeAudioService).stopBroadcast(eq(TEST_BROADCAST_ID));
4488 
4489         /* Imitate first device being in disconnected state */
4490         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
4491 
4492         /* Imitate device 2/2 disconnection from StateMachine context */
4493         mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true);
4494 
4495         /* After second device disconnection and de-synchronization expect not stopping broadcast */
4496         verify(mLeAudioService).stopBroadcast(eq(TEST_BROADCAST_ID));
4497     }
4498 
4499     @Test
testAudioSharingUnintentionalDisconnection()4500     public void testAudioSharingUnintentionalDisconnection() throws RemoteException {
4501         /* Imitate broadcast being active */
4502         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4503 
4504         prepareTwoSynchronizedDevicesForLocalBroadcast();
4505 
4506         /* Imitate devices being primary */
4507         doReturn(true).when(mLeAudioService).isPrimaryDevice(mCurrentDevice);
4508         doReturn(false).when(mLeAudioService).isPrimaryDevice(mCurrentDevice1);
4509 
4510         /* Imitate device 1/2 disconnection from StateMachine context */
4511         mBassClientService.handleDeviceDisconnection(mCurrentDevice, false);
4512 
4513         /* After first device disconnection and de-synchronization expect not stopping broadcast */
4514         verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID));
4515 
4516         /* Imitate first device being in disconnected state */
4517         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
4518 
4519         /* Imitate device 2/2 disconnection from StateMachine context */
4520         mBassClientService.handleDeviceDisconnection(mCurrentDevice1, false);
4521 
4522         /* After second device disconnection and de-synchronization timeout to be fired */
4523         verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID));
4524     }
4525 
4526     @Test
testNotifyBroadcastStateChangedStopped()4527     public void testNotifyBroadcastStateChangedStopped() throws RemoteException {
4528         /* Imitate broadcast being active */
4529         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
4530 
4531         prepareTwoSynchronizedDevicesForLocalBroadcast();
4532 
4533         mBassClientService.notifyBroadcastStateChanged(
4534                 0 /* BROADCAST_STATE_STOPPED */, TEST_BROADCAST_ID);
4535 
4536         /* Imitate scenario when if there would be broadcast - stop would be called */
4537         mBassClientService.handleDeviceDisconnection(mCurrentDevice, true);
4538         mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true);
4539 
4540         /* After second device disconnection and de-synchronization expect not calling broadcast to
4541          * stop due to previous broadcast stream stopped */
4542         verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID));
4543     }
4544 
4545     @Test
onPeriodicAdvertisingReport_withoutBaseData_cancelActiveSync()4546     public void onPeriodicAdvertisingReport_withoutBaseData_cancelActiveSync() {
4547         prepareConnectedDeviceGroup();
4548         startSearchingForSources();
4549         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
4550         mInOrderMethodProxy
4551                 .verify(mMethodProxy)
4552                 .periodicAdvertisingManagerRegisterSync(
4553                         any(), any(), anyInt(), anyInt(), any(), any());
4554         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
4555         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
4556         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4557         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4558                 .isEqualTo(mSourceDevice);
4559         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4560                 .isEqualTo(TEST_BROADCAST_ID);
4561         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
4562 
4563         byte[] scanRecord =
4564                 new byte[] {
4565                     0x02,
4566                     0x01,
4567                     0x1a, // advertising flags
4568                     0x05,
4569                     0x02,
4570                     0x0b,
4571                     0x11,
4572                     0x0a,
4573                     0x11, // 16 bit service uuids
4574                     0x04,
4575                     0x09,
4576                     0x50,
4577                     0x65,
4578                     0x64, // name
4579                     0x02,
4580                     0x0A,
4581                     (byte) 0xec, // tx power level
4582                     0x05,
4583                     0x16,
4584                     0x0b,
4585                     0x11,
4586                     0x50,
4587                     0x64, // service data
4588                     0x05,
4589                     (byte) 0xff,
4590                     (byte) 0xe0,
4591                     0x00,
4592                     0x02,
4593                     0x15, // manufacturer specific data
4594                     0x03,
4595                     0x50,
4596                     0x01,
4597                     0x02, // an unknown data type won't cause trouble
4598                 };
4599         PeriodicAdvertisingReport report =
4600                 new PeriodicAdvertisingReport(
4601                         TEST_SYNC_HANDLE, 0, 0, 0, ScanRecord.parseFromBytes(scanRecord));
4602 
4603         BassClientService.PACallback callback = mBassClientService.new PACallback();
4604 
4605         callback.onPeriodicAdvertisingReport(report);
4606         callback.onPeriodicAdvertisingReport(report);
4607         callback.onPeriodicAdvertisingReport(report);
4608         callback.onPeriodicAdvertisingReport(report);
4609 
4610         // Not canceled, not updated base
4611         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
4612         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4613         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4614                 .isEqualTo(mSourceDevice);
4615         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4616                 .isEqualTo(TEST_BROADCAST_ID);
4617         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
4618         mInOrderMethodProxy
4619                 .verify(mMethodProxy, never())
4620                 .periodicAdvertisingManagerUnregisterSync(any(), any());
4621 
4622         callback.onPeriodicAdvertisingReport(report);
4623 
4624         // Canceled, not updated base
4625         expect.that(mBassClientService.getActiveSyncedSources()).isEmpty();
4626         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
4627         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4628                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
4629         expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
4630         mInOrderMethodProxy
4631                 .verify(mMethodProxy)
4632                 .periodicAdvertisingManagerUnregisterSync(any(), any());
4633     }
4634 
4635     @Test
onPeriodicAdvertisingReport_wrongBaseData_cancelActiveSync()4636     public void onPeriodicAdvertisingReport_wrongBaseData_cancelActiveSync() {
4637         prepareConnectedDeviceGroup();
4638         startSearchingForSources();
4639         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
4640         mInOrderMethodProxy
4641                 .verify(mMethodProxy)
4642                 .periodicAdvertisingManagerRegisterSync(
4643                         any(), any(), anyInt(), anyInt(), any(), any());
4644         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
4645         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
4646         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4647         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4648                 .isEqualTo(mSourceDevice);
4649         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4650                 .isEqualTo(TEST_BROADCAST_ID);
4651         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
4652 
4653         byte[] scanRecord =
4654                 new byte[] {
4655                     (byte) 0x02,
4656                     (byte) 0x01,
4657                     (byte) 0x1a, // advertising flags
4658                     (byte) 0x05,
4659                     (byte) 0x02,
4660                     (byte) 0x51,
4661                     (byte) 0x18,
4662                     (byte) 0x0a,
4663                     (byte) 0x11, // 16 bit service uuids
4664                     (byte) 0x04,
4665                     (byte) 0x09,
4666                     (byte) 0x50,
4667                     (byte) 0x65,
4668                     (byte) 0x64, // name
4669                     (byte) 0x02,
4670                     (byte) 0x0A,
4671                     (byte) 0xec, // tx power level
4672                     (byte) 0x19,
4673                     (byte) 0x16,
4674                     (byte) 0x51,
4675                     (byte) 0x18, // service data (base data with 18 bytes)
4676                     // LEVEL 1
4677                     (byte) 0x01,
4678                     (byte) 0x02,
4679                     (byte) 0x03, // mPresentationDelay
4680                     (byte) 0x01, // mNumSubGroups
4681                     // LEVEL 3
4682                     (byte) 0x04, // mIndex
4683                     (byte) 0x03, // mCodecConfigLength
4684                     (byte) 0x02,
4685                     (byte) 'B',
4686                     (byte) 'C', // mCodecConfigInfo
4687                     (byte) 0x05,
4688                     (byte) 0xff,
4689                     (byte) 0xe0,
4690                     (byte) 0x00,
4691                     (byte) 0x02,
4692                     (byte) 0x15, // manufacturer specific data
4693                     (byte) 0x03,
4694                     (byte) 0x50,
4695                     (byte) 0x01,
4696                     (byte) 0x02, // an unknown data type won't cause trouble
4697                 };
4698         PeriodicAdvertisingReport report =
4699                 new PeriodicAdvertisingReport(
4700                         TEST_SYNC_HANDLE, 0, 0, 0, ScanRecord.parseFromBytes(scanRecord));
4701 
4702         BassClientService.PACallback callback = mBassClientService.new PACallback();
4703 
4704         callback.onPeriodicAdvertisingReport(report);
4705         callback.onPeriodicAdvertisingReport(report);
4706         callback.onPeriodicAdvertisingReport(report);
4707         callback.onPeriodicAdvertisingReport(report);
4708 
4709         // Not canceled, not updated base
4710         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
4711         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4712         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4713                 .isEqualTo(mSourceDevice);
4714         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4715                 .isEqualTo(TEST_BROADCAST_ID);
4716         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
4717         mInOrderMethodProxy
4718                 .verify(mMethodProxy, never())
4719                 .periodicAdvertisingManagerUnregisterSync(any(), any());
4720 
4721         callback.onPeriodicAdvertisingReport(report);
4722 
4723         // Canceled, not updated base
4724         expect.that(mBassClientService.getActiveSyncedSources()).isEmpty();
4725         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
4726         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4727                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
4728         expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
4729         mInOrderMethodProxy
4730                 .verify(mMethodProxy)
4731                 .periodicAdvertisingManagerUnregisterSync(any(), any());
4732     }
4733 
4734     @Test
onPeriodicAdvertisingReport_updateBase()4735     public void onPeriodicAdvertisingReport_updateBase() {
4736         prepareConnectedDeviceGroup();
4737         startSearchingForSources();
4738         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
4739         mInOrderMethodProxy
4740                 .verify(mMethodProxy)
4741                 .periodicAdvertisingManagerRegisterSync(
4742                         any(), any(), anyInt(), anyInt(), any(), any());
4743         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
4744         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
4745         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4746         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4747                 .isEqualTo(mSourceDevice);
4748         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4749                 .isEqualTo(TEST_BROADCAST_ID);
4750         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
4751 
4752         onPeriodicAdvertisingReport();
4753 
4754         // Not canceled, updated base
4755         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
4756         expect.that(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4757         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4758                 .isEqualTo(mSourceDevice);
4759         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4760                 .isEqualTo(TEST_BROADCAST_ID);
4761         expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
4762         mInOrderMethodProxy
4763                 .verify(mMethodProxy, never())
4764                 .periodicAdvertisingManagerUnregisterSync(any(), any());
4765     }
4766 
4767     @Test
onPeriodicAdvertisingReport_updateBaseAfterWrongBaseData()4768     public void onPeriodicAdvertisingReport_updateBaseAfterWrongBaseData() {
4769         prepareConnectedDeviceGroup();
4770         startSearchingForSources();
4771         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
4772         mInOrderMethodProxy
4773                 .verify(mMethodProxy)
4774                 .periodicAdvertisingManagerRegisterSync(
4775                         any(), any(), anyInt(), anyInt(), any(), any());
4776         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
4777         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
4778         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4779         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4780                 .isEqualTo(mSourceDevice);
4781         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4782                 .isEqualTo(TEST_BROADCAST_ID);
4783         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
4784 
4785         byte[] scanRecordNoBaseData =
4786                 new byte[] {
4787                     0x02,
4788                     0x01,
4789                     0x1a, // advertising flags
4790                     0x05,
4791                     0x02,
4792                     0x0b,
4793                     0x11,
4794                     0x0a,
4795                     0x11, // 16 bit service uuids
4796                     0x04,
4797                     0x09,
4798                     0x50,
4799                     0x65,
4800                     0x64, // name
4801                     0x02,
4802                     0x0A,
4803                     (byte) 0xec, // tx power level
4804                     0x05,
4805                     0x16,
4806                     0x0b,
4807                     0x11,
4808                     0x50,
4809                     0x64, // service data
4810                     0x05,
4811                     (byte) 0xff,
4812                     (byte) 0xe0,
4813                     0x00,
4814                     0x02,
4815                     0x15, // manufacturer specific data
4816                     0x03,
4817                     0x50,
4818                     0x01,
4819                     0x02, // an unknown data type won't cause trouble
4820                 };
4821 
4822         byte[] scanRecordWrongBaseData =
4823                 new byte[] {
4824                     (byte) 0x02,
4825                     (byte) 0x01,
4826                     (byte) 0x1a, // advertising flags
4827                     (byte) 0x05,
4828                     (byte) 0x02,
4829                     (byte) 0x51,
4830                     (byte) 0x18,
4831                     (byte) 0x0a,
4832                     (byte) 0x11, // 16 bit service uuids
4833                     (byte) 0x04,
4834                     (byte) 0x09,
4835                     (byte) 0x50,
4836                     (byte) 0x65,
4837                     (byte) 0x64, // name
4838                     (byte) 0x02,
4839                     (byte) 0x0A,
4840                     (byte) 0xec, // tx power level
4841                     (byte) 0x19,
4842                     (byte) 0x16,
4843                     (byte) 0x51,
4844                     (byte) 0x18, // service data (base data with 18 bytes)
4845                     // LEVEL 1
4846                     (byte) 0x01,
4847                     (byte) 0x02,
4848                     (byte) 0x03, // mPresentationDelay
4849                     (byte) 0x01, // mNumSubGroups
4850                     // LEVEL 3
4851                     (byte) 0x04, // mIndex
4852                     (byte) 0x03, // mCodecConfigLength
4853                     (byte) 0x02,
4854                     (byte) 'B',
4855                     (byte) 'C', // mCodecConfigInfo
4856                     (byte) 0x05,
4857                     (byte) 0xff,
4858                     (byte) 0xe0,
4859                     (byte) 0x00,
4860                     (byte) 0x02,
4861                     (byte) 0x15, // manufacturer specific data
4862                     (byte) 0x03,
4863                     (byte) 0x50,
4864                     (byte) 0x01,
4865                     (byte) 0x02, // an unknown data type won't cause trouble
4866                 };
4867 
4868         BassClientService.PACallback callback = mBassClientService.new PACallback();
4869 
4870         PeriodicAdvertisingReport report =
4871                 new PeriodicAdvertisingReport(
4872                         TEST_SYNC_HANDLE, 0, 0, 0, ScanRecord.parseFromBytes(scanRecordNoBaseData));
4873         callback.onPeriodicAdvertisingReport(report);
4874         report =
4875                 new PeriodicAdvertisingReport(
4876                         TEST_SYNC_HANDLE,
4877                         0,
4878                         0,
4879                         0,
4880                         ScanRecord.parseFromBytes(scanRecordWrongBaseData));
4881         callback.onPeriodicAdvertisingReport(report);
4882 
4883         // Not canceled, not updated base
4884         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
4885         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4886         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4887                 .isEqualTo(mSourceDevice);
4888         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4889                 .isEqualTo(TEST_BROADCAST_ID);
4890         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
4891         mInOrderMethodProxy
4892                 .verify(mMethodProxy, never())
4893                 .periodicAdvertisingManagerUnregisterSync(any(), any());
4894 
4895         onPeriodicAdvertisingReport();
4896 
4897         // Not canceled, updated base
4898         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
4899         expect.that(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4900         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4901                 .isEqualTo(mSourceDevice);
4902         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4903                 .isEqualTo(TEST_BROADCAST_ID);
4904         expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
4905         mInOrderMethodProxy
4906                 .verify(mMethodProxy, never())
4907                 .periodicAdvertisingManagerUnregisterSync(any(), any());
4908     }
4909 
4910     @Test
notifySourceFound_once_updateRssi()4911     public void notifySourceFound_once_updateRssi() throws RemoteException {
4912         prepareConnectedDeviceGroup();
4913         startSearchingForSources();
4914         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
4915         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
4916         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
4917         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4918         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4919                 .isEqualTo(mSourceDevice);
4920         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4921                 .isEqualTo(TEST_BROADCAST_ID);
4922         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
4923 
4924         onPeriodicAdvertisingReport();
4925 
4926         // Not canceled, updated base
4927         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
4928         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
4929         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
4930                 .isEqualTo(mSourceDevice);
4931         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
4932                 .isEqualTo(TEST_BROADCAST_ID);
4933         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
4934 
4935         if (!Flags.leaudioBigDependsOnAudioState()) {
4936             onBigInfoAdvertisingReport();
4937         }
4938 
4939         // Notified
4940         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
4941         ArgumentCaptor<BluetoothLeBroadcastMetadata> metaData =
4942                 ArgumentCaptor.forClass(BluetoothLeBroadcastMetadata.class);
4943         InOrder inOrder = inOrder(mCallback);
4944         inOrder.verify(mCallback).onSourceFound(metaData.capture());
4945         assertThat(metaData.getValue().getRssi()).isEqualTo(TEST_RSSI);
4946 
4947         // Any of them should not notified second time
4948         onPeriodicAdvertisingReport();
4949         onBigInfoAdvertisingReport();
4950 
4951         // Not notified second time
4952         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
4953         inOrder.verify(mCallback, never()).onSourceFound(any());
4954     }
4955 
4956     @Test
4957     @EnableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE)
notifySourceFound_without_public_announcement()4958     public void notifySourceFound_without_public_announcement() throws RemoteException {
4959         prepareConnectedDeviceGroup();
4960         startSearchingForSources();
4961 
4962         byte[] broadcastScanRecord =
4963                 new byte[] {
4964                     0x02,
4965                     0x01,
4966                     0x1a, // advertising flags
4967                     0x05,
4968                     0x02,
4969                     0x52,
4970                     0x18,
4971                     0x0a,
4972                     0x11, // 16 bit service uuids
4973                     0x04,
4974                     0x09,
4975                     0x50,
4976                     0x65,
4977                     0x64, // name
4978                     0x02,
4979                     0x0A,
4980                     (byte) 0xec, // tx power level
4981                     0x05,
4982                     0x30,
4983                     0x54,
4984                     0x65,
4985                     0x73,
4986                     0x74, // broadcast name: Test
4987                     0x06,
4988                     0x16,
4989                     0x52,
4990                     0x18,
4991                     (byte) TEST_BROADCAST_ID,
4992                     (byte) (TEST_BROADCAST_ID >> 8),
4993                     (byte) (TEST_BROADCAST_ID >> 16), // service data, broadcast id
4994                     0x05,
4995                     (byte) 0xff,
4996                     (byte) 0xe0,
4997                     0x00,
4998                     0x02,
4999                     0x15, // manufacturer specific data
5000                     0x03,
5001                     0x50,
5002                     0x01,
5003                     0x02, // an unknown data type won't cause trouble
5004                 };
5005         ScanResult scanResult =
5006                 new ScanResult(
5007                         mSourceDevice,
5008                         0,
5009                         0,
5010                         0,
5011                         0,
5012                         0,
5013                         TEST_RSSI,
5014                         0,
5015                         ScanRecord.parseFromBytes(broadcastScanRecord),
5016                         0);
5017         generateScanResult(scanResult);
5018 
5019         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5020         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
5021         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
5022         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
5023                 .isEqualTo(mSourceDevice);
5024         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
5025                 .isEqualTo(TEST_BROADCAST_ID);
5026         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
5027 
5028         // No public announcement so it will not notify
5029         onPeriodicAdvertisingReport();
5030 
5031         // Not canceled, updated base
5032         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
5033         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
5034         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
5035                 .isEqualTo(mSourceDevice);
5036         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
5037                 .isEqualTo(TEST_BROADCAST_ID);
5038         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
5039 
5040         // Not notified
5041         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5042         InOrder inOrder = inOrder(mCallback);
5043         inOrder.verify(mCallback, never()).onSourceFound(any());
5044 
5045         // onBigInfoAdvertisingReport causes notification
5046         onBigInfoAdvertisingReport();
5047 
5048         // Notified
5049         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5050         inOrder.verify(mCallback).onSourceFound(any());
5051     }
5052 
5053     @Test
notifySourceFound_periodic_after_big()5054     public void notifySourceFound_periodic_after_big() throws RemoteException {
5055         prepareConnectedDeviceGroup();
5056         startSearchingForSources();
5057         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
5058         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5059         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
5060         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
5061         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
5062                 .isEqualTo(mSourceDevice);
5063         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
5064                 .isEqualTo(TEST_BROADCAST_ID);
5065         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
5066 
5067         // Big report before periodic so before base update
5068         onBigInfoAdvertisingReport();
5069 
5070         // Not notified
5071         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5072         InOrder inOrder = inOrder(mCallback);
5073         inOrder.verify(mCallback, never()).onSourceFound(any());
5074 
5075         onPeriodicAdvertisingReport();
5076 
5077         // Not canceled, updated base
5078         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
5079         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
5080         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
5081                 .isEqualTo(mSourceDevice);
5082         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
5083                 .isEqualTo(TEST_BROADCAST_ID);
5084         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
5085 
5086         if (!Flags.leaudioBigDependsOnAudioState()) {
5087             // onBigInfoAdvertisingReport causes notification
5088             onBigInfoAdvertisingReport();
5089         }
5090 
5091         // Notified
5092         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5093         inOrder.verify(mCallback).onSourceFound(any());
5094     }
5095 
5096     @Test
5097     @EnableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE)
notifySourceFound_periodic_after_wrong_periodic()5098     public void notifySourceFound_periodic_after_wrong_periodic() throws RemoteException {
5099         prepareConnectedDeviceGroup();
5100         startSearchingForSources();
5101         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
5102         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5103         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
5104         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
5105         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
5106                 .isEqualTo(mSourceDevice);
5107         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
5108                 .isEqualTo(TEST_BROADCAST_ID);
5109         assertThat(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNull();
5110 
5111         byte[] scanRecordNoBaseData =
5112                 new byte[] {
5113                     0x02,
5114                     0x01,
5115                     0x1a, // advertising flags
5116                     0x05,
5117                     0x02,
5118                     0x0b,
5119                     0x11,
5120                     0x0a,
5121                     0x11, // 16 bit service uuids
5122                     0x04,
5123                     0x09,
5124                     0x50,
5125                     0x65,
5126                     0x64, // name
5127                     0x02,
5128                     0x0A,
5129                     (byte) 0xec, // tx power level
5130                     0x05,
5131                     0x16,
5132                     0x0b,
5133                     0x11,
5134                     0x50,
5135                     0x64, // service data
5136                     0x05,
5137                     (byte) 0xff,
5138                     (byte) 0xe0,
5139                     0x00,
5140                     0x02,
5141                     0x15, // manufacturer specific data
5142                     0x03,
5143                     0x50,
5144                     0x01,
5145                     0x02, // an unknown data type won't cause trouble
5146                 };
5147 
5148         BassClientService.PACallback callback = mBassClientService.new PACallback();
5149 
5150         PeriodicAdvertisingReport report =
5151                 new PeriodicAdvertisingReport(
5152                         TEST_SYNC_HANDLE, 0, 0, 0, ScanRecord.parseFromBytes(scanRecordNoBaseData));
5153 
5154         // Wrong base data not cause notification
5155         callback.onPeriodicAdvertisingReport(report);
5156 
5157         // Not notified
5158         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5159         InOrder inOrder = inOrder(mCallback);
5160         inOrder.verify(mCallback, never()).onSourceFound(any());
5161 
5162         onPeriodicAdvertisingReport();
5163 
5164         // Not canceled, updated base
5165         expect.that(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
5166         expect.that(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
5167         expect.that(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
5168                 .isEqualTo(mSourceDevice);
5169         expect.that(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
5170                 .isEqualTo(TEST_BROADCAST_ID);
5171         expect.that(mBassClientService.getBase(TEST_SYNC_HANDLE)).isNotNull();
5172 
5173         // Notified
5174         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5175         inOrder.verify(mCallback).onSourceFound(any());
5176     }
5177 
5178     @Test
notifySourceFound_alreadySynced_clearFlag()5179     public void notifySourceFound_alreadySynced_clearFlag() throws RemoteException {
5180         // Scan
5181         prepareConnectedDeviceGroup();
5182         startSearchingForSources();
5183         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
5184 
5185         // Source synced
5186         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5187 
5188         onPeriodicAdvertisingReport();
5189         if (!Flags.leaudioBigDependsOnAudioState()) {
5190             // onBigInfoAdvertisingReport causes notification
5191             onBigInfoAdvertisingReport();
5192         }
5193 
5194         // Notified
5195         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5196         InOrder inOrder = inOrder(mCallback);
5197         inOrder.verify(mCallback).onSourceFound(any());
5198 
5199         // Stop searching, unsyc all broadcasters and clear all data except mCachedBroadcasts
5200         mBassClientService.stopSearchingForSources();
5201 
5202         // Add source to unsynced broadcast, causes synchronization first
5203         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
5204         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
5205         handleHandoverSupport();
5206 
5207         // Source synced
5208         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5209 
5210         onPeriodicAdvertisingReport();
5211         if (!Flags.leaudioBigDependsOnAudioState()) {
5212             // onBigInfoAdvertisingReport causes notification
5213             onBigInfoAdvertisingReport();
5214         }
5215 
5216         // Notified
5217         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5218         inOrder.verify(mCallback).onSourceFound(any());
5219 
5220         // Start searching again clears timeout, mCachedBroadcasts and notifiedFlags but keep syncs
5221         startSearchingForSources();
5222 
5223         onPeriodicAdvertisingReport();
5224         if (!Flags.leaudioBigDependsOnAudioState()) {
5225             // onBigInfoAdvertisingReport should notified again
5226             onBigInfoAdvertisingReport();
5227         }
5228         // Notified
5229         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5230         inOrder.verify(mCallback).onSourceFound(any());
5231     }
5232 
5233     @Test
5234     @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
onSyncLost_notifySourceLostAndCancelSync_noResyncFlag()5235     public void onSyncLost_notifySourceLostAndCancelSync_noResyncFlag() throws RemoteException {
5236         prepareConnectedDeviceGroup();
5237         startSearchingForSources();
5238         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
5239         mInOrderMethodProxy
5240                 .verify(mMethodProxy)
5241                 .periodicAdvertisingManagerRegisterSync(
5242                         any(), any(), anyInt(), anyInt(), any(), any());
5243         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5244         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
5245         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
5246         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
5247                 .isEqualTo(mSourceDevice);
5248         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
5249                 .isEqualTo(TEST_BROADCAST_ID);
5250 
5251         onSyncLost();
5252 
5253         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5254         verify(mCallback).onSourceLost(eq(TEST_BROADCAST_ID));
5255 
5256         // Cleaned all
5257         assertThat(mBassClientService.getActiveSyncedSources()).isEmpty();
5258         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
5259         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
5260                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
5261 
5262         // Could try to sync again
5263         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
5264         mInOrderMethodProxy
5265                 .verify(mMethodProxy)
5266                 .periodicAdvertisingManagerRegisterSync(
5267                         any(), any(), anyInt(), anyInt(), any(), any());
5268     }
5269 
5270     @Test
5271     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
onSyncLost_notifySourceLostAndCancelSync()5272     public void onSyncLost_notifySourceLostAndCancelSync() throws RemoteException {
5273         prepareConnectedDeviceGroup();
5274         startSearchingForSources();
5275         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
5276         mInOrderMethodProxy
5277                 .verify(mMethodProxy)
5278                 .periodicAdvertisingManagerRegisterSync(
5279                         any(), any(), anyInt(), anyInt(), any(), any());
5280         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5281         assertThat(mBassClientService.getActiveSyncedSources()).hasSize(1);
5282         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
5283         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
5284                 .isEqualTo(mSourceDevice);
5285         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
5286                 .isEqualTo(TEST_BROADCAST_ID);
5287 
5288         onSyncLost();
5289         checkAndDispatchTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_SYNC_LOST_TIMEOUT);
5290 
5291         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5292         verify(mCallback).onSourceLost(eq(TEST_BROADCAST_ID));
5293 
5294         // Cleaned all
5295         assertThat(mBassClientService.getActiveSyncedSources()).isEmpty();
5296         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
5297         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
5298                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
5299 
5300         // Could try to sync again
5301         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
5302         mInOrderMethodProxy
5303                 .verify(mMethodProxy)
5304                 .periodicAdvertisingManagerRegisterSync(
5305                         any(), any(), anyInt(), anyInt(), any(), any());
5306     }
5307 
5308     @Test
5309     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
monitorBroadcastAfterSyncMaxLimit()5310     public void monitorBroadcastAfterSyncMaxLimit() throws RemoteException {
5311         final BluetoothDevice device1 =
5312                 mBluetoothAdapter.getRemoteLeDevice(
5313                         "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
5314         final BluetoothDevice device2 =
5315                 mBluetoothAdapter.getRemoteLeDevice(
5316                         "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM);
5317         final BluetoothDevice device3 =
5318                 mBluetoothAdapter.getRemoteLeDevice(
5319                         "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM);
5320         final BluetoothDevice device4 =
5321                 mBluetoothAdapter.getRemoteLeDevice(
5322                         "00:11:22:33:44:44", BluetoothDevice.ADDRESS_TYPE_RANDOM);
5323         final BluetoothDevice device5 =
5324                 mBluetoothAdapter.getRemoteLeDevice(
5325                         "00:11:22:33:44:55", BluetoothDevice.ADDRESS_TYPE_RANDOM);
5326         final int handle1 = 0;
5327         final int handle2 = 1;
5328         final int handle3 = 2;
5329         final int handle4 = 3;
5330         final int handle5 = 4;
5331         final int broadcastId1 = 1111;
5332         final int broadcastId2 = 2222;
5333         final int broadcastId3 = 3333;
5334         final int broadcastId4 = 4444;
5335         final int broadcastId5 = 5555;
5336 
5337         prepareConnectedDeviceGroup();
5338         startSearchingForSources();
5339 
5340         // Scan and sync 5 sources cause removing 1 synced element
5341         onScanResult(device1, broadcastId1);
5342         onSyncEstablished(device1, handle1);
5343         onScanResult(device2, broadcastId2);
5344         onSyncEstablished(device2, handle2);
5345         onScanResult(device3, broadcastId3);
5346         onSyncEstablished(device3, handle3);
5347         onScanResult(device4, broadcastId4);
5348         onSyncEstablished(device4, handle4);
5349         onScanResult(device5, broadcastId5);
5350         mInOrderMethodProxy
5351                 .verify(mMethodProxy, times(4))
5352                 .periodicAdvertisingManagerRegisterSync(
5353                         any(), any(), anyInt(), anyInt(), any(), any());
5354         mInOrderMethodProxy
5355                 .verify(mMethodProxy)
5356                 .periodicAdvertisingManagerUnregisterSync(any(), any());
5357 
5358         checkTimeout(broadcastId1, BassClientService.MESSAGE_SYNC_LOST_TIMEOUT);
5359 
5360         mInOrderMethodProxy
5361                 .verify(mMethodProxy)
5362                 .periodicAdvertisingManagerRegisterSync(
5363                         any(), any(), anyInt(), anyInt(), any(), any());
5364         onSyncEstablished(device5, handle5);
5365 
5366         // Couldn't sync again as broadcast is in the cache
5367         onScanResult(device1, broadcastId1);
5368         mInOrderMethodProxy
5369                 .verify(mMethodProxy, never())
5370                 .periodicAdvertisingManagerRegisterSync(
5371                         any(), any(), anyInt(), anyInt(), any(), any());
5372 
5373         // Lost should notify about lost and clear cache
5374         checkAndDispatchTimeout(broadcastId1, BassClientService.MESSAGE_SYNC_LOST_TIMEOUT);
5375 
5376         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
5377         verify(mCallback).onSourceLost(eq(broadcastId1));
5378 
5379         // Could try to sync again
5380         onScanResult(device1, broadcastId1);
5381         mInOrderMethodProxy
5382                 .verify(mMethodProxy)
5383                 .periodicAdvertisingManagerRegisterSync(
5384                         any(), any(), anyInt(), anyInt(), any(), any());
5385     }
5386 
prepareSynchronizedPair()5387     private void prepareSynchronizedPair() {
5388         prepareConnectedDeviceGroup();
5389         startSearchingForSources();
5390 
5391         // Scan and sync
5392         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
5393         mInOrderMethodProxy
5394                 .verify(mMethodProxy)
5395                 .periodicAdvertisingManagerRegisterSync(
5396                         any(), any(), anyInt(), anyInt(), any(), any());
5397         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5398 
5399         // Add source
5400         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
5401         verifyAddSourceForGroup(meta);
5402 
5403         // Bis synced
5404         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ true);
5405         verify(mLeAudioService).activeBroadcastAssistantNotification(eq(true));
5406     }
5407 
prepareSynchronizedPairAndStopSearching()5408     private void prepareSynchronizedPairAndStopSearching() {
5409         prepareSynchronizedPair();
5410 
5411         // Stop searching
5412         mBassClientService.stopSearchingForSources();
5413         mInOrderMethodProxy
5414                 .verify(mMethodProxy)
5415                 .periodicAdvertisingManagerUnregisterSync(any(), any());
5416     }
5417 
sinkUnintentionalWithoutScanning()5418     private void sinkUnintentionalWithoutScanning() {
5419         prepareSynchronizedPairAndStopSearching();
5420 
5421         // Bis and PA unsynced, SINK_UNINTENTIONAL
5422         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
5423         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
5424         mInOrderMethodProxy
5425                 .verify(mMethodProxy)
5426                 .periodicAdvertisingManagerRegisterSync(
5427                         any(), any(), anyInt(), anyInt(), any(), any());
5428         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5429         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5430     }
5431 
sinkUnintentionalDuringScanning()5432     private void sinkUnintentionalDuringScanning() {
5433         prepareSynchronizedPair();
5434 
5435         // Bis and PA unsynced, SINK_UNINTENTIONAL
5436         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
5437         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
5438         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5439     }
5440 
checkResumeSynchronizationByBig()5441     private void checkResumeSynchronizationByBig() {
5442         // BIG causes resume synchronization
5443         for (BassClientStateMachine sm : mStateMachines.values()) {
5444             clearInvocations(sm);
5445         }
5446         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // In case of add source to inactive
5447         onPeriodicAdvertisingReport();
5448         onBigInfoAdvertisingReport();
5449         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
5450         verifyAllGroupMembersGettingUpdateOrAddSource(meta);
5451     }
5452 
checkNoResumeSynchronizationByBig()5453     private void checkNoResumeSynchronizationByBig() {
5454         // BIG not cause resume synchronization
5455         for (BassClientStateMachine sm : mStateMachines.values()) {
5456             clearInvocations(sm);
5457         }
5458         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // In case of add source to inactive
5459         onPeriodicAdvertisingReport();
5460         onBigInfoAdvertisingReport();
5461         for (BassClientStateMachine sm : mStateMachines.values()) {
5462             verify(sm, never()).sendMessage(any());
5463         }
5464     }
5465 
checkResumeSynchronizationByHost()5466     private void checkResumeSynchronizationByHost() {
5467         for (BassClientStateMachine sm : mStateMachines.values()) {
5468             clearInvocations(sm);
5469         }
5470         mBassClientService.resumeReceiversSourceSynchronization();
5471         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // In case of add source to inactive
5472         verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
5473     }
5474 
checkNoResumeSynchronizationByHost()5475     private void checkNoResumeSynchronizationByHost() {
5476         // Verify empty resume list
5477         for (BassClientStateMachine sm : mStateMachines.values()) {
5478             clearInvocations(sm);
5479         }
5480         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // In case of add source to inactive
5481         mBassClientService.resumeReceiversSourceSynchronization();
5482         for (BassClientStateMachine sm : mStateMachines.values()) {
5483             verify(sm, never()).sendMessage(any());
5484         }
5485     }
5486 
verifyStopBigMonitoringWithUnsync()5487     private void verifyStopBigMonitoringWithUnsync() {
5488         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5489         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
5490         mInOrderMethodProxy
5491                 .verify(mMethodProxy)
5492                 .periodicAdvertisingManagerUnregisterSync(any(), any());
5493     }
5494 
verifyStopBigMonitoringWithoutUnsync()5495     private void verifyStopBigMonitoringWithoutUnsync() {
5496         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5497         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
5498         mInOrderMethodProxy
5499                 .verify(mMethodProxy, never())
5500                 .periodicAdvertisingManagerUnregisterSync(any(), any());
5501     }
5502 
resyncAndVerifyWithUnsync()5503     private void resyncAndVerifyWithUnsync() {
5504         // Resync, verify stopBigMonitoring with broadcast unsync
5505         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
5506         injectRemoteSourceStateChanged(meta, /* isPaSynced */ true, /* isBisSynced */ true);
5507         verifyStopBigMonitoringWithUnsync();
5508     }
5509 
resyncAndVerifyWithoutUnsync()5510     private void resyncAndVerifyWithoutUnsync() {
5511         // Resync, verify stopBigMonitoring without broadcast unsync
5512         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
5513         injectRemoteSourceStateChanged(meta, /* isPaSynced */ true, /* isBisSynced */ true);
5514         verifyStopBigMonitoringWithoutUnsync();
5515     }
5516 
checkNoSinkPause()5517     private void checkNoSinkPause() {
5518         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
5519         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
5520         mInOrderMethodProxy
5521                 .verify(mMethodProxy, never())
5522                 .periodicAdvertisingManagerRegisterSync(
5523                         any(), any(), anyInt(), anyInt(), any(), any());
5524         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5525         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
5526     }
5527 
checkSinkPause()5528     private void checkSinkPause() {
5529         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
5530         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
5531         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5532     }
5533 
5534     @Test
5535     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_resync_withoutScanning()5536     public void sinkUnintentional_resync_withoutScanning() {
5537         sinkUnintentionalWithoutScanning();
5538 
5539         checkResumeSynchronizationByBig();
5540         resyncAndVerifyWithUnsync();
5541     }
5542 
5543     @Test
5544     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_resync_duringScanning()5545     public void sinkUnintentional_resync_duringScanning() {
5546         sinkUnintentionalDuringScanning();
5547 
5548         checkResumeSynchronizationByBig();
5549         resyncAndVerifyWithoutUnsync();
5550     }
5551 
5552     @Test
5553     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_resyncByRemote_withoutScanning()5554     public void sinkUnintentional_resyncByRemote_withoutScanning() {
5555         sinkUnintentionalWithoutScanning();
5556 
5557         resyncAndVerifyWithUnsync();
5558     }
5559 
5560     @Test
5561     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_resyncByRemote_duringScanning()5562     public void sinkUnintentional_resyncByRemote_duringScanning() {
5563         sinkUnintentionalDuringScanning();
5564 
5565         resyncAndVerifyWithoutUnsync();
5566     }
5567 
5568     @Test
5569     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_addNewSource()5570     public void sinkUnintentional_addNewSource() {
5571         sinkUnintentionalDuringScanning();
5572 
5573         // Scan and sync second broadcast
5574         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
5575         mInOrderMethodProxy
5576                 .verify(mMethodProxy)
5577                 .periodicAdvertisingManagerRegisterSync(
5578                         any(), any(), anyInt(), anyInt(), any(), any());
5579         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
5580 
5581         // Add second source, HOST_INTENTIONAL
5582         BluetoothLeBroadcastMetadata.Builder builder =
5583                 new BluetoothLeBroadcastMetadata.Builder()
5584                         .setEncrypted(false)
5585                         .setSourceDevice(mSourceDevice2, BluetoothDevice.ADDRESS_TYPE_RANDOM)
5586                         .setSourceAdvertisingSid(TEST_ADVERTISER_SID)
5587                         .setBroadcastId(TEST_BROADCAST_ID + 1)
5588                         .setBroadcastCode(null)
5589                         .setPaSyncInterval(TEST_PA_SYNC_INTERVAL)
5590                         .setPresentationDelayMicros(TEST_PRESENTATION_DELAY_MS);
5591         // builder expect at least one subgroup
5592         builder.addSubgroup(createBroadcastSubgroup());
5593         BluetoothLeBroadcastMetadata meta2 = builder.build();
5594         mBassClientService.addSource(mCurrentDevice, meta2, /* isGroupOp */ true);
5595         verifyStopBigMonitoringWithoutUnsync();
5596 
5597         // BIG for first broadcast not cause resume synchronization
5598         checkNoResumeSynchronizationByBig();
5599     }
5600 
5601     @Test
5602     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_addSameSource()5603     public void sinkUnintentional_addSameSource() {
5604         sinkUnintentionalDuringScanning();
5605 
5606         // Verify add source clear the SINK_UNINTENTIONAL
5607         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
5608         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
5609         checkSinkPause();
5610     }
5611 
5612     @Test
5613     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_removeSource_withoutScanning()5614     public void sinkUnintentional_removeSource_withoutScanning() {
5615         sinkUnintentionalWithoutScanning();
5616 
5617         // Remove source, HOST_INTENTIONAL
5618         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
5619         verifyStopBigMonitoringWithUnsync();
5620         verifyRemoveMessageAndInjectSourceRemoval();
5621         checkNoResumeSynchronizationByBig();
5622     }
5623 
5624     @Test
5625     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_removeSource_duringScanning()5626     public void sinkUnintentional_removeSource_duringScanning() {
5627         sinkUnintentionalDuringScanning();
5628 
5629         // Remove source, HOST_INTENTIONAL
5630         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
5631         verifyStopBigMonitoringWithoutUnsync();
5632         verifyRemoveMessageAndInjectSourceRemoval();
5633         checkNoResumeSynchronizationByBig();
5634     }
5635 
5636     @Test
5637     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_stopReceivers_withoutScanning()5638     public void sinkUnintentional_stopReceivers_withoutScanning() {
5639         sinkUnintentionalWithoutScanning();
5640 
5641         // Stop receivers, HOST_INTENTIONAL
5642         mBassClientService.stopReceiversSourceSynchronization(TEST_BROADCAST_ID);
5643         verifyStopBigMonitoringWithUnsync();
5644         verifyRemoveMessageAndInjectSourceRemoval();
5645         checkNoResumeSynchronizationByBig();
5646         checkNoResumeSynchronizationByHost();
5647     }
5648 
5649     @Test
5650     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_stopReceivers_duringScanning()5651     public void sinkUnintentional_stopReceivers_duringScanning() {
5652         sinkUnintentionalDuringScanning();
5653 
5654         // Stop receivers, HOST_INTENTIONAL
5655         mBassClientService.stopReceiversSourceSynchronization(TEST_BROADCAST_ID);
5656         verifyStopBigMonitoringWithoutUnsync();
5657         verifyRemoveMessageAndInjectSourceRemoval();
5658         checkNoResumeSynchronizationByBig();
5659         checkNoResumeSynchronizationByHost();
5660     }
5661 
5662     @Test
5663     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_suspendReceivers_withoutScanning()5664     public void sinkUnintentional_suspendReceivers_withoutScanning() {
5665         sinkUnintentionalWithoutScanning();
5666 
5667         // Suspend receivers, HOST_INTENTIONAL
5668         mBassClientService.suspendReceiversSourceSynchronization(TEST_BROADCAST_ID);
5669         verifyStopBigMonitoringWithUnsync();
5670         verifyModifyMessageAndInjectSourceModfified();
5671         checkNoResumeSynchronizationByBig();
5672         checkResumeSynchronizationByHost();
5673     }
5674 
5675     @Test
5676     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_suspendReceivers_duringScanning()5677     public void sinkUnintentional_suspendReceivers_duringScanning() {
5678         sinkUnintentionalDuringScanning();
5679 
5680         // Suspend receivers, HOST_INTENTIONAL
5681         mBassClientService.suspendReceiversSourceSynchronization(TEST_BROADCAST_ID);
5682         verifyStopBigMonitoringWithoutUnsync();
5683         verifyModifyMessageAndInjectSourceModfified();
5684         checkNoResumeSynchronizationByBig();
5685         checkResumeSynchronizationByHost();
5686     }
5687 
5688     @Test
5689     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_suspendAllReceivers_withoutScanning()5690     public void sinkUnintentional_suspendAllReceivers_withoutScanning() {
5691         sinkUnintentionalWithoutScanning();
5692 
5693         // Suspend all receivers, HOST_INTENTIONAL
5694         mBassClientService.suspendAllReceiversSourceSynchronization();
5695         verifyStopBigMonitoringWithUnsync();
5696         verifyModifyMessageAndInjectSourceModfified();
5697         checkNoResumeSynchronizationByBig();
5698         checkResumeSynchronizationByHost();
5699     }
5700 
5701     @Test
5702     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_suspendAllReceivers_duringScanning()5703     public void sinkUnintentional_suspendAllReceivers_duringScanning() {
5704         sinkUnintentionalDuringScanning();
5705 
5706         // Suspend all receivers, HOST_INTENTIONAL
5707         mBassClientService.suspendAllReceiversSourceSynchronization();
5708         verifyStopBigMonitoringWithoutUnsync();
5709         verifyModifyMessageAndInjectSourceModfified();
5710         checkNoResumeSynchronizationByBig();
5711         checkResumeSynchronizationByHost();
5712     }
5713 
5714     @Test
5715     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_publicStopBigMonitoring_withoutScanning()5716     public void sinkUnintentional_publicStopBigMonitoring_withoutScanning() {
5717         sinkUnintentionalWithoutScanning();
5718 
5719         mBassClientService.stopBigMonitoring();
5720         verifyStopBigMonitoringWithUnsync();
5721         checkNoResumeSynchronizationByBig();
5722     }
5723 
5724     @Test
5725     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_publicStopBigMonitoring_duringScanning()5726     public void sinkUnintentional_publicStopBigMonitoring_duringScanning() {
5727         sinkUnintentionalDuringScanning();
5728 
5729         mBassClientService.stopBigMonitoring();
5730         verifyStopBigMonitoringWithoutUnsync();
5731         checkNoResumeSynchronizationByBig();
5732     }
5733 
5734     @Test
5735     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_unsync_withoutScanning()5736     public void sinkUnintentional_unsync_withoutScanning() {
5737         sinkUnintentionalWithoutScanning();
5738 
5739         // Unsync not all sinks not cause stop monitoring
5740         for (BassClientStateMachine sm : mStateMachines.values()) {
5741             // Update receiver state
5742             if (sm.getDevice().equals(mCurrentDevice)) {
5743                 injectRemoteSourceStateChanged(
5744                         sm,
5745                         createEmptyBroadcastMetadata(),
5746                         TEST_SOURCE_ID,
5747                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
5748                         BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
5749                         null,
5750                         (long) 0x00000000);
5751             }
5752         }
5753         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5754 
5755         // Unsync all sinks cause stop monitoring
5756         for (BassClientStateMachine sm : mStateMachines.values()) {
5757             // Update receiver state
5758             if (sm.getDevice().equals(mCurrentDevice1)) {
5759                 injectRemoteSourceStateChanged(
5760                         sm,
5761                         createEmptyBroadcastMetadata(),
5762                         TEST_SOURCE_ID + 1,
5763                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
5764                         BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
5765                         null,
5766                         (long) 0x00000000);
5767             }
5768         }
5769         verifyStopBigMonitoringWithUnsync();
5770         checkNoResumeSynchronizationByBig();
5771     }
5772 
5773     @Test
5774     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_unsync_duringScanning()5775     public void sinkUnintentional_unsync_duringScanning() {
5776         sinkUnintentionalDuringScanning();
5777 
5778         // Unsync not all sinks not cause stop monitoring
5779         for (BassClientStateMachine sm : mStateMachines.values()) {
5780             // Update receiver state
5781             if (sm.getDevice().equals(mCurrentDevice)) {
5782                 injectRemoteSourceStateChanged(
5783                         sm,
5784                         createEmptyBroadcastMetadata(),
5785                         TEST_SOURCE_ID,
5786                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
5787                         BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
5788                         null,
5789                         (long) 0x00000000);
5790             }
5791         }
5792         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5793 
5794         // Unsync all sinks cause stop monitoring
5795         for (BassClientStateMachine sm : mStateMachines.values()) {
5796             // Update receiver state
5797             if (sm.getDevice().equals(mCurrentDevice1)) {
5798                 injectRemoteSourceStateChanged(
5799                         sm,
5800                         createEmptyBroadcastMetadata(),
5801                         TEST_SOURCE_ID + 1,
5802                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
5803                         BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
5804                         null,
5805                         (long) 0x00000000);
5806             }
5807         }
5808         verifyStopBigMonitoringWithoutUnsync();
5809         checkNoResumeSynchronizationByBig();
5810     }
5811 
5812     @Test
5813     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_disconnect_withoutScanning()5814     public void sinkUnintentional_disconnect_withoutScanning() {
5815         sinkUnintentionalWithoutScanning();
5816 
5817         // Disconnect not all sinks not cause stop monitoring
5818         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
5819         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
5820         mBassClientService.connectionStateChanged(
5821                 mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
5822         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5823 
5824         // Disconnect all sinks cause stop monitoring
5825         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice1)).getConnectionState();
5826         doReturn(false).when(mStateMachines.get(mCurrentDevice1)).isConnected();
5827         mBassClientService.connectionStateChanged(
5828                 mCurrentDevice1, STATE_CONNECTED, STATE_DISCONNECTED);
5829         verifyStopBigMonitoringWithUnsync();
5830         checkNoResumeSynchronizationByBig();
5831     }
5832 
5833     @Test
5834     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_disconnect_duringScanning()5835     public void sinkUnintentional_disconnect_duringScanning() {
5836         sinkUnintentionalDuringScanning();
5837 
5838         // Disconnect not all sinks not cause stop monitoring
5839         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
5840         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
5841         mBassClientService.connectionStateChanged(
5842                 mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
5843         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5844 
5845         // Disconnect all sinks cause stop monitoring
5846         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice1)).getConnectionState();
5847         doReturn(false).when(mStateMachines.get(mCurrentDevice1)).isConnected();
5848         mBassClientService.connectionStateChanged(
5849                 mCurrentDevice1, STATE_CONNECTED, STATE_DISCONNECTED);
5850         verifyStopBigMonitoringWithoutUnsync();
5851         checkNoResumeSynchronizationByBig();
5852     }
5853 
5854     @Test
5855     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_syncLost_withoutScanning_outOfRange()5856     public void sinkUnintentional_syncLost_withoutScanning_outOfRange() {
5857         sinkUnintentionalWithoutScanning();
5858 
5859         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
5860 
5861         onSyncLost();
5862         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
5863         mInOrderMethodProxy
5864                 .verify(mMethodProxy)
5865                 .periodicAdvertisingManagerRegisterSync(
5866                         any(), any(), anyInt(), anyInt(), any(), any());
5867 
5868         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
5869         mInOrderMethodProxy
5870                 .verify(mMethodProxy)
5871                 .periodicAdvertisingManagerRegisterSync(
5872                         any(), any(), anyInt(), anyInt(), any(), any());
5873 
5874         checkAndDispatchTimeout(
5875                 TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
5876         mInOrderMethodProxy
5877                 .verify(mMethodProxy)
5878                 .periodicAdvertisingManagerUnregisterSync(any(), any());
5879         verifyRemoveMessageAndInjectSourceRemoval();
5880         checkNoResumeSynchronizationByBig();
5881     }
5882 
5883     @Test
5884     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_syncLost_duringScanning_outOfRange()5885     public void sinkUnintentional_syncLost_duringScanning_outOfRange() {
5886         sinkUnintentionalDuringScanning();
5887 
5888         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
5889 
5890         onSyncLost();
5891         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
5892         mInOrderMethodProxy
5893                 .verify(mMethodProxy)
5894                 .periodicAdvertisingManagerRegisterSync(
5895                         any(), any(), anyInt(), anyInt(), any(), any());
5896 
5897         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
5898 
5899         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
5900         mInOrderMethodProxy
5901                 .verify(mMethodProxy)
5902                 .periodicAdvertisingManagerRegisterSync(
5903                         any(), any(), anyInt(), anyInt(), any(), any());
5904 
5905         checkAndDispatchTimeout(
5906                 TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
5907         mInOrderMethodProxy
5908                 .verify(mMethodProxy, never())
5909                 .periodicAdvertisingManagerUnregisterSync(any(), any());
5910         verifyRemoveMessageAndInjectSourceRemoval();
5911         checkNoResumeSynchronizationByBig();
5912     }
5913 
5914     @Test
5915     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_bigMonitorTimeout_withoutScanning()5916     public void sinkUnintentional_bigMonitorTimeout_withoutScanning() {
5917         sinkUnintentionalWithoutScanning();
5918 
5919         checkAndDispatchTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5920         mInOrderMethodProxy
5921                 .verify(mMethodProxy)
5922                 .periodicAdvertisingManagerUnregisterSync(any(), any());
5923         verifyRemoveMessageAndInjectSourceRemoval();
5924         checkNoResumeSynchronizationByBig();
5925     }
5926 
5927     @Test
5928     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_bigMonitorTimeout_duringScanning()5929     public void sinkUnintentional_bigMonitorTimeout_duringScanning() {
5930         sinkUnintentionalDuringScanning();
5931 
5932         checkAndDispatchTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
5933         mInOrderMethodProxy
5934                 .verify(mMethodProxy, never())
5935                 .periodicAdvertisingManagerUnregisterSync(any(), any());
5936         verifyRemoveMessageAndInjectSourceRemoval();
5937         checkNoResumeSynchronizationByBig();
5938     }
5939 
5940     @Test
5941     @EnableFlags({
5942         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
5943         Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR
5944     })
sinkUnintentional_handleUnicastSourceStreamStatusChange_withoutScanning()5945     public void sinkUnintentional_handleUnicastSourceStreamStatusChange_withoutScanning() {
5946         sinkUnintentionalWithoutScanning();
5947 
5948         /* Unicast would like to stream */
5949         mBassClientService.handleUnicastSourceStreamStatusChange(
5950                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
5951         verifyStopBigMonitoringWithUnsync();
5952         checkNoResumeSynchronizationByBig();
5953 
5954         /* Unicast finished streaming */
5955         mBassClientService.handleUnicastSourceStreamStatusChange(
5956                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
5957         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5958         verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
5959     }
5960 
5961     @Test
5962     @EnableFlags({
5963         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
5964         Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR
5965     })
sinkUnintentional_handleUnicastSourceStreamStatusChange_duringScanning()5966     public void sinkUnintentional_handleUnicastSourceStreamStatusChange_duringScanning() {
5967         sinkUnintentionalDuringScanning();
5968 
5969         /* Unicast would like to stream */
5970         mBassClientService.handleUnicastSourceStreamStatusChange(
5971                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
5972         verifyStopBigMonitoringWithoutUnsync();
5973         checkNoResumeSynchronizationByBig();
5974 
5975         /* Unicast finished streaming */
5976         mBassClientService.handleUnicastSourceStreamStatusChange(
5977                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
5978         verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
5979     }
5980 
5981     @Test
5982     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_handleUnicastSourceStreamStatusChangeNoContext_withoutScanning()5983     public void sinkUnintentional_handleUnicastSourceStreamStatusChangeNoContext_withoutScanning() {
5984         sinkUnintentionalWithoutScanning();
5985 
5986         /* Unicast would like to stream */
5987         mBassClientService.handleUnicastSourceStreamStatusChange(
5988                 3 /* STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE */);
5989         verifyStopBigMonitoringWithUnsync();
5990         verifyModifyMessageAndInjectSourceModfified();
5991         checkNoResumeSynchronizationByBig();
5992 
5993         /* Unicast finished streaming */
5994         mBassClientService.handleUnicastSourceStreamStatusChange(
5995                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
5996         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
5997         verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
5998     }
5999 
6000     @Test
6001     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkUnintentional_handleUnicastSourceStreamStatusChangeNoContext_duringScanning()6002     public void sinkUnintentional_handleUnicastSourceStreamStatusChangeNoContext_duringScanning() {
6003         sinkUnintentionalDuringScanning();
6004 
6005         /* Unicast would like to stream */
6006         mBassClientService.handleUnicastSourceStreamStatusChange(
6007                 3 /* STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE */);
6008         verifyStopBigMonitoringWithoutUnsync();
6009         verifyModifyMessageAndInjectSourceModfified();
6010         checkNoResumeSynchronizationByBig();
6011 
6012         /* Unicast finished streaming */
6013         mBassClientService.handleUnicastSourceStreamStatusChange(
6014                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
6015         verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
6016     }
6017 
6018     @Test
6019     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
6020     @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_PREVENT_RESUME_INTERRUPTION)
sinkUnintentional_autoSyncToBroadcast_onStopSearching()6021     public void sinkUnintentional_autoSyncToBroadcast_onStopSearching() {
6022         sinkUnintentionalDuringScanning();
6023 
6024         // Verify that start searching cause sync when broadcaster synced to sinks
6025         mBassClientService.stopSearchingForSources();
6026         mInOrderMethodProxy
6027                 .verify(mMethodProxy)
6028                 .periodicAdvertisingManagerUnregisterSync(any(), any());
6029         mInOrderMethodProxy
6030                 .verify(mMethodProxy)
6031                 .periodicAdvertisingManagerRegisterSync(
6032                         any(), any(), anyInt(), anyInt(), any(), any());
6033     }
6034 
6035     @Test
6036     @EnableFlags({
6037         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
6038         Flags.FLAG_LEAUDIO_BROADCAST_PREVENT_RESUME_INTERRUPTION
6039     })
sinkUnintentional_remainEstablishedSync_onStopSearching()6040     public void sinkUnintentional_remainEstablishedSync_onStopSearching() {
6041         sinkUnintentionalDuringScanning();
6042 
6043         // Scan and sync to another broadcaster
6044         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
6045         mInOrderMethodProxy
6046                 .verify(mMethodProxy)
6047                 .periodicAdvertisingManagerRegisterSync(
6048                         any(), any(), anyInt(), anyInt(), any(), any());
6049         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
6050 
6051         // Scan and add add sync to pending
6052         final BluetoothDevice sourceDevice3 =
6053                 mBluetoothAdapter.getRemoteLeDevice(
6054                         "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM);
6055         onScanResult(sourceDevice3, TEST_BROADCAST_ID + 2);
6056         mInOrderMethodProxy
6057                 .verify(mMethodProxy)
6058                 .periodicAdvertisingManagerRegisterSync(
6059                         any(), any(), anyInt(), anyInt(), any(), any());
6060 
6061         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(2);
6062         assertThat(mBassClientService.getActiveSyncedSources())
6063                 .containsExactly(TEST_SYNC_HANDLE, TEST_SYNC_HANDLE + 1);
6064         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
6065                 .isEqualTo(mSourceDevice);
6066         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1))
6067                 .isEqualTo(mSourceDevice2);
6068         assertThat(mBassClientService.getDeviceForSyncHandle(BassConstants.PENDING_SYNC_HANDLE))
6069                 .isEqualTo(sourceDevice3);
6070         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6071                 .isEqualTo(TEST_BROADCAST_ID);
6072         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6073                 .isEqualTo(TEST_BROADCAST_ID + 1);
6074         assertThat(
6075                         mBassClientService.getBroadcastIdForSyncHandle(
6076                                 BassConstants.PENDING_SYNC_HANDLE))
6077                 .isEqualTo(TEST_BROADCAST_ID + 2);
6078 
6079         // Verify that stop searching remain the unintentional sync
6080         mBassClientService.stopSearchingForSources();
6081         // Unintentional sync remain, another sync was removed, pending was canceled
6082         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
6083         assertThat(mBassClientService.getActiveSyncedSources()).containsExactly(TEST_SYNC_HANDLE);
6084         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
6085                 .isEqualTo(mSourceDevice);
6086         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6087         assertThat(mBassClientService.getDeviceForSyncHandle(BassConstants.PENDING_SYNC_HANDLE))
6088                 .isNull();
6089         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6090                 .isEqualTo(TEST_BROADCAST_ID);
6091         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6092                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6093         assertThat(
6094                         mBassClientService.getBroadcastIdForSyncHandle(
6095                                 BassConstants.PENDING_SYNC_HANDLE))
6096                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6097 
6098         // Resume without another register sync is possible
6099         mBassClientService.resumeReceiversSourceSynchronization();
6100         mInOrderMethodProxy
6101                 .verify(mMethodProxy, never())
6102                 .periodicAdvertisingManagerRegisterSync(
6103                         any(), any(), anyInt(), anyInt(), any(), any());
6104         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6105         verifyAllGroupMembersGettingUpdateOrAddSource(meta);
6106     }
6107 
6108     @Test
6109     @EnableFlags({
6110         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
6111         Flags.FLAG_LEAUDIO_BROADCAST_PREVENT_RESUME_INTERRUPTION
6112     })
waitingForPast_remainPendingSync_onStopSearching()6113     public void waitingForPast_remainPendingSync_onStopSearching() {
6114         prepareSynchronizedPair();
6115 
6116         // Scan and sync to another broadcaster
6117         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
6118         mInOrderMethodProxy
6119                 .verify(mMethodProxy)
6120                 .periodicAdvertisingManagerRegisterSync(
6121                         any(), any(), anyInt(), anyInt(), any(), any());
6122         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
6123 
6124         // Sync lost without triggering timeout to keep cache
6125         onSyncLost();
6126 
6127         // Sync info request force syncing to broadcaster and add sinks pending for PAST
6128         mBassClientService.syncRequestForPast(mCurrentDevice, TEST_BROADCAST_ID, TEST_SOURCE_ID);
6129         mBassClientService.syncRequestForPast(
6130                 mCurrentDevice1, TEST_BROADCAST_ID, TEST_SOURCE_ID + 1);
6131         mInOrderMethodProxy
6132                 .verify(mMethodProxy)
6133                 .periodicAdvertisingManagerRegisterSync(
6134                         any(), any(), anyInt(), anyInt(), any(), any());
6135 
6136         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
6137         assertThat(mBassClientService.getActiveSyncedSources())
6138                 .containsExactly(TEST_SYNC_HANDLE + 1);
6139         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
6140         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1))
6141                 .isEqualTo(mSourceDevice2);
6142         assertThat(mBassClientService.getDeviceForSyncHandle(BassConstants.PENDING_SYNC_HANDLE))
6143                 .isEqualTo(mSourceDevice);
6144         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6145                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6146         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6147                 .isEqualTo(TEST_BROADCAST_ID + 1);
6148         assertThat(
6149                         mBassClientService.getBroadcastIdForSyncHandle(
6150                                 BassConstants.PENDING_SYNC_HANDLE))
6151                 .isEqualTo(TEST_BROADCAST_ID);
6152 
6153         // Verify that stop searching remain the pending sync
6154         mBassClientService.stopSearchingForSources();
6155         // Pending remain, another unsynced
6156         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6157         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
6158         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6159         assertThat(mBassClientService.getDeviceForSyncHandle(BassConstants.PENDING_SYNC_HANDLE))
6160                 .isEqualTo(mSourceDevice);
6161         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6162                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6163         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6164                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6165         assertThat(
6166                         mBassClientService.getBroadcastIdForSyncHandle(
6167                                 BassConstants.PENDING_SYNC_HANDLE))
6168                 .isEqualTo(TEST_BROADCAST_ID);
6169 
6170         // Establishment possible without register sync
6171         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6172         verifyInitiatePaSyncTransferAndNoOthers();
6173     }
6174 
6175     @Test
6176     @EnableFlags({
6177         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
6178         Flags.FLAG_LEAUDIO_BROADCAST_PREVENT_RESUME_INTERRUPTION
6179     })
pendingSourceToAdd_remainPendingSync_onStopSearching()6180     public void pendingSourceToAdd_remainPendingSync_onStopSearching() {
6181         prepareConnectedDeviceGroup();
6182         startSearchingForSources();
6183 
6184         // Scan and sync
6185         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
6186         mInOrderMethodProxy
6187                 .verify(mMethodProxy)
6188                 .periodicAdvertisingManagerRegisterSync(
6189                         any(), any(), anyInt(), anyInt(), any(), any());
6190         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6191 
6192         // Scan and sync to another broadcaster
6193         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
6194         mInOrderMethodProxy
6195                 .verify(mMethodProxy)
6196                 .periodicAdvertisingManagerRegisterSync(
6197                         any(), any(), anyInt(), anyInt(), any(), any());
6198         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
6199 
6200         // Sync lost without triggering timeout to keep cache
6201         onSyncLost();
6202 
6203         // Add source force syncing to broadcaster and add sinks to pendingSourcesToAdd
6204         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6205         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
6206         mInOrderMethodProxy
6207                 .verify(mMethodProxy)
6208                 .periodicAdvertisingManagerRegisterSync(
6209                         any(), any(), anyInt(), anyInt(), any(), any());
6210 
6211         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
6212         assertThat(mBassClientService.getActiveSyncedSources())
6213                 .containsExactly(TEST_SYNC_HANDLE + 1);
6214         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
6215         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1))
6216                 .isEqualTo(mSourceDevice2);
6217         assertThat(mBassClientService.getDeviceForSyncHandle(BassConstants.PENDING_SYNC_HANDLE))
6218                 .isEqualTo(mSourceDevice);
6219         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6220                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6221         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6222                 .isEqualTo(TEST_BROADCAST_ID + 1);
6223         assertThat(
6224                         mBassClientService.getBroadcastIdForSyncHandle(
6225                                 BassConstants.PENDING_SYNC_HANDLE))
6226                 .isEqualTo(TEST_BROADCAST_ID);
6227 
6228         // Verify that stop searching remain the pending sync
6229         mBassClientService.stopSearchingForSources();
6230         // Pending remain, another unsynced
6231         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6232         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
6233         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6234         assertThat(mBassClientService.getDeviceForSyncHandle(BassConstants.PENDING_SYNC_HANDLE))
6235                 .isEqualTo(mSourceDevice);
6236         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6237                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6238         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6239                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6240         assertThat(
6241                         mBassClientService.getBroadcastIdForSyncHandle(
6242                                 BassConstants.PENDING_SYNC_HANDLE))
6243                 .isEqualTo(TEST_BROADCAST_ID);
6244 
6245         // Establishment possible without register sync
6246         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6247         verifyAllGroupMembersGettingUpdateOrAddSource(meta);
6248     }
6249 
6250     @Test
6251     @EnableFlags({
6252         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
6253         Flags.FLAG_LEAUDIO_BROADCAST_PREVENT_RESUME_INTERRUPTION
6254     })
alreadySynced_remainSyncAndCache_onStartSearching()6255     public void alreadySynced_remainSyncAndCache_onStartSearching() {
6256         prepareConnectedDeviceGroup();
6257         startSearchingForSources();
6258 
6259         // Scan and sync
6260         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
6261         mInOrderMethodProxy
6262                 .verify(mMethodProxy)
6263                 .periodicAdvertisingManagerRegisterSync(
6264                         any(), any(), anyInt(), anyInt(), any(), any());
6265         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6266 
6267         // Scan and sync to another broadcaster
6268         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
6269         mInOrderMethodProxy
6270                 .verify(mMethodProxy)
6271                 .periodicAdvertisingManagerRegisterSync(
6272                         any(), any(), anyInt(), anyInt(), any(), any());
6273         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
6274 
6275         // Cancel all syncs by stop searching
6276         mBassClientService.stopSearchingForSources();
6277         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6278         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
6279         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6280         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6281                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6282         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6283                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6284 
6285         // Add source force syncing to broadcaster
6286         // Not finished to not add UNINTENTIONAL_PAUSE or to not unsync
6287         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6288         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
6289         mInOrderMethodProxy
6290                 .verify(mMethodProxy)
6291                 .periodicAdvertisingManagerRegisterSync(
6292                         any(), any(), anyInt(), anyInt(), any(), any());
6293 
6294         // Synced
6295         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6296         verifyAllGroupMembersGettingUpdateOrAddSource(meta);
6297         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
6298         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
6299                 .isEqualTo(mSourceDevice);
6300         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6301         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6302                 .isEqualTo(TEST_BROADCAST_ID);
6303         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6304                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6305 
6306         // Start searching sources remain synced broadcasters and their cache but remove others
6307         startSearchingForSources();
6308         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
6309         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
6310                 .isEqualTo(mSourceDevice);
6311         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6312         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6313                 .isEqualTo(TEST_BROADCAST_ID);
6314         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6315                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6316 
6317         // Sync lost without triggering timeout to keep cache
6318         onSyncLost();
6319 
6320         // Finish adding source without PA and BIS to detect UNINTENTIONAL_PAUSE which will sync
6321         // again. This will confirm that cache is available
6322         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
6323         mInOrderMethodProxy
6324                 .verify(mMethodProxy)
6325                 .periodicAdvertisingManagerRegisterSync(
6326                         any(), any(), anyInt(), anyInt(), any(), any());
6327         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6328 
6329         // Remove source to allow add again
6330         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
6331         verifyRemoveMessageAndInjectSourceRemoval();
6332 
6333         // Check if cache is NOT remaining for second broadcaster by adding source
6334         BluetoothLeBroadcastMetadata meta2 = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
6335         mBassClientService.addSource(mCurrentDevice, meta2, /* isGroupOp */ true);
6336         mInOrderMethodProxy
6337                 .verify(mMethodProxy, never())
6338                 .periodicAdvertisingManagerRegisterSync(
6339                         any(), any(), anyInt(), anyInt(), any(), any());
6340     }
6341 
6342     @Test
6343     @EnableFlags({
6344         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
6345         Flags.FLAG_LEAUDIO_BROADCAST_PREVENT_RESUME_INTERRUPTION
6346     })
alreadySyncedWithSinks_syncAndRemainCache_onStartSearching()6347     public void alreadySyncedWithSinks_syncAndRemainCache_onStartSearching() {
6348         prepareSynchronizedPair();
6349 
6350         // Scan and sync to another broadcaster
6351         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
6352         mInOrderMethodProxy
6353                 .verify(mMethodProxy)
6354                 .periodicAdvertisingManagerRegisterSync(
6355                         any(), any(), anyInt(), anyInt(), any(), any());
6356         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
6357 
6358         // Cancel all syncs by stop searching
6359         mBassClientService.stopSearchingForSources();
6360         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6361         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
6362         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6363         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6364                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6365         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6366                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6367 
6368         // Start searching sources syncs to the broadcasters already synced with sinks
6369         startSearchingForSources();
6370         mInOrderMethodProxy
6371                 .verify(mMethodProxy)
6372                 .periodicAdvertisingManagerRegisterSync(
6373                         any(), any(), anyInt(), anyInt(), any(), any());
6374         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6375         assertThat(mBassClientService.getDeviceForSyncHandle(BassConstants.PENDING_SYNC_HANDLE))
6376                 .isEqualTo(mSourceDevice);
6377         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6378         assertThat(
6379                         mBassClientService.getBroadcastIdForSyncHandle(
6380                                 BassConstants.PENDING_SYNC_HANDLE))
6381                 .isEqualTo(TEST_BROADCAST_ID);
6382         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6383                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6384 
6385         // Synced
6386         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6387         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
6388         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
6389                 .isEqualTo(mSourceDevice);
6390         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6391         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6392                 .isEqualTo(TEST_BROADCAST_ID);
6393         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6394                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6395 
6396         // Sync lost without triggering timeout to keep cache
6397         onSyncLost();
6398 
6399         // Remove source to allow add again
6400         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
6401         verifyRemoveMessageAndInjectSourceRemoval();
6402 
6403         // Check if cache is NOT remaining for second broadcaster by adding source
6404         BluetoothLeBroadcastMetadata meta2 = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
6405         mBassClientService.addSource(mCurrentDevice, meta2, /* isGroupOp */ true);
6406         mInOrderMethodProxy
6407                 .verify(mMethodProxy, never())
6408                 .periodicAdvertisingManagerRegisterSync(
6409                         any(), any(), anyInt(), anyInt(), any(), any());
6410 
6411         // Check if cache is remaining for already synced broadcaster by adding source
6412         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6413         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
6414         mInOrderMethodProxy
6415                 .verify(mMethodProxy)
6416                 .periodicAdvertisingManagerRegisterSync(
6417                         any(), any(), anyInt(), anyInt(), any(), any());
6418     }
6419 
6420     @Test
6421     @EnableFlags({
6422         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
6423         Flags.FLAG_LEAUDIO_BROADCAST_PREVENT_RESUME_INTERRUPTION
6424     })
waitingForPast_remainPendingSyncAndCache_onStartSearching()6425     public void waitingForPast_remainPendingSyncAndCache_onStartSearching() {
6426         prepareSynchronizedPair();
6427 
6428         // Scan and sync to another broadcaster
6429         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
6430         mInOrderMethodProxy
6431                 .verify(mMethodProxy)
6432                 .periodicAdvertisingManagerRegisterSync(
6433                         any(), any(), anyInt(), anyInt(), any(), any());
6434         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
6435 
6436         // Cancel all syncs by stop searching
6437         mBassClientService.stopSearchingForSources();
6438         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6439         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
6440         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6441         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6442                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6443         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6444                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6445 
6446         // Sync info request force syncing to broadcaster and add sinks pending for PAST
6447         mBassClientService.syncRequestForPast(mCurrentDevice, TEST_BROADCAST_ID, TEST_SOURCE_ID);
6448         mBassClientService.syncRequestForPast(
6449                 mCurrentDevice1, TEST_BROADCAST_ID, TEST_SOURCE_ID + 1);
6450         mInOrderMethodProxy
6451                 .verify(mMethodProxy)
6452                 .periodicAdvertisingManagerRegisterSync(
6453                         any(), any(), anyInt(), anyInt(), any(), any());
6454 
6455         // Start searching sources remain pending sync and cache for broadcaster waiting for past
6456         startSearchingForSources();
6457         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6458         assertThat(mBassClientService.getDeviceForSyncHandle(BassConstants.PENDING_SYNC_HANDLE))
6459                 .isEqualTo(mSourceDevice);
6460         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6461         assertThat(
6462                         mBassClientService.getBroadcastIdForSyncHandle(
6463                                 BassConstants.PENDING_SYNC_HANDLE))
6464                 .isEqualTo(TEST_BROADCAST_ID);
6465         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6466                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6467 
6468         // Synced
6469         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6470         verifyInitiatePaSyncTransferAndNoOthers();
6471         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
6472         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
6473                 .isEqualTo(mSourceDevice);
6474         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6475         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6476                 .isEqualTo(TEST_BROADCAST_ID);
6477         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6478                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6479 
6480         // Sync lost without triggering timeout to keep cache
6481         onSyncLost();
6482 
6483         // Remove source to allow add again
6484         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
6485         verifyRemoveMessageAndInjectSourceRemoval();
6486 
6487         // Check if cache is NOT remaining for second broadcaster by adding source
6488         BluetoothLeBroadcastMetadata meta2 = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
6489         mBassClientService.addSource(mCurrentDevice, meta2, /* isGroupOp */ true);
6490         mInOrderMethodProxy
6491                 .verify(mMethodProxy, never())
6492                 .periodicAdvertisingManagerRegisterSync(
6493                         any(), any(), anyInt(), anyInt(), any(), any());
6494 
6495         // Check if cache is remaining for already synced broadcaster by adding source
6496         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6497         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
6498         mInOrderMethodProxy
6499                 .verify(mMethodProxy)
6500                 .periodicAdvertisingManagerRegisterSync(
6501                         any(), any(), anyInt(), anyInt(), any(), any());
6502     }
6503 
6504     @Test
6505     @EnableFlags({
6506         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
6507         Flags.FLAG_LEAUDIO_BROADCAST_PREVENT_RESUME_INTERRUPTION
6508     })
pendingSourcesToAdd_remainPendingSyncAndCache_onStartSearching()6509     public void pendingSourcesToAdd_remainPendingSyncAndCache_onStartSearching() {
6510         prepareConnectedDeviceGroup();
6511         startSearchingForSources();
6512 
6513         // Scan and sync
6514         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
6515         mInOrderMethodProxy
6516                 .verify(mMethodProxy)
6517                 .periodicAdvertisingManagerRegisterSync(
6518                         any(), any(), anyInt(), anyInt(), any(), any());
6519         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6520 
6521         // Scan and sync to another broadcaster
6522         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
6523         mInOrderMethodProxy
6524                 .verify(mMethodProxy)
6525                 .periodicAdvertisingManagerRegisterSync(
6526                         any(), any(), anyInt(), anyInt(), any(), any());
6527         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
6528 
6529         // Cancel all syncs by stop searching
6530         mBassClientService.stopSearchingForSources();
6531         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6532         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
6533         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6534         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6535                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6536         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6537                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6538 
6539         // Add source force syncing to broadcaster
6540         // Not finished to not add UNINTENTIONAL_PAUSE or to not unsync
6541         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6542         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
6543         mInOrderMethodProxy
6544                 .verify(mMethodProxy)
6545                 .periodicAdvertisingManagerRegisterSync(
6546                         any(), any(), anyInt(), anyInt(), any(), any());
6547 
6548         // Start searching sources remain pending sync and cache for broadcaster
6549         startSearchingForSources();
6550         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6551         assertThat(mBassClientService.getDeviceForSyncHandle(BassConstants.PENDING_SYNC_HANDLE))
6552                 .isEqualTo(mSourceDevice);
6553         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6554         assertThat(
6555                         mBassClientService.getBroadcastIdForSyncHandle(
6556                                 BassConstants.PENDING_SYNC_HANDLE))
6557                 .isEqualTo(TEST_BROADCAST_ID);
6558         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6559                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6560 
6561         // Synced
6562         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6563         verifyAllGroupMembersGettingUpdateOrAddSource(meta);
6564         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
6565         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
6566                 .isEqualTo(mSourceDevice);
6567         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6568         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6569                 .isEqualTo(TEST_BROADCAST_ID);
6570         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6571                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6572 
6573         // Sync lost without triggering timeout to keep cache
6574         onSyncLost();
6575 
6576         // Finish adding source without PA and BIS to detect UNINTENTIONAL_PAUSE which will sync
6577         // again. This will confirm that cache is available
6578         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
6579         mInOrderMethodProxy
6580                 .verify(mMethodProxy)
6581                 .periodicAdvertisingManagerRegisterSync(
6582                         any(), any(), anyInt(), anyInt(), any(), any());
6583         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6584 
6585         // Remove source to allow add again
6586         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
6587         verifyRemoveMessageAndInjectSourceRemoval();
6588 
6589         // Check if cache is NOT remaining for second broadcaster by adding source
6590         BluetoothLeBroadcastMetadata meta2 = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
6591         mBassClientService.addSource(mCurrentDevice, meta2, /* isGroupOp */ true);
6592         mInOrderMethodProxy
6593                 .verify(mMethodProxy, never())
6594                 .periodicAdvertisingManagerRegisterSync(
6595                         any(), any(), anyInt(), anyInt(), any(), any());
6596     }
6597 
6598     @Test
6599     @EnableFlags({
6600         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
6601         Flags.FLAG_LEAUDIO_BROADCAST_PREVENT_RESUME_INTERRUPTION
6602     })
hostIntentional_SyncAndRemainCache_onStartSearching()6603     public void hostIntentional_SyncAndRemainCache_onStartSearching() {
6604         prepareSynchronizedPair();
6605 
6606         // Scan and sync to another broadcaster
6607         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
6608         mInOrderMethodProxy
6609                 .verify(mMethodProxy)
6610                 .periodicAdvertisingManagerRegisterSync(
6611                         any(), any(), anyInt(), anyInt(), any(), any());
6612         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
6613 
6614         // Cancel all syncs by stop searching
6615         mBassClientService.stopSearchingForSources();
6616         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6617         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE)).isNull();
6618         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6619         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6620                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6621         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6622                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6623 
6624         // Suspend all receivers, HOST_INTENTIONAL
6625         mBassClientService.suspendAllReceiversSourceSynchronization();
6626         verifyModifyMessageAndInjectSourceModfified();
6627 
6628         // Start searching sources sync to paused broadcaster and remain cache
6629         startSearchingForSources();
6630         mInOrderMethodProxy
6631                 .verify(mMethodProxy)
6632                 .periodicAdvertisingManagerRegisterSync(
6633                         any(), any(), anyInt(), anyInt(), any(), any());
6634         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(0);
6635         assertThat(mBassClientService.getDeviceForSyncHandle(BassConstants.PENDING_SYNC_HANDLE))
6636                 .isEqualTo(mSourceDevice);
6637         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6638         assertThat(
6639                         mBassClientService.getBroadcastIdForSyncHandle(
6640                                 BassConstants.PENDING_SYNC_HANDLE))
6641                 .isEqualTo(TEST_BROADCAST_ID);
6642         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6643                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6644 
6645         // Synced
6646         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6647         assertThat(mBassClientService.getActiveSyncedSources().size()).isEqualTo(1);
6648         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE))
6649                 .isEqualTo(mSourceDevice);
6650         assertThat(mBassClientService.getDeviceForSyncHandle(TEST_SYNC_HANDLE + 1)).isNull();
6651         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE))
6652                 .isEqualTo(TEST_BROADCAST_ID);
6653         assertThat(mBassClientService.getBroadcastIdForSyncHandle(TEST_SYNC_HANDLE + 1))
6654                 .isEqualTo(BassConstants.INVALID_BROADCAST_ID);
6655 
6656         // Resume broadcast
6657         mBassClientService.resumeReceiversSourceSynchronization();
6658         mInOrderMethodProxy
6659                 .verify(mMethodProxy, never())
6660                 .periodicAdvertisingManagerRegisterSync(
6661                         any(), any(), anyInt(), anyInt(), any(), any());
6662         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6663         verifyAllGroupMembersGettingUpdateOrAddSource(meta);
6664         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ true);
6665 
6666         // Sync lost without triggering timeout to keep cache
6667         onSyncLost();
6668 
6669         // Remove source to allow add again
6670         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
6671         mBassClientService.removeSource(mCurrentDevice1, TEST_SOURCE_ID + 1);
6672         verifyRemoveMessageAndInjectSourceRemoval();
6673 
6674         // Check if cache is NOT remaining for second broadcaster by adding source
6675         BluetoothLeBroadcastMetadata meta2 = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
6676         mBassClientService.addSource(mCurrentDevice, meta2, /* isGroupOp */ true);
6677         mInOrderMethodProxy
6678                 .verify(mMethodProxy, never())
6679                 .periodicAdvertisingManagerRegisterSync(
6680                         any(), any(), anyInt(), anyInt(), any(), any());
6681 
6682         // Check if cache is remaining for already synced broadcaster by adding source
6683         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
6684         mInOrderMethodProxy
6685                 .verify(mMethodProxy)
6686                 .periodicAdvertisingManagerRegisterSync(
6687                         any(), any(), anyInt(), anyInt(), any(), any());
6688     }
6689 
6690     @Test
6691     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_addSameSource()6692     public void hostIntentional_addSameSource() {
6693         prepareSynchronizedPair();
6694 
6695         // Remove source, HOST_INTENTIONAL
6696         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
6697         checkNoSinkPause();
6698         verifyRemoveMessageAndInjectSourceRemoval();
6699 
6700         // Verify add source clear the HOST_INTENTIONAL
6701         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6702         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
6703         verifyAddSourceForGroup(meta);
6704         checkSinkPause();
6705     }
6706 
6707     @Test
6708     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_removeSource_withoutScanning()6709     public void hostIntentional_removeSource_withoutScanning() {
6710         prepareSynchronizedPairAndStopSearching();
6711 
6712         // Remove source, HOST_INTENTIONAL
6713         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
6714         checkNoSinkPause();
6715         verifyRemoveMessageAndInjectSourceRemoval();
6716     }
6717 
6718     @Test
6719     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_removeSource_duringScanning()6720     public void hostIntentional_removeSource_duringScanning() {
6721         prepareSynchronizedPair();
6722 
6723         // Remove source, HOST_INTENTIONAL
6724         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
6725         checkNoSinkPause();
6726         verifyRemoveMessageAndInjectSourceRemoval();
6727     }
6728 
6729     @Test
6730     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_stopReceivers_withoutScanning()6731     public void hostIntentional_stopReceivers_withoutScanning() {
6732         prepareSynchronizedPairAndStopSearching();
6733 
6734         // Stop receivers, HOST_INTENTIONAL
6735         mBassClientService.stopReceiversSourceSynchronization(TEST_BROADCAST_ID);
6736         checkNoSinkPause();
6737         verifyRemoveMessageAndInjectSourceRemoval();
6738     }
6739 
6740     @Test
6741     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_stopReceivers_duringScanning()6742     public void hostIntentional_stopReceivers_duringScanning() {
6743         prepareSynchronizedPair();
6744 
6745         // Stop receivers, HOST_INTENTIONAL
6746         mBassClientService.stopReceiversSourceSynchronization(TEST_BROADCAST_ID);
6747         checkNoSinkPause();
6748         verifyRemoveMessageAndInjectSourceRemoval();
6749     }
6750 
6751     @Test
6752     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_suspendReceivers_withoutScanning()6753     public void hostIntentional_suspendReceivers_withoutScanning() {
6754         prepareSynchronizedPairAndStopSearching();
6755 
6756         // Suspend receivers, HOST_INTENTIONAL
6757         mBassClientService.suspendReceiversSourceSynchronization(TEST_BROADCAST_ID);
6758         checkNoSinkPause();
6759         checkResumeSynchronizationByHost();
6760     }
6761 
6762     @Test
6763     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_suspendReceivers_duringScanning()6764     public void hostIntentional_suspendReceivers_duringScanning() {
6765         prepareSynchronizedPair();
6766 
6767         // Suspend receivers, HOST_INTENTIONAL
6768         mBassClientService.suspendReceiversSourceSynchronization(TEST_BROADCAST_ID);
6769         checkNoSinkPause();
6770         checkResumeSynchronizationByHost();
6771     }
6772 
6773     @Test
6774     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_suspendAllReceivers_withoutScanning()6775     public void hostIntentional_suspendAllReceivers_withoutScanning() {
6776         prepareSynchronizedPairAndStopSearching();
6777 
6778         // Suspend all receivers, HOST_INTENTIONAL
6779         mBassClientService.suspendAllReceiversSourceSynchronization();
6780         checkNoSinkPause();
6781         checkResumeSynchronizationByHost();
6782     }
6783 
6784     @Test
6785     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_suspendAllReceivers_duringScanning()6786     public void hostIntentional_suspendAllReceivers_duringScanning() {
6787         prepareSynchronizedPair();
6788 
6789         // Suspend all receivers, HOST_INTENTIONAL
6790         mBassClientService.suspendAllReceiversSourceSynchronization();
6791         checkNoSinkPause();
6792         checkResumeSynchronizationByHost();
6793     }
6794 
6795     @Test
6796     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_handleUnicastSourceStreamStatusChange_withoutScanning()6797     public void hostIntentional_handleUnicastSourceStreamStatusChange_withoutScanning() {
6798         prepareSynchronizedPairAndStopSearching();
6799 
6800         /* Unicast would like to stream */
6801         mBassClientService.handleUnicastSourceStreamStatusChange(
6802                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
6803         checkNoSinkPause();
6804 
6805         /* Unicast finished streaming */
6806         mBassClientService.handleUnicastSourceStreamStatusChange(
6807                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
6808         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // In case of add source to inactive
6809         verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
6810     }
6811 
6812     @Test
6813     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_handleUnicastSourceStreamStatusChange_duringScanning()6814     public void hostIntentional_handleUnicastSourceStreamStatusChange_duringScanning() {
6815         prepareSynchronizedPair();
6816 
6817         /* Unicast would like to stream */
6818         mBassClientService.handleUnicastSourceStreamStatusChange(
6819                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
6820         checkNoSinkPause();
6821 
6822         /* Unicast finished streaming */
6823         mBassClientService.handleUnicastSourceStreamStatusChange(
6824                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
6825         verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
6826     }
6827 
6828     @Test
6829     @EnableFlags({
6830         Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER,
6831         Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR
6832     })
hostIntentional_handleUnicastSourceStreamStatusChange_beforeResumeCompleted()6833     public void hostIntentional_handleUnicastSourceStreamStatusChange_beforeResumeCompleted() {
6834         prepareSynchronizedPairAndStopSearching();
6835 
6836         /* Unicast would like to stream */
6837         mBassClientService.handleUnicastSourceStreamStatusChange(
6838                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
6839         checkNoSinkPause();
6840 
6841         /* Unicast finished streaming */
6842         mBassClientService.handleUnicastSourceStreamStatusChange(
6843                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
6844         mInOrderMethodProxy
6845                 .verify(mMethodProxy)
6846                 .periodicAdvertisingManagerRegisterSync(
6847                         any(), any(), anyInt(), anyInt(), any(), any());
6848 
6849         /* Unicast would like to stream again before previous resume was complete*/
6850         mBassClientService.handleUnicastSourceStreamStatusChange(
6851                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
6852 
6853         /* Unicast finished streaming */
6854         mBassClientService.handleUnicastSourceStreamStatusChange(
6855                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
6856         mInOrderMethodProxy
6857                 .verify(mMethodProxy)
6858                 .periodicAdvertisingManagerRegisterSync(
6859                         any(), any(), anyInt(), anyInt(), any(), any());
6860         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // In case of add source to inactive
6861         verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
6862     }
6863 
6864     @Test
6865     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_handleUnicastSourceStreamStatusChangeNoContext_withoutScanning()6866     public void hostIntentional_handleUnicastSourceStreamStatusChangeNoContext_withoutScanning() {
6867         prepareSynchronizedPairAndStopSearching();
6868 
6869         /* Unicast would like to stream */
6870         mBassClientService.handleUnicastSourceStreamStatusChange(
6871                 3 /* STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE */);
6872         checkNoSinkPause();
6873         verifyModifyMessageAndInjectSourceModfified();
6874 
6875         /* Unicast finished streaming */
6876         mBassClientService.handleUnicastSourceStreamStatusChange(
6877                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
6878         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6879         verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
6880     }
6881 
6882     @Test
6883     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
hostIntentional_handleUnicastSourceStreamStatusChangeNoContext_duringScanning()6884     public void hostIntentional_handleUnicastSourceStreamStatusChangeNoContext_duringScanning() {
6885         prepareSynchronizedPair();
6886 
6887         /* Unicast would like to stream */
6888         mBassClientService.handleUnicastSourceStreamStatusChange(
6889                 3 /* STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE */);
6890         checkNoSinkPause();
6891         verifyModifyMessageAndInjectSourceModfified();
6892 
6893         /* Unicast finished streaming */
6894         mBassClientService.handleUnicastSourceStreamStatusChange(
6895                 2 /* STATUS_LOCAL_STREAM_SUSPENDED */);
6896         verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID));
6897     }
6898 
6899     @Test
6900     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
outOfRange_syncEstablishedFailed_stopMonitoringAfterTimeout()6901     public void outOfRange_syncEstablishedFailed_stopMonitoringAfterTimeout() {
6902         prepareSynchronizedPairAndStopSearching();
6903 
6904         // Bis and PA unsynced, SINK_UNINTENTIONAL
6905         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6906         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
6907         mInOrderMethodProxy
6908                 .verify(mMethodProxy)
6909                 .periodicAdvertisingManagerRegisterSync(
6910                         any(), any(), anyInt(), anyInt(), any(), any());
6911         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
6912         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
6913 
6914         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
6915         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
6916         mInOrderMethodProxy
6917                 .verify(mMethodProxy)
6918                 .periodicAdvertisingManagerRegisterSync(
6919                         any(), any(), anyInt(), anyInt(), any(), any());
6920 
6921         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
6922         mInOrderMethodProxy
6923                 .verify(mMethodProxy)
6924                 .periodicAdvertisingManagerRegisterSync(
6925                         any(), any(), anyInt(), anyInt(), any(), any());
6926 
6927         checkAndDispatchTimeout(
6928                 TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
6929         mInOrderMethodProxy
6930                 .verify(mMethodProxy)
6931                 .periodicAdvertisingManagerUnregisterSync(any(), any());
6932     }
6933 
6934     @Test
6935     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
outOfRange_syncEstablishedFailed_clearTimeout()6936     public void outOfRange_syncEstablishedFailed_clearTimeout() {
6937         prepareSynchronizedPairAndStopSearching();
6938 
6939         // Bis and PA unsynced, SINK_UNINTENTIONAL
6940         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6941         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
6942         mInOrderMethodProxy
6943                 .verify(mMethodProxy)
6944                 .periodicAdvertisingManagerRegisterSync(
6945                         any(), any(), anyInt(), anyInt(), any(), any());
6946         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
6947         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
6948 
6949         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
6950         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
6951         mInOrderMethodProxy
6952                 .verify(mMethodProxy)
6953                 .periodicAdvertisingManagerRegisterSync(
6954                         any(), any(), anyInt(), anyInt(), any(), any());
6955 
6956         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
6957         mInOrderMethodProxy
6958                 .verify(mMethodProxy)
6959                 .periodicAdvertisingManagerRegisterSync(
6960                         any(), any(), anyInt(), anyInt(), any(), any());
6961 
6962         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
6963         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
6964     }
6965 
6966     @Test
6967     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
outOfRange_syncEstablishedFailed_restartSearching()6968     public void outOfRange_syncEstablishedFailed_restartSearching() {
6969         prepareSynchronizedPairAndStopSearching();
6970 
6971         // Bis and PA unsynced, SINK_UNINTENTIONAL
6972         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
6973         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
6974         mInOrderMethodProxy
6975                 .verify(mMethodProxy)
6976                 .periodicAdvertisingManagerRegisterSync(
6977                         any(), any(), anyInt(), anyInt(), any(), any());
6978         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
6979         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
6980 
6981         // Start OOR monitoring
6982         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
6983         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
6984         mInOrderMethodProxy
6985                 .verify(mMethodProxy)
6986                 .periodicAdvertisingManagerRegisterSync(
6987                         any(), any(), anyInt(), anyInt(), any(), any());
6988 
6989         // Starting a search should not clear the cache for SINK_UNINTENTIONAL, which allows
6990         // register sync again if available or synchronization attempts after stopping the search
6991         startSearchingForSources();
6992         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
6993         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
6994 
6995         // During a search, unintentionally paused broadcasts are monitored via onScanResult
6996         // Test below does not guarantee that the cache is preserved; this will be checked later
6997         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
6998         mInOrderMethodProxy
6999                 .verify(mMethodProxy)
7000                 .periodicAdvertisingManagerRegisterSync(
7001                         any(), any(), anyInt(), anyInt(), any(), any());
7002         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
7003         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
7004 
7005         // After a search is stopped, start syncing in a loop for unintentionally paused broadcasts
7006         mBassClientService.stopSearchingForSources();
7007         mInOrderMethodProxy
7008                 .verify(mMethodProxy)
7009                 .periodicAdvertisingManagerRegisterSync(
7010                         any(), any(), anyInt(), anyInt(), any(), any());
7011 
7012         // Still OOR
7013         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
7014         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
7015         mInOrderMethodProxy
7016                 .verify(mMethodProxy)
7017                 .periodicAdvertisingManagerRegisterSync(
7018                         any(), any(), anyInt(), anyInt(), any(), any());
7019 
7020         // Check if cache is not cleared after start searching by using addSource
7021         startSearchingForSources();
7022         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
7023         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
7024 
7025         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
7026         mInOrderMethodProxy
7027                 .verify(mMethodProxy)
7028                 .periodicAdvertisingManagerRegisterSync(
7029                         any(), any(), anyInt(), anyInt(), any(), any());
7030     }
7031 
7032     @Test
7033     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
outOfRange_syncEstablishedFailed_allowSyncAnotherBroadcaster()7034     public void outOfRange_syncEstablishedFailed_allowSyncAnotherBroadcaster() {
7035         prepareSynchronizedPairAndStopSearching();
7036 
7037         // Bis and PA unsynced, SINK_UNINTENTIONAL
7038         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7039         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7040         mInOrderMethodProxy
7041                 .verify(mMethodProxy)
7042                 .periodicAdvertisingManagerRegisterSync(
7043                         any(), any(), anyInt(), anyInt(), any(), any());
7044         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
7045         checkNoTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
7046 
7047         // Start OOR monitoring
7048         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
7049         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BROADCAST_MONITOR_TIMEOUT);
7050         mInOrderMethodProxy
7051                 .verify(mMethodProxy)
7052                 .periodicAdvertisingManagerRegisterSync(
7053                         any(), any(), anyInt(), anyInt(), any(), any());
7054 
7055         // Starting a search should not clear the cache for SINK_UNINTENTIONAL, which allows
7056         // register sync again if available or synchronization attempts after stopping the search
7057         startSearchingForSources();
7058         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
7059 
7060         // Check sync to another broadcaster during OOR monitoring
7061         ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class);
7062         checkTimeout(TEST_BROADCAST_ID, BassClientService.MESSAGE_BIG_MONITOR_TIMEOUT);
7063         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
7064         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
7065         mInOrderMethodProxy
7066                 .verify(mMethodProxy)
7067                 .periodicAdvertisingManagerRegisterSync(
7068                         any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any());
7069         assertThat(
7070                         BassUtils.parseBroadcastId(
7071                                 resultCaptor
7072                                         .getValue()
7073                                         .getScanRecord()
7074                                         .getServiceData()
7075                                         .get(BassConstants.BAAS_UUID)))
7076                 .isEqualTo(TEST_BROADCAST_ID + 1);
7077     }
7078 
7079     @Test
7080     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
autoSyncToBroadcast_AlreadySyncedToSink_onStartSearching()7081     public void autoSyncToBroadcast_AlreadySyncedToSink_onStartSearching() {
7082         prepareSynchronizedPairAndStopSearching();
7083 
7084         // Verify that start searching cause sync when broadcaster synced to sinks
7085         startSearchingForSources();
7086         mInOrderMethodProxy
7087                 .verify(mMethodProxy)
7088                 .periodicAdvertisingManagerRegisterSync(
7089                         any(), any(), anyInt(), anyInt(), any(), any());
7090     }
7091 
7092     /**
7093      * Test add source will be triggered if new device connected and its peer is synced to broadcast
7094      * source
7095      */
7096     @Test
7097     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkBassStateReady_addSourceIfPeerDeviceSynced()7098     public void sinkBassStateReady_addSourceIfPeerDeviceSynced() throws RemoteException {
7099         // Imitate broadcast being active
7100         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
7101         prepareTwoSynchronizedDevicesForLocalBroadcast();
7102 
7103         mBassClientService.getCallbacks().notifyBassStateReady(mCurrentDevice);
7104         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
7105 
7106         assertThat(mStateMachines).hasSize(2);
7107         for (BassClientStateMachine sm : mStateMachines.values()) {
7108             // No adding source if device remain synced
7109             verify(sm, never()).sendMessage(any());
7110         }
7111 
7112         // Remove source on the mCurrentDevice
7113         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice), TEST_SOURCE_ID);
7114 
7115         mBassClientService.getCallbacks().notifyBassStateReady(mCurrentDevice);
7116         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
7117 
7118         for (BassClientStateMachine sm : mStateMachines.values()) {
7119             // Verify mCurrentDevice is resuming the broadcast
7120             if (sm.getDevice().equals(mCurrentDevice1)) {
7121                 verify(sm, never()).sendMessage(any());
7122             } else if (sm.getDevice().equals(mCurrentDevice)) {
7123                 ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
7124                 verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
7125 
7126                 Message msg =
7127                         messageCaptor.getAllValues().stream()
7128                                 .filter(m -> (m.what == BassClientStateMachine.ADD_BCAST_SOURCE))
7129                                 .findFirst()
7130                                 .orElse(null);
7131                 assertThat(msg).isNotNull();
7132                 clearInvocations(sm);
7133             } else {
7134                 throw new AssertionError("Unexpected device");
7135             }
7136         }
7137     }
7138 
7139     /** Test add pending source when BASS state get ready */
7140     @Test
7141     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkBassStateReady_addPendingSource()7142     public void sinkBassStateReady_addPendingSource() throws RemoteException {
7143         prepareConnectedDeviceGroup();
7144         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7145         // Verify adding source when Bass state not ready
7146         for (BassClientStateMachine sm : mStateMachines.values()) {
7147             doReturn(false).when(sm).isBassStateReady();
7148         }
7149         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
7150         doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta)))
7151                 .when(mLeAudioService)
7152                 .getAllBroadcastMetadata();
7153         // Add broadcast source and got queued due to BASS not ready
7154         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ false);
7155 
7156         mBassClientService.getCallbacks().notifyBassStateSetupFailed(mCurrentDevice);
7157         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
7158 
7159         // Verify adding source callback is triggered if BASS state initiate failed
7160         verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce())
7161                 .onSourceAddFailed(
7162                         eq(mCurrentDevice),
7163                         eq(meta),
7164                         eq(BluetoothStatusCodes.ERROR_REMOTE_NOT_ENOUGH_RESOURCES));
7165 
7166         // Verify not getting ADD_BCAST_SOURCE message if no pending source to add
7167         for (BassClientStateMachine sm : mStateMachines.values()) {
7168             doReturn(true).when(sm).isBassStateReady();
7169         }
7170         mBassClientService.getCallbacks().notifyBassStateReady(mCurrentDevice);
7171         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
7172 
7173         for (BassClientStateMachine sm : mStateMachines.values()) {
7174             if (sm.getDevice().equals(mCurrentDevice)) {
7175                 verify(sm, never()).sendMessage(any());
7176                 clearInvocations(sm);
7177             }
7178         }
7179 
7180         for (BassClientStateMachine sm : mStateMachines.values()) {
7181             doReturn(false).when(sm).isBassStateReady();
7182         }
7183         // Add broadcast source and got queued due to BASS not ready
7184         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ false);
7185 
7186         for (BassClientStateMachine sm : mStateMachines.values()) {
7187             doReturn(true).when(sm).isBassStateReady();
7188         }
7189         mBassClientService.getCallbacks().notifyBassStateReady(mCurrentDevice);
7190         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
7191 
7192         // Verify adding source is resumed once BASS state ready
7193         for (BassClientStateMachine sm : mStateMachines.values()) {
7194             if (sm.getDevice().equals(mCurrentDevice)) {
7195                 ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
7196                 verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
7197 
7198                 Message msg =
7199                         messageCaptor.getAllValues().stream()
7200                                 .filter(m -> (m.what == BassClientStateMachine.ADD_BCAST_SOURCE))
7201                                 .findFirst()
7202                                 .orElse(null);
7203                 assertThat(msg).isNotNull();
7204                 clearInvocations(sm);
7205             }
7206         }
7207     }
7208 
7209     /** Test add pending source when BASS state get ready */
7210     @Test
7211     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkBassStateReady_addPendingSourceGroup_oneByOneReady()7212     public void sinkBassStateReady_addPendingSourceGroup_oneByOneReady() throws RemoteException {
7213         prepareConnectedDeviceGroup();
7214         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7215         // Verify adding source when Bass state not ready
7216         for (BassClientStateMachine sm : mStateMachines.values()) {
7217             doReturn(false).when(sm).isBassStateReady();
7218         }
7219         doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
7220         doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta)))
7221                 .when(mLeAudioService)
7222                 .getAllBroadcastMetadata();
7223         // Add broadcast source and got queued due to BASS not ready
7224         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
7225 
7226         // First BASS ready
7227         doReturn(true).when(mStateMachines.get(mCurrentDevice)).isBassStateReady();
7228         mBassClientService.getCallbacks().notifyBassStateReady(mCurrentDevice);
7229         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
7230 
7231         // Verify adding source is resumed once BASS state ready
7232         for (BassClientStateMachine sm : mStateMachines.values()) {
7233             if (sm.getDevice().equals(mCurrentDevice)) {
7234                 ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
7235                 verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
7236 
7237                 Message msg =
7238                         messageCaptor.getAllValues().stream()
7239                                 .filter(m -> (m.what == BassClientStateMachine.ADD_BCAST_SOURCE))
7240                                 .findFirst()
7241                                 .orElse(null);
7242                 assertThat(msg).isNotNull();
7243                 clearInvocations(sm);
7244             } else if (sm.getDevice().equals(mCurrentDevice1)) {
7245                 verify(sm, never()).sendMessage(any());
7246             }
7247         }
7248 
7249         // Second BASS ready
7250         doReturn(true).when(mStateMachines.get(mCurrentDevice1)).isBassStateReady();
7251         mBassClientService.getCallbacks().notifyBassStateReady(mCurrentDevice1);
7252         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
7253 
7254         // Verify adding source is resumed once BASS state ready
7255         for (BassClientStateMachine sm : mStateMachines.values()) {
7256             if (sm.getDevice().equals(mCurrentDevice)) {
7257                 verify(sm, never()).sendMessage(any());
7258             } else if (sm.getDevice().equals(mCurrentDevice1)) {
7259                 ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
7260                 verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
7261 
7262                 Message msg =
7263                         messageCaptor.getAllValues().stream()
7264                                 .filter(m -> (m.what == BassClientStateMachine.ADD_BCAST_SOURCE))
7265                                 .findFirst()
7266                                 .orElse(null);
7267                 assertThat(msg).isNotNull();
7268                 clearInvocations(sm);
7269             }
7270         }
7271     }
7272 
7273     @Test
testIsLocalBroadcast()7274     public void testIsLocalBroadcast() {
7275         int broadcastId = 12345;
7276 
7277         BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata(broadcastId);
7278         BluetoothLeBroadcastReceiveState receiveState =
7279                 new BluetoothLeBroadcastReceiveState(
7280                         TEST_SOURCE_ID,
7281                         metadata.getSourceAddressType(),
7282                         metadata.getSourceDevice(),
7283                         metadata.getSourceAdvertisingSid(),
7284                         metadata.getBroadcastId(),
7285                         BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
7286                         BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
7287                         null,
7288                         metadata.getSubgroups().size(),
7289                         // Bis sync states
7290                         metadata.getSubgroups().stream()
7291                                 .map(e -> (long) 0x00000001)
7292                                 .collect(Collectors.toList()),
7293                         metadata.getSubgroups().stream()
7294                                 .map(e -> e.getContentMetadata())
7295                                 .collect(Collectors.toList()));
7296 
7297         /* External broadcast check */
7298         doReturn(new ArrayList<BluetoothLeBroadcastMetadata>())
7299                 .when(mLeAudioService)
7300                 .getAllBroadcastMetadata();
7301 
7302         assertThat(mBassClientService.isLocalBroadcast(metadata)).isFalse();
7303         assertThat(mBassClientService.isLocalBroadcast(receiveState)).isFalse();
7304 
7305         /* Local broadcast check */
7306         doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(metadata)))
7307                 .when(mLeAudioService)
7308                 .getAllBroadcastMetadata();
7309 
7310         assertThat(mBassClientService.isLocalBroadcast(metadata)).isTrue();
7311         assertThat(mBassClientService.isLocalBroadcast(receiveState)).isTrue();
7312     }
7313 
verifyInitiatePaSyncTransferAndNoOthers()7314     private void verifyInitiatePaSyncTransferAndNoOthers() {
7315         expect.that(mStateMachines.size()).isEqualTo(2);
7316         for (BassClientStateMachine sm : mStateMachines.values()) {
7317             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
7318             verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
7319             long count;
7320             if (sm.getDevice().equals(mCurrentDevice)) {
7321                 count =
7322                         messageCaptor.getAllValues().stream()
7323                                 .filter(
7324                                         m ->
7325                                                 (m.what
7326                                                                 == BassClientStateMachine
7327                                                                         .INITIATE_PA_SYNC_TRANSFER)
7328                                                         && (m.arg1 == TEST_SYNC_HANDLE)
7329                                                         && (m.arg2 == TEST_SOURCE_ID))
7330                                 .count();
7331                 assertThat(count).isEqualTo(1);
7332                 count =
7333                         messageCaptor.getAllValues().stream()
7334                                 .filter(
7335                                         m ->
7336                                                 m.what
7337                                                         != BassClientStateMachine
7338                                                                 .INITIATE_PA_SYNC_TRANSFER)
7339                                 .count();
7340                 assertThat(count).isEqualTo(0);
7341             } else if (sm.getDevice().equals(mCurrentDevice1)) {
7342                 count =
7343                         messageCaptor.getAllValues().stream()
7344                                 .filter(
7345                                         m ->
7346                                                 (m.what
7347                                                                 == BassClientStateMachine
7348                                                                         .INITIATE_PA_SYNC_TRANSFER)
7349                                                         && (m.arg1 == TEST_SYNC_HANDLE)
7350                                                         && (m.arg2 == TEST_SOURCE_ID + 1))
7351                                 .count();
7352                 assertThat(count).isEqualTo(1);
7353                 count =
7354                         messageCaptor.getAllValues().stream()
7355                                 .filter(
7356                                         m ->
7357                                                 m.what
7358                                                         != BassClientStateMachine
7359                                                                 .INITIATE_PA_SYNC_TRANSFER)
7360                                 .count();
7361                 assertThat(count).isEqualTo(0);
7362             } else {
7363                 throw new AssertionError("Unexpected device");
7364             }
7365         }
7366     }
7367 
7368     @Test
7369     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
initiatePaSyncTransfer()7370     public void initiatePaSyncTransfer() {
7371         prepareSynchronizedPairAndStopSearching();
7372 
7373         // Sync info request force syncing to broadcaster and add sinks pending for PAST
7374         mBassClientService.syncRequestForPast(mCurrentDevice, TEST_BROADCAST_ID, TEST_SOURCE_ID);
7375         mBassClientService.syncRequestForPast(
7376                 mCurrentDevice1, TEST_BROADCAST_ID, TEST_SOURCE_ID + 1);
7377         mInOrderMethodProxy
7378                 .verify(mMethodProxy)
7379                 .periodicAdvertisingManagerRegisterSync(
7380                         any(), any(), anyInt(), anyInt(), any(), any());
7381 
7382         // Sync will INITIATE_PA_SYNC_TRANSFER
7383         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7384         verifyInitiatePaSyncTransferAndNoOthers();
7385     }
7386 
7387     @Test
7388     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
InitiatePaSyncTransfer_concurrentWithResume()7389     public void InitiatePaSyncTransfer_concurrentWithResume() {
7390         prepareSynchronizedPairAndStopSearching();
7391 
7392         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7393 
7394         // Cache sinks for resume and set HOST_INTENTIONAL pause
7395         mBassClientService.handleUnicastSourceStreamStatusChange(
7396                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
7397         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7398 
7399         // Resume source will force syncing to broadcaster and put pending source to add
7400         mBassClientService.resumeReceiversSourceSynchronization();
7401         mInOrderMethodProxy
7402                 .verify(mMethodProxy)
7403                 .periodicAdvertisingManagerRegisterSync(
7404                         any(), any(), anyInt(), anyInt(), any(), any());
7405 
7406         // Sync info request add sinks pending for PAST
7407         mBassClientService.syncRequestForPast(mCurrentDevice, TEST_BROADCAST_ID, TEST_SOURCE_ID);
7408         mBassClientService.syncRequestForPast(
7409                 mCurrentDevice1, TEST_BROADCAST_ID, TEST_SOURCE_ID + 1);
7410 
7411         // Sync will send INITIATE_PA_SYNC_TRANSFER and remove pending source to add
7412         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7413         verifyInitiatePaSyncTransferAndNoOthers();
7414     }
7415 
7416     @Test
7417     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
resumeSourceSynchronization_omitWhenPaSyncedOrRequested()7418     public void resumeSourceSynchronization_omitWhenPaSyncedOrRequested() {
7419         prepareSynchronizedPair();
7420 
7421         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7422 
7423         // Cache sinks for resume and set HOST_INTENTIONAL pause
7424         // Try resume while sync info requested
7425         mBassClientService.handleUnicastSourceStreamStatusChange(
7426                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
7427         injectRemoteSourceStateChanged(
7428                 meta, BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST, false);
7429         checkNoResumeSynchronizationByHost();
7430 
7431         // Cache sinks for resume and set HOST_INTENTIONAL pause
7432         // Try resume while pa synced
7433         mBassClientService.handleUnicastSourceStreamStatusChange(
7434                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
7435         injectRemoteSourceStateChanged(meta, /* isPaSynced */ true, /* isBisSynced */ false);
7436         checkNoResumeSynchronizationByHost();
7437 
7438         // Cache sinks for resume and set HOST_INTENTIONAL pause
7439         // Try resume while pa unsynced
7440         mBassClientService.handleUnicastSourceStreamStatusChange(
7441                 0 /* STATUS_LOCAL_STREAM_REQUESTED */);
7442         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7443         checkResumeSynchronizationByHost();
7444     }
7445 
7446     @Test
7447     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
removeSource_duringSuspend()7448     public void removeSource_duringSuspend() {
7449         prepareSynchronizedPair();
7450 
7451         // Suspend receivers, HOST_INTENTIONAL
7452         mBassClientService.suspendReceiversSourceSynchronization(TEST_BROADCAST_ID);
7453 
7454         // Remove source, HOST_INTENTIONAL
7455         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
7456         checkNoSinkPause();
7457         verifyRemoveMessageAndInjectSourceRemoval();
7458 
7459         checkNoResumeSynchronizationByHost();
7460     }
7461 
7462     @Test
7463     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
stopReceivers_duringSuspend()7464     public void stopReceivers_duringSuspend() {
7465         prepareSynchronizedPair();
7466 
7467         // Suspend receivers, HOST_INTENTIONAL
7468         mBassClientService.suspendReceiversSourceSynchronization(TEST_BROADCAST_ID);
7469 
7470         // Remove source, HOST_INTENTIONAL
7471         mBassClientService.stopReceiversSourceSynchronization(TEST_BROADCAST_ID);
7472         checkNoSinkPause();
7473         verifyRemoveMessageAndInjectSourceRemoval();
7474 
7475         checkNoResumeSynchronizationByHost();
7476     }
7477 
7478     @Test
7479     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
multipleSinkMetadata_clearWhenSourceAddFailed()7480     public void multipleSinkMetadata_clearWhenSourceAddFailed() throws RemoteException {
7481         prepareConnectedDeviceGroup();
7482         startSearchingForSources();
7483         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7484         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7485         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7486         verifyAddSourceForGroup(meta);
7487         prepareRemoteSourceState(meta, /* isPaSynced */ true, /* isBisSynced */ true);
7488         mBassClientService.stopSearchingForSources();
7489         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7490         for (BassClientStateMachine sm : mStateMachines.values()) {
7491             clearInvocations(sm);
7492         }
7493 
7494         // Cache and resume ended with source add failed, should remove metadata
7495         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
7496         mBassClientService.resumeReceiversSourceSynchronization();
7497         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
7498         TestUtils.waitForLooperToFinishScheduledTask(mBassClientService.getCallbacks().getLooper());
7499         verify(mCallback).onSourceLost(eq(TEST_BROADCAST_ID));
7500         verify(mCallback)
7501                 .onSourceAddFailed(
7502                         eq(mCurrentDevice),
7503                         eq(meta),
7504                         eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
7505         verify(mCallback)
7506                 .onSourceAddFailed(
7507                         eq(mCurrentDevice1),
7508                         eq(meta),
7509                         eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
7510 
7511         startSearchingForSources();
7512         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7513         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7514 
7515         // Cache and resume should not resume at all
7516         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
7517         mBassClientService.resumeReceiversSourceSynchronization();
7518         assertThat(mStateMachines.size()).isEqualTo(2);
7519         for (BassClientStateMachine sm : mStateMachines.values()) {
7520             verify(sm, never()).sendMessage(any());
7521         }
7522     }
7523 
7524     @Test
7525     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
multipleSinkMetadata_clearWhenSwitch()7526     public void multipleSinkMetadata_clearWhenSwitch() {
7527         prepareConnectedDeviceGroup();
7528         startSearchingForSources();
7529         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7530         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7531         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7532         verifyAddSourceForGroup(meta);
7533         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7534 
7535         // Add another new broadcast source should remove old metadata during switch
7536         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
7537         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
7538         BluetoothLeBroadcastMetadata newMeta = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
7539         mBassClientService.addSource(mCurrentDevice, newMeta, /* isGroupOp */ true);
7540         verifyAllGroupMembersGettingUpdateOrAddSource(newMeta);
7541         for (BassClientStateMachine sm : mStateMachines.values()) {
7542             clearInvocations(sm);
7543         }
7544         prepareRemoteSourceState(newMeta, /* isPaSynced */ false, /* isBisSynced */ false);
7545 
7546         // Cache and resume should resume only new broadcast
7547         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID + 1);
7548         mBassClientService.resumeReceiversSourceSynchronization();
7549         // Verify that only one message per sink was sent
7550         for (BassClientStateMachine sm : mStateMachines.values()) {
7551             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
7552             verify(sm).sendMessage(messageCaptor.capture());
7553         }
7554         // And this message is to resume broadcast
7555         verifyAllGroupMembersGettingUpdateOrAddSource(newMeta);
7556     }
7557 
7558     @Test
7559     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
multipleSinkMetadata_clearWhenSwitch_duringSuspend()7560     public void multipleSinkMetadata_clearWhenSwitch_duringSuspend() {
7561         prepareConnectedDeviceGroup();
7562         startSearchingForSources();
7563         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7564         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7565         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7566         verifyAddSourceForGroup(meta);
7567         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7568 
7569         /* Unicast would like to stream */
7570         mBassClientService.handleUnicastSourceStreamStatusChange(
7571                 3 /* STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE */);
7572         verifyModifyMessageAndInjectSourceModfified();
7573         for (BassClientStateMachine sm : mStateMachines.values()) {
7574             clearInvocations(sm);
7575         }
7576 
7577         // Add another new broadcast source should remove old metadata
7578         onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
7579         onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
7580         BluetoothLeBroadcastMetadata newMeta = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
7581         mBassClientService.addSource(mCurrentDevice, newMeta, /* isGroupOp */ true);
7582         verifyAllGroupMembersGettingUpdateOrAddSource(newMeta);
7583         for (BassClientStateMachine sm : mStateMachines.values()) {
7584             clearInvocations(sm);
7585         }
7586         prepareRemoteSourceState(newMeta, /* isPaSynced */ false, /* isBisSynced */ false);
7587 
7588         // Cache and resume should resume only new broadcast
7589         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID + 1);
7590         mBassClientService.resumeReceiversSourceSynchronization();
7591         // Verify that only one message per sink was sent
7592         for (BassClientStateMachine sm : mStateMachines.values()) {
7593             ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
7594             verify(sm).sendMessage(messageCaptor.capture());
7595         }
7596         // And this message is to resume broadcast
7597         verifyAllGroupMembersGettingUpdateOrAddSource(newMeta);
7598     }
7599 
7600     @Test
7601     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
multipleSinkMetadata_clearWhenRemove()7602     public void multipleSinkMetadata_clearWhenRemove() {
7603         prepareConnectedDeviceGroup();
7604         startSearchingForSources();
7605         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7606         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7607         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7608         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ false);
7609         mBassClientService.addSource(mCurrentDevice1, meta, /* isGroupOp */ false);
7610         for (BassClientStateMachine sm : mStateMachines.values()) {
7611             clearInvocations(sm);
7612         }
7613         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7614 
7615         // Remove source should remove metadata
7616         // Do not clear receive state
7617         mBassClientService.removeSource(mCurrentDevice, TEST_SOURCE_ID);
7618         for (BassClientStateMachine sm : mStateMachines.values()) {
7619             clearInvocations(sm);
7620         }
7621 
7622         // Cache and resume should resume only one broadcaster
7623         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
7624         mBassClientService.resumeReceiversSourceSynchronization();
7625         assertThat(mStateMachines.size()).isEqualTo(2);
7626         for (BassClientStateMachine sm : mStateMachines.values()) {
7627             if (sm.getDevice().equals(mCurrentDevice)) {
7628                 verify(sm, never()).sendMessage(any());
7629                 clearInvocations(sm);
7630             } else if (sm.getDevice().equals(mCurrentDevice1)) {
7631                 ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
7632                 verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
7633 
7634                 Message msg =
7635                         messageCaptor.getAllValues().stream()
7636                                 .filter(m -> (m.what == BassClientStateMachine.UPDATE_BCAST_SOURCE))
7637                                 .findFirst()
7638                                 .orElse(null);
7639                 assertThat(msg).isNotNull();
7640                 clearInvocations(sm);
7641             }
7642         }
7643 
7644         // Remove source should remove metadata
7645         // Do not clear receive state
7646         mBassClientService.removeSource(mCurrentDevice1, TEST_SOURCE_ID + 1);
7647         for (BassClientStateMachine sm : mStateMachines.values()) {
7648             clearInvocations(sm);
7649         }
7650 
7651         // Cache and resume should not resume at all
7652         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
7653         mBassClientService.resumeReceiversSourceSynchronization();
7654         assertThat(mStateMachines.size()).isEqualTo(2);
7655         for (BassClientStateMachine sm : mStateMachines.values()) {
7656             verify(sm, never()).sendMessage(any());
7657         }
7658     }
7659 
7660     @Test
7661     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
multipleSinkMetadata_clearWhenAllDisconnected()7662     public void multipleSinkMetadata_clearWhenAllDisconnected() {
7663         prepareConnectedDeviceGroup();
7664         startSearchingForSources();
7665         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7666         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7667         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7668         verifyAddSourceForGroup(meta);
7669         for (BassClientStateMachine sm : mStateMachines.values()) {
7670             clearInvocations(sm);
7671         }
7672         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7673 
7674         // Disconnect first sink not cause removing metadata
7675         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7676         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
7677         mBassClientService.connectionStateChanged(
7678                 mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
7679         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice), TEST_SOURCE_ID);
7680 
7681         // Connect again first sink
7682         doReturn(STATE_CONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7683         doReturn(true).when(mStateMachines.get(mCurrentDevice)).isConnected();
7684         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7685 
7686         // Cache and resume should resume all devices
7687         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
7688         mBassClientService.resumeReceiversSourceSynchronization();
7689         verifyAllGroupMembersGettingUpdateOrAddSource(meta);
7690         for (BassClientStateMachine sm : mStateMachines.values()) {
7691             clearInvocations(sm);
7692         }
7693 
7694         // Disconnect first sink not cause removing metadata
7695         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7696         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
7697         mBassClientService.connectionStateChanged(
7698                 mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
7699         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice), TEST_SOURCE_ID);
7700 
7701         // Disconnect second sink cause remove metadata for both devices
7702         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice1)).getConnectionState();
7703         doReturn(false).when(mStateMachines.get(mCurrentDevice1)).isConnected();
7704         mBassClientService.connectionStateChanged(
7705                 mCurrentDevice1, STATE_CONNECTED, STATE_DISCONNECTED);
7706         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice1), TEST_SOURCE_ID + 1);
7707 
7708         // Connect again both devices
7709         doReturn(STATE_CONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7710         doReturn(true).when(mStateMachines.get(mCurrentDevice)).isConnected();
7711         doReturn(STATE_CONNECTED).when(mStateMachines.get(mCurrentDevice1)).getConnectionState();
7712         doReturn(true).when(mStateMachines.get(mCurrentDevice1)).isConnected();
7713         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7714 
7715         // Cache and resume should not resume at all
7716         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
7717         mBassClientService.resumeReceiversSourceSynchronization();
7718         assertThat(mStateMachines.size()).isEqualTo(2);
7719         for (BassClientStateMachine sm : mStateMachines.values()) {
7720             verify(sm, never()).sendMessage(any());
7721         }
7722     }
7723 
7724     @Test
7725     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
multipleSinkMetadata_clearWhenAllDisconnected_duringSuspend()7726     public void multipleSinkMetadata_clearWhenAllDisconnected_duringSuspend() {
7727         prepareConnectedDeviceGroup();
7728         startSearchingForSources();
7729         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7730         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7731         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7732         verifyAddSourceForGroup(meta);
7733         for (BassClientStateMachine sm : mStateMachines.values()) {
7734             clearInvocations(sm);
7735         }
7736         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7737 
7738         /* Unicast would like to stream */
7739         mBassClientService.handleUnicastSourceStreamStatusChange(
7740                 3 /* STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE */);
7741         verifyModifyMessageAndInjectSourceModfified();
7742         for (BassClientStateMachine sm : mStateMachines.values()) {
7743             clearInvocations(sm);
7744         }
7745 
7746         // Disconnect first sink not cause removing metadata
7747         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7748         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
7749         mBassClientService.connectionStateChanged(
7750                 mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
7751         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice), TEST_SOURCE_ID);
7752 
7753         // Connect again first sink
7754         doReturn(STATE_CONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7755         doReturn(true).when(mStateMachines.get(mCurrentDevice)).isConnected();
7756         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7757 
7758         // Cache and resume should resume all devices
7759         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
7760         mBassClientService.resumeReceiversSourceSynchronization();
7761         verifyAllGroupMembersGettingUpdateOrAddSource(meta);
7762         for (BassClientStateMachine sm : mStateMachines.values()) {
7763             clearInvocations(sm);
7764         }
7765 
7766         /* Unicast would like to stream */
7767         mBassClientService.handleUnicastSourceStreamStatusChange(
7768                 3 /* STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE */);
7769         verifyModifyMessageAndInjectSourceModfified();
7770         for (BassClientStateMachine sm : mStateMachines.values()) {
7771             clearInvocations(sm);
7772         }
7773 
7774         // Disconnect first sink not cause removing metadata
7775         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7776         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
7777         mBassClientService.connectionStateChanged(
7778                 mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
7779         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice), TEST_SOURCE_ID);
7780 
7781         // Disconnect second sink cause remove metadata for both devices
7782         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice1)).getConnectionState();
7783         doReturn(false).when(mStateMachines.get(mCurrentDevice1)).isConnected();
7784         mBassClientService.connectionStateChanged(
7785                 mCurrentDevice1, STATE_CONNECTED, STATE_DISCONNECTED);
7786         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice1), TEST_SOURCE_ID + 1);
7787 
7788         // Connect again both devices
7789         doReturn(STATE_CONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7790         doReturn(true).when(mStateMachines.get(mCurrentDevice)).isConnected();
7791         doReturn(STATE_CONNECTED).when(mStateMachines.get(mCurrentDevice1)).getConnectionState();
7792         doReturn(true).when(mStateMachines.get(mCurrentDevice1)).isConnected();
7793         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7794 
7795         // Cache and resume should not resume at all
7796         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
7797         mBassClientService.resumeReceiversSourceSynchronization();
7798         assertThat(mStateMachines.size()).isEqualTo(2);
7799         for (BassClientStateMachine sm : mStateMachines.values()) {
7800             verify(sm, never()).sendMessage(any());
7801         }
7802     }
7803 
7804     @Test
7805     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
multipleSinkMetadata_clearWhenRemove_oneDisconnectedFirst()7806     public void multipleSinkMetadata_clearWhenRemove_oneDisconnectedFirst() {
7807         prepareConnectedDeviceGroup();
7808         startSearchingForSources();
7809         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7810         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7811         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7812         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ false);
7813         mBassClientService.addSource(mCurrentDevice1, meta, /* isGroupOp */ false);
7814         for (BassClientStateMachine sm : mStateMachines.values()) {
7815             clearInvocations(sm);
7816         }
7817         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7818 
7819         // Disconnect first sink not cause removing metadata
7820         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7821         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
7822         mBassClientService.connectionStateChanged(
7823                 mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
7824         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice), TEST_SOURCE_ID);
7825 
7826         // Remove second source should remove metadata for both
7827         // Do not clear receive state
7828         mBassClientService.removeSource(mCurrentDevice1, TEST_SOURCE_ID + 1);
7829         for (BassClientStateMachine sm : mStateMachines.values()) {
7830             clearInvocations(sm);
7831         }
7832 
7833         // Connect again first sink
7834         doReturn(STATE_CONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7835         doReturn(true).when(mStateMachines.get(mCurrentDevice)).isConnected();
7836         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7837 
7838         // Cache and resume should not resume at all
7839         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
7840         mBassClientService.resumeReceiversSourceSynchronization();
7841         assertThat(mStateMachines.size()).isEqualTo(2);
7842         for (BassClientStateMachine sm : mStateMachines.values()) {
7843             verify(sm, never()).sendMessage(any());
7844         }
7845     }
7846 
7847     @Test
7848     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
multipleSinkMetadata_clearWhenRemove_oneDisconnectedFirst_duringSuspend()7849     public void multipleSinkMetadata_clearWhenRemove_oneDisconnectedFirst_duringSuspend() {
7850         prepareConnectedDeviceGroup();
7851         startSearchingForSources();
7852         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7853         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7854         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7855         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ false);
7856         mBassClientService.addSource(mCurrentDevice1, meta, /* isGroupOp */ false);
7857         for (BassClientStateMachine sm : mStateMachines.values()) {
7858             clearInvocations(sm);
7859         }
7860         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7861 
7862         /* Unicast would like to stream */
7863         mBassClientService.handleUnicastSourceStreamStatusChange(
7864                 3 /* STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE */);
7865         verifyModifyMessageAndInjectSourceModfified();
7866         for (BassClientStateMachine sm : mStateMachines.values()) {
7867             clearInvocations(sm);
7868         }
7869 
7870         // Disconnect first sink not cause removing metadata
7871         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7872         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
7873         mBassClientService.connectionStateChanged(
7874                 mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
7875         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice), TEST_SOURCE_ID);
7876 
7877         // Remove second source should remove metadata for both
7878         // Do not clear receive state
7879         mBassClientService.removeSource(mCurrentDevice1, TEST_SOURCE_ID + 1);
7880         for (BassClientStateMachine sm : mStateMachines.values()) {
7881             clearInvocations(sm);
7882         }
7883 
7884         // Connect again first sink
7885         doReturn(STATE_CONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7886         doReturn(true).when(mStateMachines.get(mCurrentDevice)).isConnected();
7887         prepareRemoteSourceState(meta, /* isPaSynced */ false, /* isBisSynced */ false);
7888 
7889         // Cache and resume should not resume at all
7890         mBassClientService.cacheSuspendingSources(TEST_BROADCAST_ID);
7891         mBassClientService.resumeReceiversSourceSynchronization();
7892         assertThat(mStateMachines.size()).isEqualTo(2);
7893         for (BassClientStateMachine sm : mStateMachines.values()) {
7894             verify(sm, never()).sendMessage(any());
7895         }
7896     }
7897 
7898     @Test
7899     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
clearPendingSourceToAdd_oneByOne_whenDisconnected()7900     public void clearPendingSourceToAdd_oneByOne_whenDisconnected() {
7901         prepareConnectedDeviceGroup();
7902         startSearchingForSources();
7903         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7904         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7905 
7906         // Sync lost without triggering timeout to keep cache
7907         onSyncLost();
7908 
7909         // Add source force syncing to broadcaster and add sinks to pendingSourcesToAdd
7910         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7911         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ false);
7912         mBassClientService.addSource(mCurrentDevice1, meta, /* isGroupOp */ false);
7913 
7914         // Disconnect first sink should remove pendingSourceToAdd for it
7915         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7916         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
7917         mBassClientService.connectionStateChanged(
7918                 mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
7919         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice), TEST_SOURCE_ID);
7920 
7921         // Sync established should add source on only one sink
7922         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7923         assertThat(mStateMachines.size()).isEqualTo(2);
7924         for (BassClientStateMachine sm : mStateMachines.values()) {
7925             if (sm.getDevice().equals(mCurrentDevice)) {
7926                 verify(sm, never()).sendMessage(any());
7927             } else if (sm.getDevice().equals(mCurrentDevice1)) {
7928                 ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
7929                 verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
7930 
7931                 Message msg =
7932                         messageCaptor.getAllValues().stream()
7933                                 .filter(m -> (m.what == BassClientStateMachine.ADD_BCAST_SOURCE))
7934                                 .findFirst()
7935                                 .orElse(null);
7936                 assertThat(msg).isNotNull();
7937                 clearInvocations(sm);
7938             } else {
7939                 throw new AssertionError("Unexpected device");
7940             }
7941         }
7942     }
7943 
7944     @Test
7945     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
clearPendingSourceToAdd_group_whenDisconnected()7946     public void clearPendingSourceToAdd_group_whenDisconnected() {
7947         prepareConnectedDeviceGroup();
7948         startSearchingForSources();
7949         onScanResult(mSourceDevice, TEST_BROADCAST_ID);
7950         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7951 
7952         // Sync lost without triggering timeout to keep cache
7953         onSyncLost();
7954 
7955         // Add source force syncing to broadcaster and add sinks to pendingSourcesToAdd
7956         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
7957         mBassClientService.addSource(mCurrentDevice, meta, /* isGroupOp */ true);
7958 
7959         // Disconnect first sink should remove pendingSourceToAdd for it
7960         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
7961         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
7962         mBassClientService.connectionStateChanged(
7963                 mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
7964         injectRemoteSourceStateRemoval(mStateMachines.get(mCurrentDevice), TEST_SOURCE_ID);
7965 
7966         // Sync established should add source on only one sink
7967         onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
7968         assertThat(mStateMachines.size()).isEqualTo(2);
7969         for (BassClientStateMachine sm : mStateMachines.values()) {
7970             if (sm.getDevice().equals(mCurrentDevice)) {
7971                 verify(sm, never()).sendMessage(any());
7972             } else if (sm.getDevice().equals(mCurrentDevice1)) {
7973                 ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
7974                 verify(sm, atLeast(1)).sendMessage(messageCaptor.capture());
7975 
7976                 Message msg =
7977                         messageCaptor.getAllValues().stream()
7978                                 .filter(m -> (m.what == BassClientStateMachine.ADD_BCAST_SOURCE))
7979                                 .findFirst()
7980                                 .orElse(null);
7981                 assertThat(msg).isNotNull();
7982                 clearInvocations(sm);
7983             } else {
7984                 throw new AssertionError("Unexpected device");
7985             }
7986         }
7987     }
7988 
7989     @Test
7990     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
doNotAllowDuplicatesInAddSelectSource()7991     public void doNotAllowDuplicatesInAddSelectSource() {
7992         prepareSynchronizedPairAndStopSearching();
7993 
7994         // Sync request for past force add to select source
7995         mBassClientService.syncRequestForPast(mCurrentDevice, TEST_BROADCAST_ID, TEST_SOURCE_ID);
7996         mInOrderMethodProxy
7997                 .verify(mMethodProxy)
7998                 .periodicAdvertisingManagerRegisterSync(
7999                         any(), any(), anyInt(), anyInt(), any(), any());
8000 
8001         // Another sync request for past try add to select source again
8002         mBassClientService.syncRequestForPast(
8003                 mCurrentDevice1, TEST_BROADCAST_ID, TEST_SOURCE_ID + 1);
8004 
8005         // On sync failed should be no more sync registration
8006         onSyncEstablishedFailed(mSourceDevice, TEST_SYNC_HANDLE);
8007         mInOrderMethodProxy
8008                 .verify(mMethodProxy, never())
8009                 .periodicAdvertisingManagerRegisterSync(
8010                         any(), any(), anyInt(), anyInt(), any(), any());
8011     }
8012 
8013     @Test
8014     @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER)
sinkDisconnectionDuringResuming()8015     public void sinkDisconnectionDuringResuming() {
8016         prepareSynchronizedPairAndStopSearching();
8017 
8018         BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
8019         mBassClientService.suspendAllReceiversSourceSynchronization();
8020         injectRemoteSourceStateChanged(meta, /* isPaSynced */ false, /* isBisSynced */ false);
8021 
8022         // Prepare disconnection of one sink
8023         doReturn(STATE_DISCONNECTED).when(mStateMachines.get(mCurrentDevice)).getConnectionState();
8024         doReturn(false).when(mStateMachines.get(mCurrentDevice)).isConnected();
8025         doAnswer(
8026                         invocation -> {
8027                             mBassClientService.connectionStateChanged(
8028                                     mCurrentDevice, STATE_CONNECTED, STATE_DISCONNECTED);
8029                             return null;
8030                         })
8031                 .when(mMethodProxy)
8032                 .periodicAdvertisingManagerRegisterSync(
8033                         any(), any(), anyInt(), anyInt(), any(), any());
8034 
8035         mBassClientService.resumeReceiversSourceSynchronization();
8036     }
8037 
verifyConnectionStateIntent(BluetoothDevice device, int newState, int prevState)8038     private void verifyConnectionStateIntent(BluetoothDevice device, int newState, int prevState) {
8039         verifyIntentSent(
8040                 hasAction(BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED),
8041                 hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
8042                 hasExtra(EXTRA_STATE, newState),
8043                 hasExtra(EXTRA_PREVIOUS_STATE, prevState));
8044     }
8045 
8046     @SafeVarargs
verifyIntentSent(Matcher<Intent>.... matchers)8047     private void verifyIntentSent(Matcher<Intent>... matchers) {
8048         mInOrder.verify(mAdapterService, timeout(1000))
8049                 .sendBroadcastMultiplePermissions(
8050                         MockitoHamcrest.argThat(AllOf.allOf(matchers)),
8051                         any(),
8052                         any(BroadcastOptions.class));
8053     }
8054 }
8055