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

Commit 7edad1c8 authored by Yiyi Shen's avatar Yiyi Shen
Browse files

[Audiosharing] Returns BluetoothDevice when fetchConnectedDevicesByGroupId

CachedBluetoothDevice can change its member variable mDevice, so we can
not rely on the CachedBluetoothDevice#getDevice when we add source to
it. This change will return BluetoothDevice instead of
CachedBluetoothDevice when fetchConnectedDevicesByGroupId, so that we
won't add source to unintended BluetoothDevice via
CachedBluetoothDevice#getDevice.

Fix: 350877510
Test: atest
Flag: com.android.settingslib.flags.enable_le_audio_sharing
Change-Id: I4ee41b8f1449e7176f0a05a7dd4e59034c161824
parent b6e0cd99
Loading
Loading
Loading
Loading
+8 −7
Original line number Original line Diff line number Diff line
@@ -66,7 +66,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/** PreferenceController to control the dialog to choose the active device for calls and alarms */
/** PreferenceController to control the dialog to choose the active device for calls and alarms */
public class AudioSharingCallAudioPreferenceController extends AudioSharingBasePreferenceController
public class AudioSharingCallAudioPreferenceController extends AudioSharingBasePreferenceController
        implements BluetoothCallback {
        implements BluetoothCallback {
    private static final String TAG = "CallsAndAlarmsPreferenceController";
    private static final String TAG = "CallAudioPrefController";
    private static final String PREF_KEY = "calls_and_alarms";
    private static final String PREF_KEY = "calls_and_alarms";


    @VisibleForTesting
    @VisibleForTesting
@@ -85,7 +85,7 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP
    private final ContentObserver mSettingsObserver;
    private final ContentObserver mSettingsObserver;
    private final MetricsFeatureProvider mMetricsFeatureProvider;
    private final MetricsFeatureProvider mMetricsFeatureProvider;
    @Nullable private Fragment mFragment;
    @Nullable private Fragment mFragment;
    Map<Integer, List<CachedBluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
    Map<Integer, List<BluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
    private List<AudioSharingDeviceItem> mDeviceItemsInSharingSession = new ArrayList<>();
    private List<AudioSharingDeviceItem> mDeviceItemsInSharingSession = new ArrayList<>();
    private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false);
    private final AtomicBoolean mCallbacksRegistered = new AtomicBoolean(false);


@@ -210,11 +210,12 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP
                                                    "Skip set fallback active device: unchanged");
                                                    "Skip set fallback active device: unchanged");
                                            return;
                                            return;
                                        }
                                        }
                                        List<CachedBluetoothDevice> devices =
                                        List<BluetoothDevice> devices =
                                                mGroupedConnectedDevices.getOrDefault(
                                                mGroupedConnectedDevices.getOrDefault(
                                                        item.getGroupId(), ImmutableList.of());
                                                        item.getGroupId(), ImmutableList.of());
                                        CachedBluetoothDevice lead =
                                        CachedBluetoothDevice lead =
                                                AudioSharingUtils.getLeadDevice(devices);
                                                AudioSharingUtils.getLeadDevice(
                                                        mCacheManager, devices);
                                        if (lead != null) {
                                        if (lead != null) {
                                            Log.d(
                                            Log.d(
                                                    TAG,
                                                    TAG,
@@ -347,8 +348,8 @@ public class AudioSharingCallAudioPreferenceController extends AudioSharingBaseP
     */
     */
    private void updateSummary() {
    private void updateSummary() {
        updateDeviceItemsInSharingSession();
        updateDeviceItemsInSharingSession();
        int fallbackActiveGroupId = BluetoothUtils.getPrimaryGroupIdForBroadcast(
        int fallbackActiveGroupId =
                mContext.getContentResolver());
                BluetoothUtils.getPrimaryGroupIdForBroadcast(mContext.getContentResolver());
        if (fallbackActiveGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
        if (fallbackActiveGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
            for (AudioSharingDeviceItem item : mDeviceItemsInSharingSession) {
            for (AudioSharingDeviceItem item : mDeviceItemsInSharingSession) {
                if (item.getGroupId() == fallbackActiveGroupId) {
                if (item.getGroupId() == fallbackActiveGroupId) {
+25 −25
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package com.android.settings.connecteddevice.audiosharing;
package com.android.settings.connecteddevice.audiosharing;


import static java.util.stream.Collectors.toList;

import android.app.settings.SettingsEnums;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothDevice;
@@ -38,6 +40,7 @@ import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
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.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -49,14 +52,14 @@ import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.Executor;


public class AudioSharingDialogHandler {
public class AudioSharingDialogHandler {
    private static final String TAG = "AudioSharingDialogHandler";
    private static final String TAG = "AudioSharingDlgHandler";
    private final Context mContext;
    private final Context mContext;
    private final Fragment mHostFragment;
    private final Fragment mHostFragment;
    @Nullable private final LocalBluetoothManager mLocalBtManager;
    @Nullable private final LocalBluetoothManager mLocalBtManager;
    @Nullable private final CachedBluetoothDeviceManager mDeviceManager;
    @Nullable private final LocalBluetoothLeBroadcast mBroadcast;
    @Nullable private final LocalBluetoothLeBroadcast mBroadcast;
    @Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant;
    @Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant;
    private final MetricsFeatureProvider mMetricsFeatureProvider;
    private final MetricsFeatureProvider mMetricsFeatureProvider;
@@ -163,6 +166,7 @@ public class AudioSharingDialogHandler {
        mContext = context;
        mContext = context;
        mHostFragment = fragment;
        mHostFragment = fragment;
        mLocalBtManager = Utils.getLocalBluetoothManager(context);
        mLocalBtManager = Utils.getLocalBluetoothManager(context);
        mDeviceManager = mLocalBtManager != null ? mLocalBtManager.getCachedDeviceManager() : null;
        mBroadcast =
        mBroadcast =
                mLocalBtManager != null
                mLocalBtManager != null
                        ? mLocalBtManager.getProfileManager().getLeAudioBroadcastProfile()
                        ? mLocalBtManager.getProfileManager().getLeAudioBroadcastProfile()
@@ -212,7 +216,7 @@ public class AudioSharingDialogHandler {
        if (isBroadcasting) {
        if (isBroadcasting) {
            // Show stop audio sharing dialog when an ineligible (non LE audio) remote device
            // Show stop audio sharing dialog when an ineligible (non LE audio) remote device
            // connected during a sharing session.
            // connected during a sharing session.
            Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
            Map<Integer, List<BluetoothDevice>> groupedDevices =
                    AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
                    AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
            List<AudioSharingDeviceItem> deviceItemsInSharingSession =
            List<AudioSharingDeviceItem> deviceItemsInSharingSession =
                    AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem(
                    AudioSharingUtils.buildOrderedConnectedLeadAudioSharingDeviceItem(
@@ -256,19 +260,19 @@ public class AudioSharingDialogHandler {
            @NonNull CachedBluetoothDevice cachedDevice,
            @NonNull CachedBluetoothDevice cachedDevice,
            boolean isBroadcasting,
            boolean isBroadcasting,
            boolean userTriggered) {
            boolean userTriggered) {
        Map<Integer, List<CachedBluetoothDevice>> groupedDevices =
        Map<Integer, List<BluetoothDevice>> groupedDevices =
                AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
                AudioSharingUtils.fetchConnectedDevicesByGroupId(mLocalBtManager);
        BluetoothDevice btDevice = cachedDevice.getDevice();
        BluetoothDevice btDevice = cachedDevice.getDevice();
        String deviceAddress = btDevice == null ? "" : btDevice.getAnonymizedAddress();
        String deviceAddress = btDevice == null ? "" : btDevice.getAnonymizedAddress();
        int groupId = BluetoothUtils.getGroupId(cachedDevice);
        if (isBroadcasting) {
        if (isBroadcasting) {
            // If another device within the same is already in the sharing session, add source to
            // If another device within the same is already in the sharing session, add source to
            // the device automatically.
            // the device automatically.
            int groupId = BluetoothUtils.getGroupId(cachedDevice);
            if (groupedDevices.containsKey(groupId)
            if (groupedDevices.containsKey(groupId)
                    && groupedDevices.get(groupId).stream()
                    && groupedDevices.get(groupId).stream()
                            .anyMatch(
                            .anyMatch(
                                    device ->
                                    device ->
                                            BluetoothUtils.hasConnectedBroadcastSource(
                                            BluetoothUtils.hasConnectedBroadcastSourceForBtDevice(
                                                    device, mLocalBtManager))) {
                                                    device, mLocalBtManager))) {
                Log.d(
                Log.d(
                        TAG,
                        TAG,
@@ -352,14 +356,17 @@ public class AudioSharingDialogHandler {
        } else {
        } else {
            // Build a list of AudioSharingDeviceItem for connected devices other than cachedDevice.
            // Build a list of AudioSharingDeviceItem for connected devices other than cachedDevice.
            List<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
            List<AudioSharingDeviceItem> deviceItems = new ArrayList<>();
            for (List<CachedBluetoothDevice> devices : groupedDevices.values()) {
            for (Map.Entry<Integer, List<BluetoothDevice>> entry : groupedDevices.entrySet()) {
                if (entry.getKey() == groupId) continue;
                // Use random device in the group within the sharing session to represent the group.
                // Use random device in the group within the sharing session to represent the group.
                CachedBluetoothDevice device = devices.get(0);
                for (BluetoothDevice device : entry.getValue()) {
                if (BluetoothUtils.getGroupId(device)
                    CachedBluetoothDevice cDevice =
                        == BluetoothUtils.getGroupId(cachedDevice)) {
                            mDeviceManager != null ? mDeviceManager.findDevice(device) : null;
                    continue;
                    if (cDevice != null) {
                        deviceItems.add(AudioSharingUtils.buildAudioSharingDeviceItem(cDevice));
                        break;
                    }
                }
                }
                deviceItems.add(AudioSharingUtils.buildAudioSharingDeviceItem(device));
            }
            }
            // Show audio sharing join dialog when the second eligible (LE audio) remote
            // Show audio sharing join dialog when the second eligible (LE audio) remote
            // device connect and no sharing session.
            // device connect and no sharing session.
@@ -368,13 +375,10 @@ public class AudioSharingDialogHandler {
                        new AudioSharingJoinDialogFragment.DialogEventListener() {
                        new AudioSharingJoinDialogFragment.DialogEventListener() {
                            @Override
                            @Override
                            public void onShareClick() {
                            public void onShareClick() {
                                mTargetSinks = new ArrayList<>();
                                mTargetSinks =
                                for (List<CachedBluetoothDevice> devices :
                                        groupedDevices.values().stream()
                                        groupedDevices.values()) {
                                                .flatMap(items -> items.stream())
                                    for (CachedBluetoothDevice device : devices) {
                                                .collect(toList());
                                        mTargetSinks.add(device.getDevice());
                                    }
                                }
                                Log.d(TAG, "Start broadcast with sinks = " + mTargetSinks.size());
                                Log.d(TAG, "Start broadcast with sinks = " + mTargetSinks.size());
                                if (mBroadcast != null) {
                                if (mBroadcast != null) {
                                    mBroadcast.startPrivateBroadcast();
                                    mBroadcast.startPrivateBroadcast();
@@ -493,7 +497,7 @@ public class AudioSharingDialogHandler {
    }
    }


    private void removeSourceForGroup(
    private void removeSourceForGroup(
            int groupId, Map<Integer, List<CachedBluetoothDevice>> groupedDevices) {
            int groupId, Map<Integer, List<BluetoothDevice>> groupedDevices) {
        if (mAssistant == null) {
        if (mAssistant == null) {
            Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
            Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
            return;
            return;
@@ -503,8 +507,6 @@ public class AudioSharingDialogHandler {
            return;
            return;
        }
        }
        groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream()
        groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream()
                .map(CachedBluetoothDevice::getDevice)
                .filter(Objects::nonNull)
                .forEach(
                .forEach(
                        device -> {
                        device -> {
                            for (BluetoothLeBroadcastReceiveState source :
                            for (BluetoothLeBroadcastReceiveState source :
@@ -515,7 +517,7 @@ public class AudioSharingDialogHandler {
    }
    }


    private void addSourceForGroup(
    private void addSourceForGroup(
            int groupId, Map<Integer, List<CachedBluetoothDevice>> groupedDevices) {
            int groupId, Map<Integer, List<BluetoothDevice>> groupedDevices) {
        if (mBroadcast == null || mAssistant == null) {
        if (mBroadcast == null || mAssistant == null) {
            Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
            Log.d(TAG, "Fail to add source due to null profiles, group = " + groupId);
            return;
            return;
@@ -525,8 +527,6 @@ public class AudioSharingDialogHandler {
            return;
            return;
        }
        }
        groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream()
        groupedDevices.getOrDefault(groupId, ImmutableList.of()).stream()
                .map(CachedBluetoothDevice::getDevice)
                .filter(Objects::nonNull)
                .forEach(
                .forEach(
                        device ->
                        device ->
                                mAssistant.addSource(
                                mAssistant.addSource(
+15 −24
Original line number Original line Diff line number Diff line
@@ -48,7 +48,6 @@ import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -63,17 +62,15 @@ import java.util.HashMap;
import java.util.List;
import java.util.List;
import java.util.Locale;
import java.util.Locale;
import java.util.Map;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;


public class AudioSharingSwitchBarController extends BasePreferenceController
public class AudioSharingSwitchBarController extends BasePreferenceController
        implements DefaultLifecycleObserver,
        implements DefaultLifecycleObserver,
        OnCheckedChangeListener,
        OnCheckedChangeListener,
        LocalBluetoothProfileManager.ServiceListener {
        LocalBluetoothProfileManager.ServiceListener {
    private static final String TAG = "AudioSharingSwitchBarCtl";
    private static final String TAG = "AudioSharingSwitchCtlr";
    private static final String PREF_KEY = "audio_sharing_main_switch";
    private static final String PREF_KEY = "audio_sharing_main_switch";


    interface OnAudioSharingStateChangedListener {
    interface OnAudioSharingStateChangedListener {
@@ -103,7 +100,7 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
    private final Executor mExecutor;
    private final Executor mExecutor;
    private final MetricsFeatureProvider mMetricsFeatureProvider;
    private final MetricsFeatureProvider mMetricsFeatureProvider;
    private final OnAudioSharingStateChangedListener mListener;
    private final OnAudioSharingStateChangedListener mListener;
    private Map<Integer, List<CachedBluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
    private Map<Integer, List<BluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
    private List<BluetoothDevice> mTargetActiveSinks = new ArrayList<>();
    private List<BluetoothDevice> mTargetActiveSinks = new ArrayList<>();
    private List<AudioSharingDeviceItem> mDeviceItemsForSharing = new ArrayList<>();
    private List<AudioSharingDeviceItem> mDeviceItemsForSharing = new ArrayList<>();
    @VisibleForTesting IntentFilter mIntentFilter;
    @VisibleForTesting IntentFilter mIntentFilter;
@@ -454,13 +451,11 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
        mDeviceItemsForSharing = new ArrayList<>(deviceItems);
        mDeviceItemsForSharing = new ArrayList<>(deviceItems);
        mTargetActiveSinks = new ArrayList<>();
        mTargetActiveSinks = new ArrayList<>();
        if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) {
        if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) {
            for (CachedBluetoothDevice device :
                    mGroupedConnectedDevices.getOrDefault(
                            deviceItems.get(0).getGroupId(), ImmutableList.of())) {
            // If active device exists for audio sharing, share to it
            // If active device exists for audio sharing, share to it
            // automatically once the broadcast is started.
            // automatically once the broadcast is started.
                mTargetActiveSinks.add(device.getDevice());
            mTargetActiveSinks =
            }
                    mGroupedConnectedDevices.getOrDefault(
                            deviceItems.get(0).getGroupId(), ImmutableList.of());
            mDeviceItemsForSharing.remove(0);
            mDeviceItemsForSharing.remove(0);
        }
        }
        if (mBroadcast != null) {
        if (mBroadcast != null) {
@@ -541,12 +536,8 @@ public class AudioSharingSwitchBarController extends BasePreferenceController
                    @Override
                    @Override
                    public void onItemClick(@NonNull AudioSharingDeviceItem item) {
                    public void onItemClick(@NonNull AudioSharingDeviceItem item) {
                        AudioSharingUtils.addSourceToTargetSinks(
                        AudioSharingUtils.addSourceToTargetSinks(
                                mGroupedConnectedDevices
                                mGroupedConnectedDevices.getOrDefault(
                                        .getOrDefault(item.getGroupId(), ImmutableList.of())
                                        item.getGroupId(), ImmutableList.of()),
                                        .stream()
                                        .map(CachedBluetoothDevice::getDevice)
                                        .filter(Objects::nonNull)
                                        .collect(Collectors.toList()),
                                mBtManager);
                                mBtManager);
                        mGroupedConnectedDevices.clear();
                        mGroupedConnectedDevices.clear();
                        mDeviceItemsForSharing.clear();
                        mDeviceItemsForSharing.clear();
+59 −41
Original line number Original line Diff line number Diff line
@@ -22,6 +22,8 @@ import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtil
import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED;


import static java.util.stream.Collectors.toList;

import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastMetadata;
@@ -44,10 +46,11 @@ import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.bluetooth.VolumeControlProfile;
import com.android.settingslib.bluetooth.VolumeControlProfile;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashMap;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.Objects;


public class AudioSharingUtils {
public class AudioSharingUtils {
    private static final String TAG = "AudioSharingUtils";
    private static final String TAG = "AudioSharingUtils";
@@ -62,15 +65,15 @@ public class AudioSharingUtils {
    }
    }


    /**
    /**
     * Fetch {@link CachedBluetoothDevice}s connected to the broadcast assistant. The devices are
     * Fetch {@link BluetoothDevice}s connected to the broadcast assistant. The devices are grouped
     * grouped by CSIP group id.
     * by CSIP group id.
     *
     *
     * @param localBtManager The BT manager to provide BT functions.
     * @param localBtManager The BT manager to provide BT functions.
     * @return A map of connected devices grouped by CSIP group id.
     * @return A map of connected devices grouped by CSIP group id.
     */
     */
    public static Map<Integer, List<CachedBluetoothDevice>> fetchConnectedDevicesByGroupId(
    public static Map<Integer, List<BluetoothDevice>> fetchConnectedDevicesByGroupId(
            @Nullable LocalBluetoothManager localBtManager) {
            @Nullable LocalBluetoothManager localBtManager) {
        Map<Integer, List<CachedBluetoothDevice>> groupedDevices = new HashMap<>();
        Map<Integer, List<BluetoothDevice>> groupedDevices = new HashMap<>();
        if (localBtManager == null) {
        if (localBtManager == null) {
            Log.d(TAG, "Skip fetchConnectedDevicesByGroupId due to bt manager is null");
            Log.d(TAG, "Skip fetchConnectedDevicesByGroupId due to bt manager is null");
            return groupedDevices;
            return groupedDevices;
@@ -99,7 +102,7 @@ public class AudioSharingUtils {
            if (!groupedDevices.containsKey(groupId)) {
            if (!groupedDevices.containsKey(groupId)) {
                groupedDevices.put(groupId, new ArrayList<>());
                groupedDevices.put(groupId, new ArrayList<>());
            }
            }
            groupedDevices.get(groupId).add(cachedDevice);
            groupedDevices.get(groupId).add(device);
        }
        }
        if (DEBUG) {
        if (DEBUG) {
            Log.d(TAG, "fetchConnectedDevicesByGroupId: " + groupedDevices);
            Log.d(TAG, "fetchConnectedDevicesByGroupId: " + groupedDevices);
@@ -122,11 +125,16 @@ public class AudioSharingUtils {
     */
     */
    public static List<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
    public static List<CachedBluetoothDevice> buildOrderedConnectedLeadDevices(
            @Nullable LocalBluetoothManager localBtManager,
            @Nullable LocalBluetoothManager localBtManager,
            Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
            Map<Integer, List<BluetoothDevice>> groupedConnectedDevices,
            boolean filterByInSharing) {
            boolean filterByInSharing) {
        List<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
        List<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
        for (List<CachedBluetoothDevice> devices : groupedConnectedDevices.values()) {
        if (localBtManager == null) {
            CachedBluetoothDevice leadDevice = getLeadDevice(devices);
            Log.d(TAG, "Skip buildOrderedConnectedLeadDevices due to bt manager is null");
            return orderedDevices;
        }
        CachedBluetoothDeviceManager deviceManager = localBtManager.getCachedDeviceManager();
        for (List<BluetoothDevice> devices : groupedConnectedDevices.values()) {
            CachedBluetoothDevice leadDevice = getLeadDevice(deviceManager, devices);
            if (leadDevice == null) {
            if (leadDevice == null) {
                Log.d(TAG, "Skip due to no lead device");
                Log.d(TAG, "Skip due to no lead device");
                continue;
                continue;
@@ -141,52 +149,39 @@ public class AudioSharingUtils {
            }
            }
            orderedDevices.add(leadDevice);
            orderedDevices.add(leadDevice);
        }
        }
        orderedDevices.sort(
        orderedDevices.sort(sCachedDeviceComparator);
                (CachedBluetoothDevice d1, CachedBluetoothDevice d2) -> {
                    // Active above not inactive
                    int comparison =
                            (isActiveLeAudioDevice(d2) ? 1 : 0)
                                    - (isActiveLeAudioDevice(d1) ? 1 : 0);
                    if (comparison != 0) return comparison;
                    // Bonded above not bonded
                    comparison =
                            (d2.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0)
                                    - (d1.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
                    if (comparison != 0) return comparison;
                    // Bond timestamp available above unavailable
                    comparison =
                            (d2.getBondTimestamp() != null ? 1 : 0)
                                    - (d1.getBondTimestamp() != null ? 1 : 0);
                    if (comparison != 0) return comparison;
                    // Order by bond timestamp if it is available
                    // Otherwise order by device name
                    return d1.getBondTimestamp() != null
                            ? d1.getBondTimestamp().compareTo(d2.getBondTimestamp())
                            : d1.getName().compareTo(d2.getName());
                });
        return orderedDevices;
        return orderedDevices;
    }
    }


    /**
    /**
     * Get the lead device from a list of devices with same group id.
     * Get the lead device from a list of devices with same group id.
     *
     *
     * @param deviceManager CachedBluetoothDeviceManager
     * @param devices A list of devices with same group id.
     * @param devices A list of devices with same group id.
     * @return The lead device
     * @return The lead device
     */
     */
    @Nullable
    @Nullable
    public static CachedBluetoothDevice getLeadDevice(
    public static CachedBluetoothDevice getLeadDevice(
            @NonNull List<CachedBluetoothDevice> devices) {
            @Nullable CachedBluetoothDeviceManager deviceManager,
        if (devices.isEmpty()) return null;
            @NonNull List<BluetoothDevice> devices) {
        for (CachedBluetoothDevice device : devices) {
        if (deviceManager == null || devices.isEmpty()) return null;
            if (!device.getMemberDevice().isEmpty()) {
        List<CachedBluetoothDevice> cachedDevices =
                return device;
                devices.stream()
            }
                        .map(device -> deviceManager.findDevice(device))
        }
                        .filter(Objects::nonNull)
        CachedBluetoothDevice leadDevice = devices.get(0);
                        .collect(toList());
        for (CachedBluetoothDevice cachedDevice : cachedDevices) {
            if (!cachedDevice.getMemberDevice().isEmpty()) {
                return cachedDevice;
            }
        }
        CachedBluetoothDevice leadDevice = cachedDevices.isEmpty() ? null : cachedDevices.get(0);
        Log.d(
        Log.d(
                TAG,
                TAG,
                "No lead device in the group, pick arbitrary device as the lead: "
                "No lead device in the group, pick arbitrary device as the lead: "
                        + leadDevice.getDevice().getAnonymizedAddress());
                        + (leadDevice == null
                                ? "null"
                                : leadDevice.getDevice().getAnonymizedAddress()));
        return leadDevice;
        return leadDevice;
    }
    }


@@ -206,13 +201,13 @@ public class AudioSharingUtils {
    @NonNull
    @NonNull
    public static List<AudioSharingDeviceItem> buildOrderedConnectedLeadAudioSharingDeviceItem(
    public static List<AudioSharingDeviceItem> buildOrderedConnectedLeadAudioSharingDeviceItem(
            @Nullable LocalBluetoothManager localBtManager,
            @Nullable LocalBluetoothManager localBtManager,
            Map<Integer, List<CachedBluetoothDevice>> groupedConnectedDevices,
            Map<Integer, List<BluetoothDevice>> groupedConnectedDevices,
            boolean filterByInSharing) {
            boolean filterByInSharing) {
        return buildOrderedConnectedLeadDevices(
        return buildOrderedConnectedLeadDevices(
                        localBtManager, groupedConnectedDevices, filterByInSharing)
                        localBtManager, groupedConnectedDevices, filterByInSharing)
                .stream()
                .stream()
                .map(AudioSharingUtils::buildAudioSharingDeviceItem)
                .map(AudioSharingUtils::buildAudioSharingDeviceItem)
                .collect(Collectors.toList());
                .collect(toList());
    }
    }


    /** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
    /** Build {@link AudioSharingDeviceItem} from {@link CachedBluetoothDevice}. */
@@ -361,4 +356,27 @@ public class AudioSharingUtils {
            Pair.create(METRIC_KEY_CANDIDATE_DEVICE_COUNT.ordinal(), candidateDeviceCount)
            Pair.create(METRIC_KEY_CANDIDATE_DEVICE_COUNT.ordinal(), candidateDeviceCount)
        };
        };
    }
    }

    private static final Comparator<CachedBluetoothDevice> sCachedDeviceComparator =
            (CachedBluetoothDevice d1, CachedBluetoothDevice d2) -> {
                // Active above not inactive
                int comparison =
                        (isActiveLeAudioDevice(d2) ? 1 : 0) - (isActiveLeAudioDevice(d1) ? 1 : 0);
                if (comparison != 0) return comparison;
                // Bonded above not bonded
                comparison =
                        (d2.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0)
                                - (d1.getBondState() == BluetoothDevice.BOND_BONDED ? 1 : 0);
                if (comparison != 0) return comparison;
                // Bond timestamp available above unavailable
                comparison =
                        (d2.getBondTimestamp() != null ? 1 : 0)
                                - (d1.getBondTimestamp() != null ? 1 : 0);
                if (comparison != 0) return comparison;
                // Order by bond timestamp if it is available
                // Otherwise order by device name
                return d1.getBondTimestamp() != null
                        ? d1.getBondTimestamp().compareTo(d2.getBondTimestamp())
                        : d1.getName().compareTo(d2.getName());
            };
}
}
+0 −1
Original line number Original line Diff line number Diff line
@@ -23,7 +23,6 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud


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


import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;