Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e1968482 authored by Rongxuan Liu's avatar Rongxuan Liu Committed by Gerrit Code Review
Browse files

Merge "Bass: Handle sync info request in all cases" into main

parents ad6cf89c 028c2458
Loading
Loading
Loading
Loading
+129 −39
Original line number Diff line number Diff line
@@ -169,6 +169,8 @@ public class BassClientService extends ProfileService {
    private final Map<BluetoothDevice, BluetoothLeBroadcastMetadata> mBroadcastMetadataMap =
            new ConcurrentHashMap<>();
    private final HashSet<BluetoothDevice> mPausedBroadcastSinks = new HashSet<>();
    private final Map<BluetoothDevice, Pair<Integer, Integer>> mSinksWaitingForPast =
            new HashMap<>();
    private final Map<Integer, PauseType> mPausedBroadcastIds = new HashMap<>();
    private final Deque<AddSourceData> mPendingAddSources = new ArrayDeque<>();
    private final Map<Integer, HashSet<BluetoothDevice>> mLocalBroadcastReceivers =
@@ -276,13 +278,6 @@ public class BassClientService extends ProfileService {
                                            {
                                                log("MESSAGE_BIG_MONITOR_TIMEOUT");
                                                stopSourceReceivers(broadcastId);
                                                synchronized (mSearchScanCallbackLock) {
                                                    // when searching is stopped then clear all sync
                                                    // data
                                                    if (mSearchScanCallback == null) {
                                                        clearAllSyncData();
                                                    }
                                                }
                                                break;
                                            }
                                        default:
@@ -1107,6 +1102,24 @@ public class BassClientService extends ProfileService {
        }
    }

    void syncRequestForPast(BluetoothDevice sink, int broadcastId, int sourceId) {
        log(
                "syncRequestForPast sink: "
                        + sink
                        + ", broadcastId: "
                        + broadcastId
                        + ", sourceId: "
                        + sourceId);

        if (!leaudioBroadcastResyncHelper()) {
            return;
        }
        synchronized (mSinksWaitingForPast) {
            mSinksWaitingForPast.put(sink, new Pair<Integer, Integer>(broadcastId, sourceId));
        }
        addSelectSourceRequest(broadcastId, true);
    }

    private void localNotifyReceiveStateChanged(
            BluetoothDevice sink, BluetoothLeBroadcastReceiveState receiveState) {
        int broadcastId = receiveState.getBroadcastId();
@@ -1114,24 +1127,14 @@ public class BassClientService extends ProfileService {
                && !isLocalBroadcast(receiveState)
                && !isEmptyBluetoothDevice(receiveState.getSourceDevice())
                && !isHostPauseType(broadcastId)) {
            boolean isReadyToAutoResync = false;
            if (receiveState.getPaSyncState()
                    == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED) {
                isReadyToAutoResync = true;
            } else {
                for (int i = 0; i < receiveState.getNumSubgroups(); i++) {
                    Long syncState = receiveState.getBisSyncState().get(i);
                    /* Synced to BIS */
                    if (syncState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS
                            && syncState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG) {
                        isReadyToAutoResync = true;
                        break;
                    }
                }
            }

            if (isReadyToAutoResync) {
            if (isReceiverActive(receiveState)
                    || receiveState.getPaSyncState()
                            == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST) {
                mPausedBroadcastSinks.remove(sink);
                if (isAllReceiversActive(broadcastId) && mPausedBroadcastSinks.isEmpty()) {
                    stopBigMonitoring(broadcastId, false);
                }
            } else if (!mPausedBroadcastIds.containsKey(broadcastId)) {
                if (mCachedBroadcasts.containsKey(broadcastId)) {
                    addSelectSourceRequest(broadcastId, true);
@@ -1144,6 +1147,9 @@ public class BassClientService extends ProfileService {
                }
            }
        } else if (isEmptyBluetoothDevice(receiveState.getSourceDevice())) {
            synchronized (mSinksWaitingForPast) {
                mSinksWaitingForPast.remove(sink);
            }
            checkAndStopBigMonitoring();
        }

@@ -1584,8 +1590,9 @@ public class BassClientService extends ProfileService {
        if (toState == BluetoothProfile.STATE_DISCONNECTED) {
            mPendingGroupOp.remove(device);
            mPausedBroadcastSinks.remove(device);

            checkAndStopBigMonitoring();
            synchronized (mSinksWaitingForPast) {
                mSinksWaitingForPast.remove(device);
            }

            int bondState = mAdapterService.getBondState(device);
            if (bondState == BluetoothDevice.BOND_NONE) {
@@ -1593,6 +1600,20 @@ public class BassClientService extends ProfileService {
                removeStateMachine(device);
            }

            checkAndStopBigMonitoring();

            if (getConnectedDevices().isEmpty()
                    || (mPausedBroadcastSinks.isEmpty()
                            && mSinksWaitingForPast.isEmpty()
                            && !isAnyConnectedDeviceSwitchingSource())) {
                synchronized (mSearchScanCallbackLock) {
                    // when searching is stopped then clear all sync data
                    if (mSearchScanCallback == null) {
                        clearAllSyncData();
                    }
                }
            }

            /* Restore allowed context mask for unicast in case if last connected broadcast
             * delegator device which has external source disconnectes.
             */
@@ -2167,6 +2188,41 @@ public class BassClientService extends ProfileService {
                }
                mBisDiscoveryCounterMap.put(syncHandle, MAX_BIS_DISCOVERY_TRIES_NUM);

                synchronized (mSinksWaitingForPast) {
                    Iterator<Map.Entry<BluetoothDevice, Pair<Integer, Integer>>> iterator =
                            mSinksWaitingForPast.entrySet().iterator();
                    while (iterator.hasNext()) {
                        Map.Entry<BluetoothDevice, Pair<Integer, Integer>> entry = iterator.next();
                        BluetoothDevice sinkDevice = entry.getKey();
                        int broadcastIdForPast = entry.getValue().first;
                        if (broadcastId == broadcastIdForPast) {
                            int sourceId = entry.getValue().second;
                            synchronized (mStateMachines) {
                                BassClientStateMachine sm = getOrCreateStateMachine(sinkDevice);
                                Message message =
                                        sm.obtainMessage(
                                                BassClientStateMachine.INITIATE_PA_SYNC_TRANSFER);
                                message.arg1 = syncHandle;
                                message.arg2 = sourceId;
                                sm.sendMessage(message);
                            }
                            synchronized (mPendingSourcesToAdd) {
                                Iterator<AddSourceData> addIterator =
                                        mPendingSourcesToAdd.iterator();
                                while (addIterator.hasNext()) {
                                    AddSourceData pendingSourcesToAdd = addIterator.next();
                                    if (pendingSourcesToAdd.mSourceMetadata.getBroadcastId()
                                                    == broadcastId
                                            && pendingSourcesToAdd.mSink.equals(sinkDevice)) {
                                        addIterator.remove();
                                    }
                                }
                            }
                            iterator.remove();
                        }
                    }
                }

                synchronized (mPendingSourcesToAdd) {
                    Iterator<AddSourceData> iterator = mPendingSourcesToAdd.iterator();
                    while (iterator.hasNext()) {
@@ -3698,14 +3754,23 @@ public class BassClientService extends ProfileService {
                                .filter(e -> e.getBroadcastId() == metadata.getBroadcastId())
                                .findAny();

                if (leaudioBroadcastResyncHelper()
                        && receiveState.isPresent()
                        && (receiveState.get().getPaSyncState()
                                        == BluetoothLeBroadcastReceiveState
                                                .PA_SYNC_STATE_SYNCINFO_REQUEST
                                || receiveState.get().getPaSyncState()
                                        == BluetoothLeBroadcastReceiveState
                                                .PA_SYNC_STATE_SYNCHRONIZED)) {
                    iterator.remove();
                    continue;
                }

                List<Integer> activeSyncedSrc = getActiveSyncedSources();

                if (receiveState.isPresent()
                        && (!leaudioBroadcastResyncHelper()
                                || isLocalBroadcast(metadata)
                                || receiveState.get().getPaSyncState()
                                        == BluetoothLeBroadcastReceiveState
                                                .PA_SYNC_STATE_SYNCHRONIZED
                                || activeSyncedSrc.contains(
                                        getSyncHandleForBroadcastId(metadata.getBroadcastId())))) {
                    int sourceId = receiveState.get().getSourceId();
@@ -3862,24 +3927,49 @@ public class BassClientService extends ProfileService {
        return false;
    }

    private Set<Integer> getExternalBroadcastsActiveOnSinks() {
        HashSet<Integer> syncedBroadcasts = new HashSet<>();
        for (BluetoothDevice device : getConnectedDevices()) {
            for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
                if (!isLocalBroadcast(receiveState)) {
    private boolean isReceiverActive(BluetoothLeBroadcastReceiveState receiveState) {
        if (receiveState.getPaSyncState()
                == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED) {
            return true;
        } else {
            for (int i = 0; i < receiveState.getNumSubgroups(); i++) {
                Long syncState = receiveState.getBisSyncState().get(i);
                /* Synced to BIS */
                if (syncState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS
                        && syncState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG) {
                    return true;
                }
            }
        }
        return false;
    }

    private Set<Integer> getExternalBroadcastsActiveOnSinks() {
        HashSet<Integer> syncedBroadcasts = new HashSet<>();
        for (BluetoothDevice device : getConnectedDevices()) {
            for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
                if (isLocalBroadcast(receiveState)) {
                    continue;
                }
                if (isReceiverActive(receiveState)) {
                    syncedBroadcasts.add(receiveState.getBroadcastId());
                    log("getExternalBroadcastsActiveOnSinks: " + receiveState);
                }
            }
        }
        return syncedBroadcasts;
    }

    private boolean isAllReceiversActive(int broadcastId) {
        for (BluetoothDevice device : getConnectedDevices()) {
            for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
                if (receiveState.getBroadcastId() == broadcastId
                        && !isReceiverActive(receiveState)) {
                    return false;
                }
        return syncedBroadcasts;
            }
        }
        return true;
    }

    /** Get sink devices synced to the broadcasts */
+78 −59
Original line number Diff line number Diff line
@@ -84,8 +84,7 @@ import java.util.Scanner;
import java.util.UUID;
import java.util.stream.IntStream;

@VisibleForTesting
public class BassClientStateMachine extends StateMachine {
class BassClientStateMachine extends StateMachine {
    private static final String TAG = "BassClientStateMachine";
    @VisibleForTesting static final byte[] REMOTE_SCAN_STOP = {00};
    @VisibleForTesting static final byte[] REMOTE_SCAN_START = {01};
@@ -113,6 +112,7 @@ public class BassClientStateMachine extends StateMachine {
    static final int REACHED_MAX_SOURCE_LIMIT = 16;
    static final int SWITCH_BCAST_SOURCE = 17;
    static final int CANCEL_PENDING_SOURCE_OPERATION = 18;
    static final int INITIATE_PA_SYNC_TRANSFER = 19;

    // NOTE: the value is not "final" - it is modified in the unit tests
    @VisibleForTesting private int mConnectTimeoutMs;
@@ -816,43 +816,11 @@ public class BassClientStateMachine extends StateMachine {
        int state = recvState.getPaSyncState();
        if (state == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST) {
            log("Initiate PAST procedure");
            PeriodicAdvertisementResult result =
                    mService.getPeriodicAdvertisementResult(
                            recvState.getSourceDevice(), recvState.getBroadcastId());
            if (result != null) {
                int syncHandle = result.getSyncHandle();
                log("processPASyncState: syncHandle " + result.getSyncHandle());
                if (syncHandle != BassConstants.INVALID_SYNC_HANDLE) {
                    serviceData = 0x000000FF & recvState.getSourceId();
                    serviceData = serviceData << 8;
                    // advA matches EXT_ADV_ADDRESS
                    // also matches source address (as we would have written)
                    serviceData =
                            serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS);
                    serviceData =
                            serviceData
                                    & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS);
                    log(
                            "Initiate PAST for: "
                                    + mDevice
                                    + ", syncHandle: "
                                    + syncHandle
                                    + "serviceData"
                                    + serviceData);
                    BluetoothMethodProxy.getInstance()
                            .periodicAdvertisingManagerTransferSync(
                                    BassClientPeriodicAdvertisingManager
                                            .getPeriodicAdvertisingManager(),
                                    mDevice,
                                    serviceData,
                                    syncHandle);
                }
            } else {
                BluetoothLeBroadcastMetadata currentMetadata =
                        getCurrentBroadcastMetadata(recvState.getSourceId());
            int sourceId = recvState.getSourceId();
            BluetoothLeBroadcastMetadata currentMetadata = getCurrentBroadcastMetadata(sourceId);
            if (mService.isLocalBroadcast(currentMetadata)) {
                int advHandle = currentMetadata.getSourceAdvertisingSid();
                    serviceData = 0x000000FF & recvState.getSourceId();
                serviceData = 0x000000FF & sourceId;
                serviceData = serviceData << 8;
                // Address we set in the Source Address can differ from the address in the air
                serviceData =
@@ -873,9 +841,52 @@ public class BassClientStateMachine extends StateMachine {
                                advHandle,
                                mLocalPeriodicAdvCallback);
            } else {
                    Log.e(TAG, "There is no valid sync handle for this Source");
                int broadcastId = recvState.getBroadcastId();
                PeriodicAdvertisementResult result =
                        mService.getPeriodicAdvertisementResult(
                                recvState.getSourceDevice(), broadcastId);
                if (result != null) {
                    int syncHandle = result.getSyncHandle();
                    if (syncHandle != BassConstants.INVALID_SYNC_HANDLE) {
                        initiatePaSyncTransfer(syncHandle, sourceId);
                        return;
                    }
                }
                mService.syncRequestForPast(mDevice, broadcastId, sourceId);
            }
        }
    }

    private void initiatePaSyncTransfer(int syncHandle, int sourceId) {
        if (syncHandle != BassConstants.INVALID_SYNC_HANDLE
                && sourceId != BassConstants.INVALID_SOURCE_ID) {
            int serviceData = 0x000000FF & sourceId;
            serviceData = serviceData << 8;
            // advA matches EXT_ADV_ADDRESS
            // also matches source address (as we would have written)
            serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS);
            serviceData =
                    serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS);
            log(
                    "Initiate PAST for: "
                            + mDevice
                            + ", syncHandle: "
                            + syncHandle
                            + ", serviceData: "
                            + serviceData);
            BluetoothMethodProxy.getInstance()
                    .periodicAdvertisingManagerTransferSync(
                            BassClientPeriodicAdvertisingManager.getPeriodicAdvertisingManager(),
                            mDevice,
                            serviceData,
                            syncHandle);
        } else {
            Log.e(
                    TAG,
                    "Invalid syncHandle or sourceId for PAST, syncHandle: "
                            + syncHandle
                            + ", sourceId: "
                            + sourceId);
        }
    }

@@ -2350,6 +2361,11 @@ public class BassClientStateMachine extends StateMachine {
                    int broadcastId = message.arg1;
                    cancelPendingSourceOperation(broadcastId);
                    break;
                case INITIATE_PA_SYNC_TRANSFER:
                    int syncHandle = message.arg1;
                    int sourceIdForPast = message.arg2;
                    initiatePaSyncTransfer(syncHandle, sourceIdForPast);
                    break;
                default:
                    log("CONNECTED: not handled message:" + message.what);
                    return NOT_HANDLED;
@@ -2539,6 +2555,7 @@ public class BassClientStateMachine extends StateMachine {
                case REACHED_MAX_SOURCE_LIMIT:
                case SWITCH_BCAST_SOURCE:
                case PSYNC_ACTIVE_TIMEOUT:
                case INITIATE_PA_SYNC_TRANSFER:
                    log(
                            "defer the message: "
                                    + messageWhatToString(message.what)
@@ -2646,6 +2663,8 @@ public class BassClientStateMachine extends StateMachine {
                return "CONNECT_TIMEOUT";
            case CANCEL_PENDING_SOURCE_OPERATION:
                return "CANCEL_PENDING_SOURCE_OPERATION";
            case INITIATE_PA_SYNC_TRANSFER:
                return "INITIATE_PA_SYNC_TRANSFER";
            default:
                break;
        }
+177 −4

File changed.

Preview size limit exceeded, changes collapsed.

+38 −1
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import static com.android.bluetooth.bass_client.BassClientStateMachine.CONNECT_T
import static com.android.bluetooth.bass_client.BassClientStateMachine.DISCONNECT;
import static com.android.bluetooth.bass_client.BassClientStateMachine.GATT_TXN_PROCESSED;
import static com.android.bluetooth.bass_client.BassClientStateMachine.GATT_TXN_TIMEOUT;
import static com.android.bluetooth.bass_client.BassClientStateMachine.INITIATE_PA_SYNC_TRANSFER;
import static com.android.bluetooth.bass_client.BassClientStateMachine.PSYNC_ACTIVE_TIMEOUT;
import static com.android.bluetooth.bass_client.BassClientStateMachine.REACHED_MAX_SOURCE_LIMIT;
import static com.android.bluetooth.bass_client.BassClientStateMachine.READ_BASS_CHARACTERISTICS;
@@ -894,12 +895,23 @@ public class BassClientStateMachineTest {
        when(characteristic.getValue()).thenReturn(value);
        when(mBassClientService.getPeriodicAdvertisementResult(any(), anyInt()))
                .thenReturn(paResult);
        when(paResult.getSyncHandle()).thenReturn(100);
        int syncHandle = 100;
        when(paResult.getSyncHandle()).thenReturn(syncHandle);

        Mockito.clearInvocations(callbacks);
        cb.onCharacteristicRead(null, characteristic, GATT_SUCCESS);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        int serviceData = 0x000000FF & sourceId;
        serviceData = serviceData << 8;
        // advA matches EXT_ADV_ADDRESS
        // also matches source address (as we would have written)
        serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS);
        serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS);
        verify(mMethodProxy)
                .periodicAdvertisingManagerTransferSync(
                        any(), any(), eq(serviceData), eq(syncHandle));

        verify(callbacks)
                .notifyReceiveStateChanged(any(), eq(sourceId), receiveStateCaptor.capture());
        Assert.assertEquals(receiveStateCaptor.getValue().getSourceDevice(), mSourceTestDevice);
@@ -1706,6 +1718,26 @@ public class BassClientStateMachineTest {
        assertThat(mBassClientStateMachine.mPendingSourceId).isEqualTo(sid);
    }

    @Test
    public void sendInitiatePaSyncTransferMessage_inConnectedState() {
        initToConnectedState();
        int syncHandle = 1234;
        int sourceId = 4321;

        mBassClientStateMachine.sendMessage(INITIATE_PA_SYNC_TRANSFER, syncHandle, sourceId);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        int serviceData = 0x000000FF & sourceId;
        serviceData = serviceData << 8;
        // advA matches EXT_ADV_ADDRESS
        // also matches source address (as we would have written)
        serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS);
        serviceData = serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS);
        verify(mMethodProxy)
                .periodicAdvertisingManagerTransferSync(
                        any(), any(), eq(serviceData), eq(syncHandle));
    }

    @Test
    public void sendConnectMessage_inConnectedProcessingState_doesNotChangeState() {
        initToConnectedProcessingState();
@@ -1906,6 +1938,11 @@ public class BassClientStateMachineTest {
        mBassClientStateMachine.sendMessage(SWITCH_BCAST_SOURCE);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(SWITCH_BCAST_SOURCE)).isTrue();

        mBassClientStateMachine.sendMessage(INITIATE_PA_SYNC_TRANSFER);
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        assertThat(mBassClientStateMachine.hasDeferredMessagesSuper(INITIATE_PA_SYNC_TRANSFER))
                .isTrue();
    }

    @Test