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

Commit 3b5b19f7 authored by Rahul Sabnis's avatar Rahul Sabnis
Browse files

Uses the dual mode audio system property throughout the dual mode audio codebase

Tag: #feature
Bug: 265077700
Test: Manual
Change-Id: Icd00b1daff0ff837b26b197f75e5aaab03db835b
parent 7cdf85a0
Loading
Loading
Loading
Loading
+16 −2
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static com.android.bluetooth.Utils.enforceDumpPermission;
import static com.android.bluetooth.Utils.enforceLocalMacAddressPermission;
import static com.android.bluetooth.Utils.getBytesFromAddress;
import static com.android.bluetooth.Utils.hasBluetoothPrivilegedPermission;
import static com.android.bluetooth.Utils.isDualModeAudioEnabled;
import static com.android.bluetooth.Utils.isPackageNameAccurate;

import android.annotation.NonNull;
@@ -4612,6 +4613,11 @@ public class AdapterService extends Service {
            }
            enforceBluetoothPrivilegedPermission(service);

            // If LE only mode is enabled, the dual mode audio feature is disabled
            if (!isDualModeAudioEnabled()) {
                return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
            }

            service.mPreferredAudioProfilesCallbacks.register(callback);
            return BluetoothStatusCodes.SUCCESS;
        }
