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

Commit b7518c56 authored by Yiyi Shen's avatar Yiyi Shen Committed by Android (Google) Code Review
Browse files

Merge "[Audiosharing] Revert ag/31626042 (exclude utils)" into main

parents c543fa81 2c5f451b
Loading
Loading
Loading
Loading
+28 −66
Original line number Diff line number Diff line
@@ -46,7 +46,6 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
import com.android.settingslib.flags.Flags;
import com.android.settingslib.utils.ThreadUtils;

import com.google.common.collect.ImmutableList;
@@ -63,7 +62,6 @@ import java.util.concurrent.TimeUnit;
public abstract class BluetoothDevicePairingDetailBase extends DeviceListPreferenceFragment {
    private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(15);
    private static final int AUTO_DISMISS_MESSAGE_ID = 1001;
    private static final int AUTO_FINISH_MESSAGE_ID = 1002;
    private static final ImmutableList<Integer> AUDIO_SHARING_PROFILES = ImmutableList.of(
            BluetoothProfile.LE_AUDIO,
            BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, BluetoothProfile.VOLUME_CONTROL);
@@ -91,8 +89,8 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
    // onDeviceBondStateChanged(BOND_BONDED), BluetoothDevicePreference's summary has already
    // change from "Pairing..." to empty since it listens to metadata changes happens earlier.
    //
    // In pairing flow during audio sharing, we have to wait on this page till the device is
    // connected to check the device type and handle extra logic for audio sharing.
    // In share then pair flow, we have to wait on this page till the device is connected to check
    // the device type and handle extra logic for audio sharing.
    // The BluetoothDevicePreference summary will be blank for seconds between "Pairing..." and
    // "Connecting..." To help users better understand the process, we listen to metadata change
    // as well and show a progress dialog with "Connecting to ...." once BluetoothDevice.getState()
@@ -103,11 +101,10 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
                public void onMetadataChanged(@NonNull BluetoothDevice device, int key,
                        @Nullable byte[] value) {
                    Log.d(getLogTag(), "onMetadataChanged device = " + device + ", key  = " + key);
                    if ((mShouldTriggerShareThenPairFlow || shouldSetTempBondMetadata())
                            && mProgressDialog == null
                    if (mShouldTriggerShareThenPairFlow && mProgressDialog == null
                            && device.getBondState() == BluetoothDevice.BOND_BONDED
                            && mSelectedList.contains(device)) {
                        handleDeviceBondedInAudioSharing(device);
                        handleShareThenPair(device);
                        // Once device is bonded, remove the listener
                        removeOnMetadataChangedListener(device);
                    }
@@ -181,12 +178,11 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere

    @Override
    public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
        boolean shouldSetTempBond = shouldSetTempBondMetadata();
        if (bondState == BluetoothDevice.BOND_BONDED) {
            if (cachedDevice != null && (mShouldTriggerShareThenPairFlow || shouldSetTempBond)) {
            if (cachedDevice != null && mShouldTriggerShareThenPairFlow) {
                BluetoothDevice device = cachedDevice.getDevice();
                if (device != null && mSelectedList.contains(device)) {
                    handleDeviceBondedInAudioSharing(device);
                    handleShareThenPair(device);
                    removeOnMetadataChangedListener(device);
                    return;
                }
@@ -195,7 +191,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
            finish();
            return;
        } else if (bondState == BluetoothDevice.BOND_BONDING) {
            if ((mShouldTriggerShareThenPairFlow || shouldSetTempBond) && cachedDevice != null) {
            if (mShouldTriggerShareThenPairFlow && cachedDevice != null) {
                BluetoothDevice device = cachedDevice.getDevice();
                if (device != null && mSelectedList.contains(device)) {
                    addOnMetadataChangedListener(device);
@@ -208,7 +204,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
                    pageId);
            HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice);
        } else if (bondState == BluetoothDevice.BOND_NONE) {
            if ((mShouldTriggerShareThenPairFlow || shouldSetTempBond) && cachedDevice != null) {
            if (mShouldTriggerShareThenPairFlow && cachedDevice != null) {
                BluetoothDevice device = cachedDevice.getDevice();
                if (device != null && mSelectedList.contains(device)) {
                    removeOnMetadataChangedListener(device);
@@ -240,22 +236,15 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
                    && mSelectedList.contains(device)) {
                var unused = ThreadUtils.postOnBackgroundThread(() -> {
                    if (BluetoothUtils.isAudioSharingUIAvailable(getContext())) {
                        if ((mShouldTriggerShareThenPairFlow || shouldSetTempBondMetadata())
                        if (mShouldTriggerShareThenPairFlow
                                && state == BluetoothAdapter.STATE_CONNECTED
                                && device.equals(mJustBonded)
                                && AUDIO_SHARING_PROFILES.contains(bluetoothProfile)
                                && isReadyForAudioSharing(cachedDevice, bluetoothProfile)) {
                            Log.d(getLogTag(), "onProfileConnectionStateChanged, lea eligible");
                            dismissConnectingDialog();
                            BluetoothUtils.setTemporaryBondMetadata(device);
                            if (mShouldTriggerShareThenPairFlow) {
                            mHandler.removeMessages(AUTO_DISMISS_MESSAGE_ID);
                                postOnMainThread(() ->
                                        finishFragmentWithResultForAudioSharing(device));
                            } else {
                                mHandler.removeMessages(AUTO_FINISH_MESSAGE_ID);
                                postOnMainThread(() -> finish());
                            }
                            postOnMainThread(() -> finishFragmentWithResultForAudioSharing(device));
                        }
                    } else {
                        postOnMainThread(() -> finish());
@@ -341,16 +330,6 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
        return false;
    }

    private boolean shouldSetTempBondMetadata() {
        return Flags.enableTemporaryBondDevicesUi()
                && BluetoothUtils.isAudioSharingUIAvailable(getContext())
                && BluetoothUtils.isBroadcasting(mLocalManager)
                && mLocalManager != null
                && mLocalManager.getCachedDeviceManager() != null
                && mLocalManager.getProfileManager().getLeAudioBroadcastAssistantProfile() != null
                && !Utils.shouldBlockPairingInAudioSharing(mLocalManager);
    }

    private boolean isReadyForAudioSharing(@NonNull CachedBluetoothDevice cachedDevice,
            int justConnectedProfile) {
        for (int profile : AUDIO_SHARING_PROFILES) {
@@ -405,10 +384,10 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
        });
    }

    private void handleDeviceBondedInAudioSharing(@Nullable BluetoothDevice device) {
    private void handleShareThenPair(@Nullable BluetoothDevice device) {
        var unused = ThreadUtils.postOnBackgroundThread(() -> {
            if (mJustBonded != null) {
                Log.d(getLogTag(), "Skip handleDeviceBondedInAudioSharing, already done");
                Log.d(getLogTag(), "Skip handleShareThenPair, already done");
                return;
            }
            mJustBonded = device;
@@ -417,7 +396,6 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
            String deviceName = TextUtils.isEmpty(aliasName) ? device.getAddress()
                    : aliasName;
            showConnectingDialog(deviceName);
            if (mShouldTriggerShareThenPairFlow) {
            // For share then pair flow, we have strong signal that users wish to pair new
            // device to join sharing.
            // So we wait for AUTO_DISMISS_TIME_THRESHOLD_MS, if we find that the bonded device
@@ -428,28 +406,12 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
                mHandler.postDelayed(() ->
                        postOnMainThread(
                                () -> {
                                        Log.d(getLogTag(),
                                                "Show incompatible dialog when timeout");
                                    Log.d(getLogTag(), "Show incompatible dialog when timeout");
                                    dismissConnectingDialog();
                                        AudioSharingIncompatibleDialogFragment.show(this,
                                                deviceName,
                                    AudioSharingIncompatibleDialogFragment.show(this, deviceName,
                                            () -> finish());
                                }), AUTO_DISMISS_MESSAGE_ID, AUTO_DISMISS_TIME_THRESHOLD_MS);
            }
            } else {
                // For other pairing request during audio sharing with sinks < 2, we wait for
                // AUTO_DISMISS_TIME_THRESHOLD_MS, if we find that the bonded device is lea in
                // onProfileConnectionStateChanged, we finish the activity and set the device as
                // temp bond; otherwise, we just finish the activity.
                if (!mHandler.hasMessages(AUTO_FINISH_MESSAGE_ID)) {
                    mHandler.postDelayed(() ->
                            postOnMainThread(
                                    () -> {
                                        Log.d(getLogTag(), "Finish activity when timeout");
                                        finish();
                                    }), AUTO_FINISH_MESSAGE_ID, AUTO_DISMISS_TIME_THRESHOLD_MS);
                }
            }
        });
    }

+0 −136
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.settings.bluetooth;

import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_PAIR_AND_JOIN_SHARING;
import static com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS;

import static com.google.common.truth.Truth.assertThat;

@@ -37,7 +36,6 @@ import static org.robolectric.Shadows.shadowOf;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
@@ -66,15 +64,9 @@ import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.flags.Flags;

import com.google.common.collect.ImmutableList;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -122,14 +114,6 @@ public class BluetoothDevicePairingDetailBaseTest {
    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private LocalBluetoothManager mLocalManager;
    @Mock
    private CachedBluetoothDeviceManager mDeviceManager;
    @Mock
    private LocalBluetoothProfileManager mProfileManager;
    @Mock
    private LocalBluetoothLeBroadcast mBroadcast;
    @Mock
    private LocalBluetoothLeBroadcastAssistant mAssistant;
    @Mock
    private CachedBluetoothDevice mCachedBluetoothDevice;
    @Mock
    private Drawable mDrawable;
@@ -221,7 +205,6 @@ public class BluetoothDevicePairingDetailBaseTest {
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
        mFragment.mSelectedList.add(mBluetoothDevice);
        setUpFragmentWithShareThenPairIntent(false);
        setUpAudioSharingStates(/* enabled = */ false, /* needSetTempBondMetadata = */ false);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);

        verify(mFragment).finish();
@@ -236,31 +219,6 @@ public class BluetoothDevicePairingDetailBaseTest {
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
        mFragment.mSelectedList.add(mBluetoothDevice);
        setUpFragmentWithShareThenPairIntent(true);
        setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ false);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
        shadowOf(Looper.getMainLooper()).idle();

        ProgressDialogFragment progressDialog = mFragment.mProgressDialog;
        assertThat(progressDialog).isNotNull();
        assertThat(progressDialog.getMessage()).isEqualTo(
                mContext.getString(R.string.progress_dialog_connect_device_content,
                        TEST_DEVICE_ADDRESS));
        assertThat(
                ShadowDialogFragment.isIsShowing(ProgressDialogFragment.class.getName())).isTrue();
        verify(mFragment, never()).finish();

        ShadowDialogFragment.reset();
    }

    @Test
    @Config(shadows = ShadowDialogFragment.class)
    @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
    public void onDeviceBondStateChanged_bonded_pairAfterShare_handle() {
        ShadowDialogFragment.reset();
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
        mFragment.mSelectedList.add(mBluetoothDevice);
        setUpFragmentWithShareThenPairIntent(false);
        setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ true);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
        shadowOf(Looper.getMainLooper()).idle();

@@ -283,7 +241,6 @@ public class BluetoothDevicePairingDetailBaseTest {
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
        mFragment.mSelectedList.add(mBluetoothDevice);
        setUpFragmentWithShareThenPairIntent(false);
        setUpAudioSharingStates(/* enabled = */ false, /* needSetTempBondMetadata = */ false);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);

        verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(any(BluetoothDevice.class),
@@ -297,21 +254,6 @@ public class BluetoothDevicePairingDetailBaseTest {
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
        mFragment.mSelectedList.add(mBluetoothDevice);
        setUpFragmentWithShareThenPairIntent(true);
        setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ false);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);

        verify(mBluetoothAdapter).addOnMetadataChangedListener(eq(mBluetoothDevice),
                any(Executor.class),
                any(BluetoothAdapter.OnMetadataChangedListener.class));
    }

    @Test
    @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
    public void onDeviceBondStateChanged_bonding_pairAfterShare_addListener() {
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
        mFragment.mSelectedList.add(mBluetoothDevice);
        setUpFragmentWithShareThenPairIntent(false);
        setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ true);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);

        verify(mBluetoothAdapter).addOnMetadataChangedListener(eq(mBluetoothDevice),
@@ -337,21 +279,6 @@ public class BluetoothDevicePairingDetailBaseTest {
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
        mFragment.mSelectedList.add(mBluetoothDevice);
        setUpFragmentWithShareThenPairIntent(true);
        setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ false);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE);

        verify(mBluetoothAdapter).removeOnMetadataChangedListener(eq(mBluetoothDevice),
                any(BluetoothAdapter.OnMetadataChangedListener.class));
    }

    @Test
    @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
    public void onDeviceBondStateChanged_unbonded_pairAfterShare_removeListener() {
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
        mFragment.mSelectedList.add(mBluetoothDevice);
        setUpFragmentWithShareThenPairIntent(false);
        setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ true);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE);

@@ -387,7 +314,6 @@ public class BluetoothDevicePairingDetailBaseTest {
        when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
        mFragment.mSelectedList.add(device);
        setUpFragmentWithShareThenPairIntent(true);
        setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ false);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
        shadowOf(Looper.getMainLooper()).idle();

@@ -400,7 +326,6 @@ public class BluetoothDevicePairingDetailBaseTest {
                BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
        shadowOf(Looper.getMainLooper()).idle();

        verify(device).setMetadata(eq(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS), any());
        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
        verify(mFragment.getActivity()).setResult(eq(Activity.RESULT_OK), captor.capture());
        Intent intent = captor.getValue();
@@ -416,35 +341,6 @@ public class BluetoothDevicePairingDetailBaseTest {
        ShadowDialogFragment.reset();
    }

    @Test
    @Config(shadows = ShadowDialogFragment.class)
    @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
    public void
            onProfileConnectionStateChanged_inSelectedListAndConnected_pairAfterShare_handle() {
        ShadowDialogFragment.reset();
        BluetoothDevice device = spy(mBluetoothDevice);
        when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
        mFragment.mSelectedList.add(device);
        setUpFragmentWithShareThenPairIntent(false);
        setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ true);
        mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
        shadowOf(Looper.getMainLooper()).idle();

        when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
        when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
        when(mCachedBluetoothDevice.isConnectedLeAudioBroadcastAssistantDevice()).thenReturn(true);
        when(mCachedBluetoothDevice.isConnectedVolumeControlDevice()).thenReturn(true);

        mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
                BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
        shadowOf(Looper.getMainLooper()).idle();

        verify(device).setMetadata(eq(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS), any());
        verify(mFragment).finish();

        ShadowDialogFragment.reset();
    }

    @Test
    @DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
    public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() {
@@ -537,38 +433,6 @@ public class BluetoothDevicePairingDetailBaseTest {
        mFragment.mShouldTriggerShareThenPairFlow = mFragment.shouldTriggerShareThenPairFlow();
    }

    private void setUpAudioSharingStates(boolean enabled, boolean needSetTempBondMetadata) {
        when(mLocalManager.getProfileManager()).thenReturn(mProfileManager);
        when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
        when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
        when(mLocalManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
        if (!enabled) {
            when(mBroadcast.isEnabled(null)).thenReturn(false);
        } else {
            when(mBroadcast.isEnabled(null)).thenReturn(true);
            when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
            BluetoothDevice device1 = mock(BluetoothDevice.class);
            CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class);
            when(mDeviceManager.findDevice(device1)).thenReturn(cachedDevice1);
            when(cachedDevice1.getGroupId()).thenReturn(1);
            when(cachedDevice1.getDevice()).thenReturn(device1);
            BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
            when(state.getBroadcastId()).thenReturn(1);
            when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(state));
            if (needSetTempBondMetadata) {
                when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(device1));
            } else {
                BluetoothDevice device2 = mock(BluetoothDevice.class);
                CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
                when(mDeviceManager.findDevice(device2)).thenReturn(cachedDevice2);
                when(cachedDevice2.getGroupId()).thenReturn(2);
                when(cachedDevice2.getDevice()).thenReturn(device2);
                when(mAssistant.getAllConnectedDevices()).thenReturn(
                        ImmutableList.of(device1, device2));
            }
        }
    }

    private static class TestBluetoothDevicePairingDetailBase extends
            BluetoothDevicePairingDetailBase {