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

Commit 8d759273 authored by Yiyi Shen's avatar Yiyi Shen
Browse files

[Audiosharing] Notify receiver when new device connected in PAS

Test: atest
Flag: com.android.settingslib.flags.promote_audio_sharing_for_second_auto_connected_lea_device
Bug: 395786392
Change-Id: Ie6b64e20b8f0666ffa0624bd694b4d8449ddc402
parent 5ac62be2
Loading
Loading
Loading
Loading
+29 −10
Original line number Original line Diff line number Diff line
@@ -29,6 +29,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.Log;


@@ -37,6 +38,8 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.VisibleForTesting;


import com.android.settingslib.R;
import com.android.settingslib.R;
import com.android.settingslib.flags.Flags;
import com.android.settingslib.utils.ThreadUtils;


import java.util.Collection;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashMap;
@@ -65,6 +68,7 @@ public class BluetoothEventManager {
    private final android.os.Handler mReceiverHandler;
    private final android.os.Handler mReceiverHandler;
    private final UserHandle mUserHandle;
    private final UserHandle mUserHandle;
    private final Context mContext;
    private final Context mContext;
    private boolean mIsWorkProfile = false;


    interface Handler {
    interface Handler {
        void onReceive(Context context, Intent intent, BluetoothDevice device);
        void onReceive(Context context, Intent intent, BluetoothDevice device);
@@ -140,6 +144,9 @@ public class BluetoothEventManager {
        addHandler(BluetoothAdapter.ACTION_AUTO_ON_STATE_CHANGED, new AutoOnStateChangedHandler());
        addHandler(BluetoothAdapter.ACTION_AUTO_ON_STATE_CHANGED, new AutoOnStateChangedHandler());


        registerAdapterIntentReceiver();
        registerAdapterIntentReceiver();

        UserManager userManager = context.getSystemService(UserManager.class);
        mIsWorkProfile = userManager != null && userManager.isManagedProfile();
    }
    }


    /** Register to start receiving callbacks for Bluetooth events. */
    /** Register to start receiving callbacks for Bluetooth events. */
@@ -220,20 +227,32 @@ public class BluetoothEventManager {
            callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
            callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
        }
        }


        if (mIsWorkProfile) {
            Log.d(TAG, "Skip profileConnectionStateChanged for audio sharing, work profile");
            return;
        }

        LocalBluetoothLeBroadcast broadcast = mBtManager == null ? null
                : mBtManager.getProfileManager().getLeAudioBroadcastProfile();
        LocalBluetoothLeBroadcastAssistant assistant = mBtManager == null ? null
                : mBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
        // Trigger updateFallbackActiveDeviceIfNeeded when ASSISTANT profile disconnected when
        // Trigger updateFallbackActiveDeviceIfNeeded when ASSISTANT profile disconnected when
        // audio sharing is enabled.
        // audio sharing is enabled.
        if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
        if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
                && state == BluetoothAdapter.STATE_DISCONNECTED
                && state == BluetoothAdapter.STATE_DISCONNECTED
                && BluetoothUtils.isAudioSharingUIAvailable(mContext)) {
                && BluetoothUtils.isAudioSharingUIAvailable(mContext)
            LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
                && broadcast != null && assistant != null && broadcast.isProfileReady()
            if (profileManager != null
                && assistant.isProfileReady()) {
                    && profileManager.getLeAudioBroadcastProfile() != null
                    && profileManager.getLeAudioBroadcastProfile().isProfileReady()
                    && profileManager.getLeAudioBroadcastAssistantProfile() != null
                    && profileManager.getLeAudioBroadcastAssistantProfile().isProfileReady()) {
            Log.d(TAG, "updateFallbackActiveDeviceIfNeeded, ASSISTANT profile disconnected");
            Log.d(TAG, "updateFallbackActiveDeviceIfNeeded, ASSISTANT profile disconnected");
                profileManager.getLeAudioBroadcastProfile().updateFallbackActiveDeviceIfNeeded();
            broadcast.updateFallbackActiveDeviceIfNeeded();
            }
        }
        // Dispatch handleOnProfileStateChanged to local broadcast profile
        if (Flags.promoteAudioSharingForSecondAutoConnectedLeaDevice()
                && broadcast != null
                && state == BluetoothAdapter.STATE_CONNECTED) {
            Log.d(TAG, "dispatchProfileConnectionStateChanged to local broadcast profile");
            var unused = ThreadUtils.postOnBackgroundThread(
                    () -> broadcast.handleProfileConnected(device, bluetoothProfile, mBtManager));
        }
        }
    }
    }


