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

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

Merge changes Idcf3502b,I9e4cb5b8,I50024e85 into main

* changes:
  le_audio: native Source Monitoring tests
  bass: Add handover related test cases
  le_audio: Introduce service handling of Source listening mode
parents 78e26955 34645359
Loading
Loading
Loading
Loading
+207 −14
Original line number Diff line number Diff line
@@ -60,8 +60,10 @@ import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -79,6 +81,10 @@ public class BassClientService extends ProfileService {
    private static final int MAX_BASS_CLIENT_STATE_MACHINES = 10;
    private static final int MAX_ACTIVE_SYNCED_SOURCES_NUM = 4;

    private static final int STATUS_LOCAL_STREAM_REQUESTED = 0;
    private static final int STATUS_LOCAL_STREAM_STREAMING = 1;
    private static final int STATUS_LOCAL_STREAM_SUSPENDED = 2;

    private static BassClientService sService;

    private final Map<BluetoothDevice, BassClientStateMachine> mStateMachines = new HashMap<>();
@@ -94,6 +100,7 @@ public class BassClientService extends ProfileService {
    private final Map<BluetoothDevice, BluetoothLeBroadcastMetadata> mBroadcastMetadataMap =
            new ConcurrentHashMap<>();
    private final LinkedList<BluetoothDevice> mPausedBroadcastSinks = new LinkedList<>();
    private final Deque<AddSourceData> mPendingAddSources = new ArrayDeque<>();

    private HandlerThread mStateMachinesThread;
    private HandlerThread mCallbackHandlerThread;
@@ -117,6 +124,8 @@ public class BassClientService extends ProfileService {
            mPeriodicAdvertisementResultMap;
    private ScanCallback mSearchScanCallback;
    private Callbacks mCallbacks;
    private boolean mIsAssistantActive = false;
    Optional<Integer> mUnicastSourceStreamStatus = Optional.empty();

    private static final int LOG_NB_EVENTS = 100;
    private static final BluetoothEventLogger sEventLogger =
@@ -142,6 +151,21 @@ public class BassClientService extends ProfileService {
                && BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false);
    }

    private static class AddSourceData {
        BluetoothDevice mSink;
        BluetoothLeBroadcastMetadata mSourceMetadata;
        boolean mIsGroupOp;

        AddSourceData(
                BluetoothDevice sink,
                BluetoothLeBroadcastMetadata sourceMetadata,
                boolean isGroupOp) {
            mSink = sink;
            mSourceMetadata = sourceMetadata;
            mIsGroupOp = isGroupOp;
        }
    }

    void updatePeriodicAdvertisementResultMap(
            BluetoothDevice device,
            int addressType,
@@ -389,6 +413,15 @@ public class BassClientService extends ProfileService {
            Log.d(TAG, "stop()");
        }

        mUnicastSourceStreamStatus = Optional.empty();

        if (mIsAssistantActive) {
            LeAudioService leAudioService = mServiceFactory.getLeAudioService();
            if (leAudioService != null) {
                leAudioService.activeBroadcastAssistantNotification(false);
            }
        }

        synchronized (mStateMachines) {
            for (BassClientStateMachine sm : mStateMachines.values()) {
                BassObjectsFactory.getInstance().destroyStateMachine(sm);
@@ -513,6 +546,24 @@ public class BassClientService extends ProfileService {
        return ret;
    }

    private boolean isAnyPendingAddSourceOperation() {
        for (BluetoothDevice device : getConnectedDevices()) {
            List<Pair<Integer, Object>> operations = mPendingGroupOp.get(device);
            if (operations == null) {
                continue;
            }

            boolean isAnyPendingAddSourceOperationForDevice = operations.stream()
                    .anyMatch(e -> e.first.equals(BassClientStateMachine.ADD_BCAST_SOURCE));

            if (isAnyPendingAddSourceOperationForDevice) {
                return true;
            }
        }

        return false;
    }

    private void checkForPendingGroupOpRequest(BluetoothDevice sink, int reason, int reqMsg,
            Object obj) {
        log("checkForPendingGroupOpRequest device: " + sink + ", reason: " + reason
@@ -546,6 +597,17 @@ public class BassClientService extends ProfileService {
                            (m.first.equals(BassClientStateMachine.ADD_BCAST_SOURCE))
                            && (metadata.getBroadcastId()
                                    == ((BluetoothLeBroadcastMetadata) m.second).getBroadcastId()));

                    if (!isAnyPendingAddSourceOperation() && mIsAssistantActive
                            && mPausedBroadcastSinks.isEmpty()) {
                        LeAudioService leAudioService = mServiceFactory.getLeAudioService();
                        mIsAssistantActive = false;
                        mUnicastSourceStreamStatus = Optional.empty();

                        if (leAudioService != null) {
                            leAudioService.activeBroadcastAssistantNotification(false);
                        }
                    }
                }
                break;
            case BassClientStateMachine.REMOVE_BCAST_SOURCE:
@@ -561,6 +623,33 @@ public class BassClientService extends ProfileService {
        }
    }

    private void localNotifyReceiveStateChanged() {
        LeAudioService leAudioService = mServiceFactory.getLeAudioService();
        if (leAudioService == null) {
            return;
        }

        boolean isAssistantActive = isAnyReceiverReceivingBroadcast();

        if (isAssistantActive) {
            /* Assistant become active */
            if (!mIsAssistantActive) {
                mIsAssistantActive = true;
                leAudioService.activeBroadcastAssistantNotification(true);
                return;
            }
        } else {
            /* Assistant become inactive */
            if (mIsAssistantActive && mPausedBroadcastSinks.isEmpty()) {
                mIsAssistantActive = false;
                mUnicastSourceStreamStatus = Optional.empty();
                leAudioService.activeBroadcastAssistantNotification(false);

                return;
            }
        }
    }

    private void setSourceGroupManaged(BluetoothDevice sink, int sourceId, boolean isGroupOp) {
        log("setSourceGroupManaged device: " + sink);
        if (isGroupOp) {
@@ -1231,10 +1320,17 @@ public class BassClientService extends ProfileService {
     * @param isGroupOp set to true If Application wants to perform this operation for all
     *     coordinated set members, False otherwise
     */
    public void addSource(BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata,
    public void addSource(
            BluetoothDevice sink,
            BluetoothLeBroadcastMetadata sourceMetadata,
            boolean isGroupOp) {
        log("addSource: device: " + sink + " sourceMetadata" + sourceMetadata
                + " isGroupOp: " + isGroupOp);
        log(
                "addSource: device: "
                        + sink
                        + " sourceMetadata"
                        + sourceMetadata
                        + " isGroupOp: "
                        + isGroupOp);

        List<BluetoothDevice> devices = getTargetDeviceList(sink, isGroupOp);
        // Don't coordinate it as a group if there's no group or there is one device only
@@ -1247,6 +1343,13 @@ public class BassClientService extends ProfileService {
            return;
        }

        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) {
            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
@@ -1592,13 +1695,14 @@ public class BassClientService extends ProfileService {
        }
    }

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

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

@@ -1606,44 +1710,130 @@ public class BassClientService extends ProfileService {

        for (BluetoothDevice device : getConnectedDevices()) {
            for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
                /* Check if local/last broadcast is the synced one */
                if (receiveState.getBroadcastId() != broadcastId) continue;
                /* 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;
                }

                if (store && !mPausedBroadcastSinks.contains(device)) {
                    sEventLogger.logd(DBG, TAG, "Add broadcast sink to paused cache: " + device);
                    mPausedBroadcastSinks.add(device);
                }

                sourcesToRemove.put(device, receiveState.getSourceId());
            }
        }

        for (Map.Entry<BluetoothDevice, Integer> entry : sourcesToRemove.entrySet()) {
            removeSource(entry.getKey(), entry.getValue());
        }
    }

    private boolean isAllowedToAddSource() {
        if (mFeatureFlags.leaudioBroadcastAudioHandoverPolicies()) {
            /* Check if should wait for status update */
            if (mUnicastSourceStreamStatus.isEmpty()) {
                /* Assistant was not active, inform about activation */
                if (!mIsAssistantActive) {
                    mIsAssistantActive = true;

                    LeAudioService leAudioService = mServiceFactory.getLeAudioService();
                    if (leAudioService != null) {
                        leAudioService.activeBroadcastAssistantNotification(true);
                    }

                }

                return false;
            }

            return mUnicastSourceStreamStatus.get() == STATUS_LOCAL_STREAM_SUSPENDED;
        }

        /* Don't block if this is not a handover case */
        return true;
    }

    private boolean isAnyReceiverReceivingBroadcast() {
        for (BluetoothDevice device : getConnectedDevices()) {
            for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
                for (int i = 0; i < receiveState.getNumSubgroups(); i++) {
                    Long syncState = receiveState.getBisSyncState().get(i);

                    /* Not synced to BIS of failed to sync to BIG */
                    if (syncState == 0x00000000 || syncState == 0xFFFFFFFF) {
                        continue;
                    }

                    return true;
                }
            }
        }

        return false;
    }

    /** Request receivers to suspend broadcast sources synchronization */
    public void suspendReceiversSourceSynchronization(int broadcastId) {
        sEventLogger.logd(DBG, TAG, "Suspend receivers source synchronization: " + broadcastId);
        stopLocalSourceReceivers(broadcastId, true);
        stopSourceReceivers(broadcastId, true);
    }

    /** Request all receivers to suspend broadcast sources synchronization */
    public void suspendAllReceiversSourceSynchronization() {
        sEventLogger.logd(DBG, TAG, "Suspend all receivers source synchronization");
        stopSourceReceivers(BassConstants.INVALID_BROADCAST_ID, true);
    }

    /** Request receivers to stop broadcast sources synchronization and remove them */
    public void stopReceiversSourceSynchronization(int broadcastId) {
        sEventLogger.logd(DBG, TAG, "Stop receivers source synchronization: " + broadcastId);
        stopLocalSourceReceivers(broadcastId, false);
        stopSourceReceivers(broadcastId, false);
    }

    /** Request receivers to resume broadcast source synchronization */
    public void resumeReceiversSourceSynchronization(int broadcastId) {
        sEventLogger.logd(DBG, TAG, "Resume receivers source synchronization: " + broadcastId);
    public void resumeReceiversSourceSynchronization() {
        sEventLogger.logd(DBG, TAG, "Resume receivers source synchronization");

        while (!mPausedBroadcastSinks.isEmpty()) {
            BluetoothDevice sink = mPausedBroadcastSinks.remove();
            sEventLogger.logd(DBG, TAG, "Remove broadcast sink from paused cache: " + sink);
            BluetoothLeBroadcastMetadata metadata = mBroadcastMetadataMap.get(sink);

            if (metadata != null) {
                addSource(sink, metadata, true);
                addSource(sink, metadata, false);
            }
        }
    }

    /** Handle Unicast source stream status change */
    public void handleUnicastSourceStreamStatusChange(int status) {
        mUnicastSourceStreamStatus = Optional.of(status);

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

            /* Add pending sources if there are some */
            while (!mPendingAddSources.isEmpty()) {
                AddSourceData addSourceData = mPendingAddSources.pop();

                addSource(
                        addSourceData.mSink,
                        addSourceData.mSourceMetadata,
                        addSourceData.mIsGroupOp);
            }
        } else if (status == STATUS_LOCAL_STREAM_STREAMING) {
            Log.d(TAG, "Ignore STREAMING source status");
        }
    }

@@ -1912,6 +2102,9 @@ public class BassClientService extends ProfileService {
        void notifyReceiveStateChanged(BluetoothDevice sink, int sourceId,
                BluetoothLeBroadcastReceiveState state) {
            ObjParams param = new ObjParams(sink, state);

            sService.localNotifyReceiveStateChanged();

            String subgroupState = " / SUB GROUPS: ";
            for (int i = 0; i < state.getNumSubgroups(); i++) {
                subgroupState += "IDX: " + i + ", SYNC: " + state.getBisSyncState().get(i);
+12 −0
Original line number Diff line number Diff line
@@ -394,6 +394,17 @@ public class LeAudioNativeInterface {
        setUnicastMonitorModeNative(direction, enable);
    }

    /**
     * Confirm streaming request by other profile if there were such request
     */
    public void confirmUnicastStreamRequest() {
        if (DBG) {
            Log.d(TAG, "confirmUnicastStreamRequest");
        }

        confirmUnicastStreamRequestNative();
    }

    /**
     * Sends the audio preferences for the groupId to the native stack.
     *
@@ -428,6 +439,7 @@ public class LeAudioNativeInterface {
    private native void setInCallNative(boolean inCall);

    private native void setUnicastMonitorModeNative(int direction, boolean enable);
    private native void confirmUnicastStreamRequestNative();
    /*package*/
    private native void sendAudioProfilePreferencesNative(int groupId,
            boolean isOutputPreferenceLeAudio, boolean isDuplexPreferenceLeAudio);
+71 −10
Original line number Diff line number Diff line
@@ -155,6 +155,7 @@ public class LeAudioService extends ProfileService {
    private boolean mAwaitingBroadcastCreateResponse = false;
    private final LinkedList<BluetoothLeBroadcastSettings> mCreateBroadcastQueue =
            new LinkedList<>();
    boolean mIsSourceStreamMonitorModeEnabled = false;

    @VisibleForTesting
    TbsService mTbsService;
@@ -402,6 +403,7 @@ public class LeAudioService extends ProfileService {
        mQueuedInCallValue = Optional.empty();
        mCreateBroadcastQueue.clear();
        mAwaitingBroadcastCreateResponse = false;
        mIsSourceStreamMonitorModeEnabled = false;

        mHandler.removeCallbacks(this::init);
        removeActiveDevice(false);
@@ -1104,6 +1106,29 @@ public class LeAudioService extends ProfileService {
        return 1;
    }

    /**
     * Active Broadcast Assistant notification handler
     */
    public void activeBroadcastAssistantNotification(boolean active) {
        if (getBassClientService() == null) {
            Log.w(TAG, "Ignore active Broadcast Assistant notification");
            return;
        }

        if (active) {
            mIsSourceStreamMonitorModeEnabled = true;
            mLeAudioNativeInterface
                    .setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SOURCE, true);
        } else {
            if (mIsSourceStreamMonitorModeEnabled) {
                mLeAudioNativeInterface
                        .setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SOURCE, false);
            }

            mIsSourceStreamMonitorModeEnabled = false;
        }
    }

    private boolean areBroadcastsAllStopped() {
        if (mBroadcastDescriptors == null) {
            Log.e(TAG, "areBroadcastsAllStopped: Invalid Broadcast Descriptors");
@@ -1130,6 +1155,18 @@ public class LeAudioService extends ProfileService {
        return Optional.empty();
    }

    private boolean areAllGroupsInNotActiveState() {
        synchronized (mGroupLock) {
            for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : mGroupDescriptors.entrySet()) {
                LeAudioGroupDescriptor descriptor = entry.getValue();
                if (descriptor.mIsActive) {
                    return false;
                }
            }
        }
        return true;
    }

    private BluetoothDevice getLeadDeviceForTheGroup(Integer groupId) {
        if (groupId == LE_AUDIO_GROUP_ID_INVALID) {
            return null;
@@ -2059,7 +2096,7 @@ public class LeAudioService extends ProfileService {
        }
    }

    private void handleUnicastStreamStatusChange(int status) {
    private void handleSinkStreamStatusChange(int status) {
        if (DBG) {
            Log.d(TAG, "status: " + status);
        }
@@ -2081,9 +2118,33 @@ public class LeAudioService extends ProfileService {
            mBroadcastIdDeactivatedForUnicastTransition = Optional.of(broadcastId.get());
            pauseBroadcast(broadcastId.get());
        } else if (status == LeAudioStackEvent.STATUS_LOCAL_STREAM_SUSPENDED) {
            if (!areAllGroupsInNotActiveState()) {
                removeActiveDevice(true);
            }
        }
    }

    private void handleSourceStreamStatusChange(int status) {
        BassClientService bassClientService = getBassClientService();
        if (bassClientService == null) {
            Log.e(TAG, "handleSourceStreamStatusChange: BASS Client service is not available");

            mLeAudioNativeInterface.setUnicastMonitorMode(
                    LeAudioStackEvent.DIRECTION_SOURCE, false);
        }

        bassClientService.handleUnicastSourceStreamStatusChange(status);
    }

    private void handleUnicastStreamStatusChange(int direction, int status) {
        if (direction == LeAudioStackEvent.DIRECTION_SINK) {
            handleSinkStreamStatusChange(status);
        } else if (direction == LeAudioStackEvent.DIRECTION_SOURCE) {
            handleSourceStreamStatusChange(status);
        } else {
            Log.e(TAG, "handleUnicastStreamStatusChange: invalid direction: " + direction);
        }
    }

    @VisibleForTesting
    void handleGroupIdleDuringCall() {
@@ -2654,7 +2715,7 @@ public class LeAudioService extends ProfileService {

                    if (previousState == LeAudioStackEvent.BROADCAST_STATE_PAUSED) {
                        if (bassClientService != null) {
                            bassClientService.resumeReceiversSourceSynchronization(broadcastId);
                            bassClientService.resumeReceiversSourceSynchronization();
                        }
                    }

@@ -2699,11 +2760,7 @@ public class LeAudioService extends ProfileService {
                mTmapStarted = registerTmap();
            }
        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS) {
            if (stackEvent.valueInt1 == LeAudioStackEvent.DIRECTION_SINK) {
                handleUnicastStreamStatusChange(stackEvent.valueInt2);
            } else {
                Log.e(TAG, "Invalid direction: " + stackEvent.valueInt2);
            }
            handleUnicastStreamStatusChange(stackEvent.valueInt1, stackEvent.valueInt2);
        }
    }

@@ -3011,7 +3068,9 @@ public class LeAudioService extends ProfileService {
            mQueuedInCallValue = Optional.of(true);

            /* Request activation of unicast group */
            handleUnicastStreamStatusChange(LeAudioStackEvent.STATUS_LOCAL_STREAM_REQUESTED);
            handleUnicastStreamStatusChange(
                    LeAudioStackEvent.DIRECTION_SINK,
                    LeAudioStackEvent.STATUS_LOCAL_STREAM_REQUESTED);
            return;
        }

@@ -3020,7 +3079,9 @@ public class LeAudioService extends ProfileService {
        /* For clearing inCall mode */
        if (mFeatureFlags.leaudioBroadcastAudioHandoverPolicies() && !inCall
                && mBroadcastIdDeactivatedForUnicastTransition.isPresent()) {
            handleUnicastStreamStatusChange(LeAudioStackEvent.STATUS_LOCAL_STREAM_SUSPENDED);
            handleUnicastStreamStatusChange(
                    LeAudioStackEvent.DIRECTION_SINK,
                    LeAudioStackEvent.STATUS_LOCAL_STREAM_SUSPENDED);
        }
    }

+219 −24

File changed.

Preview size limit exceeded, changes collapsed.

+268 −1

File changed.

Preview size limit exceeded, changes collapsed.