• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.settings.bluetooth;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.bluetooth.BluetoothProfile;
21 import android.content.Context;
22 import android.os.SystemProperties;
23 import android.provider.DeviceConfig;
24 import android.text.TextUtils;
25 import android.util.Log;
26 
27 import androidx.annotation.VisibleForTesting;
28 import androidx.preference.Preference;
29 import androidx.preference.PreferenceCategory;
30 import androidx.preference.PreferenceFragmentCompat;
31 import androidx.preference.PreferenceScreen;
32 import androidx.preference.SwitchPreference;
33 
34 import com.android.settings.R;
35 import com.android.settings.core.SettingsUIDeviceConfig;
36 import com.android.settingslib.bluetooth.A2dpProfile;
37 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
38 import com.android.settingslib.bluetooth.HeadsetProfile;
39 import com.android.settingslib.bluetooth.LeAudioProfile;
40 import com.android.settingslib.bluetooth.LocalBluetoothManager;
41 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
42 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
43 import com.android.settingslib.bluetooth.MapProfile;
44 import com.android.settingslib.bluetooth.PanProfile;
45 import com.android.settingslib.bluetooth.PbapServerProfile;
46 import com.android.settingslib.core.lifecycle.Lifecycle;
47 
48 import java.util.ArrayList;
49 import java.util.HashMap;
50 import java.util.List;
51 import java.util.Map;
52 
53 /**
54  * This class adds switches for toggling the individual profiles that a Bluetooth device
55  * supports, such as "Phone audio", "Media audio", "Contact sharing", etc.
56  */
57 public class BluetoothDetailsProfilesController extends BluetoothDetailsController
58         implements Preference.OnPreferenceClickListener,
59         LocalBluetoothProfileManager.ServiceListener {
60     private static final String TAG = "BtDetailsProfilesCtrl";
61 
62     private static final String KEY_PROFILES_GROUP = "bluetooth_profiles";
63     private static final String KEY_BOTTOM_PREFERENCE = "bottom_preference";
64     private static final int ORDINAL = 99;
65 
66     @VisibleForTesting
67     static final String HIGH_QUALITY_AUDIO_PREF_TAG = "A2dpProfileHighQualityAudio";
68 
69     private static final String ENABLE_DUAL_MODE_AUDIO =
70             "persist.bluetooth.enable_dual_mode_audio";
71     private static final String CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT = "le_audio_enabled_by_default";
72     private static final boolean LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE = true;
73     private static final String LE_AUDIO_TOGGLE_VISIBLE_PROPERTY =
74             "persist.bluetooth.leaudio.toggle_visible";
75 
76     private LocalBluetoothManager mManager;
77     private LocalBluetoothProfileManager mProfileManager;
78     private CachedBluetoothDevice mCachedDevice;
79     private List<CachedBluetoothDevice> mAllOfCachedDevices;
80     private Map<String, List<CachedBluetoothDevice>> mProfileDeviceMap =
81             new HashMap<String, List<CachedBluetoothDevice>>();
82     private boolean mIsLeContactSharingEnabled = false;
83     private boolean mIsLeAudioToggleEnabled = false;
84 
85     @VisibleForTesting
86     PreferenceCategory mProfilesContainer;
87 
BluetoothDetailsProfilesController(Context context, PreferenceFragmentCompat fragment, LocalBluetoothManager manager, CachedBluetoothDevice device, Lifecycle lifecycle)88     public BluetoothDetailsProfilesController(Context context, PreferenceFragmentCompat fragment,
89             LocalBluetoothManager manager, CachedBluetoothDevice device, Lifecycle lifecycle) {
90         super(context, fragment, device, lifecycle);
91         mManager = manager;
92         mProfileManager = mManager.getProfileManager();
93         mCachedDevice = device;
94         mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
95         lifecycle.addObserver(this);
96     }
97 
98     @Override
init(PreferenceScreen screen)99     protected void init(PreferenceScreen screen) {
100         mProfilesContainer = (PreferenceCategory)screen.findPreference(getPreferenceKey());
101         mProfilesContainer.setLayoutResource(R.layout.preference_bluetooth_profile_category);
102         // Call refresh here even though it will get called later in onResume, to avoid the
103         // list of switches appearing to "pop" into the page.
104         refresh();
105     }
106 
107     /**
108      * Creates a switch preference for the particular profile.
109      *
110      * @param context The context to use when creating the SwitchPreference
111      * @param profile The profile for which the preference controls.
112      * @return A preference that allows the user to choose whether this profile
113      * will be connected to.
114      */
createProfilePreference(Context context, LocalBluetoothProfile profile)115     private SwitchPreference createProfilePreference(Context context,
116             LocalBluetoothProfile profile) {
117         SwitchPreference pref = new SwitchPreference(context);
118         pref.setKey(profile.toString());
119         pref.setTitle(profile.getNameResource(mCachedDevice.getDevice()));
120         pref.setOnPreferenceClickListener(this);
121         pref.setOrder(profile.getOrdinal());
122 
123         if (profile instanceof LeAudioProfile) {
124             pref.setSummary(R.string.device_details_leaudio_toggle_summary);
125         }
126         return pref;
127     }
128 
129     /**
130      * Refreshes the state for an existing SwitchPreference for a profile.
131      */
refreshProfilePreference(SwitchPreference profilePref, LocalBluetoothProfile profile)132     private void refreshProfilePreference(SwitchPreference profilePref,
133             LocalBluetoothProfile profile) {
134         BluetoothDevice device = mCachedDevice.getDevice();
135         boolean isLeAudioEnabled = isLeAudioEnabled();
136         if (profile instanceof A2dpProfile || profile instanceof HeadsetProfile
137                 || profile instanceof LeAudioProfile) {
138             List<CachedBluetoothDevice> deviceList = mProfileDeviceMap.get(
139                     profile.toString());
140             boolean isBusy = deviceList != null
141                     && deviceList.stream().anyMatch(item -> item.isBusy());
142             profilePref.setEnabled(!isBusy);
143         } else if (profile instanceof PbapServerProfile
144                 && isLeAudioEnabled
145                 && !mIsLeContactSharingEnabled) {
146             profilePref.setEnabled(false);
147         } else {
148             profilePref.setEnabled(!mCachedDevice.isBusy());
149         }
150 
151         if (profile instanceof LeAudioProfile) {
152             profilePref.setVisible(mIsLeAudioToggleEnabled);
153         }
154 
155         if (profile instanceof MapProfile) {
156             profilePref.setChecked(device.getMessageAccessPermission()
157                     == BluetoothDevice.ACCESS_ALLOWED);
158         } else if (profile instanceof PbapServerProfile) {
159             profilePref.setChecked(device.getPhonebookAccessPermission()
160                     == BluetoothDevice.ACCESS_ALLOWED);
161         } else if (profile instanceof PanProfile) {
162             profilePref.setChecked(profile.getConnectionStatus(device) ==
163                     BluetoothProfile.STATE_CONNECTED);
164         } else {
165             profilePref.setChecked(profile.isEnabled(device));
166         }
167 
168         if (profile instanceof A2dpProfile) {
169             A2dpProfile a2dp = (A2dpProfile) profile;
170             SwitchPreference highQualityPref = (SwitchPreference) mProfilesContainer.findPreference(
171                     HIGH_QUALITY_AUDIO_PREF_TAG);
172             if (highQualityPref != null) {
173                 if (a2dp.isEnabled(device) && a2dp.supportsHighQualityAudio(device)) {
174                     highQualityPref.setVisible(true);
175                     highQualityPref.setTitle(a2dp.getHighQualityAudioOptionLabel(device));
176                     highQualityPref.setChecked(a2dp.isHighQualityAudioEnabled(device));
177                     highQualityPref.setEnabled(!mCachedDevice.isBusy());
178                 } else {
179                     highQualityPref.setVisible(false);
180                 }
181             }
182         }
183     }
184 
isLeAudioEnabled()185     private boolean isLeAudioEnabled(){
186         LocalBluetoothProfile leAudio = mProfileManager.getLeAudioProfile();
187         if (leAudio != null) {
188             List<CachedBluetoothDevice> leAudioDeviceList = mProfileDeviceMap.get(
189                     leAudio.toString());
190             if (leAudioDeviceList != null
191                     && leAudioDeviceList.stream()
192                     .anyMatch(item -> leAudio.isEnabled(item.getDevice()))) {
193                 return true;
194             }
195         }
196         return false;
197     }
198 
199     /**
200      * Helper method to enable a profile for a device.
201      */
enableProfile(LocalBluetoothProfile profile)202     private void enableProfile(LocalBluetoothProfile profile) {
203         final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
204         if (profile instanceof PbapServerProfile) {
205             bluetoothDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
206             // We don't need to do the additional steps below for this profile.
207             return;
208         }
209         if (profile instanceof MapProfile) {
210             bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
211         }
212 
213         if (profile instanceof LeAudioProfile) {
214             enableLeAudioProfile(profile);
215             return;
216         }
217 
218         profile.setEnabled(bluetoothDevice, true);
219     }
220 
221     /**
222      * Helper method to disable a profile for a device
223      */
disableProfile(LocalBluetoothProfile profile)224     private void disableProfile(LocalBluetoothProfile profile) {
225         if (profile instanceof LeAudioProfile) {
226             disableLeAudioProfile(profile);
227             return;
228         }
229 
230         final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
231         profile.setEnabled(bluetoothDevice, false);
232 
233         if (profile instanceof MapProfile) {
234             bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
235         } else if (profile instanceof PbapServerProfile) {
236             bluetoothDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
237         }
238     }
239 
240     /**
241      * When the pref for a bluetooth profile is clicked on, we want to toggle the enabled/disabled
242      * state for that profile.
243      */
244     @Override
onPreferenceClick(Preference preference)245     public boolean onPreferenceClick(Preference preference) {
246         LocalBluetoothProfile profile = mProfileManager.getProfileByName(preference.getKey());
247         if (profile == null) {
248             // It might be the PbapServerProfile, which is not stored by name.
249             PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
250             if (TextUtils.equals(preference.getKey(), psp.toString())) {
251                 profile = psp;
252             } else {
253                 return false;
254             }
255         }
256         SwitchPreference profilePref = (SwitchPreference) preference;
257         if (profilePref.isChecked()) {
258             enableProfile(profile);
259         } else {
260             disableProfile(profile);
261         }
262         refreshProfilePreference(profilePref, profile);
263         return true;
264     }
265 
266     /**
267      * Helper to get the list of connectable and special profiles.
268      */
getProfiles()269     private List<LocalBluetoothProfile> getProfiles() {
270         List<LocalBluetoothProfile> result = new ArrayList<LocalBluetoothProfile>();
271         mProfileDeviceMap.clear();
272         if (mAllOfCachedDevices == null || mAllOfCachedDevices.isEmpty()) {
273             return result;
274         }
275         for (CachedBluetoothDevice cachedItem : mAllOfCachedDevices) {
276             List<LocalBluetoothProfile> tmpResult = cachedItem.getConnectableProfiles();
277             for (LocalBluetoothProfile profile : tmpResult) {
278                 if (mProfileDeviceMap.containsKey(profile.toString())) {
279                     mProfileDeviceMap.get(profile.toString()).add(cachedItem);
280                 } else {
281                     List<CachedBluetoothDevice> tmpCachedDeviceList =
282                             new ArrayList<CachedBluetoothDevice>();
283                     tmpCachedDeviceList.add(cachedItem);
284                     mProfileDeviceMap.put(profile.toString(), tmpCachedDeviceList);
285                     result.add(profile);
286                 }
287             }
288         }
289 
290         final BluetoothDevice device = mCachedDevice.getDevice();
291         final int pbapPermission = device.getPhonebookAccessPermission();
292         // Only provide PBAP cabability if the client device has requested PBAP.
293         if (pbapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
294             final PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
295             result.add(psp);
296         }
297 
298         final MapProfile mapProfile = mManager.getProfileManager().getMapProfile();
299         final int mapPermission = device.getMessageAccessPermission();
300         if (mapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
301             result.add(mapProfile);
302         }
303 
304         // Removes phone calls & media audio toggles for dual mode devices
305         boolean leAudioSupported = result.contains(
306                 mManager.getProfileManager().getLeAudioProfile());
307         boolean classicAudioSupported = result.contains(
308                 mManager.getProfileManager().getA2dpProfile()) || result.contains(
309                 mManager.getProfileManager().getHeadsetProfile());
310         if (leAudioSupported && classicAudioSupported) {
311             result.remove(mManager.getProfileManager().getA2dpProfile());
312             result.remove(mManager.getProfileManager().getHeadsetProfile());
313         }
314         Log.d(TAG, "getProfiles:Map:" + mProfileDeviceMap);
315         return result;
316     }
317 
318     /**
319      * Disable the Le Audio profile for each of the Le Audio devices.
320      *
321      * @param profile the LeAudio profile
322      */
disableLeAudioProfile(LocalBluetoothProfile profile)323     private void disableLeAudioProfile(LocalBluetoothProfile profile) {
324         if (profile == null || mProfileDeviceMap.get(profile.toString()) == null) {
325             Log.e(TAG, "There is no the LE profile or no device in mProfileDeviceMap. Do nothing.");
326             return;
327         }
328 
329         LocalBluetoothProfile asha = mProfileManager.getHearingAidProfile();
330 
331         for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) {
332             Log.d(TAG,
333                     "device:" + leAudioDevice.getDevice().getAnonymizedAddress()
334                             + "disable LE profile");
335             profile.setEnabled(leAudioDevice.getDevice(), false);
336             if (asha != null) {
337                 asha.setEnabled(leAudioDevice.getDevice(), true);
338             }
339         }
340 
341         if (!SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false)) {
342             Log.i(TAG, "Enabling classic audio profiles because dual mode is disabled");
343             enableProfileAfterUserDisablesLeAudio(mProfileManager.getA2dpProfile());
344             enableProfileAfterUserDisablesLeAudio(mProfileManager.getHeadsetProfile());
345         }
346     }
347 
348     /**
349      * Enable the Le Audio profile for each of the Le Audio devices.
350      *
351      * @param profile the LeAudio profile
352      */
enableLeAudioProfile(LocalBluetoothProfile profile)353     private void enableLeAudioProfile(LocalBluetoothProfile profile) {
354         if (profile == null || mProfileDeviceMap.get(profile.toString()) == null) {
355             Log.e(TAG, "There is no the LE profile or no device in mProfileDeviceMap. Do nothing.");
356             return;
357         }
358 
359         if (!SystemProperties.getBoolean(ENABLE_DUAL_MODE_AUDIO, false)) {
360             Log.i(TAG, "Disabling classic audio profiles because dual mode is disabled");
361             disableProfileBeforeUserEnablesLeAudio(mProfileManager.getA2dpProfile());
362             disableProfileBeforeUserEnablesLeAudio(mProfileManager.getHeadsetProfile());
363         }
364         LocalBluetoothProfile asha = mProfileManager.getHearingAidProfile();
365 
366         for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) {
367             Log.d(TAG,
368                     "device:" + leAudioDevice.getDevice().getAnonymizedAddress()
369                             + "enable LE profile");
370             profile.setEnabled(leAudioDevice.getDevice(), true);
371             if (asha != null) {
372                 asha.setEnabled(leAudioDevice.getDevice(), false);
373             }
374         }
375     }
376 
disableProfileBeforeUserEnablesLeAudio(LocalBluetoothProfile profile)377     private void disableProfileBeforeUserEnablesLeAudio(LocalBluetoothProfile profile) {
378         if (profile != null && mProfileDeviceMap.get(profile.toString()) != null) {
379             Log.d(TAG, "Disable " + profile.toString() + " before user enables LE");
380             for (CachedBluetoothDevice profileDevice : mProfileDeviceMap.get(profile.toString())) {
381                 if (profile.isEnabled(profileDevice.getDevice())) {
382                     Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":"
383                             + profile.toString() + " set disable");
384                     profile.setEnabled(profileDevice.getDevice(), false);
385                 } else {
386                     Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":"
387                             + profile.toString() + " profile is disabled. Do nothing.");
388                 }
389             }
390         } else {
391             if (profile == null) {
392                 Log.w(TAG, "profile is null");
393             } else {
394                 Log.w(TAG, profile.toString() + " is not in " + mProfileDeviceMap);
395             }
396         }
397     }
398 
enableProfileAfterUserDisablesLeAudio(LocalBluetoothProfile profile)399     private void enableProfileAfterUserDisablesLeAudio(LocalBluetoothProfile profile) {
400         if (profile != null && mProfileDeviceMap.get(profile.toString()) != null) {
401             Log.d(TAG, "enable " + profile.toString() + "after user disables LE");
402             for (CachedBluetoothDevice profileDevice : mProfileDeviceMap.get(profile.toString())) {
403                 if (!profile.isEnabled(profileDevice.getDevice())) {
404                     Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":"
405                             + profile.toString() + " set enable");
406                     profile.setEnabled(profileDevice.getDevice(), true);
407                 } else {
408                     Log.d(TAG, "The " + profileDevice.getDevice().getAnonymizedAddress() + ":"
409                             + profile.toString() + " profile is enabled. Do nothing.");
410                 }
411             }
412         } else {
413             if (profile == null) {
414                 Log.w(TAG, "profile is null");
415             } else {
416                 Log.w(TAG, profile.toString() + " is not in " + mProfileDeviceMap);
417             }
418         }
419     }
420 
421     /**
422      * This is a helper method to be called after adding a Preference for a profile. If that
423      * profile happened to be A2dp and the device supports high quality audio, it will add a
424      * separate preference for controlling whether to actually use high quality audio.
425      *
426      * @param profile the profile just added
427      */
maybeAddHighQualityAudioPref(LocalBluetoothProfile profile)428     private void maybeAddHighQualityAudioPref(LocalBluetoothProfile profile) {
429         if (!(profile instanceof A2dpProfile)) {
430             return;
431         }
432         BluetoothDevice device = mCachedDevice.getDevice();
433         A2dpProfile a2dp = (A2dpProfile) profile;
434         if (a2dp.isProfileReady() && a2dp.supportsHighQualityAudio(device)) {
435             SwitchPreference highQualityAudioPref = new SwitchPreference(
436                     mProfilesContainer.getContext());
437             highQualityAudioPref.setKey(HIGH_QUALITY_AUDIO_PREF_TAG);
438             highQualityAudioPref.setVisible(false);
439             highQualityAudioPref.setOnPreferenceClickListener(clickedPref -> {
440                 boolean enable = ((SwitchPreference) clickedPref).isChecked();
441                 a2dp.setHighQualityAudioEnabled(mCachedDevice.getDevice(), enable);
442                 return true;
443             });
444             mProfilesContainer.addPreference(highQualityAudioPref);
445         }
446     }
447 
448     @Override
onPause()449     public void onPause() {
450         for (CachedBluetoothDevice item : mAllOfCachedDevices) {
451             item.unregisterCallback(this);
452         }
453         mProfileManager.removeServiceListener(this);
454     }
455 
456     @Override
onResume()457     public void onResume() {
458         updateLeAudioConfig();
459         for (CachedBluetoothDevice item : mAllOfCachedDevices) {
460             item.registerCallback(this);
461         }
462         mProfileManager.addServiceListener(this);
463         refresh();
464     }
465 
updateLeAudioConfig()466     private void updateLeAudioConfig() {
467         mIsLeContactSharingEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
468                 SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, true);
469         boolean isLeAudioToggleVisible = SystemProperties.getBoolean(
470                 LE_AUDIO_TOGGLE_VISIBLE_PROPERTY, LE_AUDIO_TOGGLE_VISIBLE_DEFAULT_VALUE);
471         boolean isLeEnabledByDefault = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
472                 CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT, false);
473         mIsLeAudioToggleEnabled = isLeAudioToggleVisible || isLeEnabledByDefault;
474         Log.d(TAG, "BT_LE_AUDIO_CONTACT_SHARING_ENABLED:" + mIsLeContactSharingEnabled
475                 + ", LE_AUDIO_TOGGLE_VISIBLE_PROPERTY:" + isLeAudioToggleVisible
476                 + ", CONFIG_LE_AUDIO_ENABLED_BY_DEFAULT:" + isLeEnabledByDefault);
477     }
478 
479     @Override
onDeviceAttributesChanged()480     public void onDeviceAttributesChanged() {
481         for (CachedBluetoothDevice item : mAllOfCachedDevices) {
482             item.unregisterCallback(this);
483         }
484         mAllOfCachedDevices = Utils.getAllOfCachedBluetoothDevices(mManager, mCachedDevice);
485         for (CachedBluetoothDevice item : mAllOfCachedDevices) {
486             item.registerCallback(this);
487         }
488 
489         super.onDeviceAttributesChanged();
490     }
491 
492     @Override
onServiceConnected()493     public void onServiceConnected() {
494         refresh();
495     }
496 
497     @Override
onServiceDisconnected()498     public void onServiceDisconnected() {
499         refresh();
500     }
501 
502     /**
503      * Refreshes the state of the switches for all profiles, possibly adding or removing switches as
504      * needed.
505      */
506     @Override
refresh()507     protected void refresh() {
508         for (LocalBluetoothProfile profile : getProfiles()) {
509             if (profile == null || !profile.isProfileReady()) {
510                 continue;
511             }
512             SwitchPreference pref = mProfilesContainer.findPreference(
513                     profile.toString());
514             if (pref == null) {
515                 pref = createProfilePreference(mProfilesContainer.getContext(), profile);
516                 mProfilesContainer.addPreference(pref);
517                 maybeAddHighQualityAudioPref(profile);
518             }
519             refreshProfilePreference(pref, profile);
520         }
521         for (LocalBluetoothProfile removedProfile : mCachedDevice.getRemovedProfiles()) {
522             final SwitchPreference pref = mProfilesContainer.findPreference(
523                     removedProfile.toString());
524             if (pref != null) {
525                 mProfilesContainer.removePreference(pref);
526             }
527         }
528 
529         Preference preference = mProfilesContainer.findPreference(KEY_BOTTOM_PREFERENCE);
530         if (preference == null) {
531             preference = new Preference(mContext);
532             preference.setLayoutResource(R.layout.preference_bluetooth_profile_category);
533             preference.setEnabled(false);
534             preference.setKey(KEY_BOTTOM_PREFERENCE);
535             preference.setOrder(ORDINAL);
536             preference.setSelectable(false);
537             mProfilesContainer.addPreference(preference);
538         }
539     }
540 
541     @Override
getPreferenceKey()542     public String getPreferenceKey() {
543         return KEY_PROFILES_GROUP;
544     }
545 }
546