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

Commit 0dfefc84 authored by Alice Kuo's avatar Alice Kuo
Browse files

LE Audio Allowlist refactor for default connection

The patch changes the LE Audio Allowlist usage to allow the default
connection perference. If the device is in the allow list, it would use
LE audio as default conneciton after paring; Otherwise, the classic
profile would be connected. Bypass LE Audio Allowlist is to allow use LE
audio connection by default for all the devices.

The allow list could be bypass by
1. developer option toggle - "Bypass LE Audio Allowlist"
2. adb shell setprop persist.bluetooth.leaudio.bypass_allow_lis true

Bug: 300012501
Test: atest BluetoothInstrumentationTests
Change-Id: I4566537629bb2b1815ae3924a691087ea39d161b
parent 7dd0a4e1
Loading
Loading
Loading
Loading
+13 −51
Original line number Diff line number Diff line
@@ -96,7 +96,6 @@ import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
@@ -208,7 +207,6 @@ public class AdapterService extends Service {
    private final ArrayList<ProfileService> mRegisteredProfiles = new ArrayList<>();
    private final ArrayList<ProfileService> mRunningProfiles = new ArrayList<>();
    private HashSet<String> mLeAudioAllowDevices = new HashSet<>();
    private boolean mLeAudioAllowListEnabled = false;

    public static final String ACTION_LOAD_ADAPTER_PROPERTIES =
            "com.android.bluetooth.btservice.action.LOAD_ADAPTER_PROPERTIES";
@@ -236,8 +234,7 @@ public class AdapterService extends Service {
    static final String LOCAL_MAC_ADDRESS_PERM = android.Manifest.permission.LOCAL_MAC_ADDRESS;
    static final String RECEIVE_MAP_PERM = android.Manifest.permission.RECEIVE_BLUETOOTH_MAP;
    static final String BLUETOOTH_LE_AUDIO_ALLOW_LIST = "persist.bluetooth.leaudio.allow_list";
    static final String BLUETOOTH_ENABLE_LE_AUDIO_ALLOW_LIST =
            "persist.bluetooth.leaudio.enable_allow_list";


    static final String PHONEBOOK_ACCESS_PERMISSION_PREFERENCE_FILE = "phonebook_access_permission";
    static final String MESSAGE_ACCESS_PERMISSION_PREFERENCE_FILE = "message_access_permission";
@@ -673,7 +670,6 @@ public class AdapterService extends Service {
        mBluetoothQualityReportNativeInterface.init();

        mSdpManager = SdpManager.init(this);
        loadLeAudioAllowDevices();

        mDatabaseManager = new DatabaseManager(this);
        mDatabaseManager.start(MetadataDatabase.createDatabase(this));
@@ -1554,8 +1550,7 @@ public class AdapterService extends Service {
            return Utils.arrayContains(remoteDeviceUuids, BluetoothUuid.COORDINATED_SET);
        }
        if (profile == BluetoothProfile.LE_AUDIO) {
            return Utils.arrayContains(remoteDeviceUuids, BluetoothUuid.LE_AUDIO)
                    && isLeAudioAllowed(device);
            return Utils.arrayContains(remoteDeviceUuids, BluetoothUuid.LE_AUDIO);
        }
        if (profile == BluetoothProfile.HAP_CLIENT) {
            return Utils.arrayContains(remoteDeviceUuids, BluetoothUuid.HAS);
@@ -7503,15 +7498,16 @@ public class AdapterService extends Service {
                                ScanManager.SCAN_MODE_SCREEN_OFF_BALANCED_INTERVAL_MS);
                mLeAudioAllowList = properties.getString(LE_AUDIO_ALLOW_LIST, "");

                if (mLeAudioAllowList.isEmpty()) {
                    List<String> leAudioAllowDevices = BluetoothProperties.le_audio_allow_list();
                    if (leAudioAllowDevices != null && !leAudioAllowDevices.isEmpty()) {
                        mLeAudioAllowDevices = new HashSet<String>(leAudioAllowDevices);
                if (!mLeAudioAllowList.isEmpty()) {
                    List<String> leAudioAllowlistFromDeviceConfig =
                            Arrays.asList(mLeAudioAllowList.split(","));
                    BluetoothProperties.le_audio_allow_list(leAudioAllowlistFromDeviceConfig);
                }
                } else {
                    List<String> leAudioAllowDevices = Arrays.asList(mLeAudioAllowList.split(","));
                    BluetoothProperties.le_audio_allow_list(leAudioAllowDevices);
                    mLeAudioAllowDevices = new HashSet<String>(leAudioAllowDevices);

                List<String> leAudioAllowlistProp = BluetoothProperties.le_audio_allow_list();
                if (leAudioAllowlistProp != null && !leAudioAllowlistProp.isEmpty()) {
                    mLeAudioAllowDevices.clear();
                    mLeAudioAllowDevices.addAll(leAudioAllowlistProp);
                }
            }
        }
@@ -7735,56 +7731,22 @@ public class AdapterService extends Service {
        mNativeInterface.interopDatabaseAddRemoveName(false, feature.name(), name);
    }

    private void loadLeAudioAllowDevices() {
        Log.i(TAG, "loadLeAudioAllowDevices");
        mLeAudioAllowListEnabled =
                SystemProperties.getBoolean(BLUETOOTH_ENABLE_LE_AUDIO_ALLOW_LIST, false);

        if (!mLeAudioAllowListEnabled) {
            Log.i(TAG, "LE Audio allow list is disabled.");
            return;
        }

        synchronized (mDeviceConfigLock) {
            mLeAudioAllowDevices = new HashSet<String>(Arrays.asList(mLeAudioAllowList.split(",")));
        }
        return;
    }

    /**
     * Checks the remote device is in the LE Audio allow list or not.
     *
     * @param device the device to check
     * @return boolean true if le audio allow list is not enabled or the device is in the allow
     *     list, false otherwise.
     * @return boolean true if the device is in the allow list, false otherwise.
     */
    public boolean isLeAudioAllowed(BluetoothDevice device) {
        if (!mLeAudioAllowListEnabled) {
            return true;
        }

        DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);

        if (deviceProp == null
                || deviceProp.getModelName() == null
                || !mLeAudioAllowDevices.contains(deviceProp.getModelName())) {

            if (mLeAudioService != null) {
                mLeAudioService.setConnectionPolicy(
                        device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
            }

            Log.e(
                    TAG,
                    String.format("Device %s not in the LE Audio allow list, ", device)
                            + "force LE Audio policy to forbidden");
            return false;
        }

        if (mLeAudioService != null) {
            mLeAudioService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        }

        return true;
    }

+31 −18
Original line number Diff line number Diff line
@@ -73,10 +73,13 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {

    @VisibleForTesting static final String AUTO_CONNECT_PROFILES_PROPERTY =
            "bluetooth.auto_connect_profiles.enabled";
    private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";

    private static boolean sLeAudioEnabledByDefault = DeviceConfig.getBoolean(
            DeviceConfig.NAMESPACE_BLUETOOTH, CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false);
    private static final String LE_AUDIO_CONNECTION_BY_DEFAULT_PROPERTY =
            "ro.bluetooth.leaudio.le_audio_connection_by_default";

    @VisibleForTesting
    static final String BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY =
            "persist.bluetooth.leaudio.bypass_allow_list";

    /** flag for multi auto connect */
    public static boolean sIsHfpMultiAutoConnectEnabled =
@@ -97,6 +100,7 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
    private final HashSet<BluetoothDevice> mA2dpRetrySet = new HashSet<>();
    private final HashSet<BluetoothDevice> mConnectOtherProfilesDeviceSet = new HashSet<>();
    @VisibleForTesting boolean mAutoConnectProfilesSupported;
    @VisibleForTesting boolean mLeAudioEnabledByDefault;

    @Override
    public void onBluetoothStateChange(int prevState, int newState) {
@@ -184,6 +188,8 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
        mHandler = new PhonePolicyHandler(service.getMainLooper());
        mAutoConnectProfilesSupported =
                SystemProperties.getBoolean(AUTO_CONNECT_PROFILES_PROPERTY, false);
        mLeAudioEnabledByDefault =
                SystemProperties.getBoolean(LE_AUDIO_CONNECTION_BY_DEFAULT_PROPERTY, true);
    }

    // Policy implementation, all functions MUST be private
@@ -204,12 +210,24 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
        BassClientService bcService = mFactory.getBassClientService();
        BatteryService batteryService = mFactory.getBatteryService();

        boolean isLeAudioProfileAllowed = (leAudioService != null)
        final boolean isBypassLeAudioAllowlist =
                SystemProperties.getBoolean(BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY, false);

        boolean isLeAudioProfileAllowed =
                (leAudioService != null)
                        && Utils.arrayContains(uuids, BluetoothUuid.LE_AUDIO)
                        && (leAudioService.getConnectionPolicy(device)
                                != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)
                && mAdapterService.isLeAudioAllowed(device)
                && (sLeAudioEnabledByDefault || isDualModeAudioEnabled());
                        && (mLeAudioEnabledByDefault || isDualModeAudioEnabled())
                        && (isBypassLeAudioAllowlist || mAdapterService.isLeAudioAllowed(device));

        debugLog(
                "mLeAudioEnabledByDefault: "
                        + mLeAudioEnabledByDefault
                        + ", isBypassLeAudioAllowlist: "
                        + isBypassLeAudioAllowlist
                        + ", isLeAudioAllowDevice: "
                        + mAdapterService.isLeAudioAllowed(device));

        // Set profile priorities only for the profiles discovered on the remote device.
        // This avoids needless auto-connect attempts to profiles non-existent on the remote device
@@ -790,11 +808,11 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
        }
        if (leAudioService != null) {
            List<BluetoothDevice> leAudioConnDevList = leAudioService.getConnectedDevices();
            if (!leAudioConnDevList.contains(device) && (leAudioService.getConnectionPolicy(device)
            if (!leAudioConnDevList.contains(device)
                    && (leAudioService.getConnectionPolicy(device)
                            == BluetoothProfile.CONNECTION_POLICY_ALLOWED)
                    && (leAudioService.getConnectionState(device)
                    == BluetoothProfile.STATE_DISCONNECTED)
                    && mAdapterService.isLeAudioAllowed(device)) {
                            == BluetoothProfile.STATE_DISCONNECTED)) {
                debugLog("Retrying connection to LEAudio with device " + device);
                leAudioService.connect(device);
            }
@@ -866,11 +884,6 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback {
        }
    }

    @VisibleForTesting
    void setLeAudioEnabledByDefaultForTesting(boolean enabled) {
        sLeAudioEnabledByDefault = enabled;
    }

    private static void debugLog(String msg) {
        if (DBG) {
            Log.i(TAG, msg);
+19 −12
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.os.HandlerThread;
import android.os.ParcelUuid;
import android.os.SystemProperties;

import androidx.room.Room;
import androidx.test.filters.MediumTest;
@@ -189,7 +190,7 @@ public class PhonePolicyTest {
        when(mAdapterService.isLeAudioAllowed(device)).thenReturn(true);

        // Auto connect to LE audio, HFP, A2DP
        processInitProfilePriorities_LeAudioHelper(true, true, false);
        processInitProfilePriorities_LeAudioHelper(true, true, false, false);
        verify(mLeAudioService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
@@ -198,7 +199,7 @@ public class PhonePolicyTest {
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);

        // Does not auto connect and allow HFP and A2DP to be connected
        processInitProfilePriorities_LeAudioHelper(true, false, false);
        processInitProfilePriorities_LeAudioHelper(true, false, false, false);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
@@ -209,8 +210,8 @@ public class PhonePolicyTest {
                .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);

        // Auto connect to LE audio but disallow HFP and A2DP
        processInitProfilePriorities_LeAudioHelper(false, true, false);
        // Auto connect to HFP and A2DP but disallow LE Audio
        processInitProfilePriorities_LeAudioHelper(false, true, false, false);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO,
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
@@ -219,8 +220,8 @@ public class PhonePolicyTest {
        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);

        // Does not auto connect and disallow HFP and A2DP to be connected
        processInitProfilePriorities_LeAudioHelper(false, false, false);
        // Does not auto connect and disallow LE Audio to be connected
        processInitProfilePriorities_LeAudioHelper(false, false, false, false);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
                .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO,
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
@@ -238,7 +239,7 @@ public class PhonePolicyTest {
        when(mAdapterService.isLeAudioAllowed(device)).thenReturn(true);

        // Auto connect to LE audio, HFP, A2DP
        processInitProfilePriorities_LeAudioHelper(true, true, true);
        processInitProfilePriorities_LeAudioHelper(true, true, true, false);
        verify(mLeAudioService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
@@ -247,7 +248,7 @@ public class PhonePolicyTest {
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);

        // Does not auto connect and allow HFP and A2DP to be connected
        processInitProfilePriorities_LeAudioHelper(true, false, true);
        processInitProfilePriorities_LeAudioHelper(true, false, true, false);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
@@ -259,7 +260,7 @@ public class PhonePolicyTest {
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);

        // Auto connect to LE audio but disallow HFP and A2DP
        processInitProfilePriorities_LeAudioHelper(false, true, true);
        processInitProfilePriorities_LeAudioHelper(false, true, true, false);
        verify(mLeAudioService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
@@ -270,7 +271,7 @@ public class PhonePolicyTest {
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);

        // Does not auto connect and disallow HFP and A2DP to be connected
        processInitProfilePriorities_LeAudioHelper(false, false, true);
        processInitProfilePriorities_LeAudioHelper(false, false, true, false);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
                .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
@@ -283,10 +284,16 @@ public class PhonePolicyTest {
    }

    private void processInitProfilePriorities_LeAudioHelper(
            boolean dualModeEnabled, boolean autoConnect, boolean leAudioEnabledByDefault) {
            boolean dualModeEnabled,
            boolean autoConnect,
            boolean leAudioEnabledByDefault,
            boolean bypassLeAudioAllowlist) {
        Utils.setDualModeAudioStateForTesting(dualModeEnabled);
        mPhonePolicy.setLeAudioEnabledByDefaultForTesting(leAudioEnabledByDefault);
        mPhonePolicy.mLeAudioEnabledByDefault = leAudioEnabledByDefault;
        mPhonePolicy.mAutoConnectProfilesSupported = autoConnect;
        SystemProperties.set(
                PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY,
                Boolean.toString(bypassLeAudioAllowlist));

        BluetoothDevice device = getTestDevice(mAdapter, 0);
        // Mock the HFP, A2DP and LE audio services to return unknown connection policy