+24 −0
Original line number Original line Diff line number Diff line
@@ -719,6 +719,30 @@ public class BluetoothUtils {
        }
        }
    }
    }


    /** Check if the {@link CachedBluetoothDevice} is a media device */
    @WorkerThread
    public static boolean isMediaDevice(@Nullable CachedBluetoothDevice cachedDevice) {
        if (cachedDevice == null) return false;
        return cachedDevice.getProfiles().stream()
                .anyMatch(
                        profile ->
                                profile instanceof A2dpProfile
                                        || profile instanceof HearingAidProfile
                                        || profile instanceof LeAudioProfile
                                        || profile instanceof HeadsetProfile);
    }

    /** Check if the {@link CachedBluetoothDevice} supports LE Audio profile */
    @WorkerThread
    public static boolean isLeAudioSupported(@Nullable CachedBluetoothDevice cachedDevice) {
        if (cachedDevice == null) return false;
        return cachedDevice.getProfiles().stream()
                .anyMatch(
                        profile ->
                                profile instanceof LeAudioProfile
                                        && profile.isEnabled(cachedDevice.getDevice()));
    }

    /** Returns if the broadcast is on-going. */
    /** Returns if the broadcast is on-going. */
    @WorkerThread
    @WorkerThread
    public static boolean isBroadcasting(@Nullable LocalBluetoothManager manager) {
    public static boolean isBroadcasting(@Nullable LocalBluetoothManager manager) {
+87 −0
Original line number Original line Diff line number Diff line
@@ -54,6 +54,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresApi;
import androidx.annotation.WorkerThread;


import com.android.settingslib.R;
import com.android.settingslib.R;
import com.android.settingslib.flags.Flags;
import com.android.settingslib.flags.Flags;
@@ -64,6 +65,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.SecureRandom;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Arrays;
import java.util.Collections;
import java.util.Collections;
@@ -107,6 +109,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
    private static final String SETTINGS_PKG = "com.android.settings";
    private static final String SETTINGS_PKG = "com.android.settings";
    private static final String SYSUI_PKG = "com.android.systemui";
    private static final String SYSUI_PKG = "com.android.systemui";
    private static final String TAG = "LocalBluetoothLeBroadcast";
    private static final String TAG = "LocalBluetoothLeBroadcast";
    private static final String AUTO_REJOIN_BROADCAST_TAG = "REJOIN_LE_BROADCAST_ID";
    private static final boolean DEBUG = BluetoothUtils.D;
    private static final boolean DEBUG = BluetoothUtils.D;
    private static final String VALID_PASSWORD_CHARACTERS =
    private static final String VALID_PASSWORD_CHARACTERS =
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+[]{}|;:,"
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-_=+[]{}|;:,"
@@ -120,6 +123,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
    // Order of this profile in device profiles list
    // Order of this profile in device profiles list
    private static final int ORDINAL = 1;
    private static final int ORDINAL = 1;
    static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
    static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
    private static final int JUST_BOND_MILLIS_THRESHOLD = 30000; // 30s
    private static final Uri[] SETTINGS_URIS =
    private static final Uri[] SETTINGS_URIS =
            new Uri[] {
            new Uri[] {
                Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_NAME),
                Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_NAME),
@@ -1283,4 +1287,87 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
        UserManager userManager = context.getSystemService(UserManager.class);
        UserManager userManager = context.getSystemService(UserManager.class);
        return userManager != null && userManager.isManagedProfile();
        return userManager != null && userManager.isManagedProfile();
    }
    }

    /** Handle profile connected for {@link CachedBluetoothDevice}. */
    @WorkerThread
    public void handleProfileConnected(@NonNull CachedBluetoothDevice cachedDevice,
            int bluetoothProfile, @Nullable LocalBluetoothManager btManager) {
        if (!Flags.promoteAudioSharingForSecondAutoConnectedLeaDevice()) {
            Log.d(TAG, "Skip handleProfileConnected, flag off");
            return;
        }
        if (!SYSUI_PKG.equals(mContext.getPackageName())) {
            Log.d(TAG, "Skip handleProfileConnected, not a valid caller");
            return;
        }
        if (!BluetoothUtils.isMediaDevice(cachedDevice)) {
            Log.d(TAG, "Skip handleProfileConnected, not a media device");
            return;
        }
        Timestamp bondTimestamp = cachedDevice.getBondTimestamp();
        if (bondTimestamp != null) {
            long diff = System.currentTimeMillis() - bondTimestamp.getTime();
            if (diff <= JUST_BOND_MILLIS_THRESHOLD) {
                Log.d(TAG, "Skip handleProfileConnected, just bond within " + diff);
                return;
            }
        }
        if (!isEnabled(null)) {
            Log.d(TAG, "Skip handleProfileConnected, not broadcasting");
            return;
        }
        BluetoothDevice device = cachedDevice.getDevice();
        if (device == null) {
            Log.d(TAG, "Skip handleProfileConnected, null device");
            return;
        }
        // TODO: sync source in a reasonable place
        if (BluetoothUtils.hasConnectedBroadcastSourceForBtDevice(device, btManager)) {
            Log.d(TAG, "Skip handleProfileConnected, already has source");
            return;
        }
        if (isAutoRejoinDevice(device)) {
            Log.d(TAG, "Skip handleProfileConnected, auto rejoin device");
            return;
        }
        boolean isLeAudioSupported = BluetoothUtils.isLeAudioSupported(cachedDevice);
        // For eligible (LE audio) remote device, we only check assistant profile connected.
        if (isLeAudioSupported
                && bluetoothProfile != BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) {
            Log.d(TAG, "Skip handleProfileConnected, lea sink, not the assistant profile");
            return;
        }
        boolean isFirstConnectedProfile = isFirstConnectedProfile(cachedDevice, bluetoothProfile);
        // For ineligible (classic) remote device, we only check its first connected profile.
        if (!isLeAudioSupported && !isFirstConnectedProfile) {
            Log.d(TAG, "Skip handleProfileConnected, classic sink, not the first profile");
            return;
        }

        Intent intent = new Intent(
                LocalBluetoothLeBroadcast.ACTION_LE_AUDIO_SHARING_DEVICE_CONNECTED);
        intent.putExtra(LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE, device);
        intent.setPackage(SETTINGS_PKG);
        Log.d(TAG, "notify device connected, device = " + device.getAnonymizedAddress());

        mContext.sendBroadcast(intent);
    }

    private boolean isAutoRejoinDevice(@Nullable BluetoothDevice bluetoothDevice) {
        String metadataValue = BluetoothUtils.getFastPairCustomizedField(bluetoothDevice,
                AUTO_REJOIN_BROADCAST_TAG);
        return getLatestBroadcastId() != UNKNOWN_VALUE_PLACEHOLDER && Objects.equals(metadataValue,
                String.valueOf(getLatestBroadcastId()));
    }

    private boolean isFirstConnectedProfile(@Nullable CachedBluetoothDevice cachedDevice,
            int bluetoothProfile) {
        if (cachedDevice == null) return false;
        return cachedDevice.getProfiles().stream()
                .noneMatch(
                        profile ->
                                profile.getProfileId() != bluetoothProfile
                                        && profile.getConnectionStatus(cachedDevice.getDevice())
                                        == BluetoothProfile.STATE_CONNECTED);
    }
}
}
+76 −23
Original line number Original line Diff line number Diff line
@@ -23,7 +23,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


