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