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

Commit 1f97e950 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

PhonePolicy: Fix for early switch toggle during bonding

When first device is bonded and user hits LeAudio toggle it will end up
with having only 1 device connected over LeAudio.

To recover, user needs to toggle OFF/ON the LeAudio switch
This patch fixes that

Bug: 328595936
Bug: 328595942
Test: mmm packages/modules/Bluetooth
Test: atest PhonePolicyTest
Test: manual bonding tests

Change-Id: Ia7cce32ec42a7e9ad41f0b919e0e0f934ff2a634
parent a3621575
Loading
Loading
Loading
Loading
+39 −11
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)
+126 −0
Original line number Diff line number Diff line
@@ -467,6 +467,132 @@ 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 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