Make bluetooth profile toggles configurable

BUG: 343317785
Test: atest DeviceSettingRepositoryTest
Flag: com.android.settings.flags.enable_bluetooth_device_details_polish
Change-Id: If623bf23642cc6030bf05a9f76e599ed8d59229f
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingContract.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingContract.kt
new file mode 100644
index 0000000..65adec4
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/DeviceSettingContract.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth.devicesettings
+
+/** The contract between the device settings provider services and Settings. */
+object DeviceSettingContract {
+    const val INVISIBLE_PROFILES = "INVISIBLE_PROFILES"
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
index 457d6a3..769b6e6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepository.kt
@@ -22,6 +22,7 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.devicesettings.ActionSwitchPreference
 import com.android.settingslib.bluetooth.devicesettings.DeviceSetting
+import com.android.settingslib.bluetooth.devicesettings.DeviceSettingContract
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingItem
 import com.android.settingslib.bluetooth.devicesettings.DeviceSettingsConfig
@@ -30,6 +31,9 @@
 import com.android.settingslib.bluetooth.devicesettings.MultiTogglePreference
 import com.android.settingslib.bluetooth.devicesettings.ToggleInfo
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel.AppProvidedItem
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel.BuiltinItem.BluetoothProfilesItem
+import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
 import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
@@ -103,9 +107,18 @@
 
     private fun DeviceSettingItem.toModel(): DeviceSettingConfigItemModel {
         return if (!TextUtils.isEmpty(preferenceKey)) {
-            DeviceSettingConfigItemModel.BuiltinItem(settingId, preferenceKey!!)
+            if (settingId == DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_PROFILES) {
+                BluetoothProfilesItem(
+                    settingId,
+                    preferenceKey!!,
+                    extras.getStringArrayList(DeviceSettingContract.INVISIBLE_PROFILES)
+                        ?: emptyList()
+                )
+            } else {
+                CommonBuiltinItem(settingId, preferenceKey!!)
+            }
         } else {
-            DeviceSettingConfigItemModel.AppProvidedItem(settingId)
+            AppProvidedItem(settingId)
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
index 33beb06..7eae5b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingServiceConnection.kt
@@ -23,6 +23,7 @@
 import android.content.ServiceConnection
 import android.os.IBinder
 import android.os.IInterface
+import android.text.TextUtils
 import android.util.Log
 import com.android.settingslib.bluetooth.BluetoothUtils
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -84,6 +85,10 @@
                 }
                 setAction(intentAction)
             }
+
+        fun isValid(): Boolean {
+            return !TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(intentAction)
+        }
     }
 
     private var isServiceEnabled =
@@ -96,7 +101,8 @@
                     } else if (allStatus.all { it is ServiceConnectionStatus.Connected }) {
                         allStatus
                             .filterIsInstance<
-                                ServiceConnectionStatus.Connected<IDeviceSettingsProviderService>
+                                ServiceConnectionStatus.Connected<
+                                        IDeviceSettingsProviderService>
                             >()
                             .all { it.service.serviceStatus?.enabled == true }
                     } else {
@@ -215,6 +221,7 @@
                     )
                 }
             }
