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