@@ -38,12 +37,14 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager;


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


import org.junit.Before;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Rule;
@@ -54,6 +55,8 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadow.api.Shadow;


import java.util.ArrayList;
import java.util.ArrayList;
@@ -61,7 +64,7 @@ import java.util.Collections;
import java.util.List;
import java.util.List;


@RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
@Config(shadows = {ShadowBluetoothAdapter.class, BluetoothEventManagerTest.ShadowThreadUtils.class})
public class BluetoothEventManagerTest {
public class BluetoothEventManagerTest {
    @Rule
    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -100,6 +103,8 @@ public class BluetoothEventManagerTest {
    private BluetoothUtils.ErrorListener mErrorListener;
    private BluetoothUtils.ErrorListener mErrorListener;
    @Mock
    @Mock
    private LocalBluetoothLeBroadcast mBroadcast;
    private LocalBluetoothLeBroadcast mBroadcast;
    @Mock
    private UserManager mUserManager;


    private Context mContext;
    private Context mContext;
    private Intent mIntent;
    private Intent mIntent;
@@ -130,6 +135,7 @@ public class BluetoothEventManagerTest {
        mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1);
        mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1);
        mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2);
        mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2);
        mCachedDevice3 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3);
        mCachedDevice3 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3);
        when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
        BluetoothUtils.setErrorListener(mErrorListener);
        BluetoothUtils.setErrorListener(mErrorListener);
    }
    }