@@ -4765,8 +4771,9 @@ public class AdapterService extends Service {
     * @param device is the remote device whose preferences we want to fetch
     * @return a Bundle containing the preferred audio profiles for the device
     */
    private Bundle getPreferredAudioProfiles(BluetoothDevice device) {
        if (mLeAudioService == null || !isDualModeAudioSinkDevice(device)) {
    public Bundle getPreferredAudioProfiles(BluetoothDevice device) {
        if (!isDualModeAudioEnabled() || mLeAudioService == null
                || !isDualModeAudioSinkDevice(device)) {
            return Bundle.EMPTY;
        }
        // Gets the lead device in the CSIP group to set the preference
@@ -4813,10 +4820,17 @@ public class AdapterService extends Service {
     * @return whether the preferences were successfully requested
     */
    private int setPreferredAudioProfiles(BluetoothDevice device, Bundle modeToProfileBundle) {
        Log.i(TAG, "setPreferredAudioProfiles for device=" + device.getAddressForLogging());
        if (!isDualModeAudioEnabled()) {
            Log.e(TAG, "setPreferredAudioProfiles called while sysprop is disabled");
            return BluetoothStatusCodes.FEATURE_NOT_SUPPORTED;
        }
        if (mLeAudioService == null) {
            Log.e(TAG, "setPreferredAudioProfiles: LEA service is not up");
            return BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED;
        }
        if (!isDualModeAudioSinkDevice(device)) {
            Log.e(TAG, "setPreferredAudioProfiles: Not a dual mode audio device");
            return BluetoothStatusCodes.ERROR_NOT_DUAL_MODE_AUDIO_DEVICE;
        }
        // Gets the lead device in the CSIP group to set the preference
+9 −11
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.bluetooth.btservice;

import static com.android.bluetooth.Utils.isDualModeAudioEnabled;

import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
@@ -96,8 +98,6 @@ class PhonePolicy {
    private static final int MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED = 5;
    private static final int MESSAGE_DEVICE_CONNECTED = 6;

    @VisibleForTesting static final String PREFER_LE_AUDIO_ONLY_MODE =
            "persist.bluetooth.prefer_le_audio_only_mode";
    @VisibleForTesting static final String AUTO_CONNECT_PROFILES_PROPERTY =
            "bluetooth.auto_connect_profiles.enabled";

@@ -111,8 +111,6 @@ class PhonePolicy {
    private final HashSet<BluetoothDevice> mHeadsetRetrySet = new HashSet<>();
    private final HashSet<BluetoothDevice> mA2dpRetrySet = new HashSet<>();
    private final HashSet<BluetoothDevice> mConnectOtherProfilesDeviceSet = new HashSet<>();

    @VisibleForTesting boolean mPreferLeAudioOnlyMode;
    @VisibleForTesting boolean mAutoConnectProfilesSupported;

    // Broadcast receiver for all changes to states of various profiles
@@ -293,7 +291,6 @@ class PhonePolicy {
                "DatabaseManager cannot be null when PhonePolicy starts");
        mFactory = factory;
        mHandler = new PhonePolicyHandler(service.getMainLooper());
        mPreferLeAudioOnlyMode = SystemProperties.getBoolean(PREFER_LE_AUDIO_ONLY_MODE, true);
        mAutoConnectProfilesSupported = SystemProperties.getBoolean(
                AUTO_CONNECT_PROFILES_PROPERTY, false);
    }
@@ -342,7 +339,7 @@ class PhonePolicy {
                || Utils.arrayContains(uuids, BluetoothUuid.HFP)) && (
                headsetService.getConnectionPolicy(device)
                        == BluetoothProfile.CONNECTION_POLICY_UNKNOWN))) {
            if (mPreferLeAudioOnlyMode && isLeAudioProfileAllowed) {
            if (!isDualModeAudioEnabled() && isLeAudioProfileAllowed) {
                debugLog("clear hfp profile priority for the le audio dual mode device "
                        + device);
                mAdapterService.getDatabase().setProfileConnectionPolicy(device,
@@ -362,7 +359,7 @@ class PhonePolicy {
                || Utils.arrayContains(uuids, BluetoothUuid.ADV_AUDIO_DIST)) && (
                a2dpService.getConnectionPolicy(device)
                        == BluetoothProfile.CONNECTION_POLICY_UNKNOWN)) {
            if (mPreferLeAudioOnlyMode && isLeAudioProfileAllowed) {
            if (!isDualModeAudioEnabled() && isLeAudioProfileAllowed) {
                debugLog("clear a2dp profile priority for the le audio dual mode device "
                        + device);
                mAdapterService.getDatabase().setProfileConnectionPolicy(device,
@@ -523,19 +520,20 @@ class PhonePolicy {

    /**
     * Updates the last connection date in the connection order database for the newly active device
     * if connected to a2dp profile. If the device is LE audio dual mode device,
     * mPreferLeAudioOnlyMode is true, and LEA is made active, A2DP/HFP will be disconnected.
     * if connected to the A2DP profile. If this is a dual mode audio device (supports classic and
     * LE Audio), LE Audio is made active, and {@link Utils#isDualModeAudioEnabled()} is false,
     * A2DP and HFP will be disconnected.
     *
     * @param device is the device we just made the active device
     */
    private void processActiveDeviceChanged(BluetoothDevice device, int profileId) {
        debugLog("processActiveDeviceChanged, device=" + device + ", profile=" + profileId
                + " mPreferLeAudioOnlyMode: " + mPreferLeAudioOnlyMode);
                + " isDualModeAudioEnabled=" + isDualModeAudioEnabled());

        if (device != null) {
            mDatabaseManager.setConnection(device, profileId == BluetoothProfile.A2DP);

            if (!mPreferLeAudioOnlyMode) return;
            if (isDualModeAudioEnabled()) return;
            if (profileId == BluetoothProfile.LE_AUDIO) {
                A2dpService a2dpService = mFactory.getA2dpService();
                HeadsetService hsService = mFactory.getHeadsetService();
+3 −1
Original line number Diff line number Diff line
@@ -3496,6 +3496,7 @@ public class LeAudioService extends ProfileService {
    @Override
    public void dump(StringBuilder sb) {
        super.dump(sb);
        ProfileService.println(sb, "isDualModeAudioEnabled: " + Utils.isDualModeAudioEnabled());
        ProfileService.println(sb, "Active Groups information: ");
        ProfileService.println(sb, "  currentlyActiveGroupId: " + getActiveGroupId());
        ProfileService.println(sb, "  mActiveAudioOutDevice: " + mActiveAudioOutDevice);
@@ -3510,11 +3511,12 @@ public class LeAudioService extends ProfileService {
                                                : mGroupDescriptors.entrySet()) {
                LeAudioGroupDescriptor groupDescriptor = groupEntry.getValue();
                Integer groupId = groupEntry.getKey();
                BluetoothDevice leadDevice = getConnectedGroupLeadDevice(groupId);
                ProfileService.println(sb, "Group: " + groupId);
                ProfileService.println(sb, "  isActive: " + groupDescriptor.mIsActive);
                ProfileService.println(sb, "  isConnected: " + groupDescriptor.mIsConnected);
                ProfileService.println(sb, "  mDirection: " + groupDescriptor.mDirection);
                ProfileService.println(sb, "  group lead: " + getConnectedGroupLeadDevice(groupId));
                ProfileService.println(sb, "  group lead: " + leadDevice);
                ProfileService.println(sb, "  first device: " + getFirstDeviceFromGroup(groupId));
                ProfileService.println(sb, "  lost lead device: "
                        + groupDescriptor.mLostLeadDeviceWhileStreaming);
+30 −26
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.TestUtils;
import com.android.bluetooth.Utils;
import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.hfp.HeadsetService;
@@ -63,6 +64,7 @@ public class PhonePolicyTest {
    private HandlerThread mHandlerThread;
    private BluetoothAdapter mAdapter;
    private PhonePolicy mPhonePolicy;
    private boolean mOriginalDualModeState;

    @Mock private AdapterService mAdapterService;
    @Mock private ServiceFactory mServiceFactory;
@@ -98,6 +100,7 @@ public class PhonePolicyTest {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        PhonePolicy.sConnectOtherProfilesTimeoutMillis = CONNECT_OTHER_PROFILES_TIMEOUT_MILLIS;
        mPhonePolicy = new PhonePolicy(mAdapterService, mServiceFactory);
        mOriginalDualModeState = Utils.isDualModeAudioEnabled();
    }

    @After
@@ -106,6 +109,7 @@ public class PhonePolicyTest {
            mHandlerThread.quitSafely();
        }
        TestUtils.clearAdapterService(mAdapterService);
        Utils.setDualModeAudioStateForTesting(mOriginalDualModeState);
    }

    /**
@@ -183,54 +187,54 @@ public class PhonePolicyTest {
        BluetoothDevice device = getTestDevice(mAdapter, 0);
        when(mAdapterService.isLeAudioAllowed(device)).thenReturn(true);

        // Auto connect to LE audio but disallow HFP and A2DP
        // Auto connect to LE audio, HFP, A2DP
        processInitProfilePriorities_LeAudioHelper(true, true);
        verify(mLeAudioService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET,
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.A2DP,
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
        verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);

        // Does not auto connect and disallow HFP and A2DP to be connected
        // Does not auto connect and allow HFP and A2DP to be connected
        processInitProfilePriorities_LeAudioHelper(true, false);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
                .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET,
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.A2DP,
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);

        // Auto connect to LE audio, HFP, A2DP
        // Auto connect to LE audio but disallow HFP and A2DP
        processInitProfilePriorities_LeAudioHelper(false, true);
        verify(mLeAudioService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mA2dpService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mHeadsetService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET,
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.A2DP,
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);

        // Does not auto connect and allow HFP and A2DP to be connected
        // Does not auto connect and disallow HFP and A2DP to be connected
        processInitProfilePriorities_LeAudioHelper(false, false);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
                .setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
                .setProfileConnectionPolicy(device, BluetoothProfile.A2DP,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
                .setProfileConnectionPolicy(device, BluetoothProfile.HEADSET,
                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
        verify(mDatabaseManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
                .setProfileConnectionPolicy(device, BluetoothProfile.A2DP,
                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
    }

    private void processInitProfilePriorities_LeAudioHelper(
            boolean preferLeOnly, boolean autoConnect) {
        mPhonePolicy.mPreferLeAudioOnlyMode = preferLeOnly;
            boolean dualModeEnabled, boolean autoConnect) {
        Utils.setDualModeAudioStateForTesting(dualModeEnabled);
        mPhonePolicy.mAutoConnectProfilesSupported = autoConnect;

        BluetoothDevice device = getTestDevice(mAdapter, 0);
+15 −6
Original line number Diff line number Diff line
@@ -5112,7 +5112,8 @@ public final class BluetoothAdapter {
            BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED,
            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
            BluetoothStatusCodes.ERROR_NOT_DUAL_MODE_AUDIO_DEVICE,
            BluetoothStatusCodes.ERROR_UNKNOWN
            BluetoothStatusCodes.ERROR_UNKNOWN,
            BluetoothStatusCodes.FEATURE_NOT_SUPPORTED,
    })
    public @interface SetPreferredAudioProfilesReturnValues {}

@@ -5126,7 +5127,9 @@ public final class BluetoothAdapter {
     * here. These preferences will also be ignored if the remote device is not simultaneously
     * connected to a classic audio profile (A2DP and/or HFP) and LE Audio at the same time. If the
     * remote device does not support both BR/EDR audio and LE Audio, this API returns
     * {@link BluetoothStatusCodes#ERROR_NOT_DUAL_MODE_AUDIO_DEVICE}.
     * {@link BluetoothStatusCodes#ERROR_NOT_DUAL_MODE_AUDIO_DEVICE}. If the system property
     * persist.bluetooth.enable_dual_mode_audio is set to {@code false}, this API returns
     * {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED}.
     * <p>
     * The Bundle is expected to contain the following mappings:
     * 1. For key {@link #AUDIO_MODE_OUTPUT_ONLY}, it expects an integer value of either
@@ -5218,7 +5221,9 @@ public final class BluetoothAdapter {
     * If a device does not support an audio mode, the audio mode will be omitted from the keys of
     * the Bundle. If the device is not recognized as a dual mode audio capable device (e.g. because
     * it is not bonded, does not support any audio profiles, or does not support both BR/EDR audio
     * and LE Audio), this API returns an empty Bundle.
     * and LE Audio), this API returns an empty Bundle. If the system property
     * persist.bluetooth.enable_dual_mode_audio is set to {@code false}, this API returns an empty
     * Bundle.
     * <p>
     * The Bundle can contain the following mappings:
     * <ul>
@@ -5358,7 +5363,8 @@ public final class BluetoothAdapter {
            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
            BluetoothStatusCodes.ERROR_UNKNOWN
            BluetoothStatusCodes.ERROR_UNKNOWN,
            BluetoothStatusCodes.FEATURE_NOT_SUPPORTED
    })
    public @interface RegisterPreferredAudioProfilesCallbackReturnValues {}

@@ -5366,7 +5372,9 @@ public final class BluetoothAdapter {
     * Registers a callback to be notified when the preferred audio profile changes have taken
     * effect. To unregister this callback, call
     * {@link #unregisterPreferredAudioProfilesChangedCallback(
     * PreferredAudioProfilesChangedCallback)}.
     * PreferredAudioProfilesChangedCallback)}. If the system property
     * persist.bluetooth.enable_dual_mode_audio is set to {@code false}, this API returns
     * {@link BluetoothStatusCodes#FEATURE_NOT_SUPPORTED}.
     *
     * @param executor an {@link Executor} to execute the callbacks
     * @param callback user implementation of the {@link PreferredAudioProfilesChangedCallback}
@@ -5435,7 +5443,8 @@ public final class BluetoothAdapter {
            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
            BluetoothStatusCodes.ERROR_CALLBACK_NOT_REGISTERED,
            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
            BluetoothStatusCodes.ERROR_UNKNOWN
            BluetoothStatusCodes.ERROR_UNKNOWN,
            BluetoothStatusCodes.FEATURE_NOT_SUPPORTED
    })
    public @interface UnRegisterPreferredAudioProfilesCallbackReturnValues {}