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.content.res.Resources; 22 import android.os.SystemProperties; 23 import android.util.Log; 24 25 import com.android.bluetooth.R; 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.gatt.GattService; 34 import com.android.bluetooth.hap.HapClientService; 35 import com.android.bluetooth.hearingaid.HearingAidService; 36 import com.android.bluetooth.hfp.HeadsetService; 37 import com.android.bluetooth.hfpclient.HeadsetClientService; 38 import com.android.bluetooth.hid.HidDeviceService; 39 import com.android.bluetooth.hid.HidHostService; 40 import com.android.bluetooth.le_audio.LeAudioService; 41 import com.android.bluetooth.map.BluetoothMapService; 42 import com.android.bluetooth.mapclient.MapClientService; 43 import com.android.bluetooth.mcp.McpService; 44 import com.android.bluetooth.opp.BluetoothOppService; 45 import com.android.bluetooth.pan.PanService; 46 import com.android.bluetooth.pbap.BluetoothPbapService; 47 import com.android.bluetooth.pbapclient.PbapClientService; 48 import com.android.bluetooth.sap.SapService; 49 import com.android.bluetooth.tbs.TbsService; 50 import com.android.bluetooth.vc.VolumeControlService; 51 import com.android.internal.annotations.VisibleForTesting; 52 53 import java.util.ArrayList; 54 import java.util.Arrays; 55 import java.util.HashSet; 56 import java.util.Iterator; 57 58 public class Config { 59 private static final String TAG = "AdapterServiceConfig"; 60 61 private static final String FEATURE_HEARING_AID = "settings_bluetooth_hearing_aid"; 62 private static final String FEATURE_BATTERY = "settings_bluetooth_battery"; 63 64 private static final String LE_AUDIO_DYNAMIC_SWITCH_PROPERTY = 65 "ro.bluetooth.leaudio_switcher.supported"; 66 private static final String LE_AUDIO_BROADCAST_DYNAMIC_SWITCH_PROPERTY = 67 "ro.bluetooth.leaudio_broadcast_switcher.supported"; 68 private static final String LE_AUDIO_DYNAMIC_ENABLED_PROPERTY = 69 "persist.bluetooth.leaudio_switcher.enabled"; 70 71 private static class ProfileConfig { 72 Class mClass; 73 boolean mSupported; 74 long mMask; 75 ProfileConfig(Class theClass, boolean supported, long mask)76 ProfileConfig(Class theClass, boolean supported, long mask) { 77 mClass = theClass; 78 mSupported = supported; 79 mMask = mask; 80 } 81 } 82 83 /** 84 * List of profile services related to LE audio 85 */ 86 private static final HashSet<Class> mLeAudioUnicastProfiles = new HashSet<Class>( 87 Arrays.asList(LeAudioService.class, 88 VolumeControlService.class, 89 McpService.class, 90 CsipSetCoordinatorService.class)); 91 92 /** 93 * List of profile services with the profile-supported resource flag and bit mask. 94 */ 95 private static final ProfileConfig[] PROFILE_SERVICES_AND_FLAGS = { 96 new ProfileConfig(A2dpService.class, A2dpService.isEnabled(), 97 (1 << BluetoothProfile.A2DP)), 98 new ProfileConfig(A2dpSinkService.class, A2dpSinkService.isEnabled(), 99 (1 << BluetoothProfile.A2DP_SINK)), 100 new ProfileConfig(AvrcpTargetService.class, AvrcpTargetService.isEnabled(), 101 (1 << BluetoothProfile.AVRCP)), 102 new ProfileConfig(AvrcpControllerService.class, AvrcpControllerService.isEnabled(), 103 (1 << BluetoothProfile.AVRCP_CONTROLLER)), 104 new ProfileConfig(BassClientService.class, BassClientService.isEnabled(), 105 (1 << BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT)), 106 new ProfileConfig(BatteryService.class, BatteryService.isEnabled(), 107 (1 << BluetoothProfile.BATTERY)), 108 new ProfileConfig(CsipSetCoordinatorService.class, 109 CsipSetCoordinatorService.isEnabled(), 110 (1 << BluetoothProfile.CSIP_SET_COORDINATOR)), 111 new ProfileConfig(HapClientService.class, HapClientService.isEnabled(), 112 (1 << BluetoothProfile.HAP_CLIENT)), 113 new ProfileConfig(HeadsetService.class, HeadsetService.isEnabled(), 114 (1 << BluetoothProfile.HEADSET)), 115 new ProfileConfig(HeadsetClientService.class, HeadsetClientService.isEnabled(), 116 (1 << BluetoothProfile.HEADSET_CLIENT)), 117 new ProfileConfig(HearingAidService.class, HearingAidService.isEnabled(), 118 (1 << BluetoothProfile.HEARING_AID)), 119 new ProfileConfig(HidDeviceService.class, HidDeviceService.isEnabled(), 120 (1 << BluetoothProfile.HID_DEVICE)), 121 new ProfileConfig(HidHostService.class, HidHostService.isEnabled(), 122 (1 << BluetoothProfile.HID_HOST)), 123 new ProfileConfig(GattService.class, GattService.isEnabled(), 124 (1 << BluetoothProfile.GATT)), 125 new ProfileConfig(LeAudioService.class, LeAudioService.isEnabled(), 126 (1 << BluetoothProfile.LE_AUDIO)), 127 new ProfileConfig(TbsService.class, TbsService.isEnabled(), 128 (1 << BluetoothProfile.LE_CALL_CONTROL)), 129 new ProfileConfig(BluetoothMapService.class, BluetoothMapService.isEnabled(), 130 (1 << BluetoothProfile.MAP)), 131 new ProfileConfig(MapClientService.class, MapClientService.isEnabled(), 132 (1 << BluetoothProfile.MAP_CLIENT)), 133 new ProfileConfig(McpService.class, McpService.isEnabled(), 134 (1 << BluetoothProfile.MCP_SERVER)), 135 new ProfileConfig(BluetoothOppService.class, BluetoothOppService.isEnabled(), 136 (1 << BluetoothProfile.OPP)), 137 new ProfileConfig(PanService.class, PanService.isEnabled(), 138 (1 << BluetoothProfile.PAN)), 139 new ProfileConfig(BluetoothPbapService.class, BluetoothPbapService.isEnabled(), 140 (1 << BluetoothProfile.PBAP)), 141 new ProfileConfig(PbapClientService.class, PbapClientService.isEnabled(), 142 (1 << BluetoothProfile.PBAP_CLIENT)), 143 new ProfileConfig(SapService.class, SapService.isEnabled(), 144 (1 << BluetoothProfile.SAP)), 145 new ProfileConfig(VolumeControlService.class, VolumeControlService.isEnabled(), 146 (1 << BluetoothProfile.VOLUME_CONTROL)), 147 }; 148 149 /** 150 * A test function to allow for dynamic enabled 151 */ 152 @VisibleForTesting setProfileEnabled(Class profileClass, boolean enabled)153 public static void setProfileEnabled(Class profileClass, boolean enabled) { 154 if (profileClass == null) { 155 return; 156 } 157 for (ProfileConfig profile : PROFILE_SERVICES_AND_FLAGS) { 158 if (profileClass.equals(profile.mClass)) { 159 profile.mSupported = enabled; 160 } 161 } 162 } 163 164 private static Class[] sSupportedProfiles = new Class[0]; 165 166 private static boolean sIsGdEnabledUptoScanningLayer = false; 167 init(Context ctx)168 static void init(Context ctx) { 169 if (LeAudioService.isBroadcastEnabled()) { 170 updateSupportedProfileMask( 171 true, LeAudioService.class, BluetoothProfile.LE_AUDIO_BROADCAST); 172 } 173 174 final boolean leAudioDynamicSwitchSupported = 175 SystemProperties.getBoolean(LE_AUDIO_DYNAMIC_SWITCH_PROPERTY, false); 176 177 if (leAudioDynamicSwitchSupported) { 178 final String leAudioDynamicEnabled = SystemProperties 179 .get(LE_AUDIO_DYNAMIC_ENABLED_PROPERTY, "none"); 180 if (leAudioDynamicEnabled.equals("true")) { 181 setLeAudioProfileStatus(true); 182 } else if (leAudioDynamicEnabled.equals("false")) { 183 setLeAudioProfileStatus(false); 184 } 185 } 186 187 ArrayList<Class> profiles = new ArrayList<>(PROFILE_SERVICES_AND_FLAGS.length); 188 for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) { 189 Log.i(TAG, "init: profile=" + config.mClass.getSimpleName() + ", enabled=" 190 + config.mSupported); 191 if (config.mSupported) { 192 profiles.add(config.mClass); 193 } 194 } 195 sSupportedProfiles = profiles.toArray(new Class[profiles.size()]); 196 197 if (ctx == null) { 198 return; 199 } 200 Resources resources = ctx.getResources(); 201 if (resources == null) { 202 return; 203 } 204 sIsGdEnabledUptoScanningLayer = resources.getBoolean(R.bool.enable_gd_up_to_scanning_layer); 205 } 206 setLeAudioProfileStatus(Boolean enable)207 static void setLeAudioProfileStatus(Boolean enable) { 208 setProfileEnabled(CsipSetCoordinatorService.class, enable); 209 setProfileEnabled(HapClientService.class, enable); 210 setProfileEnabled(LeAudioService.class, enable); 211 setProfileEnabled(TbsService.class, enable); 212 setProfileEnabled(McpService.class, enable); 213 setProfileEnabled(VolumeControlService.class, enable); 214 215 final boolean broadcastDynamicSwitchSupported = 216 SystemProperties.getBoolean(LE_AUDIO_BROADCAST_DYNAMIC_SWITCH_PROPERTY, false); 217 218 if (broadcastDynamicSwitchSupported) { 219 setProfileEnabled(BassClientService.class, enable); 220 updateSupportedProfileMask( 221 enable, LeAudioService.class, BluetoothProfile.LE_AUDIO_BROADCAST); 222 } 223 } 224 225 /** 226 * Remove the input profiles from the supported list. 227 */ removeProfileFromSupportedList(HashSet<Class> nonSupportedProfiles)228 static void removeProfileFromSupportedList(HashSet<Class> nonSupportedProfiles) { 229 ArrayList<Class> profilesList = new ArrayList<Class>(Arrays.asList(sSupportedProfiles)); 230 Iterator<Class> iter = profilesList.iterator(); 231 232 while (iter.hasNext()) { 233 Class profileClass = iter.next(); 234 235 if (nonSupportedProfiles.contains(profileClass)) { 236 iter.remove(); 237 Log.v(TAG, "Remove " + profileClass.getSimpleName() + " from supported list."); 238 } 239 } 240 241 sSupportedProfiles = profilesList.toArray(new Class[profilesList.size()]); 242 } 243 updateSupportedProfileMask(Boolean enable, Class profile, int supportedProfile)244 static void updateSupportedProfileMask(Boolean enable, Class profile, int supportedProfile) { 245 for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) { 246 if (config.mClass == profile) { 247 if (enable) { 248 config.mMask |= 1 << supportedProfile; 249 } else { 250 config.mMask &= ~(1 << supportedProfile); 251 } 252 return; 253 } 254 } 255 } 256 geLeAudioUnicastProfiles()257 static HashSet<Class> geLeAudioUnicastProfiles() { 258 return mLeAudioUnicastProfiles; 259 } 260 getSupportedProfiles()261 static Class[] getSupportedProfiles() { 262 return sSupportedProfiles; 263 } 264 isGdEnabledUpToScanningLayer()265 static boolean isGdEnabledUpToScanningLayer() { 266 return sIsGdEnabledUptoScanningLayer; 267 } 268 getProfileMask(Class profile)269 private static long getProfileMask(Class profile) { 270 for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) { 271 if (config.mClass == profile) { 272 return config.mMask; 273 } 274 } 275 Log.w(TAG, "Could not find profile bit mask for " + profile.getSimpleName()); 276 return 0; 277 } 278 getSupportedProfilesBitMask()279 static long getSupportedProfilesBitMask() { 280 long mask = 0; 281 for (final Class profileClass : getSupportedProfiles()) { 282 mask |= getProfileMask(profileClass); 283 } 284 return mask; 285 } 286 } 287