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

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

Merge "[le audio] Avoid ConcurrentModificationException in mPendingGroupOp handling" into main

parents e5ae7368 42338891
Loading
Loading
Loading
Loading
+75 −62
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ import java.util.Optional;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/** Broadcast Assistant Scan Service */
@@ -907,10 +908,16 @@ public class BassClientService extends ProfileService {
    private void enqueueSourceGroupOp(BluetoothDevice sink, Integer msgId, Object obj) {
        log("enqueueSourceGroupOp device: " + sink + ", msgId: " + msgId);

        if (!mPendingGroupOp.containsKey(sink)) {
            mPendingGroupOp.put(sink, new ArrayList());
        }
        mPendingGroupOp.get(sink).add(new Pair<Integer, Object>(msgId, obj));
        mPendingGroupOp.compute(
                sink,
                (key, opsToModify) -> {
                    List<Pair<Integer, Object>> operations =
                            (opsToModify == null)
                                    ? new ArrayList<>()
                                    : new ArrayList<>(opsToModify);
                    operations.add(new Pair<>(msgId, obj));
                    return operations;
                });
    }

    private boolean isSuccess(int status) {
@@ -957,43 +964,44 @@ public class BassClientService extends ProfileService {
                        + ", reqMsg: "
                        + reqMsg);

        List<Pair<Integer, Object>> operations = mPendingGroupOp.get(sink);
        if (operations == null) {
            return;
        }
        AtomicBoolean shouldUpdateAssistantActive = new AtomicBoolean(false);

        mPendingGroupOp.computeIfPresent(
                sink,
                (key, opsToModify) -> {
                    List<Pair<Integer, Object>> operations = new ArrayList<>(opsToModify);

                    switch (reqMsg) {
                        case BassClientStateMachine.ADD_BCAST_SOURCE:
                            if (obj == null) {
                    return;
                                return operations;
                            }
                            // Identify the operation by operation type and broadcastId
                            if (isSuccess(reason)) {
                                BluetoothLeBroadcastReceiveState sourceState =
                                        (BluetoothLeBroadcastReceiveState) obj;
                    boolean removed =
                            operations.removeIf(
                                    m ->
                                            (m.first.equals(
                                                            BassClientStateMachine
                                                                    .ADD_BCAST_SOURCE))
                                                    && (sourceState.getBroadcastId()
                                                            == ((BluetoothLeBroadcastMetadata)
                                                                            m.second)
                                                                    .getBroadcastId()));
                    if (removed) {
                                if (removeMatchingOperation(operations, reqMsg, obj)) {
                                    setSourceGroupManaged(sink, sourceState.getSourceId(), true);
                                }
                            } else {
                    BluetoothLeBroadcastMetadata metadata = (BluetoothLeBroadcastMetadata) obj;
                    operations.removeIf(
                            m ->
                                    (m.first.equals(BassClientStateMachine.ADD_BCAST_SOURCE))
                                            && (metadata.getBroadcastId()
                                                    == ((BluetoothLeBroadcastMetadata) m.second)
                                                            .getBroadcastId()));

                    if (!isAnyPendingAddSourceOperation()
                                removeMatchingOperation(operations, reqMsg, obj);
                                shouldUpdateAssistantActive.set(true);
                            }
                            break;
                        case BassClientStateMachine.REMOVE_BCAST_SOURCE:
                            // Identify the operation by operation type and sourceId
                            removeMatchingOperation(operations, reqMsg, obj);
                            Integer sourceId = (Integer) obj;
                            setSourceGroupManaged(sink, sourceId, false);
                            break;
                        default:
                            break;
                    }
                    return operations;
                });

        if (shouldUpdateAssistantActive.get()
                && !isAnyPendingAddSourceOperation()
                && mIsAssistantActive
                && mPausedBroadcastSinks.isEmpty()) {
            LeAudioService leAudioService = mServiceFactory.getLeAudioService();
@@ -1005,19 +1013,24 @@ public class BassClientService extends ProfileService {
            }
        }
    }
                break;
            case BassClientStateMachine.REMOVE_BCAST_SOURCE:
                // Identify the operation by operation type and sourceId
                Integer sourceId = (Integer) obj;
                operations.removeIf(
                        m ->
                                m.first.equals(BassClientStateMachine.REMOVE_BCAST_SOURCE)
                                        && (sourceId.equals((Integer) m.second)));
                setSourceGroupManaged(sink, sourceId, false);
                break;
            default:
                break;

    private boolean removeMatchingOperation(
            List<Pair<Integer, Object>> operations, int reqMsg, Object obj) {
        return operations.removeIf(
                m -> m.first.equals(reqMsg) && isMatchingOperation(m.second, obj));
    }

    private boolean isMatchingOperation(Object operationData, Object obj) {
        if (obj instanceof BluetoothLeBroadcastReceiveState) {
            return ((BluetoothLeBroadcastMetadata) operationData).getBroadcastId()
                    == ((BluetoothLeBroadcastReceiveState) obj).getBroadcastId();
        } else if (obj instanceof BluetoothLeBroadcastMetadata) {
            return ((BluetoothLeBroadcastMetadata) operationData).getBroadcastId()
                    == ((BluetoothLeBroadcastMetadata) obj).getBroadcastId();
        } else if (obj instanceof Integer) {
            return obj.equals(operationData);
        }
        return false;
    }

    private boolean isDevicePartOfActiveUnicastGroup(BluetoothDevice device) {