+            ?.filter { it.isValid() }
             ?.distinct()
             ?.associateBy(
                 { it },
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
index c1ac763..08fb3fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/devicesettings/shared/model/DeviceSettingConfigModel.kt
@@ -36,10 +36,23 @@
     @DeviceSettingId val settingId: Int
 
     /** A built-in item in Settings. */
-    data class BuiltinItem(
-        @DeviceSettingId override val settingId: Int,
-        val preferenceKey: String?
-    ) : DeviceSettingConfigItemModel
+    sealed interface BuiltinItem : DeviceSettingConfigItemModel {
+        @DeviceSettingId override val settingId: Int
+        val preferenceKey: String
+
+        /** A general built-in item in Settings. */
+        data class CommonBuiltinItem(
+            @DeviceSettingId override val settingId: Int,
+            override val preferenceKey: String,
+        ) : BuiltinItem
+
+        /** A bluetooth profiles in Settings. */
+        data class BluetoothProfilesItem(
+            @DeviceSettingId override val settingId: Int,
+            override val preferenceKey: String,
+            val invisibleProfiles: List<String>,
+        ) : BuiltinItem
+    }
 
     /** A remote item provided by other apps. */
     data class AppProvidedItem(@DeviceSettingId override val settingId: Int) :
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
index ce155b5..81b5634 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/devicesettings/data/repository/DeviceSettingRepositoryTest.kt
@@ -91,7 +91,9 @@
         `when`(cachedDevice.address).thenReturn(BLUETOOTH_ADDRESS)
         `when`(
                 bluetoothDevice.getMetadata(
-                    DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+                    DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
+                )
+            )
             .thenReturn(BLUETOOTH_DEVICE_METADATA.toByteArray())
 
         `when`(configService.queryLocalInterface(anyString())).thenReturn(configService)
@@ -114,7 +116,8 @@
                     connection.onServiceConnected(
                         ComponentName(
                             SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1,
-                            SETTING_PROVIDER_SERVICE_CLASS_NAME_1),
+                            SETTING_PROVIDER_SERVICE_CLASS_NAME_1,
+                        ),
                         settingProviderService1,
                     )
                 SETTING_PROVIDER_SERVICE_INTENT_ACTION_2 ->
