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

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

Merge changes I0d5a01c7,I2125994b into main

* changes:
  bass_client: Don't remove source on stream suspension
  bass_client: Use PA_Sync value in update source context
parents 0eceda72 b41b1922
Loading
Loading
Loading
Loading
+204 −48
Original line number Diff line number Diff line
@@ -18,7 +18,9 @@ package com.android.bluetooth.bass_client;

import static android.Manifest.permission.BLUETOOTH_CONNECT;

import static com.android.bluetooth.flags.Flags.leaudioBroadcastAudioHandoverPolicies;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastFeatureSupport;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment;
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;

import android.bluetooth.BluetoothAdapter;
@@ -43,6 +45,7 @@ import android.os.Message;
import android.os.ParcelUuid;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.provider.DeviceConfig;
import android.sysprop.BluetoothProperties;
import android.util.Log;
import android.util.Pair;
@@ -54,7 +57,6 @@ import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.csip.CsipSetCoordinatorService;
import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.VisibleForTesting;

@@ -975,6 +977,44 @@ public class BassClientService extends ProfileService {
        }
    }

    private int areValidParametersToModifySource(
            BluetoothLeBroadcastMetadata updatedMetadata,
            BassClientStateMachine stateMachine,
            Integer deviceSourceId,
            BluetoothDevice device) {
        if (updatedMetadata == null || stateMachine == null) {
            log(
                    "areValidParametersToModifySource: Error bad parameters: sourceId = "
                            + deviceSourceId
                            + " updatedMetadata = "
                            + updatedMetadata);
            return BluetoothStatusCodes.ERROR_BAD_PARAMETERS;
        }
        if (deviceSourceId == BassConstants.INVALID_SOURCE_ID) {
            log("areValidParametersToModifySource: no such sourceId for device: " + device);
            return BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID;
        }
        if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
            log("areValidParametersToModifySource: device is not connected");
            return BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR;
        }
        byte[] code = updatedMetadata.getBroadcastCode();
        if ((code != null) && (code.length != 0)) {
            if ((code.length > 16) || (code.length < 4)) {
                log(
                        "areValidParametersToModifySource: Invalid broadcast code length: "
                                + code.length
                                + ", should be between 4 and 16 octets");
                return BluetoothStatusCodes.ERROR_BAD_PARAMETERS;
            }
        }
        if (stateMachine.hasPendingSourceOperation()) {
            throw new IllegalStateException("modifySource: source operation already pending");
        }

        return BluetoothStatusCodes.SUCCESS;
    }

    void handleConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
        mHandler.post(() -> connectionStateChanged(device, fromState, toState));
    }
