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

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

Merge changes Ice378c71,I48440f7c into main

* changes:
  [le audio] Block newly connected device to become active when broadcasting
  Add aconfig flag for le audio broadcast handover policies
parents 27de968b c47c7b54
Loading
Loading
Loading
Loading
+65 −1
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.Utils;
import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.flags.FeatureFlags;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.le_audio.LeAudioService;
@@ -111,6 +112,7 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
    private Handler mHandler = null;
    private final AudioManager mAudioManager;
    private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;
    private final FeatureFlags mFeatureFlags;

    private final Object mLock = new Object();
    @GuardedBy("mLock")
@@ -250,6 +252,17 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
                return;
            }
            mA2dpConnectedDevices.add(device);
            if (isBroadcastingAudio()) {
                Log.i(
                        TAG,
                        "LE Audio Broadcast is streaming, skip setting A2dp device as active: "
                                + device);
                if (mPendingActiveDevice != null) {
                    mHandler.removeCallbacksAndMessages(mPendingActiveDevice);
                }
                return;
            }

            if (mHearingAidActiveDevices.isEmpty() && mLeHearingAidActiveDevice == null) {
                // New connected device: select it as active
                // Activate HFP and A2DP at the same time if both profile already connected.
@@ -314,6 +327,17 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
                return;
            }
            mHfpConnectedDevices.add(device);
            if (isBroadcastingAudio()) {
                Log.i(
                        TAG,
                        "LE Audio Broadcast is streaming, skip setting Hfp device as active: "
                                + device);
                if (mPendingActiveDevice != null) {
                    mHandler.removeCallbacksAndMessages(mPendingActiveDevice);
                }
                return;
            }

            if (mHearingAidActiveDevices.isEmpty() && mLeHearingAidActiveDevice == null) {
                // New connected device: select it as active
                // Activate HFP and A2DP at the same time once both profile connected.
@@ -377,6 +401,14 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
                return;
            }
            mHearingAidConnectedDevices.add(device);
            if (isBroadcastingAudio()) {
                Log.i(
                        TAG,
                        "LE Audio Broadcast is streaming, skip setting HearingAid device as "
                                + "active:  "
                                + device);
                return;
            }
            // New connected device: select it as active
            if (setHearingAidActiveDevice(device)) {
                setA2dpActiveDevice(null, true);
@@ -406,6 +438,14 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
            }

            mLeAudioConnectedDevices.add(device);
            if (isBroadcastingAudio()) {
                Log.i(
                        TAG,
                        "LE Audio Broadcast is streaming, skip setting le audio device as active: "
                                + device);
                return;
            }

            if (mHearingAidActiveDevices.isEmpty()
                    && mLeHearingAidActiveDevice == null
                    && mPendingLeHearingAidActiveDevice.isEmpty()) {
@@ -437,6 +477,14 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
                return;
            }
            mLeHearingAidConnectedDevices.add(device);
            if (isBroadcastingAudio()) {
                Log.i(
                        TAG,
                        "LE Audio Broadcast is streaming, skip setting Hap device as active: "
                                + device);
                return;
            }

            if (!mLeAudioConnectedDevices.contains(device)) {
                mPendingLeHearingAidActiveDevice.add(device);
            } else if (Objects.equals(mLeAudioActiveDevice, device)) {
@@ -763,9 +811,10 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
        }
    }

    ActiveDeviceManager(AdapterService service, ServiceFactory factory) {
    ActiveDeviceManager(AdapterService service, ServiceFactory factory, FeatureFlags featureFlags) {
        mAdapterService = service;
        mDbManager = mAdapterService.getDatabase();
        mFeatureFlags = Objects.requireNonNull(featureFlags, "Feature Flags cannot be null");
        mFactory = factory;
        mAudioManager = service.getSystemService(AudioManager.class);
        mAudioManagerAudioDeviceCallback = new AudioManagerAudioDeviceCallback();
@@ -1190,6 +1239,21 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
        return false;
    }

    /**
     * Checks if le audio broadcasting is ON
     *
     * @return {@code true} if is broadcasting audio, {@code false} otherwise
     */
    private boolean isBroadcastingAudio() {
        if (!mFeatureFlags.leaudioBroadcastAudioHandoverPolicies()) {
            // disable this if feature flag is false
            return false;
        }

        final LeAudioService leAudioService = mFactory.getLeAudioService();
        return leAudioService != null && !leAudioService.getAllBroadcastMetadata().isEmpty();
    }

    /**
     * Called when a wired audio device is connected.
     * It might be called multiple times each time a wired audio device is connected.
+4 −2
Original line number Diff line number Diff line
@@ -695,9 +695,11 @@ public class AdapterService extends Service {
        }

        if (featureFlags.audioRoutingCentralization()) {
            mActiveDeviceManager = new AudioRoutingManager(this, new ServiceFactory());
            mActiveDeviceManager =
                    new AudioRoutingManager(this, new ServiceFactory(), featureFlags);
        } else {
            mActiveDeviceManager = new ActiveDeviceManager(this, new ServiceFactory());
            mActiveDeviceManager =
                    new ActiveDeviceManager(this, new ServiceFactory(), featureFlags);
        }
        mActiveDeviceManager.start();

+3 −2
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import com.android.bluetooth.BluetoothMethodProxy;
import com.android.bluetooth.Utils;
import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.flags.FeatureFlags;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.le_audio.LeAudioService;
@@ -434,8 +435,8 @@ public class AudioRoutingManager extends ActiveDeviceManager {
        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {}
    }

    AudioRoutingManager(AdapterService service, ServiceFactory factory) {
        super(service, factory);
    AudioRoutingManager(AdapterService service, ServiceFactory factory, FeatureFlags featureFlags) {
        super(service, factory, featureFlags);
        mAdapterService = service;
        mDbManager = mAdapterService.getDatabase();
        mFactory = factory;
+66 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
@@ -34,6 +35,7 @@ import static org.mockito.Mockito.when;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSinkAudioPolicy;
import android.content.Context;
@@ -52,6 +54,7 @@ import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.flags.FakeFeatureFlagsImpl;
import com.android.bluetooth.flags.FeatureFlags;
import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.le_audio.LeAudioService;
@@ -95,6 +98,7 @@ public class ActiveDeviceManagerTest {
    private boolean mOriginalDualModeAudioState;
    private TestDatabaseManager mDatabaseManager;
    private TestLooper mTestLooper;
    private FakeFeatureFlagsImpl mFakeFlagsImpl;

    @Mock private AdapterService mAdapterService;
    @Mock private ServiceFactory mServiceFactory;
@@ -116,7 +120,10 @@ public class ActiveDeviceManagerTest {
        mTestLooper.startAutoDispatch();
        TestUtils.setAdapterService(mAdapterService);

        mDatabaseManager = new TestDatabaseManager(mAdapterService, new FakeFeatureFlagsImpl());
        mFakeFlagsImpl = new FakeFeatureFlagsImpl();
        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, false);
        mDatabaseManager = new TestDatabaseManager(mAdapterService, mFakeFlagsImpl);

        when(mAdapterService.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
        when(mAdapterService.getSystemServiceName(AudioManager.class))
                .thenReturn(Context.AUDIO_SERVICE);
@@ -126,7 +133,8 @@ public class ActiveDeviceManagerTest {
        when(mServiceFactory.getHearingAidService()).thenReturn(mHearingAidService);
        when(mServiceFactory.getLeAudioService()).thenReturn(mLeAudioService);

        mActiveDeviceManager = new ActiveDeviceManager(mAdapterService, mServiceFactory);
        mActiveDeviceManager =
                new ActiveDeviceManager(mAdapterService, mServiceFactory, mFakeFlagsImpl);
        mActiveDeviceManager.start();
        mAdapter = BluetoothAdapter.getDefaultAdapter();

@@ -1173,6 +1181,62 @@ public class ActiveDeviceManagerTest {
        verify(mHearingAidService, timeout(TIMEOUT_MS)).removeActiveDevice(false);
    }

    /**
     * Verifies if Le Audio Broadcast is streaming, connected a2dp device should not be set as
     * active.
     */
    @Test
    public void a2dpConnectedWhenBroadcasting_notSetA2dpActive() {
        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, true);
        final List<BluetoothLeBroadcastMetadata> metadataList = mock(List.class);
        when(mLeAudioService.getAllBroadcastMetadata()).thenReturn(metadataList);
        a2dpConnected(mA2dpDevice, false);
        verify(mA2dpService, never()).setActiveDevice(any());
        a2dpConnected(mA2dpDevice, true);
        verify(mA2dpService, never()).setActiveDevice(any());
    }

    /**
     * Verifies if Le Audio Broadcast is streaming, connected headset device should not be set as
     * active.
     */
    @Test
    public void headsetConnectedWhenBroadcasting_notSetHeadsetActive() {
        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, true);
        final List<BluetoothLeBroadcastMetadata> metadataList = mock(List.class);
        when(mLeAudioService.getAllBroadcastMetadata()).thenReturn(metadataList);
        headsetConnected(mHeadsetDevice, false);
        verify(mHeadsetService, never()).setActiveDevice(any());
        headsetConnected(mHeadsetDevice, true);
        verify(mHeadsetService, never()).setActiveDevice(any());
    }

    /**
     * Verifies if Le Audio Broadcast is streaming, connected hearing aid device should not be set
     * as active.
     */
    @Test
    public void hearingAidConnectedWhenBroadcasting_notSetHearingAidActive() {
        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, true);
        final List<BluetoothLeBroadcastMetadata> metadataList = mock(List.class);
        when(mLeAudioService.getAllBroadcastMetadata()).thenReturn(metadataList);
        hearingAidConnected(mHearingAidDevice);
        verify(mHearingAidService, never()).setActiveDevice(any());
    }

    /**
     * Verifies if Le Audio Broadcast is streaming, connected LE hearing aid device should not be
     * set as active.
     */
    @Test
    public void leHearingAidConnectedWhenBroadcasting_notSetLeHearingAidActive() {
        mFakeFlagsImpl.setFlag(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES, true);
        final List<BluetoothLeBroadcastMetadata> metadataList = mock(List.class);
        when(mLeAudioService.getAllBroadcastMetadata()).thenReturn(metadataList);
        leHearingAidConnected(mLeHearingAidDevice);
        verify(mLeAudioService, never()).setActiveDevice(any());
    }

    /**
     * Helper to indicate A2dp connected for a device.
     */
