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

Commit 9409ee94 authored by Ugo Yu's avatar Ugo Yu
Browse files

Do not fallback HFP active device to watches

Usually users do not expect a call gets routed to the watch
automatically. Watches usually have speaker instead of earpiece,
fallbacking the audio to a watch could lead to privacy issues.

Tag: #compatibility
Bug: 256657323
Test: atest HeadsetServiceTest
Change-Id: I3fa1364c09674c1b6b817c783d4d1e2ecbca901a
Merged-In: I3fa1364c09674c1b6b817c783d4d1e2ecbca901a
parent 74f0c9e8
Loading
Loading
Loading
Loading
+24 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;

import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
@@ -2184,10 +2185,32 @@ public class HeadsetService extends ProfileService {
    public BluetoothDevice getFallbackDevice() {
        DatabaseManager dbManager = mAdapterService.getDatabase();
        return dbManager != null ? dbManager
            .getMostRecentlyConnectedDevicesInList(getConnectedDevices())
            .getMostRecentlyConnectedDevicesInList(getFallbackCandidates(dbManager))
            : null;
    }

    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    List<BluetoothDevice> getFallbackCandidates(DatabaseManager dbManager) {
        List<BluetoothDevice> fallbackCandidates = getConnectedDevices();
        List<BluetoothDevice> uninterestedCandidates = new ArrayList<>();
        for (BluetoothDevice device : fallbackCandidates) {
            byte[] deviceType = dbManager.getCustomMeta(device,
                    BluetoothDevice.METADATA_DEVICE_TYPE);
            BluetoothClass deviceClass = device.getBluetoothClass();
            if ((deviceClass != null
                    && deviceClass.getMajorDeviceClass()
                    == BluetoothClass.Device.WEARABLE_WRIST_WATCH)
                    || (deviceType != null
                    && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) {
                uninterestedCandidates.add(device);
            }
        }
        for (BluetoothDevice device : uninterestedCandidates) {
            fallbackCandidates.remove(device);
        }
        return fallbackCandidates;
    }

    @Override
    public void dump(StringBuilder sb) {
        boolean isScoOn = mSystemInterface.getAudioManager().isBluetoothScoOn();
+64 −0
Original line number Diff line number Diff line
@@ -938,6 +938,70 @@ public class HeadsetServiceTest {
        Assert.assertEquals(null, mHeadsetService.getActiveDevice());
    }

    @Test
    public void testGetFallbackCandidates() {
        BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
        BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
        when(mDatabaseManager.getCustomMeta(any(BluetoothDevice.class),
                any(Integer.class))).thenReturn(null);

        // No connected device
        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager).isEmpty());

        // One connected device
        addConnectedDeviceHelper(deviceA);
        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager)
                .contains(deviceA));

        // Two connected devices
        addConnectedDeviceHelper(deviceB);
        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager)
                .contains(deviceA));
        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager)
                .contains(deviceB));
    }

    @Test
    public void testGetFallbackCandidates_HasWatchDevice() {
        BluetoothDevice deviceWatch = TestUtils.getTestDevice(mAdapter, 0);
        BluetoothDevice deviceRegular = TestUtils.getTestDevice(mAdapter, 1);

        // Make deviceWatch a watch
        when(mDatabaseManager.getCustomMeta(deviceWatch, BluetoothDevice.METADATA_DEVICE_TYPE))
                .thenReturn(BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
        when(mDatabaseManager.getCustomMeta(deviceRegular, BluetoothDevice.METADATA_DEVICE_TYPE))
                .thenReturn(null);

        // Has a connected watch device
        addConnectedDeviceHelper(deviceWatch);
        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager).isEmpty());

        // Two connected devices with one watch
        addConnectedDeviceHelper(deviceRegular);
        Assert.assertFalse(mHeadsetService.getFallbackCandidates(mDatabaseManager)
                .contains(deviceWatch));
        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager)
                .contains(deviceRegular));
    }

    private void addConnectedDeviceHelper(BluetoothDevice device) {
        mCurrentDevice = device;
        when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class),
                eq(BluetoothProfile.HEADSET)))
                .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
        Assert.assertTrue(mHeadsetService.connect(device));
        when(mStateMachines.get(device).getDevice()).thenReturn(device);
        when(mStateMachines.get(device).getConnectionState()).thenReturn(
                BluetoothProfile.STATE_CONNECTING);
        Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
                mHeadsetService.getConnectionState(device));
        when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn(
                BluetoothProfile.STATE_CONNECTED);
        Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
                mHeadsetService.getConnectionState(device));
        Assert.assertTrue(mHeadsetService.getConnectedDevices().contains(device));
    }

    /*
     *  Helper function to test okToAcceptConnection() method
     *