@@ -146,16 +149,24 @@
     fun getDeviceSettingsConfig_withMetadata_success() {
         testScope.runTest {
             `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
-            `when`(settingProviderService1.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
-            `when`(settingProviderService2.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
+            `when`(settingProviderService1.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
+            `when`(settingProviderService2.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
 
             val config = underTest.getDeviceSettingsConfig(cachedDevice)
 
             assertConfig(config!!, DEVICE_SETTING_CONFIG)
+            assertThat(config.mainItems[0])
+                .isInstanceOf(DeviceSettingConfigItemModel.AppProvidedItem::class.java)
+            assertThat(config.mainItems[1])
+                .isInstanceOf(
+                    DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem::class.java
+                )
+            assertThat(config.mainItems[2])
+                .isInstanceOf(
+                    DeviceSettingConfigItemModel.BuiltinItem.BluetoothProfilesItem::class.java
+                )
         }
     }
 
@@ -163,16 +174,16 @@
     fun getDeviceSettingsConfig_noMetadata_returnNull() {
         testScope.runTest {
             `when`(
-                bluetoothDevice.getMetadata(
-                    DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+                    bluetoothDevice.getMetadata(
+                        DeviceSettingServiceConnection.METADATA_FAST_PAIR_CUSTOMIZED_FIELDS
+                    )
+                )
                 .thenReturn("".toByteArray())
             `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
-            `when`(settingProviderService1.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
-            `when`(settingProviderService2.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
+            `when`(settingProviderService1.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
+            `when`(settingProviderService2.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
 
             val config = underTest.getDeviceSettingsConfig(cachedDevice)
 
@@ -184,12 +195,10 @@
     fun getDeviceSettingsConfig_providerServiceNotEnabled_returnNull() {
         testScope.runTest {
             `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
-            `when`(settingProviderService1.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(false)
-            )
-            `when`(settingProviderService2.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
+            `when`(settingProviderService1.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(false))
+            `when`(settingProviderService2.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
 
             val config = underTest.getDeviceSettingsConfig(cachedDevice)
 
@@ -219,12 +228,10 @@
                     .getArgument<IDeviceSettingsListener>(1)
                     .onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
             }
-            `when`(settingProviderService1.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
-            `when`(settingProviderService2.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
+            `when`(settingProviderService1.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
+            `when`(settingProviderService2.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
             var setting: DeviceSettingModel? = null
 
             underTest
@@ -247,12 +254,10 @@
                     .getArgument<IDeviceSettingsListener>(1)
                     .onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
             }
-            `when`(settingProviderService1.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
-            `when`(settingProviderService2.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
+            `when`(settingProviderService1.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
+            `when`(settingProviderService2.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
             var setting: DeviceSettingModel? = null
 
             underTest
@@ -270,17 +275,15 @@
         testScope.runTest {
             `when`(configService.getDeviceSettingsConfig(any())).thenReturn(DEVICE_SETTING_CONFIG)
             `when`(settingProviderService2.registerDeviceSettingsListener(any(), any())).then {
-                    input ->
+                input ->
                 input
                     .getArgument<IDeviceSettingsListener>(1)
                     .onDeviceSettingsChanged(listOf(DEVICE_SETTING_HELP))
             }
-            `when`(settingProviderService1.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
-            `when`(settingProviderService2.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
+            `when`(settingProviderService1.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
+            `when`(settingProviderService2.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
             var setting: DeviceSettingModel? = null
 
             underTest
@@ -324,12 +327,10 @@
                     .getArgument<IDeviceSettingsListener>(1)
                     .onDeviceSettingsChanged(listOf(DEVICE_SETTING_1))
             }
-            `when`(settingProviderService1.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
-            `when`(settingProviderService2.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
+            `when`(settingProviderService1.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
+            `when`(settingProviderService2.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
             var setting: DeviceSettingModel? = null
 
             underTest
@@ -347,8 +348,10 @@
                     DeviceSettingState.Builder()
                         .setSettingId(DeviceSettingId.DEVICE_SETTING_ID_HEADER)
                         .setPreferenceState(
-                            ActionSwitchPreferenceState.Builder().setChecked(false).build())
-                        .build())
+                            ActionSwitchPreferenceState.Builder().setChecked(false).build()
+                        )
+                        .build(),
+                )
         }
     }
 
@@ -362,12 +365,10 @@
                     .getArgument<IDeviceSettingsListener>(1)
                     .onDeviceSettingsChanged(listOf(DEVICE_SETTING_2))
             }
-            `when`(settingProviderService1.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
-            `when`(settingProviderService2.serviceStatus).thenReturn(
-                DeviceSettingsProviderServiceStatus(true)
-            )
+            `when`(settingProviderService1.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
+            `when`(settingProviderService2.serviceStatus)
+                .thenReturn(DeviceSettingsProviderServiceStatus(true))
             var setting: DeviceSettingModel? = null
 
             underTest
@@ -385,8 +386,10 @@
                     DeviceSettingState.Builder()
                         .setSettingId(DeviceSettingId.DEVICE_SETTING_ID_ANC)
                         .setPreferenceState(
-                            MultiTogglePreferenceState.Builder().setState(2).build())
-                        .build())
+                            MultiTogglePreferenceState.Builder().setState(2).build()
+                        )
+                        .build(),
+                )
         }
     }
 
@@ -437,7 +440,7 @@
 
     private fun assertConfig(
         actual: DeviceSettingConfigModel,
-        serviceResponse: DeviceSettingsConfig
+        serviceResponse: DeviceSettingsConfig,
     ) {
         assertThat(actual.mainItems.size).isEqualTo(serviceResponse.mainContentItems.size)
         for (i in 0..<actual.mainItems.size) {
@@ -451,7 +454,7 @@
 
     private fun assertConfigItem(
         actual: DeviceSettingConfigItemModel,
-        serviceResponse: DeviceSettingItem
+        serviceResponse: DeviceSettingItem,
     ) {
         assertThat(actual.settingId).isEqualTo(serviceResponse.settingId)
     }
@@ -485,24 +488,43 @@
                 "</DEVICE_SETTINGS_CONFIG_ACTION>"
         val DEVICE_INFO = DeviceInfo.Builder().setBluetoothAddress(BLUETOOTH_ADDRESS).build()
         const val DEVICE_SETTING_ID_HELP = 12345
-        val DEVICE_SETTING_ITEM_1 =
+        val DEVICE_SETTING_APP_PROVIDED_ITEM_1 =
             DeviceSettingItem(
                 DeviceSettingId.DEVICE_SETTING_ID_HEADER,
                 SETTING_PROVIDER_SERVICE_PACKAGE_NAME_1,
                 SETTING_PROVIDER_SERVICE_CLASS_NAME_1,
-                SETTING_PROVIDER_SERVICE_INTENT_ACTION_1)
-        val DEVICE_SETTING_ITEM_2 =
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_1,
+            )
+        val DEVICE_SETTING_APP_PROVIDED_ITEM_2 =
             DeviceSettingItem(
                 DeviceSettingId.DEVICE_SETTING_ID_ANC,
                 SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2,
                 SETTING_PROVIDER_SERVICE_CLASS_NAME_2,
-                SETTING_PROVIDER_SERVICE_INTENT_ACTION_2)
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_2,
+            )
+        val DEVICE_SETTING_BUILT_IN_ITEM =
+            DeviceSettingItem(
+                DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_AUDIO_DEVICE_TYPE_GROUP,
+                "",
+                "",
+                "",
+                "device_type",
+            )
+        val DEVICE_SETTING_BUILT_IN_BT_PROFILES_ITEM =
+            DeviceSettingItem(
+                DeviceSettingId.DEVICE_SETTING_ID_BLUETOOTH_PROFILES,
+                "",
+                "",
+                "",
+                "bluetooth_profiles",
+            )
         val DEVICE_SETTING_HELP_ITEM =
             DeviceSettingItem(
                 DEVICE_SETTING_ID_HELP,
                 SETTING_PROVIDER_SERVICE_PACKAGE_NAME_2,
                 SETTING_PROVIDER_SERVICE_CLASS_NAME_2,
-                SETTING_PROVIDER_SERVICE_INTENT_ACTION_2)
+                SETTING_PROVIDER_SERVICE_INTENT_ACTION_2,
+            )
         val DEVICE_SETTING_1 =
             DeviceSetting.Builder()
                 .setSettingId(DeviceSettingId.DEVICE_SETTING_ID_HEADER)
@@ -511,7 +533,8 @@
                         .setTitle("title1")
                         .setHasSwitch(true)
                         .setAllowedChangingState(true)
-                        .build())
+                        .build()
+                )
                 .build()
         val DEVICE_SETTING_2 =
             DeviceSetting.Builder()
@@ -524,22 +547,30 @@
                             ToggleInfo.Builder()
                                 .setLabel("label1")
                                 .setIcon(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
-                                .build())
+                                .build()
+                        )
                         .addToggleInfo(
                             ToggleInfo.Builder()
                                 .setLabel("label2")
                                 .setIcon(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
-                                .build())
-                        .build())
+                                .build()
+                        )
+                        .build()
+                )
                 .build()
-        val DEVICE_SETTING_HELP = DeviceSetting.Builder()
-            .setSettingId(DEVICE_SETTING_ID_HELP)
-            .setPreference(DeviceSettingHelpPreference.Builder().setIntent(Intent()).build())
-            .build()
+        val DEVICE_SETTING_HELP =
+            DeviceSetting.Builder()
+                .setSettingId(DEVICE_SETTING_ID_HELP)
+                .setPreference(DeviceSettingHelpPreference.Builder().setIntent(Intent()).build())
+                .build()
         val DEVICE_SETTING_CONFIG =
             DeviceSettingsConfig(
-                listOf(DEVICE_SETTING_ITEM_1),
-                listOf(DEVICE_SETTING_ITEM_2),
+                listOf(
+                    DEVICE_SETTING_APP_PROVIDED_ITEM_1,
+                    DEVICE_SETTING_BUILT_IN_ITEM,
+                    DEVICE_SETTING_BUILT_IN_BT_PROFILES_ITEM,
+                ),
+                listOf(DEVICE_SETTING_APP_PROVIDED_ITEM_2),
                 DEVICE_SETTING_HELP_ITEM,
             )
     }