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