Merge "[Audiosharing] Set temp bond metadata for just bonded lea buds in sharing" into main

This commit is contained in:
Yiyi Shen
2025-02-10 01:52:41 -08:00
committed by Android (Google) Code Review
4 changed files with 396 additions and 75 deletions

View File

@@ -46,6 +46,7 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HearingAidStatsLogUtils; import com.android.settingslib.bluetooth.HearingAidStatsLogUtils;
import com.android.settingslib.flags.Flags;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@@ -62,6 +63,7 @@ import java.util.concurrent.TimeUnit;
public abstract class BluetoothDevicePairingDetailBase extends DeviceListPreferenceFragment { public abstract class BluetoothDevicePairingDetailBase extends DeviceListPreferenceFragment {
private static final long AUTO_DISMISS_TIME_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(15); 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_DISMISS_MESSAGE_ID = 1001;
private static final int AUTO_FINISH_MESSAGE_ID = 1002;
private static final ImmutableList<Integer> AUDIO_SHARING_PROFILES = ImmutableList.of( private static final ImmutableList<Integer> AUDIO_SHARING_PROFILES = ImmutableList.of(
BluetoothProfile.LE_AUDIO, BluetoothProfile.LE_AUDIO,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, BluetoothProfile.VOLUME_CONTROL); BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, BluetoothProfile.VOLUME_CONTROL);
@@ -77,7 +79,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
@Nullable @Nullable
ProgressDialogFragment mProgressDialog = null; ProgressDialogFragment mProgressDialog = null;
@VisibleForTesting @VisibleForTesting
boolean mShouldTriggerAudioSharingShareThenPairFlow = false; boolean mShouldTriggerShareThenPairFlow = false;
private CopyOnWriteArrayList<BluetoothDevice> mDevicesWithMetadataChangedListener = private CopyOnWriteArrayList<BluetoothDevice> mDevicesWithMetadataChangedListener =
new CopyOnWriteArrayList<>(); new CopyOnWriteArrayList<>();
@@ -89,7 +91,8 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
// onDeviceBondStateChanged(BOND_BONDED), BluetoothDevicePreference's summary has already // onDeviceBondStateChanged(BOND_BONDED), BluetoothDevicePreference's summary has already
// change from "Pairing..." to empty since it listens to metadata changes happens earlier. // change from "Pairing..." to empty since it listens to metadata changes happens earlier.
// //
// In share then pair flow, we have to wait on this page till the device is connected. // 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.
// The BluetoothDevicePreference summary will be blank for seconds between "Pairing..." and // The BluetoothDevicePreference summary will be blank for seconds between "Pairing..." and
// "Connecting..." To help users better understand the process, we listen to metadata change // "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() // as well and show a progress dialog with "Connecting to ...." once BluetoothDevice.getState()
@@ -100,10 +103,11 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
public void onMetadataChanged(@NonNull BluetoothDevice device, int key, public void onMetadataChanged(@NonNull BluetoothDevice device, int key,
@Nullable byte[] value) { @Nullable byte[] value) {
Log.d(getLogTag(), "onMetadataChanged device = " + device + ", key = " + key); Log.d(getLogTag(), "onMetadataChanged device = " + device + ", key = " + key);
if (mShouldTriggerAudioSharingShareThenPairFlow && mProgressDialog == null if ((mShouldTriggerShareThenPairFlow || shouldSetTempBondMetadata())
&& mProgressDialog == null
&& device.getBondState() == BluetoothDevice.BOND_BONDED && device.getBondState() == BluetoothDevice.BOND_BONDED
&& mSelectedList.contains(device)) { && mSelectedList.contains(device)) {
triggerAudioSharingShareThenPairFlow(device); handleDeviceBondedInAudioSharing(device);
// Once device is bonded, remove the listener // Once device is bonded, remove the listener
removeOnMetadataChangedListener(device); removeOnMetadataChangedListener(device);
} }
@@ -133,7 +137,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
return; return;
} }
updateBluetooth(); updateBluetooth();
mShouldTriggerAudioSharingShareThenPairFlow = shouldTriggerAudioSharingShareThenPairFlow(); mShouldTriggerShareThenPairFlow = shouldTriggerShareThenPairFlow();
} }
@Override @Override
@@ -177,11 +181,12 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
@Override @Override
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
boolean shouldSetTempBond = shouldSetTempBondMetadata();
if (bondState == BluetoothDevice.BOND_BONDED) { if (bondState == BluetoothDevice.BOND_BONDED) {
if (cachedDevice != null && mShouldTriggerAudioSharingShareThenPairFlow) { if (cachedDevice != null && (mShouldTriggerShareThenPairFlow || shouldSetTempBond)) {
BluetoothDevice device = cachedDevice.getDevice(); BluetoothDevice device = cachedDevice.getDevice();
if (device != null && mSelectedList.contains(device)) { if (device != null && mSelectedList.contains(device)) {
triggerAudioSharingShareThenPairFlow(device); handleDeviceBondedInAudioSharing(device);
removeOnMetadataChangedListener(device); removeOnMetadataChangedListener(device);
return; return;
} }
@@ -190,7 +195,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
finish(); finish();
return; return;
} else if (bondState == BluetoothDevice.BOND_BONDING) { } else if (bondState == BluetoothDevice.BOND_BONDING) {
if (mShouldTriggerAudioSharingShareThenPairFlow && cachedDevice != null) { if ((mShouldTriggerShareThenPairFlow || shouldSetTempBond) && cachedDevice != null) {
BluetoothDevice device = cachedDevice.getDevice(); BluetoothDevice device = cachedDevice.getDevice();
if (device != null && mSelectedList.contains(device)) { if (device != null && mSelectedList.contains(device)) {
addOnMetadataChangedListener(device); addOnMetadataChangedListener(device);
@@ -203,7 +208,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
pageId); pageId);
HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice); HearingAidStatsLogUtils.setBondEntryForDevice(bondEntry, cachedDevice);
} else if (bondState == BluetoothDevice.BOND_NONE) { } else if (bondState == BluetoothDevice.BOND_NONE) {
if (mShouldTriggerAudioSharingShareThenPairFlow && cachedDevice != null) { if ((mShouldTriggerShareThenPairFlow || shouldSetTempBond) && cachedDevice != null) {
BluetoothDevice device = cachedDevice.getDevice(); BluetoothDevice device = cachedDevice.getDevice();
if (device != null && mSelectedList.contains(device)) { if (device != null && mSelectedList.contains(device)) {
removeOnMetadataChangedListener(device); removeOnMetadataChangedListener(device);
@@ -233,21 +238,29 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
final BluetoothDevice device = cachedDevice.getDevice(); final BluetoothDevice device = cachedDevice.getDevice();
if (device != null if (device != null
&& mSelectedList.contains(device)) { && mSelectedList.contains(device)) {
if (BluetoothUtils.isAudioSharingUIAvailable(getContext())) { var unused = ThreadUtils.postOnBackgroundThread(() -> {
if (mShouldTriggerAudioSharingShareThenPairFlow if (BluetoothUtils.isAudioSharingUIAvailable(getContext())) {
&& state == BluetoothAdapter.STATE_CONNECTED if ((mShouldTriggerShareThenPairFlow || shouldSetTempBondMetadata())
&& device.equals(mJustBonded) && state == BluetoothAdapter.STATE_CONNECTED
&& AUDIO_SHARING_PROFILES.contains(bluetoothProfile) && device.equals(mJustBonded)
&& isReadyForAudioSharing(cachedDevice, bluetoothProfile)) { && AUDIO_SHARING_PROFILES.contains(bluetoothProfile)
Log.d(getLogTag(), && isReadyForAudioSharing(cachedDevice, bluetoothProfile)) {
"onProfileConnectionStateChanged, ready for audio sharing"); Log.d(getLogTag(), "onProfileConnectionStateChanged, lea eligible");
dismissConnectingDialog(); dismissConnectingDialog();
mHandler.removeMessages(AUTO_DISMISS_MESSAGE_ID); BluetoothUtils.setTemporaryBondMetadata(device);
finishFragmentWithResultForAudioSharing(device); if (mShouldTriggerShareThenPairFlow) {
mHandler.removeMessages(AUTO_DISMISS_MESSAGE_ID);
postOnMainThread(() ->
finishFragmentWithResultForAudioSharing(device));
} else {
mHandler.removeMessages(AUTO_FINISH_MESSAGE_ID);
postOnMainThread(() -> finish());
}
}
} else {
postOnMainThread(() -> finish());
} }
} else { });
finish();
}
} else { } else {
onDeviceDeleted(cachedDevice); onDeviceDeleted(cachedDevice);
} }
@@ -314,7 +327,7 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
} }
@VisibleForTesting @VisibleForTesting
boolean shouldTriggerAudioSharingShareThenPairFlow() { boolean shouldTriggerShareThenPairFlow() {
if (BluetoothUtils.isAudioSharingUIAvailable(getContext())) { if (BluetoothUtils.isAudioSharingUIAvailable(getContext())) {
Activity activity = getActivity(); Activity activity = getActivity();
Intent intent = activity == null ? null : activity.getIntent(); Intent intent = activity == null ? null : activity.getIntent();
@@ -328,6 +341,16 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
return false; 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, private boolean isReadyForAudioSharing(@NonNull CachedBluetoothDevice cachedDevice,
int justConnectedProfile) { int justConnectedProfile) {
for (int profile : AUDIO_SHARING_PROFILES) { for (int profile : AUDIO_SHARING_PROFILES) {
@@ -382,11 +405,10 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
}); });
} }
private void triggerAudioSharingShareThenPairFlow( private void handleDeviceBondedInAudioSharing(@Nullable BluetoothDevice device) {
@NonNull BluetoothDevice device) {
var unused = ThreadUtils.postOnBackgroundThread(() -> { var unused = ThreadUtils.postOnBackgroundThread(() -> {
if (mJustBonded != null) { if (mJustBonded != null) {
Log.d(getLogTag(), "Skip triggerAudioSharingShareThenPairFlow, already done"); Log.d(getLogTag(), "Skip handleDeviceBondedInAudioSharing, already done");
return; return;
} }
mJustBonded = device; mJustBonded = device;
@@ -395,17 +417,38 @@ public abstract class BluetoothDevicePairingDetailBase extends DeviceListPrefere
String deviceName = TextUtils.isEmpty(aliasName) ? device.getAddress() String deviceName = TextUtils.isEmpty(aliasName) ? device.getAddress()
: aliasName; : aliasName;
showConnectingDialog(deviceName); showConnectingDialog(deviceName);
// Wait for AUTO_DISMISS_TIME_THRESHOLD_MS and check if the paired device supports audio if (mShouldTriggerShareThenPairFlow) {
// sharing. // For share then pair flow, we have strong signal that users wish to pair new
if (!mHandler.hasMessages(AUTO_DISMISS_MESSAGE_ID)) { // device to join sharing.
mHandler.postDelayed(() -> // So we wait for AUTO_DISMISS_TIME_THRESHOLD_MS, if we find that the bonded device
postOnMainThread( // is lea in onProfileConnectionStateChanged, we finish the activity, set the device
() -> { // as temp bond and auto add source; otherwise, show dialog to notify that the
Log.d(getLogTag(), "Show incompatible dialog when timeout"); // device is incompatible for audio sharing.
dismissConnectingDialog(); if (!mHandler.hasMessages(AUTO_DISMISS_MESSAGE_ID)) {
AudioSharingIncompatibleDialogFragment.show(this, deviceName, mHandler.postDelayed(() ->
() -> finish()); postOnMainThread(
}), AUTO_DISMISS_MESSAGE_ID, AUTO_DISMISS_TIME_THRESHOLD_MS); () -> {
Log.d(getLogTag(),
"Show incompatible dialog when timeout");
dismissConnectingDialog();
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);
}
} }
}); });
} }

View File

@@ -33,6 +33,7 @@ import android.provider.Settings;
import android.util.Log; import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
@@ -42,17 +43,21 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.BluetoothUtils; import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener; import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback; import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.utils.ThreadUtils;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import java.util.stream.Collectors;
/** /**
* Utils is a helper class that contains constants for various * Utils is a helper class that contains constants for various
@@ -293,4 +298,30 @@ public final class Utils {
ThreadUtils.postOnMainThread(runnable); ThreadUtils.postOnMainThread(runnable);
}); });
} }
/**
* Check if need to block pairing during audio sharing
*
* @param localBtManager {@link LocalBluetoothManager}
* @return if need to block pairing during audio sharing
*/
public static boolean shouldBlockPairingInAudioSharing(
@NonNull LocalBluetoothManager localBtManager) {
if (!BluetoothUtils.isBroadcasting(localBtManager)) return false;
LocalBluetoothLeBroadcastAssistant assistant =
localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
CachedBluetoothDeviceManager deviceManager = localBtManager.getCachedDeviceManager();
List<BluetoothDevice> connectedDevices =
assistant == null ? ImmutableList.of() : assistant.getAllConnectedDevices();
// Block the pairing if there is ongoing audio sharing session and
// a) there is already one temp bond sink connected
// or b) there are already two sinks joining the audio sharing
return assistant != null && deviceManager != null
&& (connectedDevices.stream().anyMatch(BluetoothUtils::isTemporaryBondDevice)
|| connectedDevices.stream().filter(
d -> BluetoothUtils.hasActiveLocalBroadcastSourceForBtDevice(d,
localBtManager))
.map(d -> BluetoothUtils.getGroupId(deviceManager.findDevice(d))).collect(
Collectors.toSet()).size() >= 2);
}
} }

View File

@@ -18,6 +18,7 @@ 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_BT_DEVICE_TO_AUTO_ADD_SOURCE;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_PAIR_AND_JOIN_SHARING; 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; import static com.google.common.truth.Truth.assertThat;
@@ -36,6 +37,7 @@ import static org.robolectric.Shadows.shadowOf;
import android.app.Activity; import android.app.Activity;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes; import android.bluetooth.BluetoothStatusCodes;
import android.content.Context; import android.content.Context;
@@ -44,6 +46,8 @@ import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Looper; import android.os.Looper;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Pair; import android.util.Pair;
@@ -62,9 +66,15 @@ import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settingslib.bluetooth.CachedBluetoothDevice; 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.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.flags.Flags; import com.android.settingslib.flags.Flags;
import com.google.common.collect.ImmutableList;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@@ -112,6 +122,14 @@ public class BluetoothDevicePairingDetailBaseTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private LocalBluetoothManager mLocalManager; private LocalBluetoothManager mLocalManager;
@Mock @Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private LocalBluetoothLeBroadcast mBroadcast;
@Mock
private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice; private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock @Mock
private Drawable mDrawable; private Drawable mDrawable;
@@ -197,11 +215,13 @@ public class BluetoothDevicePairingDetailBaseTest {
} }
@Test @Test
public void onDeviceBondStateChanged_bonded_pairAndJoinSharingDisabled_finish() { @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
public void onDeviceBondStateChanged_bonded_notPairInSharing_finish() {
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice); mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(false); setUpFragmentWithShareThenPairIntent(false);
setUpAudioSharingStates(/* enabled = */ false, /* needSetTempBondMetadata = */ false);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED); mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
verify(mFragment).finish(); verify(mFragment).finish();
@@ -209,12 +229,14 @@ public class BluetoothDevicePairingDetailBaseTest {
@Test @Test
@Config(shadows = ShadowDialogFragment.class) @Config(shadows = ShadowDialogFragment.class)
public void onDeviceBondStateChanged_bonded_pairAndJoinSharingEnabled_handle() { @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
public void onDeviceBondStateChanged_bonded_shareThenPair_handle() {
ShadowDialogFragment.reset(); ShadowDialogFragment.reset();
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice); mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true); setUpFragmentWithShareThenPairIntent(true);
setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ false);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED); mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
shadowOf(Looper.getMainLooper()).idle(); shadowOf(Looper.getMainLooper()).idle();
@@ -231,11 +253,37 @@ public class BluetoothDevicePairingDetailBaseTest {
} }
@Test @Test
public void onDeviceBondStateChanged_bonding_pairAndJoinSharingDisabled_doNothing() { @Config(shadows = ShadowDialogFragment.class)
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); @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); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice); mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(false); setUpFragmentWithShareThenPairIntent(false);
setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ true);
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
@EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
@DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
public void onDeviceBondStateChanged_bonding_notPairInSharing_doNothing() {
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithShareThenPairIntent(false);
setUpAudioSharingStates(/* enabled = */ false, /* needSetTempBondMetadata = */ false);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING); mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(any(BluetoothDevice.class), verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(any(BluetoothDevice.class),
@@ -243,11 +291,13 @@ public class BluetoothDevicePairingDetailBaseTest {
} }
@Test @Test
public void onDeviceBondStateChanged_bonding_pairAndJoinSharingEnabled_addListener() { @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
public void onDeviceBondStateChanged_bonding_shareThenPair_addListener() {
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice); mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true); setUpFragmentWithShareThenPairIntent(true);
setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ false);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING); mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
verify(mBluetoothAdapter).addOnMetadataChangedListener(eq(mBluetoothDevice), verify(mBluetoothAdapter).addOnMetadataChangedListener(eq(mBluetoothDevice),
@@ -256,8 +306,22 @@ public class BluetoothDevicePairingDetailBaseTest {
} }
@Test @Test
public void onDeviceBondStateChanged_unbonded_pairAndJoinSharingDisabled_doNothing() { @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 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),
any(Executor.class),
any(BluetoothAdapter.OnMetadataChangedListener.class));
}
@Test
@DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
public void onDeviceBondStateChanged_unbonded_notPairInSharing_doNothing() {
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice); mFragment.mSelectedList.add(mBluetoothDevice);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE); mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE);
@@ -267,11 +331,13 @@ public class BluetoothDevicePairingDetailBaseTest {
} }
@Test @Test
public void onDeviceBondStateChanged_unbonded_pairAndJoinSharingEnabled_removeListener() { @EnableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
public void onDeviceBondStateChanged_unbonded_shareThenPair_removeListener() {
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice); mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true); setUpFragmentWithShareThenPairIntent(true);
setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ false);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING); mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE); mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE);
@@ -280,8 +346,23 @@ public class BluetoothDevicePairingDetailBaseTest {
} }
@Test @Test
public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish() { @EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING); 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);
verify(mBluetoothAdapter).removeOnMetadataChangedListener(eq(mBluetoothDevice),
any(BluetoothAdapter.OnMetadataChangedListener.class));
}
@Test
@DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
public void
onProfileConnectionStateChanged_deviceInSelectedListAndConnected_notInSharing_finish() {
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
mFragment.mSelectedList.add(mBluetoothDevice); mFragment.mSelectedList.add(mBluetoothDevice);
mFragment.mSelectedList.add(device); mFragment.mSelectedList.add(device);
@@ -291,19 +372,22 @@ public class BluetoothDevicePairingDetailBaseTest {
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice, mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP); BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP);
shadowOf(Looper.getMainLooper()).idle();
verify(mFragment).finish(); verify(mFragment).finish();
} }
@Test @Test
@Config(shadows = ShadowDialogFragment.class) @Config(shadows = ShadowDialogFragment.class)
@EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
public void public void
onProfileConnectionStateChanged_deviceInSelectedListAndConnected_pairAndJoinSharing() { onProfileConnectionStateChanged_inSelectedListAndConnected_shareThenPair_handle() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
ShadowDialogFragment.reset(); ShadowDialogFragment.reset();
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); BluetoothDevice device = spy(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice); when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
setUpFragmentWithPairAndJoinSharingIntent(true); mFragment.mSelectedList.add(device);
setUpFragmentWithShareThenPairIntent(true);
setUpAudioSharingStates(/* enabled = */ true, /* needSetTempBondMetadata = */ false);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED); mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
shadowOf(Looper.getMainLooper()).idle(); shadowOf(Looper.getMainLooper()).idle();
@@ -316,6 +400,7 @@ public class BluetoothDevicePairingDetailBaseTest {
BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
shadowOf(Looper.getMainLooper()).idle(); shadowOf(Looper.getMainLooper()).idle();
verify(device).setMetadata(eq(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS), any());
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mFragment.getActivity()).setResult(eq(Activity.RESULT_OK), captor.capture()); verify(mFragment.getActivity()).setResult(eq(Activity.RESULT_OK), captor.capture());
Intent intent = captor.getValue(); Intent intent = captor.getValue();
@@ -325,15 +410,44 @@ public class BluetoothDevicePairingDetailBaseTest {
BluetoothDevice.class) BluetoothDevice.class)
: null; : null;
assertThat(btDevice).isNotNull(); assertThat(btDevice).isNotNull();
assertThat(btDevice).isEqualTo(mBluetoothDevice); assertThat(btDevice).isEqualTo(device);
verify(mFragment).finish(); verify(mFragment).finish();
ShadowDialogFragment.reset(); ShadowDialogFragment.reset();
} }
@Test @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() { public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
mFragment.mSelectedList.add(device); mFragment.mSelectedList.add(device);
@@ -347,8 +461,8 @@ public class BluetoothDevicePairingDetailBaseTest {
} }
@Test @Test
@DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() { public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B); final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
mFragment.mSelectedList.add(mBluetoothDevice); mFragment.mSelectedList.add(mBluetoothDevice);
mFragment.mSelectedList.add(device); mFragment.mSelectedList.add(device);
@@ -363,8 +477,8 @@ public class BluetoothDevicePairingDetailBaseTest {
} }
@Test @Test
@DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() { public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevicePreference preference = final BluetoothDevicePreference preference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
true, BluetoothDevicePreference.SortType.TYPE_FIFO); true, BluetoothDevicePreference.SortType.TYPE_FIFO);
@@ -381,8 +495,8 @@ public class BluetoothDevicePairingDetailBaseTest {
} }
@Test @Test
@DisableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING, Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI})
public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() { public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class); final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
final BluetoothDevicePreference preference = final BluetoothDevicePreference preference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
@@ -404,9 +518,9 @@ public class BluetoothDevicePairingDetailBaseTest {
// not crash // not crash
} }
private void setUpFragmentWithPairAndJoinSharingIntent(boolean enablePairAndJoinSharing) { private void setUpFragmentWithShareThenPairIntent(boolean enableShareThenPair) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putBoolean(EXTRA_PAIR_AND_JOIN_SHARING, enablePairAndJoinSharing); args.putBoolean(EXTRA_PAIR_AND_JOIN_SHARING, enableShareThenPair);
Intent intent = new Intent(); Intent intent = new Intent();
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class)); FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class));
@@ -420,8 +534,39 @@ public class BluetoothDevicePairingDetailBaseTest {
Lifecycle lifecycle = mock(Lifecycle.class); Lifecycle lifecycle = mock(Lifecycle.class);
when(lifecycle.getCurrentState()).thenReturn(Lifecycle.State.RESUMED); when(lifecycle.getCurrentState()).thenReturn(Lifecycle.State.RESUMED);
doReturn(lifecycle).when(mFragment).getLifecycle(); doReturn(lifecycle).when(mFragment).getLifecycle();
mFragment.mShouldTriggerAudioSharingShareThenPairFlow = mFragment.mShouldTriggerShareThenPairFlow = mFragment.shouldTriggerShareThenPairFlow();
mFragment.shouldTriggerAudioSharingShareThenPairFlow(); }
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 private static class TestBluetoothDevicePairingDetailBase extends

