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