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

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

Update fallback device when assistant profile disconnected

Test: atest
Bug: 329003470
Change-Id: I35a82f010817c45ee16e7180e9aa30706484af74
parent 940ed9ab
Loading
Loading
Loading
Loading
+27 −6
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ public class BluetoothEventManager {
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private final LocalBluetoothAdapter mLocalAdapter;
    private final LocalBluetoothManager mBtManager;
    private final CachedBluetoothDeviceManager mDeviceManager;
    private final IntentFilter mAdapterIntentFilter, mProfileIntentFilter;
    private final Map<String, Handler> mHandlerMap;
@@ -80,10 +81,15 @@ public class BluetoothEventManager {
     * userHandle passed in is {@code null}, we register event receiver for the
     * {@code context.getUser()} handle.
     */
    BluetoothEventManager(LocalBluetoothAdapter adapter,
            CachedBluetoothDeviceManager deviceManager, Context context,
            android.os.Handler handler, @Nullable UserHandle userHandle) {
    BluetoothEventManager(
            LocalBluetoothAdapter adapter,
            LocalBluetoothManager btManager,
            CachedBluetoothDeviceManager deviceManager,
            Context context,
            android.os.Handler handler,
            @Nullable UserHandle userHandle) {
        mLocalAdapter = adapter;
        mBtManager = btManager;
        mDeviceManager = deviceManager;
        mAdapterIntentFilter = new IntentFilter();
        mProfileIntentFilter = new IntentFilter();
@@ -210,11 +216,27 @@ public class BluetoothEventManager {
        }
    }

    void dispatchProfileConnectionStateChanged(@NonNull CachedBluetoothDevice device, int state,
            int bluetoothProfile) {
    void dispatchProfileConnectionStateChanged(
            @NonNull CachedBluetoothDevice device, int state, int bluetoothProfile) {
        for (BluetoothCallback callback : mCallbacks) {
            callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
        }

        // Trigger updateFallbackActiveDeviceIfNeeded when ASSISTANT profile disconnected when
        // audio sharing is enabled.
        if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
                && state == BluetoothAdapter.STATE_DISCONNECTED
                && BluetoothUtils.isAudioSharingEnabled()) {
            LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
            if (profileManager != null
                    && profileManager.getLeAudioBroadcastProfile() != null
                    && profileManager.getLeAudioBroadcastProfile().isProfileReady()
                    && profileManager.getLeAudioBroadcastAssistantProfile() != null
                    && profileManager.getLeAudioBroadcastAssistantProfile().isProfileReady()) {
                Log.d(TAG, "updateFallbackActiveDeviceIfNeeded, ASSISTANT profile disconnected");
                profileManager.getLeAudioBroadcastProfile().updateFallbackActiveDeviceIfNeeded();
            }
        }
    }

    private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
@@ -536,7 +558,6 @@ public class BluetoothEventManager {
                default:
                    Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
                    return;

            }
            dispatchAclStateChanged(activeDevice, state);
        }
+13 −6
Original line number Diff line number Diff line
@@ -21,11 +21,11 @@ import android.os.Handler;
import android.os.UserHandle;
import android.util.Log;

import java.lang.ref.WeakReference;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresPermission;

import java.lang.ref.WeakReference;

/**
 * LocalBluetoothManager provides a simplified interface on top of a subset of
 * the Bluetooth API. Note that {@link #getInstance} will return null
@@ -111,10 +111,17 @@ public class LocalBluetoothManager {
        mContext = context.getApplicationContext();
        mLocalAdapter = adapter;
        mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, this);
        mEventManager = new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mContext,
                handler, userHandle);
        mProfileManager = new LocalBluetoothProfileManager(mContext,
                mLocalAdapter, mCachedDeviceManager, mEventManager);
        mEventManager =
                new BluetoothEventManager(
                        mLocalAdapter,
                        this,
                        mCachedDeviceManager,
                        mContext,
                        handler,
                        userHandle);
        mProfileManager =
                new LocalBluetoothProfileManager(
                        mContext, mLocalAdapter, mCachedDeviceManager, mEventManager);

        mProfileManager.updateLocalProfiles();
        mEventManager.readPairedDevices();
+12 −7
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;

import static java.util.concurrent.TimeUnit.SECONDS;

import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
@@ -37,13 +39,11 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static java.util.concurrent.TimeUnit.SECONDS;

import java.util.concurrent.CountDownLatch;

/**
 * Test that verifies that BluetoothEventManager can receive broadcasts for non-current
 * users for all bluetooth events.
 * Test that verifies that BluetoothEventManager can receive broadcasts for non-current users for
 * all bluetooth events.
 *
 * <p>Creation and deletion of users takes a long time, so marking this as a LargeTest.
 */
@@ -64,9 +64,14 @@ public class BluetoothEventManagerIntegTest {
        mContext = InstrumentationRegistry.getTargetContext();
        mUserManager = UserManager.get(mContext);

        mBluetoothEventManager = new BluetoothEventManager(
                mock(LocalBluetoothAdapter.class), mock(CachedBluetoothDeviceManager.class),
                mContext, /* handler= */ null, UserHandle.ALL);
        mBluetoothEventManager =
                new BluetoothEventManager(
                        mock(LocalBluetoothAdapter.class),
                        mock(LocalBluetoothManager.class),
                        mock(CachedBluetoothDeviceManager.class),
                        mContext,
                        /* handler= */ null,
                        UserHandle.ALL);

        // Create and start another user in the background.
        mOtherUser = mUserManager.createUser("TestUser", /* flags= */ 0);
+190 −6
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -29,35 +30,47 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.UserHandle;
import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.TelephonyManager;

import com.android.settingslib.R;
import com.android.settingslib.flags.Flags;
import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class BluetoothEventManagerTest {
    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    private static final String DEVICE_NAME = "test_device_name";

    @Mock
    private LocalBluetoothAdapter mLocalAdapter;
    @Mock
    private LocalBluetoothManager mBtManager;
    @Mock
    private CachedBluetoothDeviceManager mCachedDeviceManager;
    @Mock
    private BluetoothCallback mBluetoothCallback;
@@ -96,8 +109,15 @@ public class BluetoothEventManagerTest {
        MockitoAnnotations.initMocks(this);
        mContext = RuntimeEnvironment.application;

        mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter,
                mCachedDeviceManager, mContext, /* handler= */ null, /* userHandle= */ null);
        mBluetoothEventManager =
                new BluetoothEventManager(
                        mLocalAdapter,
                        mBtManager,
                        mCachedDeviceManager,
                        mContext,
                        /* handler= */ null,
                        /* userHandle= */ null);
        when(mBtManager.getProfileManager()).thenReturn(mLocalProfileManager);
        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
        when(mHfpProfile.isProfileReady()).thenReturn(true);
        when(mA2dpProfile.isProfileReady()).thenReturn(true);
@@ -113,8 +133,13 @@ public class BluetoothEventManagerTest {
    public void ifUserHandleIsNull_registerReceiverIsCalled() {
        Context mockContext = mock(Context.class);
        BluetoothEventManager eventManager =
                new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mockContext,
                        /* handler= */ null, /* userHandle= */ null);
                new BluetoothEventManager(
                        mLocalAdapter,
                        mBtManager,
                        mCachedDeviceManager,
                        mockContext,
                        /* handler= */ null,
                        /* userHandle= */ null);

        verify(mockContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class),
                eq(null), eq(null), eq(Context.RECEIVER_EXPORTED));
@@ -124,8 +149,13 @@ public class BluetoothEventManagerTest {
    public void ifUserHandleSpecified_registerReceiverAsUserIsCalled() {
        Context mockContext = mock(Context.class);
        BluetoothEventManager eventManager =
                new BluetoothEventManager(mLocalAdapter, mCachedDeviceManager, mockContext,
                        /* handler= */ null, UserHandle.ALL);
                new BluetoothEventManager(
                        mLocalAdapter,
                        mBtManager,
                        mCachedDeviceManager,
                        mockContext,
                        /* handler= */ null,
                        UserHandle.ALL);

        verify(mockContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq(UserHandle.ALL),
                any(IntentFilter.class), eq(null), eq(null), eq(Context.RECEIVER_EXPORTED));
@@ -172,6 +202,160 @@ public class BluetoothEventManagerTest {
                BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
    }

    /**
     * dispatchProfileConnectionStateChanged should not call {@link
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when audio sharing flag is off.
     */
    @Test
    public void dispatchProfileConnectionStateChanged_flagOff_noUpdateFallbackDevice() {
        ShadowBluetoothAdapter shadowBluetoothAdapter =
                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
                BluetoothStatusCodes.FEATURE_SUPPORTED);
        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
                BluetoothStatusCodes.FEATURE_SUPPORTED);
        mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
        when(broadcast.isProfileReady()).thenReturn(true);
        LocalBluetoothLeBroadcastAssistant assistant =
                mock(LocalBluetoothLeBroadcastAssistant.class);
        when(assistant.isProfileReady()).thenReturn(true);
        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
        when(mBtManager.getProfileManager()).thenReturn(profileManager);
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                mCachedBluetoothDevice,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);

        verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
    }

    /**
     * dispatchProfileConnectionStateChanged should not call {@link
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when the device does not
     * support audio sharing.
     */
    @Test
    public void dispatchProfileConnectionStateChanged_notSupport_noUpdateFallbackDevice() {
        ShadowBluetoothAdapter shadowBluetoothAdapter =
                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
                BluetoothStatusCodes.FEATURE_NOT_SUPPORTED);
        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
                BluetoothStatusCodes.FEATURE_SUPPORTED);
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
        when(broadcast.isProfileReady()).thenReturn(true);
        LocalBluetoothLeBroadcastAssistant assistant =
                mock(LocalBluetoothLeBroadcastAssistant.class);
        when(assistant.isProfileReady()).thenReturn(true);
        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
        when(mBtManager.getProfileManager()).thenReturn(profileManager);
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                mCachedBluetoothDevice,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);

        verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
    }

    /**
     * dispatchProfileConnectionStateChanged should not call {@link
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when audio sharing profile is
     * not ready.
     */
    @Test
    public void dispatchProfileConnectionStateChanged_profileNotReady_noUpdateFallbackDevice() {
        ShadowBluetoothAdapter shadowBluetoothAdapter =
                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
                BluetoothStatusCodes.FEATURE_SUPPORTED);
        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
                BluetoothStatusCodes.FEATURE_SUPPORTED);
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
        when(broadcast.isProfileReady()).thenReturn(false);
        LocalBluetoothLeBroadcastAssistant assistant =
                mock(LocalBluetoothLeBroadcastAssistant.class);
        when(assistant.isProfileReady()).thenReturn(true);
        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
        when(mBtManager.getProfileManager()).thenReturn(profileManager);
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                mCachedBluetoothDevice,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);

        verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
    }

    /**
     * dispatchProfileConnectionStateChanged should not call {@link
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when triggered for profile
     * other than LE_AUDIO_BROADCAST_ASSISTANT or state other than STATE_DISCONNECTED.
     */
    @Test
    public void dispatchProfileConnectionStateChanged_notAssistantProfile_noUpdateFallbackDevice() {
        ShadowBluetoothAdapter shadowBluetoothAdapter =
                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
                BluetoothStatusCodes.FEATURE_SUPPORTED);
        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
                BluetoothStatusCodes.FEATURE_SUPPORTED);
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
        when(broadcast.isProfileReady()).thenReturn(true);
        LocalBluetoothLeBroadcastAssistant assistant =
                mock(LocalBluetoothLeBroadcastAssistant.class);
        when(assistant.isProfileReady()).thenReturn(true);
        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
        when(mBtManager.getProfileManager()).thenReturn(profileManager);
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                mCachedBluetoothDevice,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.LE_AUDIO);

        verify(broadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
    }

    /**
     * dispatchProfileConnectionStateChanged should call {@link
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when assistant profile is
     * disconnected and audio sharing is enabled.
     */
    @Test
    public void dispatchProfileConnectionStateChanged_audioSharing_updateFallbackDevice() {
        ShadowBluetoothAdapter shadowBluetoothAdapter =
                Shadow.extract(BluetoothAdapter.getDefaultAdapter());
        shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
                BluetoothStatusCodes.FEATURE_SUPPORTED);
        shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
                BluetoothStatusCodes.FEATURE_SUPPORTED);
        mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
        LocalBluetoothLeBroadcast broadcast = mock(LocalBluetoothLeBroadcast.class);
        when(broadcast.isProfileReady()).thenReturn(true);
        LocalBluetoothLeBroadcastAssistant assistant =
                mock(LocalBluetoothLeBroadcastAssistant.class);
        when(assistant.isProfileReady()).thenReturn(true);
        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(broadcast);
        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
        when(mBtManager.getProfileManager()).thenReturn(profileManager);
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                mCachedBluetoothDevice,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);

        verify(broadcast).updateFallbackActiveDeviceIfNeeded();
    }

    @Test
    public void dispatchAclConnectionStateChanged_aclDisconnected_shouldDispatchCallback() {
        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+14 −5
Original line number Diff line number Diff line
@@ -56,7 +56,8 @@ import java.util.List;
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalBluetoothProfileManagerTest {
    private final static long HISYNCID = 10;

    @Mock
    private LocalBluetoothManager mBtManager;
    @Mock
    private CachedBluetoothDeviceManager mDeviceManager;
    @Mock
@@ -77,13 +78,21 @@ public class LocalBluetoothProfileManagerTest {
        MockitoAnnotations.initMocks(this);
        mContext = spy(RuntimeEnvironment.application);
        mLocalBluetoothAdapter = LocalBluetoothAdapter.getInstance();
        mEventManager = spy(new BluetoothEventManager(mLocalBluetoothAdapter, mDeviceManager,
                mContext, /* handler= */ null, /* userHandle= */ null));
        mEventManager =
                spy(
                        new BluetoothEventManager(
                                mLocalBluetoothAdapter,
                                mBtManager,
                                mDeviceManager,
                                mContext,
                                /* handler= */ null,
                                /* userHandle= */ null));
        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
        when(mDeviceManager.findDevice(mDevice)).thenReturn(mCachedBluetoothDevice);
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mDevice);
        mProfileManager = new LocalBluetoothProfileManager(mContext, mLocalBluetoothAdapter,
                mDeviceManager, mEventManager);
        mProfileManager =
                new LocalBluetoothProfileManager(
                        mContext, mLocalBluetoothAdapter, mDeviceManager, mEventManager);
    }

    /**
Loading