View File

@@ -15,6 +15,9 @@
*/ */
package com.android.settings.bluetooth; package com.android.settings.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@@ -22,34 +25,72 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context; import android.content.Context;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
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.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.google.common.collect.ImmutableList;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Answers; import org.mockito.Answers;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothUtils.class})
public class UtilsTest { public class UtilsTest {
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
private static final String TEMP_BOND_METADATA =
"<TEMP_BOND_TYPE>le_audio_sharing</TEMP_BOND_TYPE>";
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock(answer = Answers.RETURNS_DEEP_STUBS) @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext; private Context mContext;
@Mock
private LocalBluetoothManager mLocalBtManager;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
private LocalBluetoothLeBroadcast mBroadcast;
@Mock
private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
private MetricsFeatureProvider mMetricsFeatureProvider; private MetricsFeatureProvider mMetricsFeatureProvider;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this);
mMetricsFeatureProvider = FakeFeatureFactory.setupForTest().getMetricsFeatureProvider(); mMetricsFeatureProvider = FakeFeatureFactory.setupForTest().getMetricsFeatureProvider();
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
mLocalBtManager = Utils.getLocalBtManager(mContext);
when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager);
when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
}
@After
public void tearDown() {
ShadowBluetoothUtils.reset();
} }
@Test @Test
@@ -60,4 +101,65 @@ public class UtilsTest {
verify(mMetricsFeatureProvider).visible(eq(mContext), anyInt(), verify(mMetricsFeatureProvider).visible(eq(mContext), anyInt(),
eq(MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR), anyInt()); eq(MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR), anyInt());
} }
@Test
public void shouldBlockPairingInAudioSharing_broadcastOff_returnFalse() {
when(mBroadcast.isEnabled(null)).thenReturn(false);
assertThat(Utils.shouldBlockPairingInAudioSharing(mLocalBtManager)).isFalse();
}
@Test
public void shouldBlockPairingInAudioSharing_singlePermanentBondSinkInSharing_returnFalse() {
when(mBroadcast.isEnabled(null)).thenReturn(true);
when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
BluetoothDevice device = mock(BluetoothDevice.class);
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
when(mDeviceManager.findDevice(device)).thenReturn(cachedDevice);
when(cachedDevice.getGroupId()).thenReturn(1);
when(cachedDevice.getDevice()).thenReturn(device);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(device));
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
when(state.getBroadcastId()).thenReturn(1);
when(mAssistant.getAllSources(device)).thenReturn(ImmutableList.of(state));
assertThat(Utils.shouldBlockPairingInAudioSharing(mLocalBtManager)).isFalse();
}
@Test
public void shouldBlockPairingInAudioSharing_singleTempBondSinkInSharing_returnTrue() {
when(mBroadcast.isEnabled(null)).thenReturn(true);
when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
BluetoothDevice device = mock(BluetoothDevice.class);
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
when(mDeviceManager.findDevice(device)).thenReturn(cachedDevice);
when(cachedDevice.getGroupId()).thenReturn(1);
when(cachedDevice.getDevice()).thenReturn(device);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(device));
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
when(state.getBroadcastId()).thenReturn(1);
when(mAssistant.getAllSources(device)).thenReturn(ImmutableList.of(state));
when(device.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
.thenReturn(TEMP_BOND_METADATA.getBytes());
assertThat(Utils.shouldBlockPairingInAudioSharing(mLocalBtManager)).isTrue();
}
@Test
public void shouldBlockPairingInAudioSharing_twoSinksInSharing_returnTrue() {
when(mBroadcast.isEnabled(null)).thenReturn(true);
when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
BluetoothDevice device1 = mock(BluetoothDevice.class);
BluetoothDevice device2 = mock(BluetoothDevice.class);
CachedBluetoothDevice cachedDevice1 = mock(CachedBluetoothDevice.class);
CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
when(mDeviceManager.findDevice(device1)).thenReturn(cachedDevice1);
when(mDeviceManager.findDevice(device2)).thenReturn(cachedDevice2);
when(cachedDevice1.getGroupId()).thenReturn(1);
when(cachedDevice2.getGroupId()).thenReturn(2);
when(cachedDevice1.getDevice()).thenReturn(device1);
when(cachedDevice2.getDevice()).thenReturn(device2);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(device1, device2));
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
when(state.getBroadcastId()).thenReturn(1);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(state));
assertThat(Utils.shouldBlockPairingInAudioSharing(mLocalBtManager)).isTrue();
}
} }