Loading android/app/src/com/android/bluetooth/bass_client/BassClientService.java +54 −33 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeriphe import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine; import static com.android.bluetooth.flags.Flags.leaudioBroadcastMonitorSourceSyncStatus; import static com.android.bluetooth.flags.Flags.leaudioBroadcastResyncHelper; import static com.android.bluetooth.flags.Flags.leaudioSortScansToSyncByFails; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; Loading Loading @@ -158,6 +159,7 @@ public class BassClientService extends ProfileService { new HashMap<>(); private final PriorityQueue<SourceSyncRequest> mSourceSyncRequestsQueue = new PriorityQueue<>(sSourceSyncRequestComparator); private final Map<Integer, Integer> mSyncFailureCounter = new HashMap<>(); private final Map<Integer, Integer> mBisDiscoveryCounterMap = new HashMap<>(); private final List<AddSourceData> mPendingSourcesToAdd = new ArrayList<>(); Loading Loading @@ -280,12 +282,14 @@ public class BassClientService extends ProfileService { } private static class SourceSyncRequest { private ScanResult mScanResult; private boolean mHasPriority; private final ScanResult mScanResult; private final boolean mHasPriority; private final int mSyncFailureCounter; SourceSyncRequest(ScanResult scanResult, boolean hasPriority) { SourceSyncRequest(ScanResult scanResult, boolean hasPriority, int syncFailureCounter) { this.mScanResult = scanResult; this.mHasPriority = hasPriority; this.mSyncFailureCounter = syncFailureCounter; } public ScanResult getScanResult() { Loading @@ -300,6 +304,10 @@ public class BassClientService extends ProfileService { return mHasPriority; } public int getFailsCounter() { return mSyncFailureCounter; } @Override public String toString() { return "SourceSyncRequest{" Loading @@ -307,6 +315,8 @@ public class BassClientService extends ProfileService { + mScanResult + ", mHasPriority=" + mHasPriority + ", mSyncFailureCounter=" + mSyncFailureCounter + '}'; } } Loading @@ -319,6 +329,9 @@ public class BassClientService extends ProfileService { return -1; } else if (!ssr1.hasPriority() && ssr2.hasPriority()) { return 1; } else if (leaudioSortScansToSyncByFails() && (ssr1.getFailsCounter() != ssr2.getFailsCounter())) { return Integer.compare(ssr1.getFailsCounter(), ssr2.getFailsCounter()); } else { return Integer.compare(ssr2.getRssi(), ssr1.getRssi()); } Loading Loading @@ -1046,9 +1059,8 @@ public class BassClientService extends ProfileService { if (isPlaying) { stopBigMonitoring(broadcastId, false); } else if (!mPausedBroadcastIds.containsKey(broadcastId)) { ScanResult scanRes = getCachedBroadcast(broadcastId); if (scanRes != null) { addSelectSourceRequest(scanRes, true); if (mCachedBroadcasts.containsKey(broadcastId)) { addSelectSourceRequest(broadcastId, true); mPausedBroadcastIds.put(broadcastId, PauseType.SINK_UNKNOWN); logPausedBroadcastsAndSinks(); mHandler.removeMessages(MESSAGE_BIG_CHECK_START); Loading Loading @@ -1842,11 +1854,11 @@ public class BassClientService extends ProfileService { "Broadcast Source Found: Broadcast ID: " + broadcastId); if (broadcastId != BassConstants.INVALID_BROADCAST_ID && mCachedBroadcasts.get(broadcastId) == null) { && !mCachedBroadcasts.containsKey(broadcastId)) { log("selectBroadcastSource: broadcastId " + broadcastId); mCachedBroadcasts.put(broadcastId, result); if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { addSelectSourceRequest(result, false); addSelectSourceRequest(broadcastId, false); } else { synchronized (mStateMachines) { for (BassClientStateMachine sm : Loading @@ -1866,15 +1878,13 @@ public class BassClientService extends ProfileService { informConnectedDeviceAboutScanOffloadStop(); } }; mSyncFailureCounter.clear(); mHandler.removeMessages(MESSAGE_SYNC_TIMEOUT); if (leaudioBroadcastResyncHelper()) { // Sync to the broadcasts already synced with sinks Set<Integer> syncedBroadcasts = getExternalBroadcastsActiveOnSinks(); for (int syncedBroadcast : syncedBroadcasts) { ScanResult scanRes = getCachedBroadcast(syncedBroadcast); if (scanRes != null) { addSelectSourceRequest(scanRes, true); } addSelectSourceRequest(syncedBroadcast, true); } } // when starting scan, clear the previously cached broadcast scan results Loading Loading @@ -1972,10 +1982,7 @@ public class BassClientService extends ProfileService { Integer broadcastId = entry.getKey(); PauseType pauseType = entry.getValue(); if (pauseType != PauseType.HOST_INTENTIONAL) { ScanResult scanRes = getCachedBroadcast(broadcastId); if (scanRes != null) { addSelectSourceRequest(scanRes, true); } addSelectSourceRequest(broadcastId, true); } } } Loading @@ -1985,6 +1992,7 @@ public class BassClientService extends ProfileService { private void clearAllSyncData() { log("clearAllSyncData"); mSourceSyncRequestsQueue.clear(); mSyncFailureCounter.clear(); mPendingSourcesToAdd.clear(); cancelActiveSync(null); Loading Loading @@ -2106,6 +2114,10 @@ public class BassClientService extends ProfileService { } } stopBigMonitoring(broadcastId, false); synchronized (mSourceSyncRequestsQueue) { int failsCounter = mSyncFailureCounter.getOrDefault(broadcastId, 0) + 1; mSyncFailureCounter.put(broadcastId, failsCounter); } synchronized (mSearchScanCallbackLock) { // Clear from cache to make possible sync again (only during active searching) if (mSearchScanCallback != null) { Loading Loading @@ -2180,6 +2192,10 @@ public class BassClientService extends ProfileService { mCallbacks.notifySourceLost(broadcastId); } stopBigMonitoring(broadcastId, false); synchronized (mSourceSyncRequestsQueue) { int failsCounter = mSyncFailureCounter.getOrDefault(broadcastId, 0) + 1; mSyncFailureCounter.put(broadcastId, failsCounter); } } clearAllDataForSyncHandle(syncHandle); // Clear from cache to make possible sync again (only during active searching) Loading Loading @@ -2464,19 +2480,16 @@ public class BassClientService extends ProfileService { return broadcastName; } void addSelectSourceRequest(ScanResult scanRes, boolean hasPriority) { void addSelectSourceRequest(int broadcastId, boolean hasPriority) { sEventLogger.logd( TAG, "Add Select Broadcast Source, result: " + scanRes "Add Select Broadcast Source, broadcastId: " + broadcastId + ", hasPriority: " + hasPriority); if (scanRes == null) { Log.e(TAG, "addSelectSourceRequest: Error bad parameters: scanRes cannot be null"); return; } ScanResult scanRes = getCachedBroadcast(broadcastId); if (scanRes != null) { ScanRecord scanRecord = scanRes.getScanRecord(); if (scanRecord == null) { log("addSelectSourceRequest: ScanRecord empty"); Loading @@ -2484,10 +2497,18 @@ public class BassClientService extends ProfileService { } synchronized (mSourceSyncRequestsQueue) { mSourceSyncRequestsQueue.add(new SourceSyncRequest(scanRes, hasPriority)); if (!mSyncFailureCounter.containsKey(broadcastId)) { mSyncFailureCounter.put(broadcastId, 0); } mSourceSyncRequestsQueue.add( new SourceSyncRequest( scanRes, hasPriority, mSyncFailureCounter.get(broadcastId))); } handleSelectSourceRequest(); } else { log("addSelectSourceRequest: ScanResult empty"); } } @SuppressLint("AndroidFrameworkRequiresPermission") // TODO: b/350563786 - Fix BASS annotation Loading Loading @@ -2732,7 +2753,7 @@ public class BassClientService extends ProfileService { mPendingSourcesToAdd.add( new AddSourceData(sink, sourceMetadata, isGroupOp)); if (!alreadyAdded) { addSelectSourceRequest(getCachedBroadcast(broadcastId), true); addSelectSourceRequest(broadcastId, true); } } } else { Loading android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java +255 −42 Original line number Diff line number Diff line Loading @@ -2377,35 +2377,11 @@ public class BassClientServiceTest { @Test @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void testSelectSource_withSameBroadcastId() { byte[] scanRecord = getScanRecord(TEST_BROADCAST_ID); ScanResult scanResult1 = new ScanResult( mSourceDevice, 0, 0, 0, 0, 0, TEST_RSSI, 0, ScanRecord.parseFromBytes(scanRecord), 0); ScanResult scanResult2 = new ScanResult( mSourceDevice2, 0, 0, 0, 0, 0, TEST_RSSI, 0, ScanRecord.parseFromBytes(scanRecord), 0); prepareConnectedDeviceGroup(); startSearchingForSources(); // First selectSource mBassClientService.addSelectSourceRequest(scanResult1, false); onScanResult(mSourceDevice, TEST_BROADCAST_ID); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( Loading @@ -2414,7 +2390,7 @@ public class BassClientServiceTest { onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // Second selectSource with the same broadcast id mBassClientService.addSelectSourceRequest(scanResult2, false); onScanResult(mSourceDevice2, TEST_BROADCAST_ID); mInOrderMethodProxy .verify(mMethodProxy, never()) .periodicAdvertisingManagerRegisterSync( Loading Loading @@ -2491,7 +2467,9 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord), 0); mBassClientService.addSelectSourceRequest(scanResult, false); prepareConnectedDeviceGroup(); startSearchingForSources(); mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); verify(mMethodProxy, never()) .periodicAdvertisingManagerRegisterSync( any(), any(), anyInt(), anyInt(), any(), any()); Loading Loading @@ -2591,7 +2569,9 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord), 0); mBassClientService.addSelectSourceRequest(scanResult, false); prepareConnectedDeviceGroup(); startSearchingForSources(); mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), any(), anyInt(), anyInt(), any(), any()); Loading Loading @@ -2667,7 +2647,9 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord), 0); mBassClientService.addSelectSourceRequest(scanResult, false); prepareConnectedDeviceGroup(); startSearchingForSources(); mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), any(), anyInt(), anyInt(), any(), any()); Loading Loading @@ -3318,7 +3300,7 @@ public class BassClientServiceTest { @Test @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void testSelectSource_orderOfSyncRegistering() { public void testSelectSource_orderOfSyncRegisteringByPriorityAndRssi() { final BluetoothDevice device1 = mBluetoothAdapter.getRemoteLeDevice( "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM); Loading Loading @@ -3441,20 +3423,42 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord7), 0); prepareConnectedDeviceGroup(); startSearchingForSources(); // Added and executed immidiatelly as no other in queue mBassClientService.addSelectSourceRequest(scanResult1, false); mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); // Added to queue with worst rssi mBassClientService.addSelectSourceRequest(scanResult2, false); mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2); // Added to queue with best rssi mBassClientService.addSelectSourceRequest(scanResult3, false); mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3); // Added to queue with medium rssi mBassClientService.addSelectSourceRequest(scanResult4, false); // Added to queue with priority and worst rssi mBassClientService.addSelectSourceRequest(scanResult5, true); // Added to queue with priority and best rssi mBassClientService.addSelectSourceRequest(scanResult6, true); // Added to queue with priority and medium rssi mBassClientService.addSelectSourceRequest(scanResult7, true); mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult4); // Added to queue with worst rssi (increase priority after all) mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult5); // Added to queue with best rssi (increase priority after all) mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult6); // Added to queue with medium rssi (increase priority after all) mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult7); // Increase priority of last 3 of them mBassClientService.addSelectSourceRequest(broadcastId5, true); mBassClientService.addSelectSourceRequest(broadcastId6, true); mBassClientService.addSelectSourceRequest(broadcastId7, true); ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class); mInOrderMethodProxy Loading Loading @@ -3555,6 +3559,215 @@ public class BassClientServiceTest { .isEqualTo(broadcastId2); } @Test @EnableFlags({ Flags.FLAG_LEAUDIO_SORT_SCANS_TO_SYNC_BY_FAILS, Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE }) public void testSelectSource_orderOfSyncRegisteringByRssiAndFailsCounter() { final BluetoothDevice device1 = mBluetoothAdapter.getRemoteLeDevice( "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM); final BluetoothDevice device2 = mBluetoothAdapter.getRemoteLeDevice( "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM); final BluetoothDevice device3 = mBluetoothAdapter.getRemoteLeDevice( "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM); final int broadcastId1 = 1111; final int broadcastId2 = 2222; final int broadcastId3 = 3333; byte[] scanRecord1 = getScanRecord(broadcastId1); byte[] scanRecord2 = getScanRecord(broadcastId2); byte[] scanRecord3 = getScanRecord(broadcastId3); ScanResult scanResult1 = new ScanResult( device1, 0, 0, 0, 0, 0, TEST_RSSI + 10, 0, ScanRecord.parseFromBytes(scanRecord1), 0); ScanResult scanResult2 = new ScanResult( device2, 0, 0, 0, 0, 0, TEST_RSSI + 9, 0, ScanRecord.parseFromBytes(scanRecord2), 0); ScanResult scanResult3 = new ScanResult( device3, 0, 0, 0, 0, 0, TEST_RSSI, 0, ScanRecord.parseFromBytes(scanRecord3), 0); prepareConnectedDeviceGroup(); startSearchingForSources(); // Test using onSyncEstablishedFailed // Added and executed immidiatelly as no other in queue, high rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); // Added to queue, medium rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2); // Added to queue, low rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3); ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId1); onSyncEstablishedFailed(device1, TEST_SYNC_HANDLE); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId2); // Added to queue again, high rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); onSyncEstablishedFailed(device2, TEST_SYNC_HANDLE + 1); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId3); onSyncEstablished(device3, TEST_SYNC_HANDLE + 2); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId1); // Restart searching clears the mSyncFailureCounter mBassClientService.stopSearchingForSources(); mInOrderMethodProxy .verify(mMethodProxy, times(2)) .periodicAdvertisingManagerUnregisterSync(any(), any()); startSearchingForSources(); // Test using onSyncLost // Added and executed immidiatelly as no other in queue, high rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); // Added to queue, medium rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2); // Added to queue, low rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId1); onSyncEstablished(device1, TEST_SYNC_HANDLE); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId2); onSyncLost(); // Added to queue again, high rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); onSyncEstablished(device2, TEST_SYNC_HANDLE + 1); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId3); } @Test @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void testSelectSource_invalidActiveSource() { Loading Loading
android/app/src/com/android/bluetooth/bass_client/BassClientService.java +54 −33 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeriphe import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine; import static com.android.bluetooth.flags.Flags.leaudioBroadcastMonitorSourceSyncStatus; import static com.android.bluetooth.flags.Flags.leaudioBroadcastResyncHelper; import static com.android.bluetooth.flags.Flags.leaudioSortScansToSyncByFails; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; Loading Loading @@ -158,6 +159,7 @@ public class BassClientService extends ProfileService { new HashMap<>(); private final PriorityQueue<SourceSyncRequest> mSourceSyncRequestsQueue = new PriorityQueue<>(sSourceSyncRequestComparator); private final Map<Integer, Integer> mSyncFailureCounter = new HashMap<>(); private final Map<Integer, Integer> mBisDiscoveryCounterMap = new HashMap<>(); private final List<AddSourceData> mPendingSourcesToAdd = new ArrayList<>(); Loading Loading @@ -280,12 +282,14 @@ public class BassClientService extends ProfileService { } private static class SourceSyncRequest { private ScanResult mScanResult; private boolean mHasPriority; private final ScanResult mScanResult; private final boolean mHasPriority; private final int mSyncFailureCounter; SourceSyncRequest(ScanResult scanResult, boolean hasPriority) { SourceSyncRequest(ScanResult scanResult, boolean hasPriority, int syncFailureCounter) { this.mScanResult = scanResult; this.mHasPriority = hasPriority; this.mSyncFailureCounter = syncFailureCounter; } public ScanResult getScanResult() { Loading @@ -300,6 +304,10 @@ public class BassClientService extends ProfileService { return mHasPriority; } public int getFailsCounter() { return mSyncFailureCounter; } @Override public String toString() { return "SourceSyncRequest{" Loading @@ -307,6 +315,8 @@ public class BassClientService extends ProfileService { + mScanResult + ", mHasPriority=" + mHasPriority + ", mSyncFailureCounter=" + mSyncFailureCounter + '}'; } } Loading @@ -319,6 +329,9 @@ public class BassClientService extends ProfileService { return -1; } else if (!ssr1.hasPriority() && ssr2.hasPriority()) { return 1; } else if (leaudioSortScansToSyncByFails() && (ssr1.getFailsCounter() != ssr2.getFailsCounter())) { return Integer.compare(ssr1.getFailsCounter(), ssr2.getFailsCounter()); } else { return Integer.compare(ssr2.getRssi(), ssr1.getRssi()); } Loading Loading @@ -1046,9 +1059,8 @@ public class BassClientService extends ProfileService { if (isPlaying) { stopBigMonitoring(broadcastId, false); } else if (!mPausedBroadcastIds.containsKey(broadcastId)) { ScanResult scanRes = getCachedBroadcast(broadcastId); if (scanRes != null) { addSelectSourceRequest(scanRes, true); if (mCachedBroadcasts.containsKey(broadcastId)) { addSelectSourceRequest(broadcastId, true); mPausedBroadcastIds.put(broadcastId, PauseType.SINK_UNKNOWN); logPausedBroadcastsAndSinks(); mHandler.removeMessages(MESSAGE_BIG_CHECK_START); Loading Loading @@ -1842,11 +1854,11 @@ public class BassClientService extends ProfileService { "Broadcast Source Found: Broadcast ID: " + broadcastId); if (broadcastId != BassConstants.INVALID_BROADCAST_ID && mCachedBroadcasts.get(broadcastId) == null) { && !mCachedBroadcasts.containsKey(broadcastId)) { log("selectBroadcastSource: broadcastId " + broadcastId); mCachedBroadcasts.put(broadcastId, result); if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { addSelectSourceRequest(result, false); addSelectSourceRequest(broadcastId, false); } else { synchronized (mStateMachines) { for (BassClientStateMachine sm : Loading @@ -1866,15 +1878,13 @@ public class BassClientService extends ProfileService { informConnectedDeviceAboutScanOffloadStop(); } }; mSyncFailureCounter.clear(); mHandler.removeMessages(MESSAGE_SYNC_TIMEOUT); if (leaudioBroadcastResyncHelper()) { // Sync to the broadcasts already synced with sinks Set<Integer> syncedBroadcasts = getExternalBroadcastsActiveOnSinks(); for (int syncedBroadcast : syncedBroadcasts) { ScanResult scanRes = getCachedBroadcast(syncedBroadcast); if (scanRes != null) { addSelectSourceRequest(scanRes, true); } addSelectSourceRequest(syncedBroadcast, true); } } // when starting scan, clear the previously cached broadcast scan results Loading Loading @@ -1972,10 +1982,7 @@ public class BassClientService extends ProfileService { Integer broadcastId = entry.getKey(); PauseType pauseType = entry.getValue(); if (pauseType != PauseType.HOST_INTENTIONAL) { ScanResult scanRes = getCachedBroadcast(broadcastId); if (scanRes != null) { addSelectSourceRequest(scanRes, true); } addSelectSourceRequest(broadcastId, true); } } } Loading @@ -1985,6 +1992,7 @@ public class BassClientService extends ProfileService { private void clearAllSyncData() { log("clearAllSyncData"); mSourceSyncRequestsQueue.clear(); mSyncFailureCounter.clear(); mPendingSourcesToAdd.clear(); cancelActiveSync(null); Loading Loading @@ -2106,6 +2114,10 @@ public class BassClientService extends ProfileService { } } stopBigMonitoring(broadcastId, false); synchronized (mSourceSyncRequestsQueue) { int failsCounter = mSyncFailureCounter.getOrDefault(broadcastId, 0) + 1; mSyncFailureCounter.put(broadcastId, failsCounter); } synchronized (mSearchScanCallbackLock) { // Clear from cache to make possible sync again (only during active searching) if (mSearchScanCallback != null) { Loading Loading @@ -2180,6 +2192,10 @@ public class BassClientService extends ProfileService { mCallbacks.notifySourceLost(broadcastId); } stopBigMonitoring(broadcastId, false); synchronized (mSourceSyncRequestsQueue) { int failsCounter = mSyncFailureCounter.getOrDefault(broadcastId, 0) + 1; mSyncFailureCounter.put(broadcastId, failsCounter); } } clearAllDataForSyncHandle(syncHandle); // Clear from cache to make possible sync again (only during active searching) Loading Loading @@ -2464,19 +2480,16 @@ public class BassClientService extends ProfileService { return broadcastName; } void addSelectSourceRequest(ScanResult scanRes, boolean hasPriority) { void addSelectSourceRequest(int broadcastId, boolean hasPriority) { sEventLogger.logd( TAG, "Add Select Broadcast Source, result: " + scanRes "Add Select Broadcast Source, broadcastId: " + broadcastId + ", hasPriority: " + hasPriority); if (scanRes == null) { Log.e(TAG, "addSelectSourceRequest: Error bad parameters: scanRes cannot be null"); return; } ScanResult scanRes = getCachedBroadcast(broadcastId); if (scanRes != null) { ScanRecord scanRecord = scanRes.getScanRecord(); if (scanRecord == null) { log("addSelectSourceRequest: ScanRecord empty"); Loading @@ -2484,10 +2497,18 @@ public class BassClientService extends ProfileService { } synchronized (mSourceSyncRequestsQueue) { mSourceSyncRequestsQueue.add(new SourceSyncRequest(scanRes, hasPriority)); if (!mSyncFailureCounter.containsKey(broadcastId)) { mSyncFailureCounter.put(broadcastId, 0); } mSourceSyncRequestsQueue.add( new SourceSyncRequest( scanRes, hasPriority, mSyncFailureCounter.get(broadcastId))); } handleSelectSourceRequest(); } else { log("addSelectSourceRequest: ScanResult empty"); } } @SuppressLint("AndroidFrameworkRequiresPermission") // TODO: b/350563786 - Fix BASS annotation Loading Loading @@ -2732,7 +2753,7 @@ public class BassClientService extends ProfileService { mPendingSourcesToAdd.add( new AddSourceData(sink, sourceMetadata, isGroupOp)); if (!alreadyAdded) { addSelectSourceRequest(getCachedBroadcast(broadcastId), true); addSelectSourceRequest(broadcastId, true); } } } else { Loading
android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java +255 −42 Original line number Diff line number Diff line Loading @@ -2377,35 +2377,11 @@ public class BassClientServiceTest { @Test @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void testSelectSource_withSameBroadcastId() { byte[] scanRecord = getScanRecord(TEST_BROADCAST_ID); ScanResult scanResult1 = new ScanResult( mSourceDevice, 0, 0, 0, 0, 0, TEST_RSSI, 0, ScanRecord.parseFromBytes(scanRecord), 0); ScanResult scanResult2 = new ScanResult( mSourceDevice2, 0, 0, 0, 0, 0, TEST_RSSI, 0, ScanRecord.parseFromBytes(scanRecord), 0); prepareConnectedDeviceGroup(); startSearchingForSources(); // First selectSource mBassClientService.addSelectSourceRequest(scanResult1, false); onScanResult(mSourceDevice, TEST_BROADCAST_ID); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( Loading @@ -2414,7 +2390,7 @@ public class BassClientServiceTest { onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // Second selectSource with the same broadcast id mBassClientService.addSelectSourceRequest(scanResult2, false); onScanResult(mSourceDevice2, TEST_BROADCAST_ID); mInOrderMethodProxy .verify(mMethodProxy, never()) .periodicAdvertisingManagerRegisterSync( Loading Loading @@ -2491,7 +2467,9 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord), 0); mBassClientService.addSelectSourceRequest(scanResult, false); prepareConnectedDeviceGroup(); startSearchingForSources(); mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); verify(mMethodProxy, never()) .periodicAdvertisingManagerRegisterSync( any(), any(), anyInt(), anyInt(), any(), any()); Loading Loading @@ -2591,7 +2569,9 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord), 0); mBassClientService.addSelectSourceRequest(scanResult, false); prepareConnectedDeviceGroup(); startSearchingForSources(); mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), any(), anyInt(), anyInt(), any(), any()); Loading Loading @@ -2667,7 +2647,9 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord), 0); mBassClientService.addSelectSourceRequest(scanResult, false); prepareConnectedDeviceGroup(); startSearchingForSources(); mCallbackCaptor.getValue().onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), any(), anyInt(), anyInt(), any(), any()); Loading Loading @@ -3318,7 +3300,7 @@ public class BassClientServiceTest { @Test @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void testSelectSource_orderOfSyncRegistering() { public void testSelectSource_orderOfSyncRegisteringByPriorityAndRssi() { final BluetoothDevice device1 = mBluetoothAdapter.getRemoteLeDevice( "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM); Loading Loading @@ -3441,20 +3423,42 @@ public class BassClientServiceTest { ScanRecord.parseFromBytes(scanRecord7), 0); prepareConnectedDeviceGroup(); startSearchingForSources(); // Added and executed immidiatelly as no other in queue mBassClientService.addSelectSourceRequest(scanResult1, false); mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); // Added to queue with worst rssi mBassClientService.addSelectSourceRequest(scanResult2, false); mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2); // Added to queue with best rssi mBassClientService.addSelectSourceRequest(scanResult3, false); mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3); // Added to queue with medium rssi mBassClientService.addSelectSourceRequest(scanResult4, false); // Added to queue with priority and worst rssi mBassClientService.addSelectSourceRequest(scanResult5, true); // Added to queue with priority and best rssi mBassClientService.addSelectSourceRequest(scanResult6, true); // Added to queue with priority and medium rssi mBassClientService.addSelectSourceRequest(scanResult7, true); mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult4); // Added to queue with worst rssi (increase priority after all) mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult5); // Added to queue with best rssi (increase priority after all) mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult6); // Added to queue with medium rssi (increase priority after all) mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult7); // Increase priority of last 3 of them mBassClientService.addSelectSourceRequest(broadcastId5, true); mBassClientService.addSelectSourceRequest(broadcastId6, true); mBassClientService.addSelectSourceRequest(broadcastId7, true); ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class); mInOrderMethodProxy Loading Loading @@ -3555,6 +3559,215 @@ public class BassClientServiceTest { .isEqualTo(broadcastId2); } @Test @EnableFlags({ Flags.FLAG_LEAUDIO_SORT_SCANS_TO_SYNC_BY_FAILS, Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE }) public void testSelectSource_orderOfSyncRegisteringByRssiAndFailsCounter() { final BluetoothDevice device1 = mBluetoothAdapter.getRemoteLeDevice( "00:11:22:33:44:11", BluetoothDevice.ADDRESS_TYPE_RANDOM); final BluetoothDevice device2 = mBluetoothAdapter.getRemoteLeDevice( "00:11:22:33:44:22", BluetoothDevice.ADDRESS_TYPE_RANDOM); final BluetoothDevice device3 = mBluetoothAdapter.getRemoteLeDevice( "00:11:22:33:44:33", BluetoothDevice.ADDRESS_TYPE_RANDOM); final int broadcastId1 = 1111; final int broadcastId2 = 2222; final int broadcastId3 = 3333; byte[] scanRecord1 = getScanRecord(broadcastId1); byte[] scanRecord2 = getScanRecord(broadcastId2); byte[] scanRecord3 = getScanRecord(broadcastId3); ScanResult scanResult1 = new ScanResult( device1, 0, 0, 0, 0, 0, TEST_RSSI + 10, 0, ScanRecord.parseFromBytes(scanRecord1), 0); ScanResult scanResult2 = new ScanResult( device2, 0, 0, 0, 0, 0, TEST_RSSI + 9, 0, ScanRecord.parseFromBytes(scanRecord2), 0); ScanResult scanResult3 = new ScanResult( device3, 0, 0, 0, 0, 0, TEST_RSSI, 0, ScanRecord.parseFromBytes(scanRecord3), 0); prepareConnectedDeviceGroup(); startSearchingForSources(); // Test using onSyncEstablishedFailed // Added and executed immidiatelly as no other in queue, high rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); // Added to queue, medium rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2); // Added to queue, low rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3); ArgumentCaptor<ScanResult> resultCaptor = ArgumentCaptor.forClass(ScanResult.class); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId1); onSyncEstablishedFailed(device1, TEST_SYNC_HANDLE); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId2); // Added to queue again, high rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); onSyncEstablishedFailed(device2, TEST_SYNC_HANDLE + 1); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId3); onSyncEstablished(device3, TEST_SYNC_HANDLE + 2); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId1); // Restart searching clears the mSyncFailureCounter mBassClientService.stopSearchingForSources(); mInOrderMethodProxy .verify(mMethodProxy, times(2)) .periodicAdvertisingManagerUnregisterSync(any(), any()); startSearchingForSources(); // Test using onSyncLost // Added and executed immidiatelly as no other in queue, high rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); // Added to queue, medium rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult2); // Added to queue, low rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult3); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId1); onSyncEstablished(device1, TEST_SYNC_HANDLE); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId2); onSyncLost(); // Added to queue again, high rssi mCallbackCaptor .getValue() .onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult1); onSyncEstablished(device2, TEST_SYNC_HANDLE + 1); mInOrderMethodProxy .verify(mMethodProxy) .periodicAdvertisingManagerRegisterSync( any(), resultCaptor.capture(), anyInt(), anyInt(), any(), any()); assertThat( BassUtils.parseBroadcastId( resultCaptor .getValue() .getScanRecord() .getServiceData() .get(BassConstants.BAAS_UUID))) .isEqualTo(broadcastId3); } @Test @DisableFlags(Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE) public void testSelectSource_invalidActiveSource() { Loading