blob: 1e0cce98c791d024e9bb60b9174a50e5373fa1f0 [file] [log] [blame]
Jason Monk7ce96b92015-02-02 11:27:58 -05001/*
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
17package com.android.settingslib.bluetooth;
18
19import android.bluetooth.BluetoothA2dp;
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070020import android.bluetooth.BluetoothA2dpSink;
Jason Monk7ce96b92015-02-02 11:27:58 -050021import android.bluetooth.BluetoothDevice;
22import android.bluetooth.BluetoothHeadset;
Sanket Agarwalf8a1c912016-01-26 20:12:52 -080023import android.bluetooth.BluetoothHeadsetClient;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080024import android.bluetooth.BluetoothHearingAid;
Hansong Zhangdcae2932017-11-13 10:56:23 -080025import android.bluetooth.BluetoothHidDevice;
Hansong Zhang0edf7542017-10-20 15:55:59 -070026import android.bluetooth.BluetoothHidHost;
Jason Monk7ce96b92015-02-02 11:27:58 -050027import android.bluetooth.BluetoothMap;
Joseph Pirozzo631768d2016-09-01 14:19:28 -070028import android.bluetooth.BluetoothMapClient;
Jason Monk7ce96b92015-02-02 11:27:58 -050029import android.bluetooth.BluetoothPan;
Hemant Guptadbc3d8d2017-05-12 21:14:44 +053030import android.bluetooth.BluetoothPbap;
Joseph Pirozzo563c7002016-03-21 15:49:48 -070031import android.bluetooth.BluetoothPbapClient;
Jason Monk7ce96b92015-02-02 11:27:58 -050032import android.bluetooth.BluetoothProfile;
33import android.bluetooth.BluetoothUuid;
34import android.content.Context;
35import android.content.Intent;
36import android.os.ParcelUuid;
Hansong Zhang490d0202018-04-10 16:06:19 -070037import android.support.annotation.VisibleForTesting;
Jason Monk7ce96b92015-02-02 11:27:58 -050038import android.util.Log;
Andrew Sapperstein7aeb3f52017-04-19 20:03:42 -070039import com.android.internal.R;
Jason Monk7ce96b92015-02-02 11:27:58 -050040import java.util.ArrayList;
41import java.util.Collection;
42import java.util.HashMap;
43import java.util.Map;
Jason Monk7ce96b92015-02-02 11:27:58 -050044
Hansong Zhang490d0202018-04-10 16:06:19 -070045
Jason Monk7ce96b92015-02-02 11:27:58 -050046/**
47 * LocalBluetoothProfileManager provides access to the LocalBluetoothProfile
48 * objects for the available Bluetooth profiles.
49 */
Antony Sargent374d2592017-04-20 11:23:34 -070050public class LocalBluetoothProfileManager {
Jason Monk7ce96b92015-02-02 11:27:58 -050051 private static final String TAG = "LocalBluetoothProfileManager";
52 private static final boolean DEBUG = Utils.D;
53 /** Singleton instance. */
54 private static LocalBluetoothProfileManager sInstance;
55
56 /**
57 * An interface for notifying BluetoothHeadset IPC clients when they have
58 * been connected to the BluetoothHeadset service.
59 * Only used by com.android.settings.bluetooth.DockService.
60 */
61 public interface ServiceListener {
62 /**
63 * Called to notify the client when this proxy object has been
64 * connected to the BluetoothHeadset service. Clients must wait for
65 * this callback before making IPC calls on the BluetoothHeadset
66 * service.
67 */
68 void onServiceConnected();
69
70 /**
71 * Called to notify the client that this proxy object has been
72 * disconnected from the BluetoothHeadset service. Clients must not
73 * make IPC calls on the BluetoothHeadset service after this callback.
74 * This callback will currently only occur if the application hosting
75 * the BluetoothHeadset service, but may be called more often in future.
76 */
77 void onServiceDisconnected();
78 }
79
80 private final Context mContext;
81 private final LocalBluetoothAdapter mLocalAdapter;
82 private final CachedBluetoothDeviceManager mDeviceManager;
83 private final BluetoothEventManager mEventManager;
84
85 private A2dpProfile mA2dpProfile;
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070086 private A2dpSinkProfile mA2dpSinkProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -050087 private HeadsetProfile mHeadsetProfile;
Sanket Agarwalf8a1c912016-01-26 20:12:52 -080088 private HfpClientProfile mHfpClientProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -050089 private MapProfile mMapProfile;
Joseph Pirozzo631768d2016-09-01 14:19:28 -070090 private MapClientProfile mMapClientProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -050091 private final HidProfile mHidProfile;
Hansong Zhangdcae2932017-11-13 10:56:23 -080092 private HidDeviceProfile mHidDeviceProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -050093 private OppProfile mOppProfile;
94 private final PanProfile mPanProfile;
Joseph Pirozzo563c7002016-03-21 15:49:48 -070095 private PbapClientProfile mPbapClientProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -050096 private final PbapServerProfile mPbapProfile;
Joseph Pirozzo563c7002016-03-21 15:49:48 -070097 private final boolean mUsePbapPce;
Joseph Pirozzo631768d2016-09-01 14:19:28 -070098 private final boolean mUseMapClient;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -080099 private HearingAidProfile mHearingAidProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -0500100
101 /**
102 * Mapping from profile name, e.g. "HEADSET" to profile object.
103 */
104 private final Map<String, LocalBluetoothProfile>
105 mProfileNameMap = new HashMap<String, LocalBluetoothProfile>();
106
107 LocalBluetoothProfileManager(Context context,
108 LocalBluetoothAdapter adapter,
109 CachedBluetoothDeviceManager deviceManager,
110 BluetoothEventManager eventManager) {
111 mContext = context;
112
113 mLocalAdapter = adapter;
114 mDeviceManager = deviceManager;
115 mEventManager = eventManager;
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700116 mUsePbapPce = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
Joseph Pirozzo631768d2016-09-01 14:19:28 -0700117 // MAP Client is typically used in the same situations as PBAP Client
118 mUseMapClient = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
Jason Monk7ce96b92015-02-02 11:27:58 -0500119 // pass this reference to adapter and event manager (circular dependency)
120 mLocalAdapter.setProfileManager(this);
121 mEventManager.setProfileManager(this);
122
123 ParcelUuid[] uuids = adapter.getUuids();
124
125 // uuids may be null if Bluetooth is turned off
126 if (uuids != null) {
127 updateLocalProfiles(uuids);
128 }
129
Hansong Zhangdcae2932017-11-13 10:56:23 -0800130 // Always add HID host, HID device, and PAN profiles
Jason Monk7ce96b92015-02-02 11:27:58 -0500131 mHidProfile = new HidProfile(context, mLocalAdapter, mDeviceManager, this);
132 addProfile(mHidProfile, HidProfile.NAME,
Hansong Zhang0edf7542017-10-20 15:55:59 -0700133 BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
Jason Monk7ce96b92015-02-02 11:27:58 -0500134
Hansong Zhang490d0202018-04-10 16:06:19 -0700135 mPanProfile = new PanProfile(context, mLocalAdapter);
Jason Monk7ce96b92015-02-02 11:27:58 -0500136 addPanProfile(mPanProfile, PanProfile.NAME,
137 BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
138
Hansong Zhangdcae2932017-11-13 10:56:23 -0800139 mHidDeviceProfile = new HidDeviceProfile(context, mLocalAdapter, mDeviceManager, this);
140 addProfile(mHidDeviceProfile, HidDeviceProfile.NAME,
141 BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
142
Jason Monk7ce96b92015-02-02 11:27:58 -0500143 if(DEBUG) Log.d(TAG, "Adding local MAP profile");
Joseph Pirozzo631768d2016-09-01 14:19:28 -0700144 if (mUseMapClient) {
145 mMapClientProfile = new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
146 addProfile(mMapClientProfile, MapClientProfile.NAME,
147 BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
148 } else {
149 mMapProfile = new MapProfile(mContext, mLocalAdapter, mDeviceManager, this);
150 addProfile(mMapProfile, MapProfile.NAME,
151 BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
152 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500153
Hemant Guptadbc3d8d2017-05-12 21:14:44 +0530154 //Create PBAP server profile
155 if(DEBUG) Log.d(TAG, "Adding local PBAP profile");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800156
Jason Monk7ce96b92015-02-02 11:27:58 -0500157 mPbapProfile = new PbapServerProfile(context);
Hemant Guptadbc3d8d2017-05-12 21:14:44 +0530158 addProfile(mPbapProfile, PbapServerProfile.NAME,
159 BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
Jason Monk7ce96b92015-02-02 11:27:58 -0500160
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800161 mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
162 addProfile(mHearingAidProfile, HearingAidProfile.NAME,
163 BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
Jason Monk7ce96b92015-02-02 11:27:58 -0500164 if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
165 }
166
167 /**
168 * Initialize or update the local profile objects. If a UUID was previously
169 * present but has been removed, we print a warning but don't remove the
170 * profile object as it might be referenced elsewhere, or the UUID might
171 * come back and we don't want multiple copies of the profile objects.
172 * @param uuids
173 */
174 void updateLocalProfiles(ParcelUuid[] uuids) {
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700175 // A2DP SRC
Jason Monk7ce96b92015-02-02 11:27:58 -0500176 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource)) {
177 if (mA2dpProfile == null) {
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700178 if(DEBUG) Log.d(TAG, "Adding local A2DP SRC profile");
Jason Monk7ce96b92015-02-02 11:27:58 -0500179 mA2dpProfile = new A2dpProfile(mContext, mLocalAdapter, mDeviceManager, this);
180 addProfile(mA2dpProfile, A2dpProfile.NAME,
181 BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
182 }
183 } else if (mA2dpProfile != null) {
184 Log.w(TAG, "Warning: A2DP profile was previously added but the UUID is now missing.");
185 }
186
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800187 // A2DP SINK
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700188 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink)) {
189 if (mA2dpSinkProfile == null) {
190 if(DEBUG) Log.d(TAG, "Adding local A2DP Sink profile");
191 mA2dpSinkProfile = new A2dpSinkProfile(mContext, mLocalAdapter, mDeviceManager, this);
192 addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME,
193 BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
194 }
195 } else if (mA2dpSinkProfile != null) {
196 Log.w(TAG, "Warning: A2DP Sink profile was previously added but the UUID is now missing.");
197 }
198
Jason Monk7ce96b92015-02-02 11:27:58 -0500199 // Headset / Handsfree
200 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
201 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
202 if (mHeadsetProfile == null) {
203 if (DEBUG) Log.d(TAG, "Adding local HEADSET profile");
204 mHeadsetProfile = new HeadsetProfile(mContext, mLocalAdapter,
205 mDeviceManager, this);
Amin Shaikhddcb7df2018-04-05 14:05:56 -0400206 addHeadsetProfile(mHeadsetProfile, HeadsetProfile.NAME,
207 BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
208 BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
209 BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
Jason Monk7ce96b92015-02-02 11:27:58 -0500210 }
211 } else if (mHeadsetProfile != null) {
212 Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing.");
213 }
214
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800215 // Headset HF
216 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree)) {
217 if (mHfpClientProfile == null) {
218 if(DEBUG) Log.d(TAG, "Adding local HfpClient profile");
219 mHfpClientProfile =
220 new HfpClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
Amin Shaikhddcb7df2018-04-05 14:05:56 -0400221 addHeadsetProfile(mHfpClientProfile, HfpClientProfile.NAME,
222 BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED,
223 BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED,
224 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800225 }
226 } else if (mHfpClientProfile != null) {
227 Log.w(TAG,
228 "Warning: Hfp Client profile was previously added but the UUID is now missing.");
229 } else {
230 Log.d(TAG, "Handsfree Uuid not found.");
231 }
232
Joseph Pirozzo631768d2016-09-01 14:19:28 -0700233 // Message Access Profile Client
234 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.MNS)) {
235 if (mMapClientProfile == null) {
236 if(DEBUG) Log.d(TAG, "Adding local Map Client profile");
237 mMapClientProfile =
238 new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
239 addProfile(mMapClientProfile, MapClientProfile.NAME,
240 BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
241 }
242 } else if (mMapClientProfile != null) {
243 Log.w(TAG,
244 "Warning: MAP Client profile was previously added but the UUID is now missing.");
245 } else {
246 Log.d(TAG, "MAP Client Uuid not found.");
247 }
248
Jason Monk7ce96b92015-02-02 11:27:58 -0500249 // OPP
250 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
251 if (mOppProfile == null) {
252 if(DEBUG) Log.d(TAG, "Adding local OPP profile");
253 mOppProfile = new OppProfile();
254 // Note: no event handler for OPP, only name map.
255 mProfileNameMap.put(OppProfile.NAME, mOppProfile);
256 }
257 } else if (mOppProfile != null) {
258 Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing.");
259 }
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700260
261 //PBAP Client
262 if (mUsePbapPce) {
263 if (mPbapClientProfile == null) {
264 if(DEBUG) Log.d(TAG, "Adding local PBAP Client profile");
265 mPbapClientProfile = new PbapClientProfile(mContext, mLocalAdapter, mDeviceManager,
266 this);
267 addProfile(mPbapClientProfile, PbapClientProfile.NAME,
268 BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
269 }
270 } else if (mPbapClientProfile != null) {
271 Log.w(TAG,
272 "Warning: PBAP Client profile was previously added but the UUID is now missing.");
273 }
274
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800275 //Hearing Aid Client
276 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
277 if (mHearingAidProfile == null) {
278 if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
279 mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
280 addProfile(mHearingAidProfile, HearingAidProfile.NAME,
281 BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
282 }
283 } else if (mHearingAidProfile != null) {
284 Log.w(TAG, "Warning: Hearing Aid profile was previously added but the UUID is now missing.");
285 }
286
Jason Monk7ce96b92015-02-02 11:27:58 -0500287 mEventManager.registerProfileIntentReceiver();
288
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700289 // There is no local SDP record for HID and Settings app doesn't control PBAP Server.
Jason Monk7ce96b92015-02-02 11:27:58 -0500290 }
291
Amin Shaikhddcb7df2018-04-05 14:05:56 -0400292 private void addHeadsetProfile(LocalBluetoothProfile profile, String profileName,
293 String stateChangedAction, String audioStateChangedAction, int audioDisconnectedState) {
294 BluetoothEventManager.Handler handler = new HeadsetStateChangeHandler(
295 profile, audioStateChangedAction, audioDisconnectedState);
296 mEventManager.addProfileHandler(stateChangedAction, handler);
297 mEventManager.addProfileHandler(audioStateChangedAction, handler);
298 mProfileNameMap.put(profileName, profile);
299 }
300
Jason Monk7ce96b92015-02-02 11:27:58 -0500301 private final Collection<ServiceListener> mServiceListeners =
302 new ArrayList<ServiceListener>();
303
304 private void addProfile(LocalBluetoothProfile profile,
305 String profileName, String stateChangedAction) {
306 mEventManager.addProfileHandler(stateChangedAction, new StateChangedHandler(profile));
307 mProfileNameMap.put(profileName, profile);
308 }
309
310 private void addPanProfile(LocalBluetoothProfile profile,
311 String profileName, String stateChangedAction) {
312 mEventManager.addProfileHandler(stateChangedAction,
313 new PanStateChangedHandler(profile));
314 mProfileNameMap.put(profileName, profile);
315 }
316
317 public LocalBluetoothProfile getProfileByName(String name) {
318 return mProfileNameMap.get(name);
319 }
320
321 // Called from LocalBluetoothAdapter when state changes to ON
322 void setBluetoothStateOn() {
323 ParcelUuid[] uuids = mLocalAdapter.getUuids();
324 if (uuids != null) {
325 updateLocalProfiles(uuids);
326 }
327 mEventManager.readPairedDevices();
328 }
329
330 /**
331 * Generic handler for connection state change events for the specified profile.
332 */
333 private class StateChangedHandler implements BluetoothEventManager.Handler {
334 final LocalBluetoothProfile mProfile;
335
336 StateChangedHandler(LocalBluetoothProfile profile) {
337 mProfile = profile;
338 }
339
340 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
341 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
342 if (cachedDevice == null) {
343 Log.w(TAG, "StateChangedHandler found new device: " + device);
344 cachedDevice = mDeviceManager.addDevice(mLocalAdapter,
345 LocalBluetoothProfileManager.this, device);
346 }
Amin Shaikhddcb7df2018-04-05 14:05:56 -0400347 onReceiveInternal(intent, cachedDevice);
348 }
349
350 protected void onReceiveInternal(Intent intent, CachedBluetoothDevice cachedDevice) {
Jason Monk7ce96b92015-02-02 11:27:58 -0500351 int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
352 int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
353 if (newState == BluetoothProfile.STATE_DISCONNECTED &&
354 oldState == BluetoothProfile.STATE_CONNECTING) {
355 Log.i(TAG, "Failed to connect " + mProfile + " device");
356 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500357 cachedDevice.onProfileStateChanged(mProfile, newState);
358 cachedDevice.refresh();
359 }
360 }
361
Amin Shaikhddcb7df2018-04-05 14:05:56 -0400362 /** Connectivity and audio state change handler for headset profiles. */
363 private class HeadsetStateChangeHandler extends StateChangedHandler {
364 private final String mAudioChangeAction;
365 private final int mAudioDisconnectedState;
366
367 HeadsetStateChangeHandler(LocalBluetoothProfile profile, String audioChangeAction,
368 int audioDisconnectedState) {
369 super(profile);
370 mAudioChangeAction = audioChangeAction;
371 mAudioDisconnectedState = audioDisconnectedState;
372 }
373
374 @Override
375 public void onReceiveInternal(Intent intent, CachedBluetoothDevice cachedDevice) {
376 if (mAudioChangeAction.equals(intent.getAction())) {
377 int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
378 if (newState != mAudioDisconnectedState) {
379 cachedDevice.onProfileStateChanged(mProfile, BluetoothProfile.STATE_CONNECTED);
380 }
381 cachedDevice.refresh();
382 } else {
383 super.onReceiveInternal(intent, cachedDevice);
384 }
385 }
386 }
387
Jason Monk7ce96b92015-02-02 11:27:58 -0500388 /** State change handler for NAP and PANU profiles. */
389 private class PanStateChangedHandler extends StateChangedHandler {
390
391 PanStateChangedHandler(LocalBluetoothProfile profile) {
392 super(profile);
393 }
394
395 @Override
396 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
397 PanProfile panProfile = (PanProfile) mProfile;
398 int role = intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, 0);
399 panProfile.setLocalRole(device, role);
400 super.onReceive(context, intent, device);
401 }
402 }
403
404 // called from DockService
405 public void addServiceListener(ServiceListener l) {
406 mServiceListeners.add(l);
407 }
408
409 // called from DockService
410 public void removeServiceListener(ServiceListener l) {
411 mServiceListeners.remove(l);
412 }
413
414 // not synchronized: use only from UI thread! (TODO: verify)
415 void callServiceConnectedListeners() {
416 for (ServiceListener l : mServiceListeners) {
417 l.onServiceConnected();
418 }
419 }
420
421 // not synchronized: use only from UI thread! (TODO: verify)
422 void callServiceDisconnectedListeners() {
423 for (ServiceListener listener : mServiceListeners) {
424 listener.onServiceDisconnected();
425 }
426 }
427
428 // This is called by DockService, so check Headset and A2DP.
429 public synchronized boolean isManagerReady() {
430 // Getting just the headset profile is fine for now. Will need to deal with A2DP
431 // and others if they aren't always in a ready state.
432 LocalBluetoothProfile profile = mHeadsetProfile;
433 if (profile != null) {
434 return profile.isProfileReady();
435 }
436 profile = mA2dpProfile;
437 if (profile != null) {
438 return profile.isProfileReady();
439 }
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700440 profile = mA2dpSinkProfile;
441 if (profile != null) {
442 return profile.isProfileReady();
443 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500444 return false;
445 }
446
447 public A2dpProfile getA2dpProfile() {
448 return mA2dpProfile;
449 }
450
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800451 public A2dpSinkProfile getA2dpSinkProfile() {
452 if ((mA2dpSinkProfile != null) && (mA2dpSinkProfile.isProfileReady())) {
453 return mA2dpSinkProfile;
454 } else {
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700455 return null;
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800456 }
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700457 }
458
Jason Monk7ce96b92015-02-02 11:27:58 -0500459 public HeadsetProfile getHeadsetProfile() {
460 return mHeadsetProfile;
461 }
462
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800463 public HfpClientProfile getHfpClientProfile() {
464 if ((mHfpClientProfile != null) && (mHfpClientProfile.isProfileReady())) {
465 return mHfpClientProfile;
466 } else {
467 return null;
468 }
469 }
470
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700471 public PbapClientProfile getPbapClientProfile() {
472 return mPbapClientProfile;
473 }
474
Jason Monk7ce96b92015-02-02 11:27:58 -0500475 public PbapServerProfile getPbapProfile(){
476 return mPbapProfile;
477 }
478
479 public MapProfile getMapProfile(){
480 return mMapProfile;
481 }
482
Joseph Pirozzo631768d2016-09-01 14:19:28 -0700483 public MapClientProfile getMapClientProfile() {
484 return mMapClientProfile;
485 }
486
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800487 public HearingAidProfile getHearingAidProfile() {
488 return mHearingAidProfile;
489 }
490
Hansong Zhang490d0202018-04-10 16:06:19 -0700491 @VisibleForTesting
492 HidProfile getHidProfile() {
493 return mHidProfile;
494 }
495
496 @VisibleForTesting
497 HidDeviceProfile getHidDeviceProfile() {
498 return mHidDeviceProfile;
499 }
500
Jason Monk7ce96b92015-02-02 11:27:58 -0500501 /**
502 * Fill in a list of LocalBluetoothProfile objects that are supported by
503 * the local device and the remote device.
504 *
505 * @param uuids of the remote device
506 * @param localUuids UUIDs of the local device
507 * @param profiles The list of profiles to fill
508 * @param removedProfiles list of profiles that were removed
509 */
510 synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
511 Collection<LocalBluetoothProfile> profiles,
512 Collection<LocalBluetoothProfile> removedProfiles,
513 boolean isPanNapConnected, BluetoothDevice device) {
514 // Copy previous profile list into removedProfiles
515 removedProfiles.clear();
516 removedProfiles.addAll(profiles);
Joseph Pirozzo99fecc02016-04-07 13:43:49 -0700517 if (DEBUG) {
518 Log.d(TAG,"Current Profiles" + profiles.toString());
519 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500520 profiles.clear();
521
522 if (uuids == null) {
523 return;
524 }
525
526 if (mHeadsetProfile != null) {
527 if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) &&
528 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
529 (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
530 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
531 profiles.add(mHeadsetProfile);
532 removedProfiles.remove(mHeadsetProfile);
533 }
534 }
535
Joseph Pirozzo99fecc02016-04-07 13:43:49 -0700536 if ((mHfpClientProfile != null) &&
537 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) &&
538 BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree)) {
539 profiles.add(mHfpClientProfile);
540 removedProfiles.remove(mHfpClientProfile);
541 }
542
Jason Monk7ce96b92015-02-02 11:27:58 -0500543 if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
544 mA2dpProfile != null) {
545 profiles.add(mA2dpProfile);
546 removedProfiles.remove(mA2dpProfile);
547 }
548
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700549 if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS) &&
550 mA2dpSinkProfile != null) {
551 profiles.add(mA2dpSinkProfile);
552 removedProfiles.remove(mA2dpSinkProfile);
Joseph Pirozzo99fecc02016-04-07 13:43:49 -0700553 }
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700554
Jason Monk7ce96b92015-02-02 11:27:58 -0500555 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
556 mOppProfile != null) {
557 profiles.add(mOppProfile);
558 removedProfiles.remove(mOppProfile);
559 }
560
561 if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) ||
562 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) &&
563 mHidProfile != null) {
564 profiles.add(mHidProfile);
565 removedProfiles.remove(mHidProfile);
566 }
567
Hansong Zhang490d0202018-04-10 16:06:19 -0700568 if (mHidDeviceProfile != null && mHidDeviceProfile.getConnectionStatus(device)
Hansong Zhangdcae2932017-11-13 10:56:23 -0800569 != BluetoothProfile.STATE_DISCONNECTED) {
570 profiles.add(mHidDeviceProfile);
571 removedProfiles.remove(mHidDeviceProfile);
572 }
573
Jason Monk7ce96b92015-02-02 11:27:58 -0500574 if(isPanNapConnected)
575 if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists.");
576 if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
577 mPanProfile != null) || isPanNapConnected) {
578 profiles.add(mPanProfile);
579 removedProfiles.remove(mPanProfile);
580 }
581
582 if ((mMapProfile != null) &&
583 (mMapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
584 profiles.add(mMapProfile);
585 removedProfiles.remove(mMapProfile);
586 mMapProfile.setPreferred(device, true);
587 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500588
Hemant Guptadbc3d8d2017-05-12 21:14:44 +0530589 if ((mPbapProfile != null) &&
590 (mPbapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
591 profiles.add(mPbapProfile);
592 removedProfiles.remove(mPbapProfile);
593 mPbapProfile.setPreferred(device, true);
594 }
595
Joseph Pirozzo631768d2016-09-01 14:19:28 -0700596 if (mMapClientProfile != null) {
597 profiles.add(mMapClientProfile);
598 removedProfiles.remove(mMapClientProfile);
599 }
600
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700601 if (mUsePbapPce) {
602 profiles.add(mPbapClientProfile);
603 removedProfiles.remove(mPbapClientProfile);
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700604 }
Joseph Pirozzo99fecc02016-04-07 13:43:49 -0700605
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800606 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid) &&
607 mHearingAidProfile != null) {
608 profiles.add(mHearingAidProfile);
609 removedProfiles.remove(mHearingAidProfile);
610 }
611
Joseph Pirozzo99fecc02016-04-07 13:43:49 -0700612 if (DEBUG) {
613 Log.d(TAG,"New Profiles" + profiles.toString());
614 }
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700615 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500616}