1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.btservice; 18 19 import android.bluetooth.BluetoothProfile; 20 import android.content.Context; 21 import android.os.SystemProperties; 22 import android.sysprop.BluetoothProperties; 23 import android.util.Log; 24 25 import com.android.bluetooth.Utils; 26 import com.android.bluetooth.a2dp.A2dpService; 27 import com.android.bluetooth.a2dpsink.A2dpSinkService; 28 import com.android.bluetooth.avrcp.AvrcpTargetService; 29 import com.android.bluetooth.avrcpcontroller.AvrcpControllerService; 30 import com.android.bluetooth.bas.BatteryService; 31 import com.android.bluetooth.bass_client.BassClientService; 32 import com.android.bluetooth.csip.CsipSetCoordinatorService; 33 import com.android.bluetooth.flags.Flags; 34 import com.android.bluetooth.gatt.GattService; 35 import com.android.bluetooth.hap.HapClientService; 36 import com.android.bluetooth.hearingaid.HearingAidService; 37 import com.android.bluetooth.hfp.HeadsetService; 38 import com.android.bluetooth.hfpclient.HeadsetClientService; 39 import com.android.bluetooth.hid.HidDeviceService; 40 import com.android.bluetooth.hid.HidHostService; 41 import com.android.bluetooth.le_audio.LeAudioService; 42 import com.android.bluetooth.map.BluetoothMapService; 43 import com.android.bluetooth.mapclient.MapClientService; 44 import com.android.bluetooth.mcp.McpService; 45 import com.android.bluetooth.opp.BluetoothOppService; 46 import com.android.bluetooth.pan.PanService; 47 import com.android.bluetooth.pbap.BluetoothPbapService; 48 import com.android.bluetooth.pbapclient.PbapClientService; 49 import com.android.bluetooth.sap.SapService; 50 import com.android.bluetooth.tbs.TbsService; 51 import com.android.bluetooth.vc.VolumeControlService; 52 import com.android.internal.annotations.VisibleForTesting; 53 54 import java.util.Arrays; 55 56 public class Config { 57 private static final String TAG = "AdapterServiceConfig"; 58 59 private static final String LE_AUDIO_DYNAMIC_SWITCH_PROPERTY = 60 "ro.bluetooth.leaudio_switcher.supported"; 61 private static final String LE_AUDIO_SWITCHER_DISABLED_PROPERTY = 62 "persist.bluetooth.leaudio_switcher.disabled"; 63 64 // Three modes, 1. "disabled" - all LE audio feature off. 2. "unicast" - Unicast enabled only. 65 // 3. "broadcast" - Unicast + broadcast enabled 66 private static final String LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY = 67 "persist.bluetooth.leaudio_dynamic_switcher.mode"; 68 69 private static class ProfileConfig { 70 boolean mSupported; 71 int mProfileId; 72 ProfileConfig(boolean supported, int profileId)73 ProfileConfig(boolean supported, int profileId) { 74 mSupported = supported; 75 mProfileId = profileId; 76 } 77 } 78 79 /** List of profile services related to LE audio */ 80 private static final int[] LE_AUDIO_UNICAST_PROFILES = { 81 BluetoothProfile.LE_AUDIO, 82 BluetoothProfile.VOLUME_CONTROL, 83 BluetoothProfile.CSIP_SET_COORDINATOR, 84 BluetoothProfile.MCP_SERVER, 85 BluetoothProfile.LE_CALL_CONTROL, 86 }; 87 88 /** List of profile services with the profile-supported resource flag and bit mask. */ 89 private static final ProfileConfig[] PROFILE_SERVICES_AND_FLAGS; 90 91 static { 92 if (Flags.leaudioSynchronizeStart()) { 93 PROFILE_SERVICES_AND_FLAGS = 94 new ProfileConfig[] { 95 new ProfileConfig(A2dpService.isEnabled(), BluetoothProfile.A2DP), 96 new ProfileConfig(A2dpSinkService.isEnabled(), BluetoothProfile.A2DP_SINK), 97 new ProfileConfig(AvrcpTargetService.isEnabled(), BluetoothProfile.AVRCP), 98 new ProfileConfig( 99 AvrcpControllerService.isEnabled(), 100 BluetoothProfile.AVRCP_CONTROLLER), 101 new ProfileConfig( 102 BassClientService.isEnabled(), 103 BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT), 104 new ProfileConfig(BatteryService.isEnabled(), BluetoothProfile.BATTERY), 105 new ProfileConfig( 106 CsipSetCoordinatorService.isEnabled(), 107 BluetoothProfile.CSIP_SET_COORDINATOR), 108 new ProfileConfig( 109 HapClientService.isEnabled(), BluetoothProfile.HAP_CLIENT), 110 new ProfileConfig(HeadsetService.isEnabled(), BluetoothProfile.HEADSET), 111 new ProfileConfig( 112 HeadsetClientService.isEnabled(), BluetoothProfile.HEADSET_CLIENT), 113 new ProfileConfig( 114 HearingAidService.isEnabled(), BluetoothProfile.HEARING_AID), 115 new ProfileConfig( 116 HidDeviceService.isEnabled(), BluetoothProfile.HID_DEVICE), 117 new ProfileConfig(HidHostService.isEnabled(), BluetoothProfile.HID_HOST), 118 new ProfileConfig(GattService.isEnabled(), BluetoothProfile.GATT), 119 new ProfileConfig(TbsService.isEnabled(), BluetoothProfile.LE_CALL_CONTROL), 120 new ProfileConfig(BluetoothMapService.isEnabled(), BluetoothProfile.MAP), 121 new ProfileConfig( 122 MapClientService.isEnabled(), BluetoothProfile.MAP_CLIENT), 123 new ProfileConfig(McpService.isEnabled(), BluetoothProfile.MCP_SERVER), 124 new ProfileConfig(BluetoothOppService.isEnabled(), BluetoothProfile.OPP), 125 new ProfileConfig(PanService.isEnabled(), BluetoothProfile.PAN), 126 new ProfileConfig(BluetoothPbapService.isEnabled(), BluetoothProfile.PBAP), 127 new ProfileConfig( 128 PbapClientService.isEnabled(), BluetoothProfile.PBAP_CLIENT), 129 new ProfileConfig(SapService.isEnabled(), BluetoothProfile.SAP), 130 new ProfileConfig( 131 VolumeControlService.isEnabled(), BluetoothProfile.VOLUME_CONTROL), 132 new ProfileConfig(LeAudioService.isEnabled(), BluetoothProfile.LE_AUDIO), 133 new ProfileConfig( 134 LeAudioService.isBroadcastEnabled(), 135 BluetoothProfile.LE_AUDIO_BROADCAST), 136 }; 137 } else { 138 PROFILE_SERVICES_AND_FLAGS = 139 new ProfileConfig[] { 140 new ProfileConfig(A2dpService.isEnabled(), BluetoothProfile.A2DP), 141 new ProfileConfig(A2dpSinkService.isEnabled(), BluetoothProfile.A2DP_SINK), 142 new ProfileConfig(AvrcpTargetService.isEnabled(), BluetoothProfile.AVRCP), 143 new ProfileConfig( 144 AvrcpControllerService.isEnabled(), 145 BluetoothProfile.AVRCP_CONTROLLER), 146 new ProfileConfig( 147 BassClientService.isEnabled(), 148 BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT), 149 new ProfileConfig(BatteryService.isEnabled(), BluetoothProfile.BATTERY), 150 new ProfileConfig( 151 CsipSetCoordinatorService.isEnabled(), 152 BluetoothProfile.CSIP_SET_COORDINATOR), 153 new ProfileConfig( 154 HapClientService.isEnabled(), BluetoothProfile.HAP_CLIENT), 155 new ProfileConfig(HeadsetService.isEnabled(), BluetoothProfile.HEADSET), 156 new ProfileConfig( 157 HeadsetClientService.isEnabled(), BluetoothProfile.HEADSET_CLIENT), 158 new ProfileConfig( 159 HearingAidService.isEnabled(), BluetoothProfile.HEARING_AID), 160 new ProfileConfig( 161 HidDeviceService.isEnabled(), BluetoothProfile.HID_DEVICE), 162 new ProfileConfig(HidHostService.isEnabled(), BluetoothProfile.HID_HOST), 163 new ProfileConfig(GattService.isEnabled(), BluetoothProfile.GATT), 164 new ProfileConfig(LeAudioService.isEnabled(), BluetoothProfile.LE_AUDIO), 165 new ProfileConfig( 166 LeAudioService.isBroadcastEnabled(), 167 BluetoothProfile.LE_AUDIO_BROADCAST), 168 new ProfileConfig(TbsService.isEnabled(), BluetoothProfile.LE_CALL_CONTROL), 169 new ProfileConfig(BluetoothMapService.isEnabled(), BluetoothProfile.MAP), 170 new ProfileConfig( 171 MapClientService.isEnabled(), BluetoothProfile.MAP_CLIENT), 172 new ProfileConfig(McpService.isEnabled(), BluetoothProfile.MCP_SERVER), 173 new ProfileConfig(BluetoothOppService.isEnabled(), BluetoothProfile.OPP), 174 new ProfileConfig(PanService.isEnabled(), BluetoothProfile.PAN), 175 new ProfileConfig(BluetoothPbapService.isEnabled(), BluetoothProfile.PBAP), 176 new ProfileConfig( 177 PbapClientService.isEnabled(), BluetoothProfile.PBAP_CLIENT), 178 new ProfileConfig(SapService.isEnabled(), BluetoothProfile.SAP), 179 new ProfileConfig( 180 VolumeControlService.isEnabled(), BluetoothProfile.VOLUME_CONTROL), 181 }; 182 } 183 } 184 185 /** A test function to allow for dynamic enabled */ 186 @VisibleForTesting setProfileEnabled(int profileId, boolean enabled)187 public static void setProfileEnabled(int profileId, boolean enabled) { 188 for (ProfileConfig profile : PROFILE_SERVICES_AND_FLAGS) { 189 if (profileId == profile.mProfileId) { 190 profile.mSupported = enabled; 191 break; 192 } 193 } 194 } 195 init(Context ctx)196 static void init(Context ctx) { 197 if (LeAudioService.isBroadcastEnabled()) { 198 final String leAudioSwitcherMode = 199 SystemProperties.get(LE_AUDIO_DYNAMIC_SWITCHER_MODE_PROPERTY, "none"); 200 if (leAudioSwitcherMode.equals("disabled")) { 201 setLeAudioProfileStatus(false); 202 setLeAudioBroadcastProfileStatus(false); 203 } else if (leAudioSwitcherMode.equals("unicast")) { 204 setLeAudioProfileStatus(true); 205 setLeAudioBroadcastProfileStatus(false); 206 } else if (leAudioSwitcherMode.equals("broadcast")) { 207 setLeAudioProfileStatus(true); 208 setLeAudioBroadcastProfileStatus(true); 209 } 210 } else if (LeAudioService.isEnabled()) { 211 final boolean leAudioDynamicSwitchSupported = 212 SystemProperties.getBoolean(LE_AUDIO_DYNAMIC_SWITCH_PROPERTY, false); 213 214 if (leAudioDynamicSwitchSupported) { 215 final String leAudioSwitcherDisabled = 216 SystemProperties.get(LE_AUDIO_SWITCHER_DISABLED_PROPERTY, "none"); 217 if (leAudioSwitcherDisabled.equals("true")) { 218 setLeAudioProfileStatus(false); 219 } else if (leAudioSwitcherDisabled.equals("false")) { 220 setLeAudioProfileStatus(true); 221 } 222 } 223 } 224 225 // Disable ASHA on Automotive, TV, and Watch devices if the system property is not set 226 // This means that the OS will not automatically enable ASHA on these platforms, but these 227 // platforms can choose to enable ASHA themselves 228 if (BluetoothProperties.isProfileAshaCentralEnabled().isEmpty()) { 229 if (Utils.isAutomotive(ctx) || Utils.isTv(ctx) || Utils.isWatch(ctx)) { 230 setProfileEnabled(BluetoothProfile.HEARING_AID, false); 231 } 232 } 233 234 // TODO: b/321806163 Cleanup post the flag cleanup. 235 // Disable A2DP source profile for automotive devices only if sink is enabled and 236 // concurrent support is not enabled. 237 if (!Flags.a2dpConcurrentSourceSink() 238 && Utils.isAutomotive(ctx) 239 && A2dpSinkService.isEnabled()) { 240 setProfileEnabled(BluetoothProfile.A2DP, false); 241 setProfileEnabled(BluetoothProfile.AVRCP, false); 242 } 243 244 // Disable ASHA if BLE is not supported on this platform even if the platform enabled ASHA 245 // accidentally 246 if (!Utils.isBleSupported(ctx)) { 247 setProfileEnabled(BluetoothProfile.HEARING_AID, false); 248 } 249 250 for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) { 251 Log.i( 252 TAG, 253 String.format( 254 "init: profile=%s, enabled=%s", 255 BluetoothProfile.getProfileName(config.mProfileId), config.mSupported)); 256 } 257 } 258 setLeAudioProfileStatus(Boolean enable)259 static void setLeAudioProfileStatus(Boolean enable) { 260 setProfileEnabled(BluetoothProfile.CSIP_SET_COORDINATOR, enable); 261 setProfileEnabled(BluetoothProfile.HAP_CLIENT, enable); 262 setProfileEnabled(BluetoothProfile.LE_AUDIO, enable); 263 setProfileEnabled(BluetoothProfile.LE_CALL_CONTROL, enable); 264 setProfileEnabled(BluetoothProfile.MCP_SERVER, enable); 265 setProfileEnabled(BluetoothProfile.VOLUME_CONTROL, enable); 266 } 267 setLeAudioBroadcastProfileStatus(Boolean enable)268 static void setLeAudioBroadcastProfileStatus(Boolean enable) { 269 setProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, enable); 270 setProfileEnabled(BluetoothProfile.LE_AUDIO_BROADCAST, enable); 271 } 272 getLeAudioUnicastProfiles()273 static int[] getLeAudioUnicastProfiles() { 274 return LE_AUDIO_UNICAST_PROFILES; 275 } 276 getSupportedProfiles()277 static int[] getSupportedProfiles() { 278 return Arrays.stream(PROFILE_SERVICES_AND_FLAGS) 279 .filter(config -> config.mSupported) 280 .mapToInt(config -> config.mProfileId) 281 // LE_AUDIO_BROADCAST don't have an associated class 282 .filter(profileId -> profileId != BluetoothProfile.LE_AUDIO_BROADCAST) 283 .toArray(); 284 } 285 getSupportedProfilesBitMask()286 static long getSupportedProfilesBitMask() { 287 long mask = 0; 288 for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) { 289 if (config.mSupported) { 290 mask |= (1L << config.mProfileId); 291 } 292 } 293 return mask; 294 } 295 } 296