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

Commit 20653520 authored by Yiyi Shen's avatar Yiyi Shen
Browse files

[MR2] Improve transferTo handling during broadcast

1. add isBtRoute and skip transferAction for BT device during broadcast
2. move the setActive for classic BT device to stopBroadcast and no need
   to removePreferredDeviceForStrategy

Bug: 438156758
Test: atest AudioManagerRouteControllerTest
Flag: com.android.media.flags.enable_output_switcher_personal_audio_sharing
Change-Id: If1854425e2d8a62ca882bcb3f6edcb99f84792d0
parent 0e8f3fd3
Loading
Loading
Loading
Loading
+16 −10
Original line number Original line Diff line number Diff line
@@ -323,11 +323,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
            return;
            return;
        }
        }


        if (com.android.media.flags.Flags.enableOutputSwitcherPersonalAudioSharing()) {
            // We need to stop broadcast when we transfer to another route
            stopBroadcastForTransferIfCurrentlySelected(routeId);
        }

        MediaRoute2InfoHolder mediaRoute2InfoHolder;
        MediaRoute2InfoHolder mediaRoute2InfoHolder;
        synchronized (this) {
        synchronized (this) {
            mediaRoute2InfoHolder = mRouteIdToAvailableDeviceRoutes.get(routeId);
            mediaRoute2InfoHolder = mRouteIdToAvailableDeviceRoutes.get(routeId);
@@ -336,6 +331,21 @@ import java.util.concurrent.CopyOnWriteArrayList;
            Slog.w(TAG, "transferTo: Ignoring transfer request to unknown route id : " + routeId);
            Slog.w(TAG, "transferTo: Ignoring transfer request to unknown route id : " + routeId);
            return;
            return;
        }
        }

        // We need to stop broadcast when we transfer to another route
        boolean currentOutputIsBLEBroadcast =
                com.android.media.flags.Flags.enableOutputSwitcherPersonalAudioSharing()
                        && currentOutputIsBLEBroadcast();
        if (currentOutputIsBLEBroadcast) {
            boolean isBtRoute =
                    mBluetoothRouteController.isBtRoute(mediaRoute2InfoHolder.mMediaRoute2Info);
            stopBroadcastForTransfer(isBtRoute ? routeId : null);
            if (isBtRoute) {
                Slog.d(TAG, "transferTo: Skip transfer action for BT route in broadcast");
                return;
            }
        }

        Runnable transferAction = getTransferActionForRoute(mediaRoute2InfoHolder);
        Runnable transferAction = getTransferActionForRoute(mediaRoute2InfoHolder);
        Runnable guardedTransferAction =
        Runnable guardedTransferAction =
                () -> {
                () -> {
@@ -387,11 +397,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
        mHandler.post(() -> mBluetoothRouteController.removeRouteFromBroadcast(routeId));
        mHandler.post(() -> mBluetoothRouteController.removeRouteFromBroadcast(routeId));
    }
    }


    private void stopBroadcastForTransferIfCurrentlySelected(@NonNull String routeId) {
    private void stopBroadcastForTransfer(@Nullable String routeId) {
        if (!currentOutputIsBLEBroadcast()) {
            return;
        }

        mHandler.post(() -> mBluetoothRouteController.stopBroadcast(routeId));
        mHandler.post(() -> mBluetoothRouteController.stopBroadcast(routeId));
    }
    }