@@ -196,6 +202,7 @@ public class BluetoothEventManagerTest {
     * callback.
     * callback.
     */
     */
    @Test
    @Test
    @EnableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
    public void dispatchProfileConnectionStateChanged_registerCallback_shouldDispatchCallback() {
    public void dispatchProfileConnectionStateChanged_registerCallback_shouldDispatchCallback() {
        mBluetoothEventManager.registerCallback(mBluetoothCallback);
        mBluetoothEventManager.registerCallback(mBluetoothCallback);


@@ -208,10 +215,12 @@ public class BluetoothEventManagerTest {


    /**
    /**
     * dispatchProfileConnectionStateChanged should not call {@link
     * dispatchProfileConnectionStateChanged should not call {@link
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when audio sharing flag is off.
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded and
     * {@link LocalBluetoothLeBroadcast}#handleProfileConnected when audio sharing flag is off.
     */
     */
    @Test
    @Test
    public void dispatchProfileConnectionStateChanged_flagOff_noUpdateFallbackDevice() {
    @EnableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
    public void dispatchProfileConnectionStateChanged_flagOff_noCallToBroadcastProfile() {
        setUpAudioSharing(/* enableFlag= */ false, /* enableFeature= */ true, /* enableProfile= */
        setUpAudioSharing(/* enableFlag= */ false, /* enableFeature= */ true, /* enableProfile= */
                true, /* workProfile= */ false);
                true, /* workProfile= */ false);
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
@@ -219,16 +228,19 @@ public class BluetoothEventManagerTest {
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);


        verify(mBroadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast, never()).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast, never()).handleProfileConnected(any(), anyInt(), any());
    }
    }


    /**
    /**
     * dispatchProfileConnectionStateChanged should not call {@link
     * dispatchProfileConnectionStateChanged should not call {@link
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when the device does not
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded and
     * support audio sharing.
     * {@link LocalBluetoothLeBroadcast}#handleProfileConnected when the device does not support
     * audio sharing.
     */
     */
    @Test
    @Test
    public void dispatchProfileConnectionStateChanged_notSupport_noUpdateFallbackDevice() {
    @EnableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
    public void dispatchProfileConnectionStateChanged_notSupport_noCallToBroadcastProfile() {
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ false, /* enableProfile= */
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ false, /* enableProfile= */
                true, /* workProfile= */ false);
                true, /* workProfile= */ false);
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
@@ -236,7 +248,8 @@ public class BluetoothEventManagerTest {
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);


        verify(mBroadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast, never()).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast, never()).handleProfileConnected(any(), anyInt(), any());
    }
    }


    /**
    /**
@@ -245,6 +258,7 @@ public class BluetoothEventManagerTest {
     * not ready.
     * not ready.
     */
     */
    @Test
    @Test
    @EnableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
    public void dispatchProfileConnectionStateChanged_profileNotReady_noUpdateFallbackDevice() {
    public void dispatchProfileConnectionStateChanged_profileNotReady_noUpdateFallbackDevice() {
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
                false, /* workProfile= */ false);
                false, /* workProfile= */ false);
