• 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.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