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