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