@@ -1483,12 +1523,28 @@ public class BassClientService extends ProfileService {
            return;
        }

        if (leaudioBroadcastAssistantPeripheralEntrustment()) {
            if (isLocalBroadcast(sourceMetadata)) {
                LeAudioService leAudioService = mServiceFactory.getLeAudioService();
                if (leAudioService == null
                        || !leAudioService.isPlaying(sourceMetadata.getBroadcastId())) {
                    Log.w(TAG, "addSource: Local source can't be add");

                    mCallbacks.notifySourceAddFailed(
                            sink,
                            sourceMetadata,
                            BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES);
                }
            }
        } else {
            if (!isAllowedToAddSource()) {
                Log.d(TAG, "Add source to pending list");
                mPendingAddSources.push(new AddSourceData(sink, sourceMetadata, isGroupOp));

                return;
            }
        }


        byte[] code = sourceMetadata.getBroadcastCode();
        for (BluetoothDevice device : devices) {
@@ -1628,37 +1684,14 @@ public class BassClientService extends ProfileService {
            BluetoothDevice device = deviceSourceIdPair.getKey();
            Integer deviceSourceId = deviceSourceIdPair.getValue();
            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
            if (updatedMetadata == null || stateMachine == null) {
                log("modifySource: Error bad parameters: sourceId = " + deviceSourceId
                        + " updatedMetadata = " + updatedMetadata);
                mCallbacks.notifySourceModifyFailed(device, sourceId,
                        BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
                continue;
            }
            if (deviceSourceId == BassConstants.INVALID_SOURCE_ID) {
                log("modifySource: no such sourceId for device: " + device);
                mCallbacks.notifySourceModifyFailed(device, sourceId,
                        BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID);
                continue;
            }
            if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
                log("modifySource: device is not connected");
                mCallbacks.notifySourceModifyFailed(device, sourceId,
                        BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR);
                continue;
            }
            if ((code != null) && (code.length != 0)) {
                if ((code.length > 16) || (code.length < 4)) {
                    log("Invalid broadcast code length: " + code.length
                            + ", should be between 4 and 16 octets");
                    mCallbacks.notifySourceModifyFailed(device, sourceId,
                            BluetoothStatusCodes.ERROR_BAD_PARAMETERS);

            int statusCode =
                    areValidParametersToModifySource(
                            updatedMetadata, stateMachine, deviceSourceId, device);
            if (statusCode != BluetoothStatusCodes.SUCCESS) {
                mCallbacks.notifySourceModifyFailed(device, sourceId, statusCode);
                continue;
            }
            }
            if (stateMachine.hasPendingSourceOperation()) {
                throw new IllegalStateException("modifySource: source operation already pending");
            }

            sEventLogger.logd(
                    TAG,
@@ -1672,7 +1705,7 @@ public class BassClientService extends ProfileService {
            Message message =
                    stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);
            message.arg1 = deviceSourceId;
            message.arg2 = BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_INVALID;
            message.arg2 = BassConstants.INVALID_PA_SYNC_VALUE;
            message.obj = updatedMetadata;
            stateMachine.sendMessage(message);
            if (code != null && code.length != 0) {
@@ -1744,7 +1777,7 @@ public class BassClientService extends ProfileService {
                Message message = stateMachine.obtainMessage(
                        BassClientStateMachine.UPDATE_BCAST_SOURCE);
                message.arg1 = sourceId;
                message.arg2 = BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE;
                message.arg2 = BassConstants.PA_SYNC_DO_NOT_SYNC;
                /* Pending remove set. Remove source once not synchronized to PA */
                message.obj = metaData;
                stateMachine.sendMessage(message);
@@ -1848,6 +1881,38 @@ public class BassClientService extends ProfileService {
        Log.d(TAG, msg);
    }

    private List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>>
            getReceiveStateDevicePairs(int broadcastId) {
        List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>> list = new ArrayList<>();

        for (BluetoothDevice device : getConnectedDevices()) {
            for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
                /* Check if local/last broadcast is the synced one. Invalid broadcast ID means
                 * that all receivers should be considered.
                 */
                if ((broadcastId != BassConstants.INVALID_BROADCAST_ID)
                        && (receiveState.getBroadcastId() != broadcastId)) {
                    continue;
                }

                list.add(
                        new Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>(
                                receiveState, device));
            }
        }

        return list;
    }

    private void stopSourceReceivers(int broadcastId) {
        List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>> sourcesToRemove =
                getReceiveStateDevicePairs(broadcastId);

        for (Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice> pair : sourcesToRemove) {
            removeSource(pair.second, pair.first.getSourceId());
        }
    }

    private void stopSourceReceivers(int broadcastId, boolean store) {
        Log.d(TAG, "stopSourceReceivers(), broadcastId: " + broadcastId + ", store: " + store);

@@ -1884,7 +1949,7 @@ public class BassClientService extends ProfileService {
    }

    private boolean isAllowedToAddSource() {
        if (Flags.leaudioBroadcastAudioHandoverPolicies()) {
        if (leaudioBroadcastAudioHandoverPolicies()) {
            /* Check if should wait for status update */
            if (mUnicastSourceStreamStatus.isEmpty()) {
                /* Assistant was not active, inform about activation */
@@ -2012,6 +2077,23 @@ public class BassClientService extends ProfileService {
        }
    }

    /** Cache suspending sources */
    public void cacheSuspendingSources(int broadcastId) {
        sEventLogger.logd(TAG, "Cache suspending sources: " + broadcastId);
        List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>> sourcesToCache =
                getReceiveStateDevicePairs(broadcastId);

        if (!mPausedBroadcastSinks.isEmpty()) {
            Log.w(TAG, "cacheSuspendingSources(), paused broadcast sinks are replaced");
            sEventLogger.logd(TAG, "Clear broadcast sinks paused cache");
            mPausedBroadcastSinks.clear();
        }

        for (Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice> pair : sourcesToCache) {
            mPausedBroadcastSinks.add(pair.second);
        }
    }

    /** Request receivers to suspend broadcast sources synchronization */
    public void suspendReceiversSourceSynchronization(int broadcastId) {
        sEventLogger.logd(TAG, "Suspend receivers source synchronization: " + broadcastId);
@@ -2027,8 +2109,12 @@ public class BassClientService extends ProfileService {
    /** Request receivers to stop broadcast sources synchronization and remove them */
    public void stopReceiversSourceSynchronization(int broadcastId) {
        sEventLogger.logd(TAG, "Stop receivers source synchronization: " + broadcastId);
        if (leaudioBroadcastAssistantPeripheralEntrustment()) {
            stopSourceReceivers(broadcastId);
        } else {
            stopSourceReceivers(broadcastId, false);
        }
    }

    /** Request receivers to resume broadcast source synchronization */
    public void resumeReceiversSourceSynchronization() {
@@ -2039,8 +2125,72 @@ public class BassClientService extends ProfileService {
            sEventLogger.logd(TAG, "Remove broadcast sink from paused cache: " + sink);
            BluetoothLeBroadcastMetadata metadata = mBroadcastMetadataMap.get(sink);

            if (leaudioBroadcastAssistantPeripheralEntrustment()) {
                if (metadata == null) {
                    Log.w(
                            TAG,
                            "resumeReceiversSourceSynchronization: failed to get metadata to resume"
                                    + " sink: "
                                    + sink);
                    continue;
                }

                // For each device, find the source ID having this broadcast ID
                BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);
                List<BluetoothLeBroadcastReceiveState> sources = stateMachine.getAllSources();
                Optional<BluetoothLeBroadcastReceiveState> receiveState =
                        sources.stream()
                                .filter(e -> e.getBroadcastId() == metadata.getBroadcastId())
                                .findAny();

                if (receiveState.isPresent()) {
                    /* Update metadata for sink device */
                    mBroadcastMetadataMap.put(sink, metadata);

                    int sourceId = receiveState.get().getSourceId();
                    int statusCode =
                            areValidParametersToModifySource(
                                    metadata, stateMachine, sourceId, sink);

                    if (statusCode != BluetoothStatusCodes.SUCCESS) {
                        mCallbacks.notifySourceModifyFailed(sink, sourceId, statusCode);
                        continue;
                    }

                    sEventLogger.logd(
                            TAG,
                            "Modify Broadcast Source (resume): device: "
                                    + sink
                                    + ", sourceId: "
                                    + sourceId
                                    + ", updatedMetadata: "
                                    + metadata);

                    Message message =
                            stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);
                    message.arg1 = sourceId;
                    message.arg2 =
                            DeviceConfig.getBoolean(
                                            DeviceConfig.NAMESPACE_BLUETOOTH,
                                            "persist.vendor.service.bt.defNoPAS",
                                            true)
                                    ? BassConstants.PA_SYNC_PAST_AVAILABLE
                                    : BassConstants.PA_SYNC_PAST_NOT_AVAILABLE;
                    message.obj = metadata;
                    stateMachine.sendMessage(message);
                } else {
                    addSource(sink, metadata, false);
                }
            } else {
                if (metadata != null) {
                    addSource(sink, metadata, false);
                } else {
                    Log.w(
                            TAG,
                            "resumeReceiversSourceSynchronization: failed to get metadata to resume"
                                    + " sink: "
                                    + sink);
                }
            }
        }
    }
@@ -2051,14 +2201,19 @@ public class BassClientService extends ProfileService {

        if (status == STATUS_LOCAL_STREAM_REQUESTED) {
            if (isAnyReceiverReceivingBroadcast(getConnectedDevices())) {
                if (leaudioBroadcastAssistantPeripheralEntrustment()) {
                    cacheSuspendingSources(BassConstants.INVALID_BROADCAST_ID);
                } else {
                    suspendAllReceiversSourceSynchronization();
                }
            }
        } else if (status == STATUS_LOCAL_STREAM_SUSPENDED) {
            /* Resume paused receivers if there are some */
            if (!mPausedBroadcastSinks.isEmpty()) {
                resumeReceiversSourceSynchronization();
            }

            if (!leaudioBroadcastAssistantPeripheralEntrustment()) {
                /* Add pending sources if there are some */
                while (!mPendingAddSources.isEmpty()) {
                    AddSourceData addSourceData = mPendingAddSources.pop();
@@ -2068,6 +2223,7 @@ public class BassClientService extends ProfileService {
                            addSourceData.mSourceMetadata,
                            addSourceData.mIsGroupOp);
                }
            }
        } else if (status == STATUS_LOCAL_STREAM_STREAMING) {
            Log.d(TAG, "Ignore STREAMING source status");
        }
+18 −7
Original line number Diff line number Diff line
@@ -1545,7 +1545,8 @@ public class BassClientStateMachine extends StateMachine {
            log("no existing SI for update source op");
            return null;
        }
        byte numSubGroups = (byte) metaData.getSubgroups().size();
        List<BluetoothLeBroadcastSubgroup> subGroups = metaData.getSubgroups();
        byte numSubGroups = (byte) subGroups.size();
        byte[] res = new byte[UPDATE_SOURCE_FIXED_LENGTH + numSubGroups * 5];
        int offset = 0;
        // Opcode
@@ -1553,7 +1554,7 @@ public class BassClientStateMachine extends StateMachine {
        // Source_ID
        res[offset++] = (byte) sourceId;
        // PA_Sync
        if (paSync != BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_INVALID) {
        if (paSync != BassConstants.INVALID_PA_SYNC_VALUE) {
            res[offset++] = (byte) paSync;
        } else if (existingState.getPaSyncState()
                == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED) {
@@ -1566,12 +1567,22 @@ public class BassClientStateMachine extends StateMachine {
        res[offset++] = (byte) 0xFF;
        // Num_Subgroups
        res[offset++] = numSubGroups;
        for (int i = 0; i < numSubGroups; i++) {

        for (BluetoothLeBroadcastSubgroup subGroup : subGroups) {
            int bisIndexValue;
            if (paSync != BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_INVALID) {
            if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) {
                bisIndexValue = 0;
            } else if (paSync == BassConstants.PA_SYNC_PAST_AVAILABLE
                    || paSync == BassConstants.PA_SYNC_PAST_NOT_AVAILABLE) {
                bisIndexValue = getBisSyncFromChannelPreference(subGroup.getChannels());

                // Let sink decide to which BIS sync if there is no channel preference
                if (bisIndexValue == 0) {
                    bisIndexValue = 0xFFFFFFFF;
                }
            } else {
                bisIndexValue = existingState.getBisSyncState().get(i).intValue();
                bisIndexValue =
                        existingState.getBisSyncState().get(subGroups.indexOf(subGroup)).intValue();
            }
            log("UPDATE_BCAST_SOURCE: bisIndexValue : " + bisIndexValue);
            // BIS_Sync
@@ -1780,7 +1791,7 @@ public class BassClientStateMachine extends StateMachine {
                        log("SWITCH_BCAST_SOURCE force source to lost PA sync");
                        Message msg = obtainMessage(UPDATE_BCAST_SOURCE);
                        msg.arg1 = sourceIdToRemove;
                        msg.arg2 = BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE;
                        msg.arg2 = BassConstants.PA_SYNC_DO_NOT_SYNC;
                        msg.obj = metaDataToUpdate;
                        /* Pending remove set. Remove source once not synchronized to PA */
                        sendMessage(msg);
@@ -1853,7 +1864,7 @@ public class BassClientStateMachine extends StateMachine {
                        writeBassControlPoint(updateSourceInfo);
                        mPendingOperation = message.what;
                        mPendingSourceId = (byte) sourceId;
                        if (paSync == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE) {
                        if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) {
                            setPendingRemove(sourceId, true);
                        }
                        if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) {
+5 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ public class BassConstants {
    public static final int INVALID_BROADCAST_ID = -1;
    public static final int BROADCAST_ASSIST_ADDRESS_TYPE_PUBLIC = 0;
    public static final int INVALID_SOURCE_ID = -1;
    public static final int INVALID_PA_SYNC_VALUE = -1;
    public static final int ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS = 0x00000001;
    public static final int ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS = 0x00000002;
    // types of command for select and add Broadcast source operations
@@ -76,4 +77,8 @@ public class BassConstants {
    public static final int BCAST_NAME_AD_TYPE = 0x30;
    public static final int BCAST_NAME_LEN_MIN = 4;
    public static final int BCAST_NAME_LEN_MAX = 32;
    // PA_Sync parameter value
    public static final int PA_SYNC_DO_NOT_SYNC = 0x00;
    public static final int PA_SYNC_PAST_AVAILABLE = 0x01;
    public static final int PA_SYNC_PAST_NOT_AVAILABLE = 0x02;
}
+17 −2
Original line number Diff line number Diff line
@@ -1204,6 +1204,19 @@ public class LeAudioService extends ProfileService {
            return;
        }

        if (Flags.leaudioBroadcastAssistantPeripheralEntrustment()) {
            if (!isPlaying(broadcastId)) {
                Log.d(TAG, "pauseBroadcast: Broadcast is not playing, skip pause request");
                return;
            }

            // Due to broadcast pause sinks may lose synchronization
            BassClientService bassClientService = getBassClientService();
            if (bassClientService != null) {
                bassClientService.cacheSuspendingSources(broadcastId);
            }
        }

        Log.d(TAG, "pauseBroadcast");
        mLeAudioBroadcasterNativeInterface.pauseBroadcast(broadcastId);
    }
@@ -3054,9 +3067,11 @@ public class LeAudioService extends ProfileService {
                    notifyPlaybackStopped(broadcastId,
                            BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST);

                    if (!Flags.leaudioBroadcastAssistantPeripheralEntrustment()) {
                        if (bassClientService != null) {
                            bassClientService.suspendReceiversSourceSynchronization(broadcastId);
                        }
                    }

                    transitionFromBroadcastToUnicast();
                    break;
+225 −43

File changed.

Preview size limit exceeded, changes collapsed.