1 /*
2  * Copyright (C) 2011 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.settingslib.bluetooth;
18 
19 import android.bluetooth.BluetoothA2dp;
20 import android.bluetooth.BluetoothA2dpSink;
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothHeadset;
24 import android.bluetooth.BluetoothHeadsetClient;
25 import android.bluetooth.BluetoothHearingAid;
26 import android.bluetooth.BluetoothHidDevice;
27 import android.bluetooth.BluetoothHidHost;
28 import android.bluetooth.BluetoothMap;
29 import android.bluetooth.BluetoothMapClient;
30 import android.bluetooth.BluetoothPan;
31 import android.bluetooth.BluetoothPbap;
32 import android.bluetooth.BluetoothPbapClient;
33 import android.bluetooth.BluetoothProfile;
34 import android.bluetooth.BluetoothSap;
35 import android.bluetooth.BluetoothUuid;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.os.ParcelUuid;
39 import android.util.Log;
40 
41 import androidx.annotation.VisibleForTesting;
42 
43 import com.android.internal.util.ArrayUtils;
44 import com.android.internal.util.CollectionUtils;
45 
46 import java.util.ArrayList;
47 import java.util.Collection;
48 import java.util.HashMap;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.concurrent.CopyOnWriteArrayList;
52 
53 
54 /**
55  * LocalBluetoothProfileManager provides access to the LocalBluetoothProfile
56  * objects for the available Bluetooth profiles.
57  */
58 public class LocalBluetoothProfileManager {
59     private static final String TAG = "LocalBluetoothProfileManager";
60     private static final boolean DEBUG = BluetoothUtils.D;
61 
62     /**
63      * An interface for notifying BluetoothHeadset IPC clients when they have
64      * been connected to the BluetoothHeadset service.
65      * Only used by com.android.settings.bluetooth.DockService.
66      */
67     public interface ServiceListener {
68         /**
69          * Called to notify the client when this proxy object has been
70          * connected to the BluetoothHeadset service. Clients must wait for
71          * this callback before making IPC calls on the BluetoothHeadset
72          * service.
73          */
onServiceConnected()74         void onServiceConnected();
75 
76         /**
77          * Called to notify the client that this proxy object has been
78          * disconnected from the BluetoothHeadset service. Clients must not
79          * make IPC calls on the BluetoothHeadset service after this callback.
80          * This callback will currently only occur if the application hosting
81          * the BluetoothHeadset service, but may be called more often in future.
82          */
onServiceDisconnected()83         void onServiceDisconnected();
84     }
85 
86     private final Context mContext;
87     private final CachedBluetoothDeviceManager mDeviceManager;
88     private final BluetoothEventManager mEventManager;
89 
90     private A2dpProfile mA2dpProfile;
91     private A2dpSinkProfile mA2dpSinkProfile;
92     private HeadsetProfile mHeadsetProfile;
93     private HfpClientProfile mHfpClientProfile;
94     private MapProfile mMapProfile;
95     private MapClientProfile mMapClientProfile;
96     private HidProfile mHidProfile;
97     private HidDeviceProfile mHidDeviceProfile;
98     private OppProfile mOppProfile;
99     private PanProfile mPanProfile;
100     private PbapClientProfile mPbapClientProfile;
101     private PbapServerProfile mPbapProfile;
102     private HearingAidProfile mHearingAidProfile;
103     private SapProfile mSapProfile;
104 
105     /**
106      * Mapping from profile name, e.g. "HEADSET" to profile object.
107      */
108     private final Map<String, LocalBluetoothProfile>
109             mProfileNameMap = new HashMap<String, LocalBluetoothProfile>();
110 
LocalBluetoothProfileManager(Context context, LocalBluetoothAdapter adapter, CachedBluetoothDeviceManager deviceManager, BluetoothEventManager eventManager)111     LocalBluetoothProfileManager(Context context,
112             LocalBluetoothAdapter adapter,
113             CachedBluetoothDeviceManager deviceManager,
114             BluetoothEventManager eventManager) {
115         mContext = context;
116 
117         mDeviceManager = deviceManager;
118         mEventManager = eventManager;
119         // pass this reference to adapter and event manager (circular dependency)
120         adapter.setProfileManager(this);
121 
122         if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
123     }
124 
125     /**
126      * create profile instance according to bluetooth supported profile list
127      */
updateLocalProfiles()128     void updateLocalProfiles() {
129         List<Integer> supportedList = BluetoothAdapter.getDefaultAdapter().getSupportedProfiles();
130         if (CollectionUtils.isEmpty(supportedList)) {
131             if (DEBUG) Log.d(TAG, "supportedList is null");
132             return;
133         }
134         if (mA2dpProfile == null && supportedList.contains(BluetoothProfile.A2DP)) {
135             if (DEBUG) Log.d(TAG, "Adding local A2DP profile");
136             mA2dpProfile = new A2dpProfile(mContext, mDeviceManager, this);
137             addProfile(mA2dpProfile, A2dpProfile.NAME,
138                     BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
139         }
140         if (mA2dpSinkProfile == null && supportedList.contains(BluetoothProfile.A2DP_SINK)) {
141             if (DEBUG) Log.d(TAG, "Adding local A2DP SINK profile");
142             mA2dpSinkProfile = new A2dpSinkProfile(mContext, mDeviceManager, this);
143             addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME,
144                     BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
145         }
146         if (mHeadsetProfile == null && supportedList.contains(BluetoothProfile.HEADSET)) {
147             if (DEBUG) Log.d(TAG, "Adding local HEADSET profile");
148             mHeadsetProfile = new HeadsetProfile(mContext, mDeviceManager, this);
149             addHeadsetProfile(mHeadsetProfile, HeadsetProfile.NAME,
150                     BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
151                     BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
152                     BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
153         }
154         if (mHfpClientProfile == null && supportedList.contains(BluetoothProfile.HEADSET_CLIENT)) {
155             if (DEBUG) Log.d(TAG, "Adding local HfpClient profile");
156             mHfpClientProfile = new HfpClientProfile(mContext, mDeviceManager, this);
157             addHeadsetProfile(mHfpClientProfile, HfpClientProfile.NAME,
158                     BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED,
159                     BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED,
160                     BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
161         }
162         if (mMapClientProfile == null && supportedList.contains(BluetoothProfile.MAP_CLIENT)) {
163             if (DEBUG) Log.d(TAG, "Adding local MAP CLIENT profile");
164             mMapClientProfile = new MapClientProfile(mContext, mDeviceManager,this);
165             addProfile(mMapClientProfile, MapClientProfile.NAME,
166                     BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
167         }
168         if (mMapProfile == null && supportedList.contains(BluetoothProfile.MAP)) {
169             if (DEBUG) Log.d(TAG, "Adding local MAP profile");
170             mMapProfile = new MapProfile(mContext, mDeviceManager, this);
171             addProfile(mMapProfile, MapProfile.NAME, BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
172         }
173         if (mOppProfile == null && supportedList.contains(BluetoothProfile.OPP)) {
174             if (DEBUG) Log.d(TAG, "Adding local OPP profile");
175             mOppProfile = new OppProfile();
176             // Note: no event handler for OPP, only name map.
177             mProfileNameMap.put(OppProfile.NAME, mOppProfile);
178         }
179         if (mHearingAidProfile == null && supportedList.contains(BluetoothProfile.HEARING_AID)) {
180             if (DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
181             mHearingAidProfile = new HearingAidProfile(mContext, mDeviceManager,
182                     this);
183             addProfile(mHearingAidProfile, HearingAidProfile.NAME,
184                     BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
185         }
186         if (mHidProfile == null && supportedList.contains(BluetoothProfile.HID_HOST)) {
187             if (DEBUG) Log.d(TAG, "Adding local HID_HOST profile");
188             mHidProfile = new HidProfile(mContext, mDeviceManager, this);
189             addProfile(mHidProfile, HidProfile.NAME,
190                     BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
191         }
192         if (mHidDeviceProfile == null && supportedList.contains(BluetoothProfile.HID_DEVICE)) {
193             if (DEBUG) Log.d(TAG, "Adding local HID_DEVICE profile");
194             mHidDeviceProfile = new HidDeviceProfile(mContext, mDeviceManager, this);
195             addProfile(mHidDeviceProfile, HidDeviceProfile.NAME,
196                     BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
197         }
198         if (mPanProfile == null && supportedList.contains(BluetoothProfile.PAN)) {
199             if (DEBUG) Log.d(TAG, "Adding local PAN profile");
200             mPanProfile = new PanProfile(mContext);
201             addPanProfile(mPanProfile, PanProfile.NAME,
202                     BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
203         }
204         if (mPbapProfile == null && supportedList.contains(BluetoothProfile.PBAP)) {
205             if (DEBUG) Log.d(TAG, "Adding local PBAP profile");
206             mPbapProfile = new PbapServerProfile(mContext);
207             addProfile(mPbapProfile, PbapServerProfile.NAME,
208                     BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
209         }
210         if (mPbapClientProfile == null && supportedList.contains(BluetoothProfile.PBAP_CLIENT)) {
211             if (DEBUG) Log.d(TAG, "Adding local PBAP Client profile");
212             mPbapClientProfile = new PbapClientProfile(mContext, mDeviceManager,this);
213             addProfile(mPbapClientProfile, PbapClientProfile.NAME,
214                     BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
215         }
216         if (mSapProfile == null && supportedList.contains(BluetoothProfile.SAP)) {
217             if (DEBUG) {
218                 Log.d(TAG, "Adding local SAP profile");
219             }
220             mSapProfile = new SapProfile(mContext, mDeviceManager, this);
221             addProfile(mSapProfile, SapProfile.NAME, BluetoothSap.ACTION_CONNECTION_STATE_CHANGED);
222         }
223         mEventManager.registerProfileIntentReceiver();
224     }
225 
addHeadsetProfile(LocalBluetoothProfile profile, String profileName, String stateChangedAction, String audioStateChangedAction, int audioDisconnectedState)226     private void addHeadsetProfile(LocalBluetoothProfile profile, String profileName,
227             String stateChangedAction, String audioStateChangedAction, int audioDisconnectedState) {
228         BluetoothEventManager.Handler handler = new HeadsetStateChangeHandler(
229                 profile, audioStateChangedAction, audioDisconnectedState);
230         mEventManager.addProfileHandler(stateChangedAction, handler);
231         mEventManager.addProfileHandler(audioStateChangedAction, handler);
232         mProfileNameMap.put(profileName, profile);
233     }
234 
235     private final Collection<ServiceListener> mServiceListeners =
236             new CopyOnWriteArrayList<ServiceListener>();
237 
addProfile(LocalBluetoothProfile profile, String profileName, String stateChangedAction)238     private void addProfile(LocalBluetoothProfile profile,
239             String profileName, String stateChangedAction) {
240         mEventManager.addProfileHandler(stateChangedAction, new StateChangedHandler(profile));
241         mProfileNameMap.put(profileName, profile);
242     }
243 
addPanProfile(LocalBluetoothProfile profile, String profileName, String stateChangedAction)244     private void addPanProfile(LocalBluetoothProfile profile,
245             String profileName, String stateChangedAction) {
246         mEventManager.addProfileHandler(stateChangedAction,
247                 new PanStateChangedHandler(profile));
248         mProfileNameMap.put(profileName, profile);
249     }
250 
getProfileByName(String name)251     public LocalBluetoothProfile getProfileByName(String name) {
252         return mProfileNameMap.get(name);
253     }
254 
255     // Called from LocalBluetoothAdapter when state changes to ON
setBluetoothStateOn()256     void setBluetoothStateOn() {
257         updateLocalProfiles();
258         mEventManager.readPairedDevices();
259     }
260 
261     /**
262      * Generic handler for connection state change events for the specified profile.
263      */
264     private class StateChangedHandler implements BluetoothEventManager.Handler {
265         final LocalBluetoothProfile mProfile;
266 
StateChangedHandler(LocalBluetoothProfile profile)267         StateChangedHandler(LocalBluetoothProfile profile) {
268             mProfile = profile;
269         }
270 
onReceive(Context context, Intent intent, BluetoothDevice device)271         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
272             CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
273             if (cachedDevice == null) {
274                 Log.w(TAG, "StateChangedHandler found new device: " + device);
275                 cachedDevice = mDeviceManager.addDevice(device);
276             }
277             onReceiveInternal(intent, cachedDevice);
278         }
279 
onReceiveInternal(Intent intent, CachedBluetoothDevice cachedDevice)280         protected void onReceiveInternal(Intent intent, CachedBluetoothDevice cachedDevice) {
281             int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
282             int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
283             if (newState == BluetoothProfile.STATE_DISCONNECTED &&
284                     oldState == BluetoothProfile.STATE_CONNECTING) {
285                 Log.i(TAG, "Failed to connect " + mProfile + " device");
286             }
287 
288             if (getHearingAidProfile() != null &&
289                 mProfile instanceof HearingAidProfile &&
290                 (newState == BluetoothProfile.STATE_CONNECTED)) {
291                 // Check if the HiSyncID has being initialized
292                 if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
293                     long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
294                     if (newHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
295                         cachedDevice.setHiSyncId(newHiSyncId);
296                     }
297                 }
298             }
299             cachedDevice.onProfileStateChanged(mProfile, newState);
300             // Dispatch profile changed after device update
301             if (!(cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
302                     && mDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
303                     newState))) {
304                 cachedDevice.refresh();
305                 mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
306                         mProfile.getProfileId());
307             }
308         }
309     }
310 
311     /** Connectivity and audio state change handler for headset profiles. */
312     private class HeadsetStateChangeHandler extends StateChangedHandler {
313         private final String mAudioChangeAction;
314         private final int mAudioDisconnectedState;
315 
HeadsetStateChangeHandler(LocalBluetoothProfile profile, String audioChangeAction, int audioDisconnectedState)316         HeadsetStateChangeHandler(LocalBluetoothProfile profile, String audioChangeAction,
317                 int audioDisconnectedState) {
318             super(profile);
319             mAudioChangeAction = audioChangeAction;
320             mAudioDisconnectedState = audioDisconnectedState;
321         }
322 
323         @Override
onReceiveInternal(Intent intent, CachedBluetoothDevice cachedDevice)324         public void onReceiveInternal(Intent intent, CachedBluetoothDevice cachedDevice) {
325             if (mAudioChangeAction.equals(intent.getAction())) {
326                 int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
327                 if (newState != mAudioDisconnectedState) {
328                     cachedDevice.onProfileStateChanged(mProfile, BluetoothProfile.STATE_CONNECTED);
329                 }
330                 cachedDevice.refresh();
331             } else {
332                 super.onReceiveInternal(intent, cachedDevice);
333             }
334         }
335     }
336 
337     /** State change handler for NAP and PANU profiles. */
338     private class PanStateChangedHandler extends StateChangedHandler {
339 
PanStateChangedHandler(LocalBluetoothProfile profile)340         PanStateChangedHandler(LocalBluetoothProfile profile) {
341             super(profile);
342         }
343 
344         @Override
onReceive(Context context, Intent intent, BluetoothDevice device)345         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
346             PanProfile panProfile = (PanProfile) mProfile;
347             int role = intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, 0);
348             panProfile.setLocalRole(device, role);
349             super.onReceive(context, intent, device);
350         }
351     }
352 
353     // called from DockService
addServiceListener(ServiceListener l)354     public void addServiceListener(ServiceListener l) {
355         mServiceListeners.add(l);
356     }
357 
358     // called from DockService
removeServiceListener(ServiceListener l)359     public void removeServiceListener(ServiceListener l) {
360         mServiceListeners.remove(l);
361     }
362 
363     // not synchronized: use only from UI thread! (TODO: verify)
callServiceConnectedListeners()364     void callServiceConnectedListeners() {
365         final Collection<ServiceListener> listeners = new ArrayList<>(mServiceListeners);
366 
367         for (ServiceListener l : listeners) {
368             l.onServiceConnected();
369         }
370     }
371 
372     // not synchronized: use only from UI thread! (TODO: verify)
callServiceDisconnectedListeners()373     void callServiceDisconnectedListeners() {
374         final Collection<ServiceListener> listeners = new ArrayList<>(mServiceListeners);
375 
376         for (ServiceListener listener : listeners) {
377             listener.onServiceDisconnected();
378         }
379     }
380 
381     // This is called by DockService, so check Headset and A2DP.
isManagerReady()382     public synchronized boolean isManagerReady() {
383         // Getting just the headset profile is fine for now. Will need to deal with A2DP
384         // and others if they aren't always in a ready state.
385         LocalBluetoothProfile profile = mHeadsetProfile;
386         if (profile != null) {
387             return profile.isProfileReady();
388         }
389         profile = mA2dpProfile;
390         if (profile != null) {
391             return profile.isProfileReady();
392         }
393         profile = mA2dpSinkProfile;
394         if (profile != null) {
395             return profile.isProfileReady();
396         }
397         return false;
398     }
399 
getA2dpProfile()400     public A2dpProfile getA2dpProfile() {
401         return mA2dpProfile;
402     }
403 
getA2dpSinkProfile()404     public A2dpSinkProfile getA2dpSinkProfile() {
405         if ((mA2dpSinkProfile != null) && (mA2dpSinkProfile.isProfileReady())) {
406             return mA2dpSinkProfile;
407         } else {
408             return null;
409         }
410     }
411 
getHeadsetProfile()412     public HeadsetProfile getHeadsetProfile() {
413         return mHeadsetProfile;
414     }
415 
getHfpClientProfile()416     public HfpClientProfile getHfpClientProfile() {
417         if ((mHfpClientProfile != null) && (mHfpClientProfile.isProfileReady())) {
418             return mHfpClientProfile;
419         } else {
420           return null;
421         }
422     }
423 
getPbapClientProfile()424     public PbapClientProfile getPbapClientProfile() {
425         return mPbapClientProfile;
426     }
427 
getPbapProfile()428     public PbapServerProfile getPbapProfile(){
429         return mPbapProfile;
430     }
431 
getMapProfile()432     public MapProfile getMapProfile(){
433         return mMapProfile;
434     }
435 
getMapClientProfile()436     public MapClientProfile getMapClientProfile() {
437         return mMapClientProfile;
438     }
439 
getHearingAidProfile()440     public HearingAidProfile getHearingAidProfile() {
441         return mHearingAidProfile;
442     }
443 
444     @VisibleForTesting
getHidProfile()445     HidProfile getHidProfile() {
446         return mHidProfile;
447     }
448 
449     @VisibleForTesting
getHidDeviceProfile()450     HidDeviceProfile getHidDeviceProfile() {
451         return mHidDeviceProfile;
452     }
453 
454     /**
455      * Fill in a list of LocalBluetoothProfile objects that are supported by
456      * the local device and the remote device.
457      *
458      * @param uuids of the remote device
459      * @param localUuids UUIDs of the local device
460      * @param profiles The list of profiles to fill
461      * @param removedProfiles list of profiles that were removed
462      */
updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids, Collection<LocalBluetoothProfile> profiles, Collection<LocalBluetoothProfile> removedProfiles, boolean isPanNapConnected, BluetoothDevice device)463     synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
464             Collection<LocalBluetoothProfile> profiles,
465             Collection<LocalBluetoothProfile> removedProfiles,
466             boolean isPanNapConnected, BluetoothDevice device) {
467         // Copy previous profile list into removedProfiles
468         removedProfiles.clear();
469         removedProfiles.addAll(profiles);
470         if (DEBUG) {
471             Log.d(TAG,"Current Profiles" + profiles.toString());
472         }
473         profiles.clear();
474 
475         if (uuids == null) {
476             return;
477         }
478 
479         if (mHeadsetProfile != null) {
480             if ((ArrayUtils.contains(localUuids, BluetoothUuid.HSP_AG)
481                     && ArrayUtils.contains(uuids, BluetoothUuid.HSP))
482                     || (ArrayUtils.contains(localUuids, BluetoothUuid.HFP_AG)
483                     && ArrayUtils.contains(uuids, BluetoothUuid.HFP))) {
484                 profiles.add(mHeadsetProfile);
485                 removedProfiles.remove(mHeadsetProfile);
486             }
487         }
488 
489         if ((mHfpClientProfile != null) &&
490                 ArrayUtils.contains(uuids, BluetoothUuid.HFP_AG)
491                 && ArrayUtils.contains(localUuids, BluetoothUuid.HFP)) {
492             profiles.add(mHfpClientProfile);
493             removedProfiles.remove(mHfpClientProfile);
494         }
495 
496         if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) && mA2dpProfile != null) {
497             profiles.add(mA2dpProfile);
498             removedProfiles.remove(mA2dpProfile);
499         }
500 
501         if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS)
502                 && mA2dpSinkProfile != null) {
503                 profiles.add(mA2dpSinkProfile);
504                 removedProfiles.remove(mA2dpSinkProfile);
505         }
506 
507         if (ArrayUtils.contains(uuids, BluetoothUuid.OBEX_OBJECT_PUSH) && mOppProfile != null) {
508             profiles.add(mOppProfile);
509             removedProfiles.remove(mOppProfile);
510         }
511 
512         if ((ArrayUtils.contains(uuids, BluetoothUuid.HID)
513                 || ArrayUtils.contains(uuids, BluetoothUuid.HOGP)) && mHidProfile != null) {
514             profiles.add(mHidProfile);
515             removedProfiles.remove(mHidProfile);
516         }
517 
518         if (mHidDeviceProfile != null && mHidDeviceProfile.getConnectionStatus(device)
519                 != BluetoothProfile.STATE_DISCONNECTED) {
520             profiles.add(mHidDeviceProfile);
521             removedProfiles.remove(mHidDeviceProfile);
522         }
523 
524         if(isPanNapConnected)
525             if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists.");
526         if ((ArrayUtils.contains(uuids, BluetoothUuid.NAP) && mPanProfile != null)
527                 || isPanNapConnected) {
528             profiles.add(mPanProfile);
529             removedProfiles.remove(mPanProfile);
530         }
531 
532         if ((mMapProfile != null) &&
533             (mMapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
534             profiles.add(mMapProfile);
535             removedProfiles.remove(mMapProfile);
536             mMapProfile.setEnabled(device, true);
537         }
538 
539         if ((mPbapProfile != null) &&
540             (mPbapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
541             profiles.add(mPbapProfile);
542             removedProfiles.remove(mPbapProfile);
543             mPbapProfile.setEnabled(device, true);
544         }
545 
546         if (mMapClientProfile != null) {
547             profiles.add(mMapClientProfile);
548             removedProfiles.remove(mMapClientProfile);
549         }
550 
551         if ((mPbapClientProfile != null) && ArrayUtils.contains(localUuids, BluetoothUuid.PBAP_PCE)
552                 && BluetoothUuid.containsAnyUuid(uuids, PbapClientProfile.SRC_UUIDS)) {
553             profiles.add(mPbapClientProfile);
554             removedProfiles.remove(mPbapClientProfile);
555         }
556 
557         if (ArrayUtils.contains(uuids, BluetoothUuid.HEARING_AID) && mHearingAidProfile != null) {
558             profiles.add(mHearingAidProfile);
559             removedProfiles.remove(mHearingAidProfile);
560         }
561 
562         if (mSapProfile != null && ArrayUtils.contains(uuids, BluetoothUuid.SAP)) {
563             profiles.add(mSapProfile);
564             removedProfiles.remove(mSapProfile);
565         }
566 
567         if (DEBUG) {
568             Log.d(TAG,"New Profiles" + profiles.toString());
569         }
570     }
571 }
572