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

Commit 181bf53b authored by Michal Belusiak (xWF)'s avatar Michal Belusiak (xWF) Committed by Gerrit Code Review
Browse files

Merge "Bass: Handle empty broadcast receive state using previous source ID" into main

parents cdc27a6c ad06c633
Loading
Loading
Loading
Loading
+9 −4
Original line number Diff line number Diff line
@@ -1338,12 +1338,17 @@ public class BassClientService extends ProfileService {
            return false;
        }
        boolean isRoomAvailable = false;
        for (BluetoothLeBroadcastReceiveState recvState : stateMachine.getAllSources()) {
        List<BluetoothLeBroadcastReceiveState> sources = stateMachine.getAllSources();
        if (sources.size() < stateMachine.getMaximumSourceCapacity()) {
            isRoomAvailable = true;
        } else {
            for (BluetoothLeBroadcastReceiveState recvState : sources) {
                if (isEmptyBluetoothDevice(recvState.getSourceDevice())) {
                    isRoomAvailable = true;
                    break;
                }
            }
        }
        log("isRoomAvailable: " + isRoomAvailable);
        return isRoomAvailable;
    }
+251 −6
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;

import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastReceiveStateProcessingRefactor;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastResyncHelper;

import android.annotation.Nullable;
@@ -155,6 +156,7 @@ class BassClientStateMachine extends StateMachine {
    private final Map<Integer, Boolean> mFirstTimeBisDiscoveryMap;
    private int mPASyncRetryCounter = 0;
    @VisibleForTesting int mNumOfBroadcastReceiverStates = 0;
    int mNumOfReadyBroadcastReceiverStates = 0;
    @VisibleForTesting int mPendingOperation = -1;
    @VisibleForTesting byte mPendingSourceId = -1;
    @VisibleForTesting BluetoothLeBroadcastMetadata mPendingMetadata = null;
@@ -975,10 +977,15 @@ class BassClientStateMachine extends StateMachine {
    }

    private boolean isSourceAbsent(BluetoothLeBroadcastReceiveState recvState) {
        return recvState.getSourceDevice() == null
        return recvState == null
                || recvState.getSourceDevice() == null
                || recvState.getSourceDevice().getAddress().equals("00:00:00:00:00:00");
    }

    private boolean isSourcePresent(BluetoothLeBroadcastReceiveState recvState) {
        return !isSourceAbsent(recvState);
    }

    private void checkAndUpdateBroadcastCode(BluetoothLeBroadcastReceiveState recvState) {
        log("checkAndUpdateBroadcastCode");
        // Whenever receive state indicated code requested, assistant should set the broadcast code
@@ -999,7 +1006,8 @@ class BassClientStateMachine extends StateMachine {
        }
    }

    private BluetoothLeBroadcastReceiveState parseBroadcastReceiverState(byte[] receiverState) {
    private BluetoothLeBroadcastReceiveState parseBroadcastReceiverStateObsolete(
            byte[] receiverState) {
        byte sourceId = 0;
        if (receiverState.length > 0) {
            sourceId = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ID_IDX];
@@ -1140,10 +1148,11 @@ class BassClientStateMachine extends StateMachine {
        return recvState;
    }

    private void processBroadcastReceiverState(
    private void processBroadcastReceiverStateObsolete(
            byte[] receiverState, BluetoothGattCharacteristic characteristic) {
        log("processBroadcastReceiverState: characteristic:" + characteristic);
        BluetoothLeBroadcastReceiveState recvState = parseBroadcastReceiverState(receiverState);
        BluetoothLeBroadcastReceiveState recvState =
                parseBroadcastReceiverStateObsolete(receiverState);
        if (recvState == null) {
            log("processBroadcastReceiverState: Null recvState");
            return;
@@ -1249,6 +1258,226 @@ class BassClientStateMachine extends StateMachine {
        broadcastReceiverState(recvState, sourceId);
    }

    private BluetoothLeBroadcastReceiveState parseBroadcastReceiverState(
            byte[] receiverState, int previousSourceId) {
        log("parseBroadcastReceiverState: receiverState length: " + receiverState.length);

        BluetoothLeBroadcastReceiveState recvState = null;
        if (receiverState.length == 0) {
            byte[] emptyBluetoothDeviceAddress = Utils.getBytesFromAddress("00:00:00:00:00:00");
            if (previousSourceId != BassConstants.INVALID_SOURCE_ID) {
                recvState =
                        new BluetoothLeBroadcastReceiveState(
                                previousSourceId,
                                BluetoothDevice.ADDRESS_TYPE_PUBLIC, // sourceAddressType
                                mAdapterService.getDeviceFromByte(
                                        emptyBluetoothDeviceAddress), // sourceDev
                                0, // sourceAdvertisingSid
                                0, // broadcastId
                                BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, // paSyncState
                                // bigEncryptionState
                                BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
                                null, // badCode
                                0, // numSubgroups
                                Arrays.asList(new Long[0]), // bisSyncState
                                Arrays.asList(
                                        new BluetoothLeAudioContentMetadata[0]) // subgroupMetadata
                                );
            } else {
                log("parseBroadcastReceiverState: unknown sourceId");
            }
        } else {
            byte sourceId = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ID_IDX];
            byte paSyncState = receiverState[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX];
            byte bigEncryptionStatus = receiverState[BassConstants.BCAST_RCVR_STATE_ENC_STATUS_IDX];
            byte[] badBroadcastCode = null;
            int badBroadcastCodeLen = 0;
            if (bigEncryptionStatus
                    == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE) {
                badBroadcastCode = new byte[BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE];
                System.arraycopy(
                        receiverState,
                        BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX,
                        badBroadcastCode,
                        0,
                        BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE);
                badBroadcastCodeLen = BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE;
            }
            byte numSubGroups =
                    receiverState[
                            BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX + badBroadcastCodeLen];
            int offset = BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX + badBroadcastCodeLen + 1;
            ArrayList<BluetoothLeAudioContentMetadata> metadataList =
                    new ArrayList<BluetoothLeAudioContentMetadata>();
            ArrayList<Long> bisSyncState = new ArrayList<Long>();
            for (int i = 0; i < numSubGroups; i++) {
                byte[] bisSyncIndex = new byte[Long.BYTES];
                System.arraycopy(
                        receiverState,
                        offset,
                        bisSyncIndex,
                        0,
                        BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE);
                offset += BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE;
                bisSyncState.add((long) Utils.byteArrayToLong(bisSyncIndex));

                int metaDataLength = receiverState[offset++] & 0xFF;
                if (metaDataLength > 0) {
                    log("metadata of length: " + metaDataLength + "is available");
                    byte[] metaData = new byte[metaDataLength];
                    System.arraycopy(receiverState, offset, metaData, 0, metaDataLength);
                    offset += metaDataLength;
                    metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(metaData));
                } else {
                    metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(new byte[0]));
                }
            }
            byte[] broadcastIdBytes = new byte[mBroadcastSourceIdLength];
            System.arraycopy(
                    receiverState,
                    BassConstants.BCAST_RCVR_STATE_SRC_BCAST_ID_START_IDX,
                    broadcastIdBytes,
                    0,
                    mBroadcastSourceIdLength);
            int broadcastId = BassUtils.parseBroadcastId(broadcastIdBytes);
            byte[] sourceAddress = new byte[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE];
            System.arraycopy(
                    receiverState,
                    BassConstants.BCAST_RCVR_STATE_SRC_ADDR_START_IDX,
                    sourceAddress,
                    0,
                    BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE);
            byte sourceAddressType =
                    receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_TYPE_IDX];
            BassUtils.reverse(sourceAddress);
            String address = Utils.getAddressStringFromByte(sourceAddress);
            BluetoothDevice device =
                    BluetoothAdapter.getDefaultAdapter()
                            .getRemoteLeDevice(address, sourceAddressType);
            byte sourceAdvSid = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ADV_SID_IDX];
            recvState =
                    new BluetoothLeBroadcastReceiveState(
                            sourceId,
                            (int) sourceAddressType,
                            device,
                            sourceAdvSid,
                            broadcastId,
                            (int) paSyncState,
                            (int) bigEncryptionStatus,
                            badBroadcastCode,
                            numSubGroups,
                            bisSyncState,
                            metadataList);
        }
        return recvState;
    }

    private void processBroadcastReceiverState(
            byte[] receiverState, BluetoothGattCharacteristic characteristic) {
        log(
                "processBroadcastReceiverState: characteristic:"
                        + characteristic
                        + ", instanceId:"
                        + characteristic.getInstanceId());

        BluetoothLeBroadcastReceiveState prevRecvState =
                mBluetoothLeBroadcastReceiveStates.get(characteristic.getInstanceId());
        if (prevRecvState == null
                && (mBluetoothLeBroadcastReceiveStates.size() == mNumOfBroadcastReceiverStates)) {
            Log.e(TAG, "processBroadcastReceiverState: reached the Max SourceInfos");
            return;
        }

        int prevSourceId = BassConstants.INVALID_SOURCE_ID;
        if (prevRecvState != null) {
            prevSourceId = prevRecvState.getSourceId();
        }

        BluetoothLeBroadcastReceiveState recvState =
                parseBroadcastReceiverState(receiverState, prevSourceId);
        if (recvState == null) {
            log("processBroadcastReceiverState: Null recvState");
            return;
        }

        log("processBroadcastReceiverState: Updated receiver state: " + recvState);
        mBluetoothLeBroadcastReceiveStates.put(characteristic.getInstanceId(), recvState);
        int sourceId = recvState.getSourceId();

        if (isSourceAbsent(prevRecvState) && isSourcePresent(recvState)) {
            log("processBroadcastReceiverState: Source Addition");
            removeMessages(CANCEL_PENDING_SOURCE_OPERATION);
            if (mPendingMetadata != null) {
                setCurrentBroadcastMetadata(sourceId, mPendingMetadata);
                mPendingMetadata = null;
            }
            if (mPendingOperation == ADD_BCAST_SOURCE) {
                mService.getCallbacks()
                        .notifySourceAdded(
                                mDevice, recvState, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
            } else {
                mService.getCallbacks()
                        .notifySourceAdded(
                                mDevice, recvState, BluetoothStatusCodes.REASON_REMOTE_REQUEST);
            }
            checkAndUpdateBroadcastCode(recvState);
            processPASyncState(recvState);
            processSyncStateChangeStats(recvState);
        } else if (isSourcePresent(prevRecvState) && isSourcePresent(recvState)) {
            log("processBroadcastReceiverState: Source Update");
            removeMessages(CANCEL_PENDING_SOURCE_OPERATION);
            if (mPendingMetadata != null) {
                setCurrentBroadcastMetadata(sourceId, mPendingMetadata);
                mPendingMetadata = null;
            }
            mService.getCallbacks()
                    .notifySourceModified(
                            mDevice, sourceId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
            checkAndUpdateBroadcastCode(recvState);
            processPASyncState(recvState);
            processSyncStateChangeStats(recvState);

            if (isPendingRemove(sourceId) && !isSyncedToTheSource(sourceId)) {
                Message message = obtainMessage(REMOVE_BCAST_SOURCE);
                message.arg1 = sourceId;
                sendMessage(message);
            }
        } else if (isSourcePresent(prevRecvState) && isSourceAbsent(recvState)) {
            BluetoothDevice removedDevice = prevRecvState.getSourceDevice();
            log("processBroadcastReceiverState: Source Removal " + removedDevice);
            if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) {
                cancelActiveSync(mService.getSyncHandleForBroadcastId(recvState.getBroadcastId()));
            }
            BluetoothLeBroadcastMetadata metaData = getCurrentBroadcastMetadata(sourceId);
            if (metaData != null) {
                logBroadcastSyncStatsWithStatus(
                        metaData.getBroadcastId(),
                        BluetoothStatsLog
                                .BROADCAST_AUDIO_SYNC_REPORTED__SYNC_STATUS__SYNC_STATUS_UNKNOWN);
            }
            setCurrentBroadcastMetadata(sourceId, null);
            if (mPendingSourceToSwitch != null) {
                // Source remove is triggered by switch source request
                mService.getCallbacks()
                        .notifySourceRemoved(
                                mDevice, sourceId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST);
                log("processBroadcastReceiverState: Source Switching");
                Message message = obtainMessage(ADD_BCAST_SOURCE);
                message.obj = mPendingSourceToSwitch;
                sendMessage(message);
            } else if (mPendingOperation == REMOVE_BCAST_SOURCE) {
                mService.getCallbacks()
                        .notifySourceRemoved(
                                mDevice, sourceId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
            } else {
                mService.getCallbacks()
                        .notifySourceRemoved(
                                mDevice, sourceId, BluetoothStatusCodes.REASON_REMOTE_REQUEST);
            }
        }
        broadcastReceiverState(recvState, sourceId);
    }

    // Implements callback methods for GATT events that the app cares about.
    // For example, connection change and services discovered.
    final class GattCallback extends BluetoothGattCallback {
@@ -1325,7 +1554,17 @@ class BassClientStateMachine extends StateMachine {
                        characteristic.getValue(),
                        0,
                        characteristic.getValue().length);
                if (leaudioBroadcastReceiveStateProcessingRefactor()) {
                    processBroadcastReceiverState(characteristic.getValue(), characteristic);
                    mNumOfReadyBroadcastReceiverStates++;
                    if (mNumOfReadyBroadcastReceiverStates == mNumOfBroadcastReceiverStates) {
                        // Notify service BASS state ready for operations
                        mService.getCallbacks().notifyBassStateReady(mDevice);
                    }
                } else {
                    processBroadcastReceiverStateObsolete(
                            characteristic.getValue(), characteristic);
                }
            }
            // switch to receiving notifications after initial characteristic read
            BluetoothGattDescriptor desc =
@@ -1376,7 +1615,12 @@ class BassClientStateMachine extends StateMachine {
                    Log.e(TAG, "Remote receiver state is NULL");
                    return;
                }
                if (leaudioBroadcastReceiveStateProcessingRefactor()) {
                    processBroadcastReceiverState(characteristic.getValue(), characteristic);
                } else {
                    processBroadcastReceiverStateObsolete(
                            characteristic.getValue(), characteristic);
                }
            }
        }

@@ -1625,6 +1869,7 @@ class BassClientStateMachine extends StateMachine {
            mBroadcastScanControlPoint = null;
        }
        mNumOfBroadcastReceiverStates = 0;
        mNumOfReadyBroadcastReceiverStates = 0;
        if (mBluetoothLeBroadcastReceiveStates != null) {
            mBluetoothLeBroadcastReceiveStates.clear();
        }
+24 −1
Original line number Diff line number Diff line
@@ -140,7 +140,7 @@ public class BassClientServiceTest {
    // German language code in ISO 639-3
    private static final String TEST_LANGUAGE = "deu";
    private static final int TEST_SOURCE_ID = 10;
    private static final int TEST_NUM_SOURCES = 2;
    private static final int TEST_NUM_SOURCES = 1;

    private final HashMap<BluetoothDevice, BassClientStateMachine> mStateMachines = new HashMap<>();
    private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mIntentQueue;
@@ -2176,6 +2176,29 @@ public class BassClientServiceTest {
        }
    }

    @Test
    public void testSecondAddSourceWithCapacityGreaterThanOne() {
        prepareConnectedDeviceGroup();

        // Set maximum source capacity to 2
        for (BassClientStateMachine sm : mStateMachines.values()) {
            doReturn(2).when(sm).getMaximumSourceCapacity();
        }

        startSearchingForSources();
        onScanResult(mSourceDevice, TEST_BROADCAST_ID);
        onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
        verifyAddSourceForGroup(meta);
        prepareRemoteSourceState(meta, true, true);

        // Add another new broadcast source
        onScanResult(mSourceDevice2, TEST_BROADCAST_ID + 1);
        onSyncEstablished(mSourceDevice2, TEST_SYNC_HANDLE + 1);
        BluetoothLeBroadcastMetadata newMeta = createBroadcastMetadata(TEST_BROADCAST_ID + 1);
        verifyAddSourceForGroup(newMeta);
    }

    /**
     * Test that after multiple calls to service.addSource() with a group operation flag set, there
     * are two call to service.removeSource() needed to clear the flag
+447 −23

File changed.

Preview size limit exceeded, changes collapsed.