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

Commit 37d90a00 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes I9efecb0e,Ia7cce32e into main

* changes:
  LeAudio: Fix late LeAudio connect of ASHA/LeAudio HS
  PhonePolicy: Fix for early switch toggle during bonding
parents 5e8e1858 50916164
Loading
Loading
Loading
Loading
+81 −39
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import com.android.bluetooth.pan.PanService;
import com.android.bluetooth.vc.VolumeControlService;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -595,18 +596,39 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
            return;
        }

        if (!isLeAudioOnlyGroup(device)) {
            /* Log no needed as above function will log on error. */
            return;
        }
        List<BluetoothDevice> groupDevices = new ArrayList<>();
        boolean isAnyOtherGroupMemberAlreadyAllowed = false;

        CsipSetCoordinatorService csipSetCoordinatorService =
                mFactory.getCsipSetCoordinatorService();
        if (csipSetCoordinatorService != null) {
            /* Since isLeAudioOnlyGroup return true it means csipSetCoordinatorService is valid */
        List<BluetoothDevice> groupDevices =
            groupDevices =
                    csipSetCoordinatorService.getGroupDevicesOrdered(
                            csipSetCoordinatorService.getGroupId(device, BluetoothUuid.CAP));

            if (Flags.leaudioQuickLeaudioToggleSwitchFix()) {
                for (BluetoothDevice dev : groupDevices) {
                    if (leAudioService.getConnectionPolicy(dev)
                            == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
                        isAnyOtherGroupMemberAlreadyAllowed = true;
                        break;
                    }
                }
            }
        }

        boolean isLeAudio = isLeAudioOnlyGroup(device);
        debugLog(
                "handleLeAudioOnlyDeviceAfterCsipConnect: isAnyOtherGroupMemberAlreadyAllowed = "
                        + isAnyOtherGroupMemberAlreadyAllowed
                        + ", isLeAudioOnlyGroup = "
                        + isLeAudio);
        if (!isAnyOtherGroupMemberAlreadyAllowed && !isLeAudio) {
            /* Log no needed as above function will log on error. */
            return;
        }

        debugLog("handleLeAudioOnlyDeviceAfterCsipConnect: enabling LeAudioOnlyDevice");
        for (BluetoothDevice dev : groupDevices) {
            if (leAudioService.getConnectionPolicy(dev)
@@ -624,8 +646,14 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
    @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
    private void processProfileStateChanged(BluetoothDevice device, int profileId, int nextState,
            int prevState) {
        debugLog("processProfileStateChanged, device=" + device + ", profile="
                + BluetoothProfile.getProfileName(profileId) + ", " + prevState + " -> "
        debugLog(
                "processProfileStateChanged, device="
                        + device
                        + ", profile="
                        + BluetoothProfile.getProfileName(profileId)
                        + ", "
                        + prevState
                        + " -> "
                        + nextState);
        if (((profileId == BluetoothProfile.A2DP)
                || (profileId == BluetoothProfile.HEADSET)
@@ -670,14 +698,20 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
                + BluetoothProfile.getProfileName(profileId) + " isDualModeAudioEnabled="
                + isDualModeAudioEnabled());

        if (device != null) {
        if (device == null) {
            return;
        }

        mDatabaseManager.setConnection(device, profileId);

            if (isDualModeAudioEnabled()) return;
        boolean isDualMode = isDualModeAudioEnabled();

        if (profileId == BluetoothProfile.LE_AUDIO) {
            A2dpService a2dpService = mFactory.getA2dpService();
            HeadsetService hsService = mFactory.getHeadsetService();
            LeAudioService leAudioService = mFactory.getLeAudioService();
            HearingAidService hearingAidService = mFactory.getHearingAidService();

            if (leAudioService == null) {
                debugLog("processActiveDeviceChanged: LeAudioService is null");
                return;
@@ -685,20 +719,28 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
            List<BluetoothDevice> leAudioActiveGroupDevices =
                    leAudioService.getGroupDevices(leAudioService.getGroupId(device));

                // Disable classic audio profiles for all group devices as lead can change
            // Disable classic audio profiles and ASHA for all group devices as lead can change
            for (BluetoothDevice activeGroupDevice : leAudioActiveGroupDevices) {
                    if (hsService != null) {
                        debugLog("Disable HFP for the LE audio dual mode group device "
                if (hsService != null && !isDualMode) {
                    debugLog(
                            "Disable HFP for the LE audio dual mode group device "
                                    + activeGroupDevice);
                        hsService.setConnectionPolicy(activeGroupDevice,
                                BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
                    hsService.setConnectionPolicy(
                            activeGroupDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
                }
                    if (a2dpService != null) {
                        debugLog("Disable A2DP for the LE audio dual mode group device "
                if (a2dpService != null && !isDualMode) {
                    debugLog(
                            "Disable A2DP for the LE audio dual mode group device "
                                    + activeGroupDevice);
                        a2dpService.setConnectionPolicy(activeGroupDevice,
                                BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
                    a2dpService.setConnectionPolicy(
                            activeGroupDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
                }
                if (hearingAidService != null) {
                    debugLog(
                            "Disable ASHA for the LE audio dual mode group device "
                                    + activeGroupDevice);
                    hearingAidService.setConnectionPolicy(
                            activeGroupDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
                }
            }
        }
+251 −3
Original line number Diff line number Diff line
@@ -43,6 +43,8 @@ import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.btservice.storage.MetadataDatabase;
import com.android.bluetooth.csip.CsipSetCoordinatorService;
import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.hap.HapClientService;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.le_audio.LeAudioService;

@@ -84,6 +86,8 @@ public class PhonePolicyTest {
    @Mock private LeAudioService mLeAudioService;
    @Mock private DatabaseManager mDatabaseManager;
    @Mock private CsipSetCoordinatorService mCsipSetCoordinatorService;
    @Mock private HearingAidService mHearingAidService;
    @Mock private HapClientService mHapClientService;

    private List<BluetoothDevice> mLeAudioAllowedConnectionPolicyList = new ArrayList<>();

@@ -104,6 +108,8 @@ public class PhonePolicyTest {
        doReturn(mA2dpService).when(mServiceFactory).getA2dpService();
        doReturn(mLeAudioService).when(mServiceFactory).getLeAudioService();
        doReturn(mCsipSetCoordinatorService).when(mServiceFactory).getCsipSetCoordinatorService();
        doReturn(mHearingAidService).when(mServiceFactory).getHearingAidService();
        doReturn(mHapClientService).when(mServiceFactory).getHapClientService();

        // Start handler thread for this test
        mHandlerThread = new HandlerThread("PhonePolicyTestHandlerThread");
@@ -169,11 +175,13 @@ public class PhonePolicyTest {

        // Check that the priorities of the devices for preferred profiles are set to ON
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
                .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET,
                .setProfileConnectionPolicy(
                        device,
                        BluetoothProfile.HEADSET,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
                .setProfileConnectionPolicy(device, BluetoothProfile.A2DP,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
                .setProfileConnectionPolicy(
                        device, BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
    }

    private void processInitProfilePriorities_LeAudioOnlyHelper(
@@ -467,6 +475,246 @@ public class PhonePolicyTest {
        mPhonePolicy.onUuidsDiscovered(device, uuids);
    }

    /* In this test we want to check following scenario
     * 1. First Le Audio set member bonds/connect and user switch LeAudio toggle
     * 2. Second device connects later, and LeAudio shall be enabled automatically
     */
    @Test
    public void testLateConnectOfLeAudioEnabled_DualModeBud() {
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_QUICK_LEAUDIO_TOGGLE_SWITCH_FIX);
        Utils.setDualModeAudioStateForTesting(false);
        mPhonePolicy.mLeAudioEnabledByDefault = true;
        mPhonePolicy.mAutoConnectProfilesSupported = true;

        /* Just for the moment, set to true to setup first device */
        SystemProperties.set(
                PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY, Boolean.toString(true));

        int csipGroupId = 1;
        int groupSize = 2;

        List<BluetoothDevice> connectedDevices = new ArrayList<>();
        when(mCsipSetCoordinatorService.getDesiredGroupSize(csipGroupId)).thenReturn(groupSize);
        when(mCsipSetCoordinatorService.getGroupId(any(), any())).thenReturn(csipGroupId);
        when(mLeAudioService.getGroupId(any())).thenReturn(csipGroupId);
        when(mCsipSetCoordinatorService.getGroupDevicesOrdered(csipGroupId))
                .thenReturn(connectedDevices);

        // Connect first set member
        BluetoothDevice firstDevice = getTestDevice(mAdapter, 0);
        connectedDevices.add(firstDevice);

        /* Build list of test UUIDs */
        ParcelUuid[] uuids = new ParcelUuid[4];
        uuids[0] = BluetoothUuid.LE_AUDIO;
        uuids[1] = BluetoothUuid.COORDINATED_SET;
        uuids[2] = BluetoothUuid.A2DP_SINK;
        uuids[3] = BluetoothUuid.HFP;

        // Prepare common handlers
        when(mHeadsetService.getConnectionPolicy(any(BluetoothDevice.class)))
                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        when(mA2dpService.getConnectionPolicy(any(BluetoothDevice.class)))
                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);

        when(mLeAudioService.setConnectionPolicy(
                        any(BluetoothDevice.class), eq(BluetoothProfile.CONNECTION_POLICY_ALLOWED)))
                .thenAnswer(
                        invocation -> {
                            return setLeAudioAllowedConnectionPolicy(invocation.getArgument(0));
                        });
        when(mLeAudioService.getConnectionPolicy(any(BluetoothDevice.class)))
                .thenAnswer(
                        invocation -> {
                            return getLeAudioConnectionPolicy(invocation.getArgument(0));
                        });
        when(mLeAudioService.getGroupDevices(csipGroupId)).thenReturn(connectedDevices);

        when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
        when(mAdapterService.getRemoteUuids(any(BluetoothDevice.class))).thenReturn(uuids);
        when(mAdapterService.isProfileSupported(
                        any(BluetoothDevice.class), eq(BluetoothProfile.HEARING_AID)))
                .thenReturn(false);
        when(mAdapterService.isProfileSupported(
                        any(BluetoothDevice.class), eq(BluetoothProfile.LE_AUDIO)))
                .thenReturn(true);

        /* Always DualMode for test purpose */
        when(mAdapterService.getRemoteType(any(BluetoothDevice.class)))
                .thenReturn(BluetoothDevice.DEVICE_TYPE_DUAL);

        // Inject first devices
        mPhonePolicy.onUuidsDiscovered(firstDevice, uuids);
        mPhonePolicy.profileConnectionStateChanged(
                BluetoothProfile.CSIP_SET_COORDINATOR,
                firstDevice,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_CONNECTED);
        waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        // Verify connection policy is set properly
        verify(mLeAudioService, times(1))
                .setConnectionPolicy(
                        eq(firstDevice), eq(BluetoothProfile.CONNECTION_POLICY_ALLOWED));

        mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, firstDevice);
        waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        verify(mA2dpService, times(1))
                .setConnectionPolicy(
                        eq(firstDevice), eq(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN));
        verify(mHeadsetService, times(1))
                .setConnectionPolicy(
                        eq(firstDevice), eq(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN));

        /* Remove bypass and check that second set member will be added*/
        SystemProperties.set(
                PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY, Boolean.toString(false));

        // Now connect second device and make sure
        // Connect first set member
        BluetoothDevice secondDevice = getTestDevice(mAdapter, 1);
        connectedDevices.add(secondDevice);

        // Inject second set member connection
        mPhonePolicy.onUuidsDiscovered(secondDevice, uuids);
        mPhonePolicy.profileConnectionStateChanged(
                BluetoothProfile.CSIP_SET_COORDINATOR,
                secondDevice,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_CONNECTED);
        waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        // Verify connection policy is set properly
        verify(mLeAudioService, times(1))
                .setConnectionPolicy(
                        eq(secondDevice), eq(BluetoothProfile.CONNECTION_POLICY_ALLOWED));

        mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, secondDevice);
        waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        verify(mA2dpService, times(1))
                .setConnectionPolicy(
                        eq(secondDevice), eq(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN));
        verify(mHeadsetService, times(1))
                .setConnectionPolicy(
                        eq(secondDevice), eq(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN));
    }

    @Test
    public void testLateConnectOfLeAudioEnabled_AshaAndLeAudioBud() {
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_QUICK_LEAUDIO_TOGGLE_SWITCH_FIX);
        Utils.setDualModeAudioStateForTesting(false);
        mPhonePolicy.mLeAudioEnabledByDefault = true;
        mPhonePolicy.mAutoConnectProfilesSupported = true;

        /* Just for the moment, set to true to setup first device */
        SystemProperties.set(
                PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY, Boolean.toString(true));

        int csipGroupId = 1;
        int groupSize = 2;

        List<BluetoothDevice> connectedDevices = new ArrayList<>();
        when(mCsipSetCoordinatorService.getDesiredGroupSize(csipGroupId)).thenReturn(groupSize);
        when(mCsipSetCoordinatorService.getGroupId(any(), any())).thenReturn(csipGroupId);
        when(mLeAudioService.getGroupId(any())).thenReturn(csipGroupId);
        when(mCsipSetCoordinatorService.getGroupDevicesOrdered(csipGroupId))
                .thenReturn(connectedDevices);

        // Connect first set member
        BluetoothDevice firstDevice = getTestDevice(mAdapter, 0);
        connectedDevices.add(firstDevice);

        /* Build list of test UUIDs */
        ParcelUuid[] uuids = new ParcelUuid[4];
        uuids[0] = BluetoothUuid.LE_AUDIO;
        uuids[1] = BluetoothUuid.COORDINATED_SET;
        uuids[2] = BluetoothUuid.HEARING_AID;
        uuids[3] = BluetoothUuid.HAS;

        // Prepare common handlers
        when(mHearingAidService.getConnectionPolicy(any(BluetoothDevice.class)))
                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);

        when(mLeAudioService.setConnectionPolicy(
                        any(BluetoothDevice.class), eq(BluetoothProfile.CONNECTION_POLICY_ALLOWED)))
                .thenAnswer(
                        invocation -> {
                            return setLeAudioAllowedConnectionPolicy(invocation.getArgument(0));
                        });
        when(mLeAudioService.getConnectionPolicy(any(BluetoothDevice.class)))
                .thenAnswer(
                        invocation -> {
                            return getLeAudioConnectionPolicy(invocation.getArgument(0));
                        });
        when(mLeAudioService.getGroupDevices(csipGroupId)).thenReturn(connectedDevices);

        when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
        when(mAdapterService.getRemoteUuids(any(BluetoothDevice.class))).thenReturn(uuids);
        when(mAdapterService.isProfileSupported(
                        any(BluetoothDevice.class), eq(BluetoothProfile.HEARING_AID)))
                .thenReturn(false);
        when(mAdapterService.isProfileSupported(
                        any(BluetoothDevice.class), eq(BluetoothProfile.LE_AUDIO)))
                .thenReturn(true);

        /* Always DualMode for test purpose */
        when(mAdapterService.getRemoteType(any(BluetoothDevice.class)))
                .thenReturn(BluetoothDevice.DEVICE_TYPE_LE);

        // Inject first devices
        mPhonePolicy.onUuidsDiscovered(firstDevice, uuids);
        mPhonePolicy.profileConnectionStateChanged(
                BluetoothProfile.CSIP_SET_COORDINATOR,
                firstDevice,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_CONNECTED);
        waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        // Verify connection policy is set properly
        verify(mLeAudioService, times(1))
                .setConnectionPolicy(
                        eq(firstDevice), eq(BluetoothProfile.CONNECTION_POLICY_ALLOWED));

        mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, firstDevice);
        waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        verify(mHearingAidService, times(1))
                .setConnectionPolicy(
                        eq(firstDevice), eq(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN));

        /* Remove bypass and check that second set member will be added*/
        SystemProperties.set(
                PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY, Boolean.toString(false));

        // Now connect second device and make sure
        // Connect first set member
        BluetoothDevice secondDevice = getTestDevice(mAdapter, 1);
        connectedDevices.add(secondDevice);

        // Inject second set member connection
        mPhonePolicy.onUuidsDiscovered(secondDevice, uuids);
        mPhonePolicy.profileConnectionStateChanged(
                BluetoothProfile.CSIP_SET_COORDINATOR,
                secondDevice,
                BluetoothProfile.STATE_DISCONNECTED,
                BluetoothProfile.STATE_CONNECTED);
        waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        // Verify connection policy is set properly
        verify(mLeAudioService, times(1))
                .setConnectionPolicy(
                        eq(secondDevice), eq(BluetoothProfile.CONNECTION_POLICY_ALLOWED));

        mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, secondDevice);
        waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        verify(mHearingAidService, times(1))
                .setConnectionPolicy(
                        eq(secondDevice), eq(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN));
    }

    /**
     * Test that when the adapter is turned ON then we call autoconnect on devices that have HFP and
     * A2DP enabled. NOTE that the assumption is that we have already done the pairing previously