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.ContentResolver;
21 import android.content.Context;
22 import android.content.res.Resources;
23 import android.os.SystemProperties;
24 import android.provider.Settings;
25 import android.text.TextUtils;
26 import android.util.Log;
27 
28 import com.android.bluetooth.R;
29 import com.android.bluetooth.a2dp.A2dpService;
30 import com.android.bluetooth.a2dpsink.A2dpSinkService;
31 import com.android.bluetooth.avrcp.AvrcpTargetService;
32 import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
33 import com.android.bluetooth.gatt.GattService;
34 import com.android.bluetooth.hearingaid.HearingAidService;
35 import com.android.bluetooth.hfp.HeadsetService;
36 import com.android.bluetooth.hfpclient.HeadsetClientService;
37 import com.android.bluetooth.hid.HidDeviceService;
38 import com.android.bluetooth.hid.HidHostService;
39 import com.android.bluetooth.map.BluetoothMapService;
40 import com.android.bluetooth.mapclient.MapClientService;
41 import com.android.bluetooth.opp.BluetoothOppService;
42 import com.android.bluetooth.pan.PanService;
43 import com.android.bluetooth.pbap.BluetoothPbapService;
44 import com.android.bluetooth.pbapclient.PbapClientService;
45 import com.android.bluetooth.sap.SapService;
46 
47 import java.util.ArrayList;
48 
49 public class Config {
50     private static final String TAG = "AdapterServiceConfig";
51 
52     private static class ProfileConfig {
53         Class mClass;
54         int mSupported;
55         long mMask;
56 
ProfileConfig(Class theClass, int supportedFlag, long mask)57         ProfileConfig(Class theClass, int supportedFlag, long mask) {
58             mClass = theClass;
59             mSupported = supportedFlag;
60             mMask = mask;
61         }
62     }
63 
64     /**
65      * List of profile services with the profile-supported resource flag and bit mask.
66      */
67     private static final ProfileConfig[] PROFILE_SERVICES_AND_FLAGS = {
68             new ProfileConfig(HeadsetService.class, R.bool.profile_supported_hs_hfp,
69                     (1 << BluetoothProfile.HEADSET)),
70             new ProfileConfig(A2dpService.class, R.bool.profile_supported_a2dp,
71                     (1 << BluetoothProfile.A2DP)),
72             new ProfileConfig(A2dpSinkService.class, R.bool.profile_supported_a2dp_sink,
73                     (1 << BluetoothProfile.A2DP_SINK)),
74             new ProfileConfig(HidHostService.class, R.bool.profile_supported_hid_host,
75                     (1 << BluetoothProfile.HID_HOST)),
76             new ProfileConfig(PanService.class, R.bool.profile_supported_pan,
77                     (1 << BluetoothProfile.PAN)),
78             new ProfileConfig(GattService.class, R.bool.profile_supported_gatt,
79                     (1 << BluetoothProfile.GATT)),
80             new ProfileConfig(BluetoothMapService.class, R.bool.profile_supported_map,
81                     (1 << BluetoothProfile.MAP)),
82             new ProfileConfig(HeadsetClientService.class, R.bool.profile_supported_hfpclient,
83                     (1 << BluetoothProfile.HEADSET_CLIENT)),
84             new ProfileConfig(AvrcpTargetService.class, R.bool.profile_supported_avrcp_target,
85                     (1 << BluetoothProfile.AVRCP)),
86             new ProfileConfig(AvrcpControllerService.class,
87                     R.bool.profile_supported_avrcp_controller,
88                     (1 << BluetoothProfile.AVRCP_CONTROLLER)),
89             new ProfileConfig(SapService.class, R.bool.profile_supported_sap,
90                     (1 << BluetoothProfile.SAP)),
91             new ProfileConfig(PbapClientService.class, R.bool.profile_supported_pbapclient,
92                     (1 << BluetoothProfile.PBAP_CLIENT)),
93             new ProfileConfig(MapClientService.class, R.bool.profile_supported_mapmce,
94                     (1 << BluetoothProfile.MAP_CLIENT)),
95             new ProfileConfig(HidDeviceService.class, R.bool.profile_supported_hid_device,
96                     (1 << BluetoothProfile.HID_DEVICE)),
97             new ProfileConfig(BluetoothOppService.class, R.bool.profile_supported_opp,
98                     (1 << BluetoothProfile.OPP)),
99             new ProfileConfig(BluetoothPbapService.class, R.bool.profile_supported_pbap,
100                     (1 << BluetoothProfile.PBAP)),
101             new ProfileConfig(HearingAidService.class,
102                     com.android.internal.R.bool.config_hearing_aid_profile_supported,
103                     (1 << BluetoothProfile.HEARING_AID))
104     };
105 
106     private static Class[] sSupportedProfiles = new Class[0];
107 
init(Context ctx)108     static void init(Context ctx) {
109         if (ctx == null) {
110             return;
111         }
112         Resources resources = ctx.getResources();
113         if (resources == null) {
114             return;
115         }
116 
117         ArrayList<Class> profiles = new ArrayList<>(PROFILE_SERVICES_AND_FLAGS.length);
118         for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
119             boolean supported = resources.getBoolean(config.mSupported);
120 
121             if (!supported && (config.mClass == HearingAidService.class) && isHearingAidSettingsEnabled(ctx)) {
122                 Log.v(TAG, "Feature Flag enables support for HearingAidService");
123                 supported = true;
124             }
125 
126             if (supported && !isProfileDisabled(ctx, config.mMask)) {
127                 Log.v(TAG, "Adding " + config.mClass.getSimpleName());
128                 profiles.add(config.mClass);
129             }
130         }
131         sSupportedProfiles = profiles.toArray(new Class[profiles.size()]);
132     }
133 
getSupportedProfiles()134     static Class[] getSupportedProfiles() {
135         return sSupportedProfiles;
136     }
137 
getProfileMask(Class profile)138     private static long getProfileMask(Class profile) {
139         for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
140             if (config.mClass == profile) {
141                 return config.mMask;
142             }
143         }
144         Log.w(TAG, "Could not find profile bit mask for " + profile.getSimpleName());
145         return 0;
146     }
147 
getSupportedProfilesBitMask()148     static long getSupportedProfilesBitMask() {
149         long mask = 0;
150         for (final Class profileClass : getSupportedProfiles()) {
151             mask |= getProfileMask(profileClass);
152         }
153         return mask;
154     }
155 
isProfileDisabled(Context context, long profileMask)156     private static boolean isProfileDisabled(Context context, long profileMask) {
157         final ContentResolver resolver = context.getContentResolver();
158         final long disabledProfilesBitMask =
159                 Settings.Global.getLong(resolver, Settings.Global.BLUETOOTH_DISABLED_PROFILES, 0);
160 
161         return (disabledProfilesBitMask & profileMask) != 0;
162     }
163 
isHearingAidSettingsEnabled(Context context)164     private static boolean isHearingAidSettingsEnabled(Context context) {
165         final String flagOverridePrefix = "sys.fflag.override.";
166         final String hearingAidSettings = "settings_bluetooth_hearing_aid";
167 
168         // Override precedence:
169         // Settings.Global -> sys.fflag.override.* -> static list
170 
171         // Step 1: check if hearing aid flag is set in Settings.Global.
172         String value;
173         if (context != null) {
174             value = Settings.Global.getString(context.getContentResolver(), hearingAidSettings);
175             if (!TextUtils.isEmpty(value)) {
176                 return Boolean.parseBoolean(value);
177             }
178         }
179 
180         // Step 2: check if hearing aid flag has any override.
181         value = SystemProperties.get(flagOverridePrefix + hearingAidSettings);
182         if (!TextUtils.isEmpty(value)) {
183             return Boolean.parseBoolean(value);
184         }
185 
186         // Step 3: return default value.
187         return false;
188     }
189 }
190