+5 −2
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ public class AudioRoutingManagerTest {
    private boolean mOriginalDualModeAudioState;
    private TestDatabaseManager mDatabaseManager;
    private TestLooper mTestLooper;
    private FakeFeatureFlagsImpl mFakeFlagsImpl;

    @Mock private AdapterService mAdapterService;
    @Mock private ServiceFactory mServiceFactory;
@@ -116,7 +117,8 @@ public class AudioRoutingManagerTest {
        mTestLooper.startAutoDispatch();
        TestUtils.setAdapterService(mAdapterService);

        mDatabaseManager = new TestDatabaseManager(mAdapterService, new FakeFeatureFlagsImpl());
        mFakeFlagsImpl = new FakeFeatureFlagsImpl();
        mDatabaseManager = new TestDatabaseManager(mAdapterService, mFakeFlagsImpl);

        when(mAdapterService.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
        when(mAdapterService.getSystemServiceName(AudioManager.class))
@@ -127,7 +129,8 @@ public class AudioRoutingManagerTest {
        when(mServiceFactory.getHearingAidService()).thenReturn(mHearingAidService);
        when(mServiceFactory.getLeAudioService()).thenReturn(mLeAudioService);

        mAudioRoutingManager = new AudioRoutingManager(mAdapterService, mServiceFactory);
        mAudioRoutingManager =
                new AudioRoutingManager(mAdapterService, mServiceFactory, mFakeFlagsImpl);
        mAudioRoutingManager.start();
        mAdapter = BluetoothAdapter.getDefaultAdapter();

Loading