+20 −7
Original line number Original line Diff line number Diff line
@@ -69,6 +69,11 @@ import java.util.stream.Collectors;


    private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
    private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
    private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_";
    private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_";
    private static final List<Integer> BT_DEVICE_TYPES =
            List.of(
                    MediaRoute2Info.TYPE_BLE_HEADSET,
                    MediaRoute2Info.TYPE_HEARING_AID,
                    MediaRoute2Info.TYPE_BLUETOOTH_A2DP);


    /** Interface for receiving events about Bluetooth routes changes. */
    /** Interface for receiving events about Bluetooth routes changes. */
    interface BluetoothRoutesUpdatedListener {
    interface BluetoothRoutesUpdatedListener {
@@ -301,15 +306,23 @@ import java.util.stream.Collectors;
        mBluetoothProfileMonitor.stopBroadcast();
        mBluetoothProfileMonitor.stopBroadcast();
    }
    }


    /** Returns whether {@link MediaRoute2Info} is info of Bluetooth route. */
    protected boolean isBtRoute(@NonNull MediaRoute2Info mediaRoute2Info) {
        return BT_DEVICE_TYPES.contains(mediaRoute2Info.getType());
    }

    /**
    /**
     * Trigger {@link BluetoothProfileMonitor} to stop the broadcast, optionally making a new LEA
     * Trigger {@link BluetoothProfileMonitor} to stop the broadcast, optionally making a new BT
     * device active.
     * device active.
     *
     *
     * @param routeId id of the LEA Bluetooth route to be set as active after broadcast stops.
     * @param routeId id of the Bluetooth route to be set as active after broadcast stops.
     */
     */
    protected void stopBroadcast(@Nullable String routeId) {
    protected void stopBroadcast(@Nullable String routeId) {
        Slog.d(TAG, "stopBroadcast: for route id " + routeId);
        BluetoothDevice bluetoothDevice =
        BluetoothDevice bluetoothDevice =
                mBluetoothRoutes.values().stream()
                routeId == null
                        ? null
                        : mBluetoothRoutes.values().stream()
                                .filter(routeInfo -> routeInfo.mRoute.getId().equals(routeId))
                                .filter(routeInfo -> routeInfo.mRoute.getId().equals(routeId))
                                .findFirst()
                                .findFirst()
                                .map(routeInfo -> routeInfo.mBtDevice)
                                .map(routeInfo -> routeInfo.mBtDevice)
+14 −5
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package com.android.server.media;
package com.android.server.media;


import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;

import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothA2dp;
@@ -247,13 +249,13 @@ import java.util.concurrent.ThreadLocalRandom;
    }
    }


    /**
    /**
     * Stops the broadcast, optionally making a new LEA BT device active.
     * Stops the broadcast, optionally making a new BT device active.
     *
     *
     * <p>This method is expected to use the given device to determine which unicast fallback group
     * <p>This method is expected to use the given device to determine which unicast fallback group
     * should be set when the broadcast stops.
     * should be set or which classic device should be active when the broadcast stops.
     *
     *
     * @param device LEA device that should become active once the broadcast stops, or null if no
     * @param device BT device that should become active once the broadcast stops, or null if no BT
     *     LEA device should become active once broadcast stops.
     *     device should become active once broadcast stops.
     */
     */
    public synchronized void stopBroadcast(@Nullable BluetoothDevice device) {
    public synchronized void stopBroadcast(@Nullable BluetoothDevice device) {
        if (mBroadcastProfile == null) {
        if (mBroadcastProfile == null) {
@@ -263,12 +265,19 @@ import java.util.concurrent.ThreadLocalRandom;
        if (mLeAudioProfile == null) {
        if (mLeAudioProfile == null) {
            Slog.e(TAG, "Fail to set fall back group, LeProfile is null");
            Slog.e(TAG, "Fail to set fall back group, LeProfile is null");
        } else {
        } else {
            // if no valid group id, set the fallback to -1, no LEA BT device should become active
            // if no valid group id, set the fallback to -1, no LEA device should become active
            // once broadcast stops
            // once broadcast stops
            int groupId =
            int groupId =
                    (device == null || !isProfileSupported(BluetoothProfile.LE_AUDIO, device))
                    (device == null || !isProfileSupported(BluetoothProfile.LE_AUDIO, device))
                            ? BluetoothLeAudio.GROUP_ID_INVALID
                            ? BluetoothLeAudio.GROUP_ID_INVALID
                            : (int) getGroupId(BluetoothProfile.LE_AUDIO, device);
                            : (int) getGroupId(BluetoothProfile.LE_AUDIO, device);
            if (device != null && groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
                // for classic device, we need set active for it explicitly, because when broadcast
                // stops, bt stack will only deal with fallback LEA device.
                Slog.d(TAG, "stopBroadcast: set active device to " + device.getAnonymizedAddress());
                mBluetoothAdapter.setActiveDevice(device, ACTIVE_DEVICE_AUDIO);
            }
            Slog.d(TAG, "stopBroadcast: set broadcast fallabck group to " + groupId);
            mLeAudioProfile.setBroadcastToUnicastFallbackGroup(groupId);
            mLeAudioProfile.setBroadcastToUnicastFallbackGroup(groupId);
        }
        }
        mBroadcastProfile.stopBroadcast(mBroadcastId);
        mBroadcastProfile.stopBroadcast(mBroadcastId);
+71 −0
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -297,6 +298,76 @@ public class AudioManagerRouteControllerTest {
                                AudioDeviceInfo.TYPE_WIRED_HEADSET, /* address= */ ""));
                                AudioDeviceInfo.TYPE_WIRED_HEADSET, /* address= */ ""));
    }
    }


    @Test
    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_PERSONAL_AUDIO_SHARING)
    public void transferToNonBtDevice_inBroadcast_stopsBroadcastAndSetsTheExpectedRoutingPolicy() {
        setUpControllerAndLEAudioMocks();
        when(mMockBluetoothDeviceRoutesManager.isLEAudioBroadcastSupported()).thenReturn(true);
        when(mMockBluetoothDeviceRoutesManager.isBtRoute(any())).thenReturn(false);
        when(mMockBluetoothDeviceRoutesManager.getBroadcastingDeviceRoutes())
                .thenReturn(
                        List.of(
                                createMediaRoute2Info(
                                        /* id= */ FAKE_AUDIO_DEVICE_LE_HEADSET_1_ID,
                                        /* name= */ FAKE_AUDIO_DEVICE_LE_HEADSET_1_NAME,
                                        /* address= */ FAKE_AUDIO_DEVICE_LE_HEADSET_1_ADDRESS,
                                        /* volume= */ 0)));
        addAvailableAudioDeviceInfo(
                /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_LE_HEADSET_1_BROADCASTING,
                /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_LE_HEADSET_1_BROADCASTING,
                FAKE_AUDIO_DEVICE_LE_HEADSET_2);

        clearInvocations(mMockAudioManager);
        MediaRoute2Info builtInSpeakerRoute =
                getAvailableRouteWithType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
        mControllerUnderTest.transferTo(/* requestId= */ 0L, builtInSpeakerRoute.getId());

        mLooperManager.execute(mLooperManager.next());
        verify(mMockBluetoothDeviceRoutesManager).stopBroadcast(null);
        mLooperManager.execute(mLooperManager.next());
        verify(mMockAudioManager)
                .setPreferredDeviceForStrategy(
                        mMediaAudioProductStrategy,
                        createAudioDeviceAttribute(
                                AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, /* address= */ ""));
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_PERSONAL_AUDIO_SHARING)
    public void transferToBtDevice_inBroadcast_stopsBroadcastWithoutSettingRoutingPolicy() {
        setUpControllerAndLEAudioMocks();
        when(mMockBluetoothDeviceRoutesManager.isLEAudioBroadcastSupported()).thenReturn(true);
        when(mMockBluetoothDeviceRoutesManager.isBtRoute(any())).thenReturn(true);
        addAvailableAudioDeviceInfo(
                /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP,
                /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_INFO_BLUETOOTH_A2DP,
                FAKE_AUDIO_DEVICE_INFO_WIRED_HEADSET);
        when(mMockBluetoothDeviceRoutesManager.getBroadcastingDeviceRoutes())
                .thenReturn(
                        List.of(
                                createMediaRoute2Info(
                                        /* id= */ FAKE_AUDIO_DEVICE_LE_HEADSET_1_ID,
                                        /* name= */ FAKE_AUDIO_DEVICE_LE_HEADSET_1_NAME,
                                        /* address= */ FAKE_AUDIO_DEVICE_LE_HEADSET_1_ADDRESS,
                                        /* volume= */ 0)));
        addAvailableAudioDeviceInfo(
                /* newSelectedDevice= */ FAKE_AUDIO_DEVICE_LE_HEADSET_1_BROADCASTING,
                /* newAvailableDevices...= */ FAKE_AUDIO_DEVICE_LE_HEADSET_1_BROADCASTING,
                FAKE_AUDIO_DEVICE_LE_HEADSET_2);

        clearInvocations(mMockAudioManager);
        MediaRoute2Info a2dpBluetoothRoute =
                getAvailableRouteWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
        mControllerUnderTest.transferTo(/* requestId= */ 0L, a2dpBluetoothRoute.getId());

        mLooperManager.execute(mLooperManager.next());
        verify(mMockBluetoothDeviceRoutesManager).stopBroadcast(a2dpBluetoothRoute.getId());
        verify(mMockBluetoothDeviceRoutesManager, never())
                .activateBluetoothDeviceWithAddress(a2dpBluetoothRoute.getAddress());
        verify(mMockAudioManager, never())
                .removePreferredDeviceForStrategy(mMediaAudioProductStrategy);
    }

    @Test
    @Test
    public void updateVolume_propagatesCorrectlyToRouteInfo() {
    public void updateVolume_propagatesCorrectlyToRouteInfo() {
        setUpControllerUnderTest(/* useMockBluetoothDeviceRoutesManager= */ false);
        setUpControllerUnderTest(/* useMockBluetoothDeviceRoutesManager= */ false);