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

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

Merge changes I2cdd8f25,I8baede89,I45e81089 into main

* changes:
  le_audio: Introduce Unicast fallback after Broadcast stop
  broadcast: Introduce pending broadcast
  broadcast: Introduce broadcasts descriptor
parents d41666ad f8dbc5d0
Loading
Loading
Loading
Loading
+201 −72
Original line number Diff line number Diff line
@@ -82,12 +82,12 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.SynchronousResultReceiver;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Provides Bluetooth LeAudio profile, as a service in the Bluetooth application.
@@ -139,7 +139,12 @@ public class LeAudioService extends ProfileService {
    AudioManager mAudioManager;
    LeAudioTmapGattServer mTmapGattServer;
    int mTmapRoleMask;
    int mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID;
    boolean mTmapStarted = false;
    private boolean mAwaitingBroadcastCreateResponse = false;
    private final LinkedList<BluetoothLeBroadcastSettings> mCreateBroadcastQueue =
            new LinkedList<>();


    @VisibleForTesting
    TbsService mTbsService;
@@ -202,6 +207,18 @@ public class LeAudioService extends ProfileService {
        Boolean mDevInbandRingtoneEnabled;
    }

    private static class LeAudioBroadcastDescriptor {
        LeAudioBroadcastDescriptor() {
            mState = LeAudioStackEvent.BROADCAST_STATE_STOPPED;
            mMetadata = null;
            mRequestedForDetails = false;
        }

        public Integer mState;
        public BluetoothLeBroadcastMetadata mMetadata;
        public Boolean mRequestedForDetails;
    }

    List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>();
    List<BluetoothLeAudioCodecConfig> mOutputLocalCodecCapabilities = new ArrayList<>();

@@ -209,17 +226,14 @@ public class LeAudioService extends ProfileService {
    private final Map<Integer, LeAudioGroupDescriptor> mGroupDescriptors = new LinkedHashMap<>();
    private final Map<BluetoothDevice, LeAudioDeviceDescriptor> mDeviceDescriptors =
            new LinkedHashMap<>();
    private final Map<Integer, LeAudioBroadcastDescriptor> mBroadcastDescriptors =
            new LinkedHashMap<>();

    private BroadcastReceiver mBondStateChangedReceiver;
    private Handler mHandler = new Handler(Looper.getMainLooper());
    private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback =
            new AudioManagerAudioDeviceCallback();

    private final Map<Integer, Integer> mBroadcastStateMap = new HashMap<>();
    private final Map<Integer, Boolean> mBroadcastsPlaybackMap = new HashMap<>();
    private final Map<Integer, BluetoothLeBroadcastMetadata> mBroadcastMetadataList =
            new HashMap<>();

    @Override
    protected IProfileServiceBinder initBinder() {
        return new BluetoothLeAudioBinder(this);
@@ -277,9 +291,7 @@ public class LeAudioService extends ProfileService {
        mStateMachinesThread = new HandlerThread("LeAudioService.StateMachines");
        mStateMachinesThread.start();

        mBroadcastStateMap.clear();
        mBroadcastMetadataList.clear();
        mBroadcastsPlaybackMap.clear();
        mBroadcastDescriptors.clear();

        synchronized (mGroupLock) {
            mDeviceDescriptors.clear();
@@ -354,6 +366,9 @@ public class LeAudioService extends ProfileService {
            return true;
        }

        mCreateBroadcastQueue.clear();
        mAwaitingBroadcastCreateResponse = false;

        mHandler.removeCallbacks(this::init);
        removeActiveDevice(false);

@@ -423,9 +438,7 @@ public class LeAudioService extends ProfileService {
            mLeAudioCallbacks.kill();
        }

        mBroadcastStateMap.clear();
        mBroadcastsPlaybackMap.clear();
        mBroadcastMetadataList.clear();
        mBroadcastDescriptors.clear();

        if (mLeAudioBroadcasterNativeInterface != null) {
            mLeAudioBroadcasterNativeInterface.cleanup();
@@ -805,6 +818,22 @@ public class LeAudioService extends ProfileService {
            return;
        }

        if (mAwaitingBroadcastCreateResponse) {
            mCreateBroadcastQueue.add(broadcastSettings);
            Log.i(TAG, "Broadcast creation queued due to waiting for a previous request response.");
            return;
        }

        if (getActiveGroupId() != LE_AUDIO_GROUP_ID_INVALID) {
            /* Broadcast would be created once unicast group became inactive */
            Log.i(TAG, "Unicast group is active, queueing Broadcast creation, while the Unicast"
                        + " group is deactivated.");
            mCreateBroadcastQueue.add(broadcastSettings);
            removeActiveDevice(true);

            return;
        }

        byte[] broadcastCode = broadcastSettings.getBroadcastCode();
        boolean isEncrypted = (broadcastCode != null) && (broadcastCode.length != 0);
        if (isEncrypted) {
@@ -825,6 +854,8 @@ public class LeAudioService extends ProfileService {
                broadcastSettings.getPublicBroadcastMetadata();

        Log.i(TAG, "createBroadcast: isEncrypted=" + (isEncrypted ? "true" : "false"));

        mAwaitingBroadcastCreateResponse = true;
        mLeAudioBroadcasterNativeInterface.createBroadcast(broadcastSettings.isPublicBroadcast(),
                broadcastSettings.getBroadcastName(), broadcastCode,
                publicMetadata == null ? null : publicMetadata.getRawMetadata(),
@@ -859,9 +890,12 @@ public class LeAudioService extends ProfileService {
            Log.w(TAG, "Native interface not available.");
            return;
        }
        if (!mBroadcastStateMap.containsKey(broadcastId)) {

        LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId);
        if (descriptor == null) {
            notifyBroadcastUpdateFailed(
                    broadcastId, BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID);
            Log.e(TAG, "updateBroadcast: No valid descriptor for broadcastId: " + broadcastId);
            return;
        }

@@ -893,9 +927,12 @@ public class LeAudioService extends ProfileService {
            Log.w(TAG, "Native interface not available.");
            return;
        }
        if (!mBroadcastStateMap.containsKey(broadcastId)) {

        LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId);
        if (descriptor == null) {
            notifyOnBroadcastStopFailed(
                    BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID);
            Log.e(TAG, "stopBroadcast: No valid descriptor for broadcastId: " + broadcastId);
            return;
        }

@@ -912,9 +949,12 @@ public class LeAudioService extends ProfileService {
            Log.w(TAG, "Native interface not available.");
            return;
        }
        if (!mBroadcastStateMap.containsKey(broadcastId)) {

        LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId);
        if (descriptor == null) {
            notifyOnBroadcastStopFailed(
                    BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID);
            Log.e(TAG, "destroyBroadcast: No valid descriptor for broadcastId: " + broadcastId);
            return;
        }

@@ -928,7 +968,13 @@ public class LeAudioService extends ProfileService {
     * @return true if if broadcast is playing, false otherwise
     */
    public boolean isPlaying(int broadcastId) {
        return mBroadcastsPlaybackMap.getOrDefault(broadcastId, false);
        LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId);
        if (descriptor == null) {
            Log.e(TAG, "isPlaying: No valid descriptor for broadcastId: " + broadcastId);
            return false;
        }

        return descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING);
    }

    /**
@@ -936,7 +982,9 @@ public class LeAudioService extends ProfileService {
     * @return list of all know Broadcast metadata
     */
    public List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() {
        return new ArrayList<BluetoothLeBroadcastMetadata>(mBroadcastMetadataList.values());
        return mBroadcastDescriptors.values().stream()
                .map(s -> s.mMetadata)
                .collect(Collectors.toList());
    }

    /**
@@ -1855,6 +1903,28 @@ public class LeAudioService extends ProfileService {
        }
    }

    void transitionFromBroadcastToUnicast() {
        BluetoothDevice unicastDevice =
                getLeadDeviceForTheGroup(mUnicastGroupIdDeactivatedForBroadcastTransition);
        if (unicastDevice == null) {
            Log.e(TAG, "EVENT_TYPE_BROADCAST_DESTROYED: No valid unicast device for group ID: "
                    + mUnicastGroupIdDeactivatedForBroadcastTransition);
            mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID;
            return;
        }

        if (DBG) {
            Log.d(TAG, "Transitionaing to Unicast stream for group: "
                    + mUnicastGroupIdDeactivatedForBroadcastTransition
                    + ", with device: "
                    + unicastDevice);
        }

        mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID;

        setActiveDevice(unicastDevice);
    }

    // Suppressed since this is part of a local process
    @SuppressLint("AndroidFrameworkRequiresPermission")
    void messageFromNative(LeAudioStackEvent stackEvent) {
@@ -2040,6 +2110,12 @@ public class LeAudioService extends ProfileService {
                }
                case LeAudioStackEvent.GROUP_STATUS_INACTIVE: {
                    handleGroupTransitToInactive(groupId, false);

                    if (!mCreateBroadcastQueue.isEmpty()) {
                        mUnicastGroupIdDeactivatedForBroadcastTransition = groupId;
                        BluetoothLeBroadcastSettings settings = mCreateBroadcastQueue.remove();
                        createBroadcast(settings);
                    }
                    break;
                }
                case LeAudioStackEvent.GROUP_STATUS_TURNED_IDLE_DURING_CALL: {
@@ -2062,42 +2138,75 @@ public class LeAudioService extends ProfileService {
                Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " created.");
                notifyBroadcastStarted(broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);

                mBroadcastDescriptors.put(broadcastId, new LeAudioBroadcastDescriptor());

                // Start sending the actual stream
                startBroadcast(broadcastId);

            } else {
                // TODO: Improve reason reporting or extend the native stack event with reason code
                notifyBroadcastStartFailed(broadcastId, BluetoothStatusCodes.ERROR_UNKNOWN);
            }

            mAwaitingBroadcastCreateResponse = false;

            // In case if there were additional calls to create broadcast
            if (!mCreateBroadcastQueue.isEmpty()) {
                BluetoothLeBroadcastSettings settings = mCreateBroadcastQueue.remove();
                createBroadcast(settings);
            }

        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_DESTROYED) {
            Integer broadcastId = stackEvent.valueInt1;

            // TODO: Improve reason reporting or extend the native stack event with reason code
            notifyOnBroadcastStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);

            mBroadcastsPlaybackMap.remove(broadcastId);
            mBroadcastStateMap.remove(broadcastId);
            mBroadcastMetadataList.remove(broadcastId);
            LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId);
            if (descriptor == null) {
                Log.e(TAG, "EVENT_TYPE_BROADCAST_DESTROYED: No valid descriptor for broadcastId: "
                        + broadcastId);
                return;
            }
            mBroadcastDescriptors.remove(broadcastId);

            /* Restore the Unicast stream from before the Broadcast was started. */
            if (mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID) {
                transitionFromBroadcastToUnicast();
            }

        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE) {
            int broadcastId = stackEvent.valueInt1;
            int state = stackEvent.valueInt2;

            LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId);
            if (descriptor == null) {
                Log.e(TAG, "EVENT_TYPE_BROADCAST_STATE: No valid descriptor for broadcastId: "
                        + broadcastId);
                return;
            }

            /* Request broadcast details if not known yet */
            if (!mBroadcastStateMap.containsKey(broadcastId)) {
            if (!descriptor.mRequestedForDetails) {
                mLeAudioBroadcasterNativeInterface.getBroadcastMetadata(broadcastId);
                descriptor.mRequestedForDetails = true;
            }
            mBroadcastStateMap.put(broadcastId, state);
            descriptor.mState = state;

            if (state == LeAudioStackEvent.BROADCAST_STATE_STOPPED) {
            switch (descriptor.mState) {
                case LeAudioStackEvent.BROADCAST_STATE_STOPPED:
                    if (DBG) Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopped.");

                    // Playback stopped
                mBroadcastsPlaybackMap.put(broadcastId, false);
                notifyPlaybackStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
                    notifyPlaybackStopped(broadcastId,
                            BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);

                    // Notify audio manager
                if (Collections.frequency(mBroadcastsPlaybackMap.values(), true) == 0) {
                    if (mBroadcastDescriptors.values().stream()
                            .noneMatch(
                                    d ->
                                            d.mState.equals(
                                                    LeAudioStackEvent.BROADCAST_STATE_STREAMING))) {
                        if (Objects.equals(device, mActiveAudioOutDevice)) {
                            BluetoothDevice previousDevice = mActiveAudioOutDevice;
                            mActiveAudioOutDevice = null;
@@ -2108,29 +2217,33 @@ public class LeAudioService extends ProfileService {
                    }

                    destroyBroadcast(broadcastId);

            } else if (state == LeAudioStackEvent.BROADCAST_STATE_CONFIGURING) {
                    break;
                case LeAudioStackEvent.BROADCAST_STATE_CONFIGURING:
                    if (DBG) Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " configuring.");

            } else if (state == LeAudioStackEvent.BROADCAST_STATE_PAUSED) {
                    break;
                case LeAudioStackEvent.BROADCAST_STATE_PAUSED:
                    if (DBG) Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " paused.");

                    // Playback paused
                mBroadcastsPlaybackMap.put(broadcastId, false);
                notifyPlaybackStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST);

            } else if (state == LeAudioStackEvent.BROADCAST_STATE_STOPPING) {
                    notifyPlaybackStopped(broadcastId,
                            BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST);
                    break;
                case LeAudioStackEvent.BROADCAST_STATE_STOPPING:
                    if (DBG) Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopping.");

            } else if (state == LeAudioStackEvent.BROADCAST_STATE_STREAMING) {
                    break;
                case LeAudioStackEvent.BROADCAST_STATE_STREAMING:
                    if (DBG) Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " streaming.");

                    // Stream resumed
                mBroadcastsPlaybackMap.put(broadcastId, true);
                notifyPlaybackStarted(broadcastId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST);
                    notifyPlaybackStarted(broadcastId,
                            BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST);

                    // Notify audio manager
                if (Collections.frequency(mBroadcastsPlaybackMap.values(), true) == 1) {
                    if (mBroadcastDescriptors.values().stream()
                            .anyMatch(
                                    d ->
                                            d.mState.equals(
                                                    LeAudioStackEvent.BROADCAST_STATE_STREAMING))) {
                        if (!Objects.equals(device, mActiveAudioOutDevice)) {
                            BluetoothDevice previousDevice = mActiveAudioOutDevice;
                            mActiveAudioOutDevice = device;
@@ -2139,13 +2252,23 @@ public class LeAudioService extends ProfileService {
                                    getBroadcastProfile(false));
                        }
                    }
                    break;
                default:
                    Log.e(TAG, "Invalid state of broadcast: " + descriptor.mState);
                    break;
            }
        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_METADATA_CHANGED) {
            int broadcastId = stackEvent.valueInt1;
            if (stackEvent.broadcastMetadata == null) {
                Log.e(TAG, "Missing Broadcast metadata for broadcastId: " + broadcastId);
            } else {
                mBroadcastMetadataList.put(broadcastId, stackEvent.broadcastMetadata);
                LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId);
                if (descriptor == null) {
                    Log.e(TAG, "EVENT_TYPE_BROADCAST_METADATA_CHANGED: No valid descriptor for "
                            + "broadcastId: " + broadcastId);
                    return;
                }
                descriptor.mMetadata = stackEvent.broadcastMetadata;
                notifyBroadcastMetadataChanged(broadcastId, stackEvent.broadcastMetadata);
            }
        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED) {
@@ -2839,6 +2962,10 @@ public class LeAudioService extends ProfileService {
                    handleGroupTransitToInactive(groupId, false);
                }
                mGroupDescriptors.remove(groupId);

                if (mUnicastGroupIdDeactivatedForBroadcastTransition == groupId) {
                    mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID;
                }
            }
            notifyGroupNodeRemoved(device, groupId);
        }
@@ -3843,6 +3970,8 @@ public class LeAudioService extends ProfileService {
        ProfileService.println(sb, "  currentlyActiveGroupId: " + getActiveGroupId());
        ProfileService.println(sb, "  mActiveAudioOutDevice: " + mActiveAudioOutDevice);
        ProfileService.println(sb, "  mActiveAudioInDevice: " + mActiveAudioInDevice);
        ProfileService.println(sb, "  mUnicastGroupIdDeactivatedForBroadcastTransition: "
                + mUnicastGroupIdDeactivatedForBroadcastTransition);
        ProfileService.println(sb, "  mExposedActiveDevice: " + mExposedActiveDevice);
        ProfileService.println(sb, "  mHfpHandoverDevice:" + mHfpHandoverDevice);
        ProfileService.println(sb, "  mLeAudioIsInbandRingtoneSupported:"
+6 −0
Original line number Diff line number Diff line
@@ -477,6 +477,12 @@ public class LeAudioBroadcastServiceTest {
        BluetoothLeAudioContentMetadata meta = meta_builder.build();
        mService.createBroadcast(buildBroadcastSettingsFromMetadata(meta, code, 1));

        LeAudioStackEvent create_event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
        create_event.valueInt1 = broadcastId;
        create_event.valueBool1 = true;
        mService.messageFromNative(create_event);

        // Inject metadata stack event and verify if getter API works as expected
        LeAudioStackEvent state_event =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_METADATA_CHANGED);