@@ -253,7 +267,7 @@ public class BluetoothEventManagerTest {
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);


        verify(mBroadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast, never()).updateFallbackActiveDeviceIfNeeded();
    }
    }


    /**
    /**
@@ -262,6 +276,7 @@ public class BluetoothEventManagerTest {
     * other than LE_AUDIO_BROADCAST_ASSISTANT or state other than STATE_DISCONNECTED.
     * other than LE_AUDIO_BROADCAST_ASSISTANT or state other than STATE_DISCONNECTED.
     */
     */
    @Test
    @Test
    @EnableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
    public void dispatchProfileConnectionStateChanged_notAssistantProfile_noUpdateFallbackDevice() {
    public void dispatchProfileConnectionStateChanged_notAssistantProfile_noUpdateFallbackDevice() {
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
                true, /* workProfile= */ false);
                true, /* workProfile= */ false);
@@ -270,16 +285,17 @@ public class BluetoothEventManagerTest {
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.LE_AUDIO);
                BluetoothProfile.LE_AUDIO);


        verify(mBroadcast, times(0)).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast, never()).updateFallbackActiveDeviceIfNeeded();
    }
    }


    /**
    /**
     * dispatchProfileConnectionStateChanged should not call {@link
     * dispatchProfileConnectionStateChanged should not call {@link
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded when triggered for
     * LocalBluetoothLeBroadcast}#updateFallbackActiveDeviceIfNeeded and
     * work profile.
     * {@link LocalBluetoothLeBroadcast}#handleProfileConnected when triggered for work profile.
     */
     */
    @Test
    @Test
    public void dispatchProfileConnectionStateChanged_workProfile_noUpdateFallbackDevice() {
    @EnableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
    public void dispatchProfileConnectionStateChanged_workProfile_noCallToBroadcastProfile() {
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
                true, /* workProfile= */ true);
                true, /* workProfile= */ true);
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
@@ -287,7 +303,8 @@ public class BluetoothEventManagerTest {
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);


        verify(mBroadcast).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast, never()).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast, never()).handleProfileConnected(any(), anyInt(), any());
    }
    }


    /**
    /**
@@ -296,7 +313,8 @@ public class BluetoothEventManagerTest {
     * disconnected and audio sharing is enabled.
     * disconnected and audio sharing is enabled.
     */
     */
    @Test
    @Test
    public void dispatchProfileConnectionStateChanged_audioSharing_updateFallbackDevice() {
    @EnableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
    public void dispatchProfileConnectionStateChanged_assistDisconnected_updateFallbackDevice() {
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
                true, /* workProfile= */ false);
                true, /* workProfile= */ false);
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
@@ -305,6 +323,27 @@ public class BluetoothEventManagerTest {
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);


        verify(mBroadcast).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast, never()).handleProfileConnected(any(), anyInt(), any());
    }

    /**
     * dispatchProfileConnectionStateChanged should call {@link
     * LocalBluetoothLeBroadcast}#handleProfileConnected when assistant profile is connected and
     * audio sharing is enabled.
     */
    @Test
    @EnableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
    public void dispatchProfileConnectionStateChanged_assistConnected_handleStateChanged() {
        setUpAudioSharing(/* enableFlag= */ true, /* enableFeature= */ true, /* enableProfile= */
                true, /* workProfile= */ false);
        mBluetoothEventManager.dispatchProfileConnectionStateChanged(
                mCachedBluetoothDevice,
                BluetoothProfile.STATE_CONNECTED,
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);

        verify(mBroadcast, never()).updateFallbackActiveDeviceIfNeeded();
        verify(mBroadcast).handleProfileConnected(mCachedBluetoothDevice,
                BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, mBtManager);
    }
    }


    private void setUpAudioSharing(boolean enableFlag, boolean enableFeature,
    private void setUpAudioSharing(boolean enableFlag, boolean enableFeature,
@@ -325,13 +364,19 @@ public class BluetoothEventManagerTest {
        LocalBluetoothLeBroadcastAssistant assistant =
        LocalBluetoothLeBroadcastAssistant assistant =
                mock(LocalBluetoothLeBroadcastAssistant.class);
                mock(LocalBluetoothLeBroadcastAssistant.class);
        when(assistant.isProfileReady()).thenReturn(enableProfile);
        when(assistant.isProfileReady()).thenReturn(enableProfile);
        LocalBluetoothProfileManager profileManager = mock(LocalBluetoothProfileManager.class);
        when(mLocalProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
        when(profileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
        when(mLocalProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
        when(profileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(assistant);
        when(mUserManager.isManagedProfile()).thenReturn(workProfile);
        when(mBtManager.getProfileManager()).thenReturn(profileManager);
        if (workProfile) {
        UserManager userManager = mock(UserManager.class);
            mBluetoothEventManager =
        when(mContext.getSystemService(UserManager.class)).thenReturn(userManager);
                    new BluetoothEventManager(
        when(userManager.isManagedProfile()).thenReturn(workProfile);
                            mLocalAdapter,
                            mBtManager,
                            mCachedDeviceManager,
                            mContext,
                            /* handler= */ null,
                            /* userHandle= */ null);
        }
    }
    }


    @Test
    @Test
@@ -665,4 +710,12 @@ public class BluetoothEventManagerTest {


        verify(mBluetoothCallback).onAutoOnStateChanged(anyInt());
        verify(mBluetoothCallback).onAutoOnStateChanged(anyInt());
    }
    }

    @Implements(value = ThreadUtils.class)
    public static class ShadowThreadUtils {
        @Implementation
        protected static void postOnBackgroundThread(Runnable runnable) {
            runnable.run();
        }
    }
}
}
+30 −0
Original line number Original line Diff line number Diff line
@@ -1348,6 +1348,36 @@ public class BluetoothUtilsTest {
        assertThat(BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext)).isTrue();
        assertThat(BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext)).isTrue();
    }
    }


    @Test
    public void isMediaDevice_returnsFalse() {
        when(mCachedBluetoothDevice.getProfiles()).thenReturn(ImmutableList.of(mAssistant));
        assertThat(BluetoothUtils.isMediaDevice(mCachedBluetoothDevice)).isFalse();
    }

    @Test
    public void isMediaDevice_returnsTrue() {
        when(mCachedBluetoothDevice.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile));
        assertThat(BluetoothUtils.isMediaDevice(mCachedBluetoothDevice)).isTrue();
    }

    @Test
    public void isLeAudioSupported_returnsFalse() {
        when(mCachedBluetoothDevice.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile));
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
        when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(false);

        assertThat(BluetoothUtils.isLeAudioSupported(mCachedBluetoothDevice)).isFalse();
    }

    @Test
    public void isLeAudioSupported_returnsTrue() {
        when(mCachedBluetoothDevice.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile));
        when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
        when(mLeAudioProfile.isEnabled(mBluetoothDevice)).thenReturn(true);

        assertThat(BluetoothUtils.isLeAudioSupported(mCachedBluetoothDevice)).isTrue();
    }

    @Test
    @Test
    public void isTemporaryBondDevice_hasMetadata_returnsTrue() {
    public void isTemporaryBondDevice_hasMetadata_returnsTrue() {
        when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
        when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))