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

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

[Audiosharing] Disable main toggle till device gets active.

Broadcast <-> unicast costs some time, so we grey out the audio
sharing toggle until the device get active on unicast when users toggle
off the sharing.

Flag: com.android.settingslib.flags.enable_le_audio_sharing
Test: atest
Bug: 359755881
Change-Id: I868a470430f6fb54c142f17552d70fa5fa8a2476
parent a63f6cc5
Loading
Loading
Loading
Loading
+25 −4
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.bluetooth.BluetoothLeBroadcast;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -52,7 +53,10 @@ import com.android.settings.bluetooth.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -75,7 +79,8 @@ import java.util.concurrent.atomic.AtomicInteger;
public class AudioSharingSwitchBarController extends BasePreferenceController
        implements DefaultLifecycleObserver,
                OnCheckedChangeListener,
                LocalBluetoothProfileManager.ServiceListener {
                LocalBluetoothProfileManager.ServiceListener,
                BluetoothCallback {
    private static final String TAG = "AudioSharingSwitchCtlr";
    private static final String PREF_KEY = "audio_sharing_main_switch";

@@ -99,6 +104,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
    private final SettingsMainSwitchBar mSwitchBar;
    private final BluetoothAdapter mBluetoothAdapter;
    @Nullable private final LocalBluetoothManager mBtManager;
    @Nullable private final BluetoothEventManager mEventManager;
    @Nullable private final LocalBluetoothProfileManager mProfileManager;
    @Nullable private final LocalBluetoothLeBroadcast mBroadcast;
    @Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant;
@@ -302,6 +308,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        mBtManager = Utils.getLocalBtManager(context);
        mEventManager = mBtManager == null ? null : mBtManager.getEventManager();
        mProfileManager = mBtManager == null ? null : mBtManager.getProfileManager();
        mBroadcast = mProfileManager == null ? null : mProfileManager.getLeAudioBroadcastProfile();
        mAssistant =
@@ -427,6 +434,13 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
        // Do nothing.
    }

    @Override
    public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
        if (activeDevice != null && bluetoothProfile == BluetoothProfile.LE_AUDIO) {
            updateSwitch();
        }
    }

    /**
     * Initialize the controller.
     *
@@ -447,7 +461,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
            Log.d(TAG, "Skip registerCallbacks(). Feature is not available.");
            return;
        }
        if (mBroadcast == null || mAssistant == null) {
        if (mBroadcast == null || mAssistant == null || mEventManager == null) {
            Log.d(TAG, "Skip registerCallbacks(). Profile not support on this device.");
            return;
        }
@@ -456,6 +470,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
            mSwitchBar.addOnSwitchChangeListener(this);
            mBroadcast.registerServiceCallBack(mExecutor, mBroadcastCallback);
            mAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
            mEventManager.registerCallback(this);
            mCallbacksRegistered.set(true);
        }
    }
@@ -465,7 +480,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
            Log.d(TAG, "Skip unregisterCallbacks(). Feature is not available.");
            return;
        }
        if (mBroadcast == null || mAssistant == null) {
        if (mBroadcast == null || mAssistant == null || mEventManager == null) {
            Log.d(TAG, "Skip unregisterCallbacks(). Profile not support on this device.");
            return;
        }
@@ -474,6 +489,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
            mSwitchBar.removeOnSwitchChangeListener(this);
            mBroadcast.unregisterServiceCallBack(mBroadcastCallback);
            mAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
            mEventManager.unregisterCallback(this);
            mCallbacksRegistered.set(false);
        }
    }
@@ -518,10 +534,15 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
                ThreadUtils.postOnBackgroundThread(
                        () -> {
                            boolean isBroadcasting = BluetoothUtils.isBroadcasting(mBtManager);
                            boolean hasActiveDevice =
                                    AudioSharingUtils.hasActiveConnectedLeadDevice(mBtManager);
                            boolean isStateReady =
                                    isBluetoothOn()
                                            && AudioSharingUtils.isAudioSharingProfileReady(
                                                    mProfileManager);
                                                    mProfileManager)
                                            // Disable toggle till device gets active after
                                            // broadcast ends.
                                            && (isBroadcasting || hasActiveDevice);
                            AudioSharingUtils.postOnMainThread(
                                    mContext,
                                    () -> {
+20 −4
Original line number Diff line number Diff line
@@ -115,7 +115,7 @@ public class AudioSharingUtils {
     * sharing. The active device is placed in the first place if it exists. The devices can be
     * filtered by whether it is already in the audio sharing session.
     *
     * @param localBtManager The BT manager to provide BT functions. *
     * @param localBtManager The BT manager to provide BT functions.
     * @param groupedConnectedDevices devices connected to broadcast assistant grouped by CSIP group
     *     id.
     * @param filterByInSharing Whether to filter the device by if is already in the sharing
@@ -190,7 +190,7 @@ public class AudioSharingUtils {
     * sharing. The active device is placed in the first place if it exists. The devices can be
     * filtered by whether it is already in the audio sharing session.
     *
     * @param localBtManager The BT manager to provide BT functions. *
     * @param localBtManager The BT manager to provide BT functions.
     * @param groupedConnectedDevices devices connected to broadcast assistant grouped by CSIP group
     *     id.
     * @param filterByInSharing Whether to filter the device by if is already in the sharing
@@ -210,6 +210,22 @@ public class AudioSharingUtils {
                .collect(toList());
    }

    /** Return if there exists active connected lead device. */
    public static boolean hasActiveConnectedLeadDevice(
            @Nullable LocalBluetoothManager localBtManager) {
        CachedBluetoothDeviceManager deviceManager =
                localBtManager == null ? null : localBtManager.getCachedDeviceManager();
        Map<Integer, List<BluetoothDevice>> groupedConnectedDevices =
                fetchConnectedDevicesByGroupId(localBtManager);
        for (List<BluetoothDevice> devices : groupedConnectedDevices.values()) {
            CachedBluetoothDevice leadDevice = getLeadDevice(deviceManager, devices);
            if (isActiveLeAudioDevice(leadDevice)) {
                return true;
            }
        }
        return false;
    }

    /** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
    public static AudioSharingDeviceItem buildAudioSharingDeviceItem(
            CachedBluetoothDevice cachedDevice) {
@@ -225,8 +241,8 @@ public class AudioSharingUtils {
     * @param cachedDevice The cached bluetooth device to check.
     * @return Whether the device is an active le audio device.
     */
    public static boolean isActiveLeAudioDevice(CachedBluetoothDevice cachedDevice) {
        return BluetoothUtils.isActiveLeAudioDevice(cachedDevice);
    public static boolean isActiveLeAudioDevice(@Nullable CachedBluetoothDevice cachedDevice) {
        return cachedDevice != null && BluetoothUtils.isActiveLeAudioDevice(cachedDevice);
    }

    /** Toast message on main thread. */
+46 −0
Original line number Diff line number Diff line
@@ -75,6 +75,8 @@ import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
@@ -117,6 +119,8 @@ import java.util.concurrent.Executor;
public class AudioSharingSwitchBarControllerTest {
    private static final String TEST_DEVICE_NAME1 = "test1";
    private static final String TEST_DEVICE_NAME2 = "test2";
    private static final String TEST_DEVICE_ANONYMIZED_ADDR1 = "XX:XX:01";
    private static final String TEST_DEVICE_ANONYMIZED_ADDR2 = "XX:XX:02";
    private static final int TEST_DEVICE_GROUP_ID1 = 1;
    private static final int TEST_DEVICE_GROUP_ID2 = 2;
    private static final Correspondence<Fragment, String> TAG_EQUALS =
@@ -133,6 +137,7 @@ public class AudioSharingSwitchBarControllerTest {
    @Spy Context mContext = ApplicationProvider.getApplicationContext();
    @Mock private LocalBluetoothManager mLocalBtManager;
    @Mock private CachedBluetoothDeviceManager mDeviceManager;
    @Mock private BluetoothEventManager mEventManager;
    @Mock private LocalBluetoothProfileManager mBtProfileManager;
    @Mock private LocalBluetoothLeBroadcast mBroadcast;
    @Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@@ -168,11 +173,14 @@ public class AudioSharingSwitchBarControllerTest {
        mFeatureFactory = FakeFeatureFactory.setupForTest();
        when(localBluetoothManager.getProfileManager()).thenReturn(mBtProfileManager);
        when(localBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
        when(localBluetoothManager.getEventManager()).thenReturn(mEventManager);
        when(mDevice1.getAnonymizedAddress()).thenReturn(TEST_DEVICE_ANONYMIZED_ADDR1);
        when(mDeviceManager.findDevice(mDevice1)).thenReturn(mCachedDevice1);
        when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
        when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1);
        when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
        when(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(false);
        when(mDevice2.getAnonymizedAddress()).thenReturn(TEST_DEVICE_ANONYMIZED_ADDR2);
        when(mDeviceManager.findDevice(mDevice2)).thenReturn(mCachedDevice2);
        when(mCachedDevice2.getDevice()).thenReturn(mDevice2);
        when(mCachedDevice2.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID2);
@@ -308,6 +316,7 @@ public class AudioSharingSwitchBarControllerTest {
        verify(mAssistant, never())
                .registerServiceCallBack(
                        any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
        verify(mEventManager, never()).registerCallback(any(BluetoothCallback.class));
        verify(mBtProfileManager).addServiceListener(mController);
        assertThat(mSwitchBar.isChecked()).isFalse();
        assertThat(mSwitchBar.isEnabled()).isFalse();
@@ -328,6 +337,7 @@ public class AudioSharingSwitchBarControllerTest {
        verify(mAssistant)
                .registerServiceCallBack(
                        any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
        verify(mEventManager).registerCallback(any(BluetoothCallback.class));
        verify(mBtProfileManager, never()).addServiceListener(mController);
        assertThat(mSwitchBar.isChecked()).isTrue();
        assertThat(mSwitchBar.isEnabled()).isTrue();
@@ -342,6 +352,7 @@ public class AudioSharingSwitchBarControllerTest {
                .unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
        verify(mAssistant, never())
                .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
        verify(mEventManager, never()).unregisterCallback(any(BluetoothCallback.class));
        verify(mBtProfileManager, never()).removeServiceListener(mController);
    }

@@ -358,6 +369,7 @@ public class AudioSharingSwitchBarControllerTest {
                .unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
        verify(mAssistant, never())
                .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
        verify(mEventManager, never()).unregisterCallback(any(BluetoothCallback.class));
    }

    @Test
@@ -374,6 +386,7 @@ public class AudioSharingSwitchBarControllerTest {
        verify(mBroadcast).unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
        verify(mAssistant)
                .unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
        verify(mEventManager).unregisterCallback(any(BluetoothCallback.class));
    }

    @Test
@@ -599,9 +612,11 @@ public class AudioSharingSwitchBarControllerTest {
        mOnAudioSharingStateChanged = false;
        mSwitchBar.setChecked(false);
        when(mBroadcast.isEnabled(any())).thenReturn(false);
        when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1, mDevice2));
        mController.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1);
        shadowOf(Looper.getMainLooper()).idle();
        assertThat(mSwitchBar.isChecked()).isFalse();
        assertThat(mSwitchBar.isEnabled()).isTrue();
        assertThat(mOnAudioSharingStateChanged).isFalse();
        verify(mFeatureFactory.metricsFeatureProvider)
                .action(
@@ -613,6 +628,7 @@ public class AudioSharingSwitchBarControllerTest {
        mController.mBroadcastCallback.onBroadcastStarted(/* reason= */ 1, /* broadcastId= */ 1);
        shadowOf(Looper.getMainLooper()).idle();
        assertThat(mSwitchBar.isChecked()).isTrue();
        assertThat(mSwitchBar.isEnabled()).isTrue();
        assertThat(mOnAudioSharingStateChanged).isTrue();

        mOnAudioSharingStateChanged = false;
@@ -627,9 +643,11 @@ public class AudioSharingSwitchBarControllerTest {
                        SettingsEnums.AUDIO_SHARING_SETTINGS);

        when(mBroadcast.isEnabled(any())).thenReturn(false);
        when(mCachedDevice2.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(false);
        mController.mBroadcastCallback.onBroadcastStopped(/* reason= */ 1, /* broadcastId= */ 1);
        shadowOf(Looper.getMainLooper()).idle();
        assertThat(mSwitchBar.isChecked()).isFalse();
        assertThat(mSwitchBar.isEnabled()).isFalse();
        assertThat(mOnAudioSharingStateChanged).isTrue();
    }

@@ -682,6 +700,34 @@ public class AudioSharingSwitchBarControllerTest {
        verifyNoMoreInteractions(mFeatureFactory.metricsFeatureProvider);
    }

    @Test
    public void onActiveDeviceChanged_leaProfile_updateSwitch() {
        mSwitchBar.setChecked(true);
        mSwitchBar.setEnabled(false);
        when(mBroadcast.isEnabled(null)).thenReturn(false);
        when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
        mController.onActiveDeviceChanged(mCachedDevice2, BluetoothProfile.LE_AUDIO);
        shadowOf(Looper.getMainLooper()).idle();
        assertThat(mSwitchBar.isChecked()).isFalse();
        verify(mSwitchBar).setEnabled(true);
    }

    @Test
    public void onActiveDeviceChanged_nullActiveDevice_doNothing() {
        mController.onActiveDeviceChanged(/* activeDevice= */ null, BluetoothProfile.LE_AUDIO);
        shadowOf(Looper.getMainLooper()).idle();
        verify(mSwitchBar, never()).setEnabled(anyBoolean());
        verify(mSwitchBar, never()).setChecked(anyBoolean());
    }

    @Test
    public void onActiveDeviceChanged_notLeaProfile_doNothing() {
        mController.onActiveDeviceChanged(mCachedDevice2, BluetoothProfile.HEADSET);
        shadowOf(Looper.getMainLooper()).idle();
        verify(mSwitchBar, never()).setEnabled(anyBoolean());
        verify(mSwitchBar, never()).setChecked(anyBoolean());
    }

    @Test
    public void testAccessibilityDelegate() {
        View view = new View(mContext);