Update preferred transport when HID and LeAudio profile are connected

Bug: 330581926
Test: atest CachedBluetoothDeviceTest
Change-Id: Ib7d42a74c4d174ee61efd2fa6eb7eafd963f9c40
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 54c5a14..a6d6eee 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -51,3 +51,13 @@
   description: "Hide exclusively managed Bluetooth devices in BT settings menu."
   bug: "324475542"
 }
+
+flag {
+    name: "enable_set_preferred_transport_for_le_audio_device"
+    namespace: "bluetooth"
+    description: "Enable setting preferred transport for Le Audio device"
+    bug: "330581926"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 4777b0d..04516eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.bluetooth;
 
+import static com.android.settingslib.flags.Flags.enableSetPreferredTransportForLeAudioDevice;
+
 import android.annotation.CallbackExecutor;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
@@ -288,6 +290,10 @@
                         mLocalNapRoleConnected = true;
                     }
                 }
+                if (enableSetPreferredTransportForLeAudioDevice()
+                        && profile instanceof HidProfile) {
+                    updatePreferredTransport();
+                }
             } else if (profile instanceof MapProfile
                     && newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
                 profile.setEnabled(mDevice, false);
@@ -300,12 +306,34 @@
                 mLocalNapRoleConnected = false;
             }
 
+            if (enableSetPreferredTransportForLeAudioDevice()
+                    && profile instanceof LeAudioProfile) {
+                updatePreferredTransport();
+            }
+
             HearingAidStatsLogUtils.updateHistoryIfNeeded(mContext, this, profile, newProfileState);
         }
 
         fetchActiveDevices();
     }
 
+    private void updatePreferredTransport() {
+        if (mProfiles.stream().noneMatch(p -> p instanceof LeAudioProfile)
+                || mProfiles.stream().noneMatch(p -> p instanceof HidProfile)) {
+            return;
+        }
+        // Both LeAudioProfile and HidProfile are connectable.
+        if (!mProfileManager
+                .getHidProfile()
+                .setPreferredTransport(
+                        mDevice,
+                        mProfileManager.getLeAudioProfile().isEnabled(mDevice)
+                                ? BluetoothDevice.TRANSPORT_LE
+                                : BluetoothDevice.TRANSPORT_BREDR)) {
+            Log.w(TAG, "Fail to set preferred transport");
+        }
+    }
+
     @VisibleForTesting
     void setProfileConnectedStatus(int profileId, boolean isFailed) {
         switch (profileId) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index 5b91ac9..b849d44 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -27,6 +27,8 @@
 import android.content.Context;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import com.android.settingslib.R;
 
 import java.util.List;
@@ -187,6 +189,14 @@
         }
     }
 
+    /** Set preferred transport for the device */
+    public boolean setPreferredTransport(@NonNull BluetoothDevice device, int transport) {
+        if (mService != null) {
+            mService.setPreferredTransport(device, transport);
+        }
+        return false;
+    }
+
     protected void finalize() {
         Log.d(TAG, "finalize()");
         if (mService != null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 79e4c37..4055986 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -572,8 +572,7 @@
         return mSapProfile;
     }
 
-    @VisibleForTesting
-    HidProfile getHidProfile() {
+    public HidProfile getHidProfile() {
         return mHidProfile;
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 5996dbb..646e9eb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.settingslib.bluetooth;
 
+import static com.android.settingslib.flags.Flags.FLAG_ENABLE_SET_PREFERRED_TRANSPORT_FOR_LE_AUDIO_DEVICE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -86,6 +88,9 @@
     private HapClientProfile mHapClientProfile;
     @Mock
     private LeAudioProfile mLeAudioProfile;
+
+    @Mock
+    private HidProfile mHidProfile;
     @Mock
     private BluetoothDevice mDevice;
     @Mock
@@ -104,6 +109,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
+        mSetFlagsRule.enableFlags(FLAG_ENABLE_SET_PREFERRED_TRANSPORT_FOR_LE_AUDIO_DEVICE);
         mContext = RuntimeEnvironment.application;
         mAudioManager = mContext.getSystemService(AudioManager.class);
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
@@ -118,6 +124,8 @@
         when(mHearingAidProfile.getProfileId()).thenReturn(BluetoothProfile.HEARING_AID);
         when(mLeAudioProfile.isProfileReady()).thenReturn(true);
         when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
+        when(mHidProfile.isProfileReady()).thenReturn(true);
+        when(mHidProfile.getProfileId()).thenReturn(BluetoothProfile.HID_HOST);
         mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice));
         mSubCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mSubDevice));
         doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
@@ -1819,6 +1827,32 @@
         assertThat(mCachedDevice.isConnectedHearingAidDevice()).isFalse();
     }
 
+    @Test
+    public void leAudioHidDevice_leAudioEnabled_setPreferredTransportToLE() {
+
+        when(mProfileManager.getHidProfile()).thenReturn(mHidProfile);
+        when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+        when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(true);
+
+        updateProfileStatus(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+
+        verify(mHidProfile).setPreferredTransport(mDevice, BluetoothDevice.TRANSPORT_LE);
+    }
+
+    @Test
+    public void leAudioHidDevice_leAudioDisabled_setPreferredTransportToBredr() {
+        when(mProfileManager.getHidProfile()).thenReturn(mHidProfile);
+        when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
+        when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(false);
+
+        updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+        updateProfileStatus(mLeAudioProfile, BluetoothProfile.STATE_DISCONNECTED);
+        updateProfileStatus(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+
+        verify(mHidProfile).setPreferredTransport(mDevice, BluetoothDevice.TRANSPORT_BREDR);
+    }
+
     private HearingAidInfo getLeftAshaHearingAidInfo() {
         return new HearingAidInfo.Builder()
                 .setAshaDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT)