• 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.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