blob: 0715d6927d2ae761185c6d3ee388b851a0155b6f [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;
Stanley Tnga21efa52018-04-25 09:13:08 -070043import java.util.List;
Jason Monk7ce96b92015-02-02 11:27:58 -050044import java.util.Map;
Jason Monk7ce96b92015-02-02 11:27:58 -050045
Hansong Zhang490d0202018-04-10 16:06:19 -070046
Jason Monk7ce96b92015-02-02 11:27:58 -050047/**
48 * LocalBluetoothProfileManager provides access to the LocalBluetoothProfile
49 * objects for the available Bluetooth profiles.
50 */
Antony Sargent374d2592017-04-20 11:23:34 -070051public class LocalBluetoothProfileManager {
Jason Monk7ce96b92015-02-02 11:27:58 -050052 private static final String TAG = "LocalBluetoothProfileManager";
53 private static final boolean DEBUG = Utils.D;
54 /** Singleton instance. */
55 private static LocalBluetoothProfileManager sInstance;
56
57 /**
58 * An interface for notifying BluetoothHeadset IPC clients when they have
59 * been connected to the BluetoothHeadset service.
60 * Only used by com.android.settings.bluetooth.DockService.
61 */
62 public interface ServiceListener {
63 /**
64 * Called to notify the client when this proxy object has been
65 * connected to the BluetoothHeadset service. Clients must wait for
66 * this callback before making IPC calls on the BluetoothHeadset
67 * service.
68 */
69 void onServiceConnected();
70
71 /**
72 * Called to notify the client that this proxy object has been
73 * disconnected from the BluetoothHeadset service. Clients must not
74 * make IPC calls on the BluetoothHeadset service after this callback.
75 * This callback will currently only occur if the application hosting
76 * the BluetoothHeadset service, but may be called more often in future.
77 */
78 void onServiceDisconnected();
79 }
80
81 private final Context mContext;
82 private final LocalBluetoothAdapter mLocalAdapter;
83 private final CachedBluetoothDeviceManager mDeviceManager;
84 private final BluetoothEventManager mEventManager;
85
86 private A2dpProfile mA2dpProfile;
Sanket Agarwal1bec6a52015-10-21 18:23:27 -070087 private A2dpSinkProfile mA2dpSinkProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -050088 private HeadsetProfile mHeadsetProfile;
Sanket Agarwalf8a1c912016-01-26 20:12:52 -080089 private HfpClientProfile mHfpClientProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -050090 private MapProfile mMapProfile;
Joseph Pirozzo631768d2016-09-01 14:19:28 -070091 private MapClientProfile mMapClientProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -050092 private final HidProfile mHidProfile;
Hansong Zhangdcae2932017-11-13 10:56:23 -080093 private HidDeviceProfile mHidDeviceProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -050094 private OppProfile mOppProfile;
95 private final PanProfile mPanProfile;
Joseph Pirozzo563c7002016-03-21 15:49:48 -070096 private PbapClientProfile mPbapClientProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -050097 private final PbapServerProfile mPbapProfile;
Joseph Pirozzo563c7002016-03-21 15:49:48 -070098 private final boolean mUsePbapPce;
Joseph Pirozzo631768d2016-09-01 14:19:28 -070099 private final boolean mUseMapClient;
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800100 private HearingAidProfile mHearingAidProfile;
Jason Monk7ce96b92015-02-02 11:27:58 -0500101
102 /**
103 * Mapping from profile name, e.g. "HEADSET" to profile object.
104 */
105 private final Map<String, LocalBluetoothProfile>
106 mProfileNameMap = new HashMap<String, LocalBluetoothProfile>();
107
108 LocalBluetoothProfileManager(Context context,
109 LocalBluetoothAdapter adapter,
110 CachedBluetoothDeviceManager deviceManager,
111 BluetoothEventManager eventManager) {
112 mContext = context;
113
114 mLocalAdapter = adapter;
115 mDeviceManager = deviceManager;
116 mEventManager = eventManager;
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700117 mUsePbapPce = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
Joseph Pirozzo631768d2016-09-01 14:19:28 -0700118 // MAP Client is typically used in the same situations as PBAP Client
119 mUseMapClient = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
Jason Monk7ce96b92015-02-02 11:27:58 -0500120 // pass this reference to adapter and event manager (circular dependency)
121 mLocalAdapter.setProfileManager(this);
122 mEventManager.setProfileManager(this);
123
124 ParcelUuid[] uuids = adapter.getUuids();
125
126 // uuids may be null if Bluetooth is turned off
127 if (uuids != null) {
128 updateLocalProfiles(uuids);
129 }
130
Hansong Zhangdcae2932017-11-13 10:56:23 -0800131 // Always add HID host, HID device, and PAN profiles
Jason Monk7ce96b92015-02-02 11:27:58 -0500132 mHidProfile = new HidProfile(context, mLocalAdapter, mDeviceManager, this);
133 addProfile(mHidProfile, HidProfile.NAME,
Hansong Zhang0edf7542017-10-20 15:55:59 -0700134 BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
Jason Monk7ce96b92015-02-02 11:27:58 -0500135
Hansong Zhang490d0202018-04-10 16:06:19 -0700136 mPanProfile = new PanProfile(context, mLocalAdapter);
Jason Monk7ce96b92015-02-02 11:27:58 -0500137 addPanProfile(mPanProfile, PanProfile.NAME,
138 BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
139
Hansong Zhangdcae2932017-11-13 10:56:23 -0800140 mHidDeviceProfile = new HidDeviceProfile(context, mLocalAdapter, mDeviceManager, this);
141 addProfile(mHidDeviceProfile, HidDeviceProfile.NAME,
142 BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
143
Jason Monk7ce96b92015-02-02 11:27:58 -0500144 if(DEBUG) Log.d(TAG, "Adding local MAP profile");
Joseph Pirozzo631768d2016-09-01 14:19:28 -0700145 if (mUseMapClient) {
146 mMapClientProfile = new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
147 addProfile(mMapClientProfile, MapClientProfile.NAME,
148 BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
149 } else {
150 mMapProfile = new MapProfile(mContext, mLocalAdapter, mDeviceManager, this);
151 addProfile(mMapProfile, MapProfile.NAME,
152 BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
153 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500154
Hemant Guptadbc3d8d2017-05-12 21:14:44 +0530155 //Create PBAP server profile
156 if(DEBUG) Log.d(TAG, "Adding local PBAP profile");
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800157
Jason Monk7ce96b92015-02-02 11:27:58 -0500158 mPbapProfile = new PbapServerProfile(context);
Hemant Guptadbc3d8d2017-05-12 21:14:44 +0530159 addProfile(mPbapProfile, PbapServerProfile.NAME,
160 BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
Jason Monk7ce96b92015-02-02 11:27:58 -0500161
Stanley Tnga21efa52018-04-25 09:13:08 -0700162 List<Integer> supportedList = mLocalAdapter.getSupportedProfiles();
163 if (supportedList.contains(BluetoothProfile.HEARING_AID)) {
164 mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager,
165 this);
166 addProfile(mHearingAidProfile, HearingAidProfile.NAME,
167 BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
168 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500169 if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
170 }
171
172 /**
173 * Initialize or update the local profile objects. If a UUID was previously
174 * present but has been removed, we print a warning but don't remove the
175 * profile object as it might be referenced elsewhere, or the UUID might
176 * come back and we don't want multiple copies of the profile objects.
177 * @param uuids
178 */
179 void updateLocalProfiles(ParcelUuid[] uuids) {
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700180 // A2DP SRC
Jason Monk7ce96b92015-02-02 11:27:58 -0500181 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource)) {
182 if (mA2dpProfile == null) {
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700183 if(DEBUG) Log.d(TAG, "Adding local A2DP SRC profile");
Jason Monk7ce96b92015-02-02 11:27:58 -0500184 mA2dpProfile = new A2dpProfile(mContext, mLocalAdapter, mDeviceManager, this);
185 addProfile(mA2dpProfile, A2dpProfile.NAME,
186 BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
187 }
188 } else if (mA2dpProfile != null) {
189 Log.w(TAG, "Warning: A2DP profile was previously added but the UUID is now missing.");
190 }
191
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800192 // A2DP SINK
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700193 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSink)) {
194 if (mA2dpSinkProfile == null) {
195 if(DEBUG) Log.d(TAG, "Adding local A2DP Sink profile");
196 mA2dpSinkProfile = new A2dpSinkProfile(mContext, mLocalAdapter, mDeviceManager, this);
197 addProfile(mA2dpSinkProfile, A2dpSinkProfile.NAME,
198 BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
199 }
200 } else if (mA2dpSinkProfile != null) {
201 Log.w(TAG, "Warning: A2DP Sink profile was previously added but the UUID is now missing.");
202 }
203
Jason Monk7ce96b92015-02-02 11:27:58 -0500204 // Headset / Handsfree
205 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
206 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
207 if (mHeadsetProfile == null) {
208 if (DEBUG) Log.d(TAG, "Adding local HEADSET profile");
209 mHeadsetProfile = new HeadsetProfile(mContext, mLocalAdapter,
210 mDeviceManager, this);
Amin Shaikhddcb7df2018-04-05 14:05:56 -0400211 addHeadsetProfile(mHeadsetProfile, HeadsetProfile.NAME,
212 BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
213 BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
214 BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
Jason Monk7ce96b92015-02-02 11:27:58 -0500215 }
216 } else if (mHeadsetProfile != null) {
217 Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing.");
218 }
219
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800220 // Headset HF
221 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree)) {
222 if (mHfpClientProfile == null) {
223 if(DEBUG) Log.d(TAG, "Adding local HfpClient profile");
224 mHfpClientProfile =
225 new HfpClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
Amin Shaikhddcb7df2018-04-05 14:05:56 -0400226 addHeadsetProfile(mHfpClientProfile, HfpClientProfile.NAME,
227 BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED,
228 BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED,
229 BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800230 }
231 } else if (mHfpClientProfile != null) {
232 Log.w(TAG,
233 "Warning: Hfp Client profile was previously added but the UUID is now missing.");
234 } else {
235 Log.d(TAG, "Handsfree Uuid not found.");
236 }
237
Joseph Pirozzo631768d2016-09-01 14:19:28 -0700238 // Message Access Profile Client
239 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.MNS)) {
240 if (mMapClientProfile == null) {
241 if(DEBUG) Log.d(TAG, "Adding local Map Client profile");
242 mMapClientProfile =
243 new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
244 addProfile(mMapClientProfile, MapClientProfile.NAME,
245 BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
246 }
247 } else if (mMapClientProfile != null) {
248 Log.w(TAG,
249 "Warning: MAP Client profile was previously added but the UUID is now missing.");
250 } else {
251 Log.d(TAG, "MAP Client Uuid not found.");
252 }
253
Jason Monk7ce96b92015-02-02 11:27:58 -0500254 // OPP
255 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
256 if (mOppProfile == null) {
257 if(DEBUG) Log.d(TAG, "Adding local OPP profile");
258 mOppProfile = new OppProfile();
259 // Note: no event handler for OPP, only name map.
260 mProfileNameMap.put(OppProfile.NAME, mOppProfile);
261 }
262 } else if (mOppProfile != null) {
263 Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing.");
264 }
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700265
266 //PBAP Client
267 if (mUsePbapPce) {
268 if (mPbapClientProfile == null) {
269 if(DEBUG) Log.d(TAG, "Adding local PBAP Client profile");
270 mPbapClientProfile = new PbapClientProfile(mContext, mLocalAdapter, mDeviceManager,
271 this);
272 addProfile(mPbapClientProfile, PbapClientProfile.NAME,
273 BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED);
274 }
275 } else if (mPbapClientProfile != null) {
276 Log.w(TAG,
277 "Warning: PBAP Client profile was previously added but the UUID is now missing.");
278 }
279
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800280 //Hearing Aid Client
281 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
282 if (mHearingAidProfile == null) {
283 if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
284 mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
285 addProfile(mHearingAidProfile, HearingAidProfile.NAME,
286 BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
287 }
288 } else if (mHearingAidProfile != null) {
289 Log.w(TAG, "Warning: Hearing Aid profile was previously added but the UUID is now missing.");
290 }
291
Jason Monk7ce96b92015-02-02 11:27:58 -0500292 mEventManager.registerProfileIntentReceiver();
293
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700294 // There is no local SDP record for HID and Settings app doesn't control PBAP Server.
Jason Monk7ce96b92015-02-02 11:27:58 -0500295 }
296
Amin Shaikhddcb7df2018-04-05 14:05:56 -0400297 private void addHeadsetProfile(LocalBluetoothProfile profile, String profileName,
298 String stateChangedAction, String audioStateChangedAction, int audioDisconnectedState) {
299 BluetoothEventManager.Handler handler = new HeadsetStateChangeHandler(
300 profile, audioStateChangedAction, audioDisconnectedState);
301 mEventManager.addProfileHandler(stateChangedAction, handler);
302 mEventManager.addProfileHandler(audioStateChangedAction, handler);
303 mProfileNameMap.put(profileName, profile);
304 }
305
Jason Monk7ce96b92015-02-02 11:27:58 -0500306 private final Collection<ServiceListener> mServiceListeners =
307 new ArrayList<ServiceListener>();
308
309 private void addProfile(LocalBluetoothProfile profile,
310 String profileName, String stateChangedAction) {
311 mEventManager.addProfileHandler(stateChangedAction, new StateChangedHandler(profile));
312 mProfileNameMap.put(profileName, profile);
313 }
314
315 private void addPanProfile(LocalBluetoothProfile profile,
316 String profileName, String stateChangedAction) {
317 mEventManager.addProfileHandler(stateChangedAction,
318 new PanStateChangedHandler(profile));
319 mProfileNameMap.put(profileName, profile);
320 }
321
322 public LocalBluetoothProfile getProfileByName(String name) {
323 return mProfileNameMap.get(name);
324 }
325
326 // Called from LocalBluetoothAdapter when state changes to ON
327 void setBluetoothStateOn() {
328 ParcelUuid[] uuids = mLocalAdapter.getUuids();
329 if (uuids != null) {
330 updateLocalProfiles(uuids);
331 }
332 mEventManager.readPairedDevices();
333 }
334
335 /**
336 * Generic handler for connection state change events for the specified profile.
337 */
338 private class StateChangedHandler implements BluetoothEventManager.Handler {
339 final LocalBluetoothProfile mProfile;
340
341 StateChangedHandler(LocalBluetoothProfile profile) {
342 mProfile = profile;
343 }
344
345 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
346 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
347 if (cachedDevice == null) {
348 Log.w(TAG, "StateChangedHandler found new device: " + device);
349 cachedDevice = mDeviceManager.addDevice(mLocalAdapter,
350 LocalBluetoothProfileManager.this, device);
351 }
Amin Shaikhddcb7df2018-04-05 14:05:56 -0400352 onReceiveInternal(intent, cachedDevice);
353 }
354
355 protected void onReceiveInternal(Intent intent, CachedBluetoothDevice cachedDevice) {
Jason Monk7ce96b92015-02-02 11:27:58 -0500356 int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
357 int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
358 if (newState == BluetoothProfile.STATE_DISCONNECTED &&
359 oldState == BluetoothProfile.STATE_CONNECTING) {
360 Log.i(TAG, "Failed to connect " + mProfile + " device");
361 }
Isha Bobrac3d94132018-02-08 16:04:36 -0800362
363 if (getHearingAidProfile() != null &&
364 mProfile instanceof HearingAidProfile &&
365 (newState == BluetoothProfile.STATE_CONNECTED)) {
366 // Check if the HiSyncID has being initialized
367 if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
368
369 long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
370
371 if (newHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
372 cachedDevice.setHiSyncId(newHiSyncId);
373 mDeviceManager.onHiSyncIdChanged(newHiSyncId);
374 }
375 }
376 }
377
Jason Monk7ce96b92015-02-02 11:27:58 -0500378 cachedDevice.onProfileStateChanged(mProfile, newState);
379 cachedDevice.refresh();
380 }
381 }
382
Amin Shaikhddcb7df2018-04-05 14:05:56 -0400383 /** Connectivity and audio state change handler for headset profiles. */
384 private class HeadsetStateChangeHandler extends StateChangedHandler {
385 private final String mAudioChangeAction;
386 private final int mAudioDisconnectedState;
387
388 HeadsetStateChangeHandler(LocalBluetoothProfile profile, String audioChangeAction,
389 int audioDisconnectedState) {
390 super(profile);
391 mAudioChangeAction = audioChangeAction;
392 mAudioDisconnectedState = audioDisconnectedState;
393 }
394
395 @Override
396 public void onReceiveInternal(Intent intent, CachedBluetoothDevice cachedDevice) {
397 if (mAudioChangeAction.equals(intent.getAction())) {
398 int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
399 if (newState != mAudioDisconnectedState) {
400 cachedDevice.onProfileStateChanged(mProfile, BluetoothProfile.STATE_CONNECTED);
401 }
402 cachedDevice.refresh();
403 } else {
404 super.onReceiveInternal(intent, cachedDevice);
405 }
406 }
407 }
408
Jason Monk7ce96b92015-02-02 11:27:58 -0500409 /** State change handler for NAP and PANU profiles. */
410 private class PanStateChangedHandler extends StateChangedHandler {
411
412 PanStateChangedHandler(LocalBluetoothProfile profile) {
413 super(profile);
414 }
415
416 @Override
417 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
418 PanProfile panProfile = (PanProfile) mProfile;
419 int role = intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, 0);
420 panProfile.setLocalRole(device, role);
421 super.onReceive(context, intent, device);
422 }
423 }
424
425 // called from DockService
426 public void addServiceListener(ServiceListener l) {
427 mServiceListeners.add(l);
428 }
429
430 // called from DockService
431 public void removeServiceListener(ServiceListener l) {
432 mServiceListeners.remove(l);
433 }
434
435 // not synchronized: use only from UI thread! (TODO: verify)
436 void callServiceConnectedListeners() {
437 for (ServiceListener l : mServiceListeners) {
438 l.onServiceConnected();
439 }
440 }
441
442 // not synchronized: use only from UI thread! (TODO: verify)
443 void callServiceDisconnectedListeners() {
444 for (ServiceListener listener : mServiceListeners) {
445 listener.onServiceDisconnected();
446 }
447 }
448
449 // This is called by DockService, so check Headset and A2DP.
450 public synchronized boolean isManagerReady() {
451 // Getting just the headset profile is fine for now. Will need to deal with A2DP
452 // and others if they aren't always in a ready state.
453 LocalBluetoothProfile profile = mHeadsetProfile;
454 if (profile != null) {
455 return profile.isProfileReady();
456 }
457 profile = mA2dpProfile;
458 if (profile != null) {
459 return profile.isProfileReady();
460 }
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700461 profile = mA2dpSinkProfile;
462 if (profile != null) {
463 return profile.isProfileReady();
464 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500465 return false;
466 }
467
468 public A2dpProfile getA2dpProfile() {
469 return mA2dpProfile;
470 }
471
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800472 public A2dpSinkProfile getA2dpSinkProfile() {
473 if ((mA2dpSinkProfile != null) && (mA2dpSinkProfile.isProfileReady())) {
474 return mA2dpSinkProfile;
475 } else {
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700476 return null;
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800477 }
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700478 }
479
Jason Monk7ce96b92015-02-02 11:27:58 -0500480 public HeadsetProfile getHeadsetProfile() {
481 return mHeadsetProfile;
482 }
483
Sanket Agarwalf8a1c912016-01-26 20:12:52 -0800484 public HfpClientProfile getHfpClientProfile() {
485 if ((mHfpClientProfile != null) && (mHfpClientProfile.isProfileReady())) {
486 return mHfpClientProfile;
487 } else {
488 return null;
489 }
490 }
491
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700492 public PbapClientProfile getPbapClientProfile() {
493 return mPbapClientProfile;
494 }
495
Jason Monk7ce96b92015-02-02 11:27:58 -0500496 public PbapServerProfile getPbapProfile(){
497 return mPbapProfile;
498 }
499
500 public MapProfile getMapProfile(){
501 return mMapProfile;
502 }
503
Joseph Pirozzo631768d2016-09-01 14:19:28 -0700504 public MapClientProfile getMapClientProfile() {
505 return mMapClientProfile;
506 }
507
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800508 public HearingAidProfile getHearingAidProfile() {
509 return mHearingAidProfile;
510 }
511
Hansong Zhang490d0202018-04-10 16:06:19 -0700512 @VisibleForTesting
513 HidProfile getHidProfile() {
514 return mHidProfile;
515 }
516
517 @VisibleForTesting
518 HidDeviceProfile getHidDeviceProfile() {
519 return mHidDeviceProfile;
520 }
521
Jason Monk7ce96b92015-02-02 11:27:58 -0500522 /**
523 * Fill in a list of LocalBluetoothProfile objects that are supported by
524 * the local device and the remote device.
525 *
526 * @param uuids of the remote device
527 * @param localUuids UUIDs of the local device
528 * @param profiles The list of profiles to fill
529 * @param removedProfiles list of profiles that were removed
530 */
531 synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
532 Collection<LocalBluetoothProfile> profiles,
533 Collection<LocalBluetoothProfile> removedProfiles,
534 boolean isPanNapConnected, BluetoothDevice device) {
535 // Copy previous profile list into removedProfiles
536 removedProfiles.clear();
537 removedProfiles.addAll(profiles);
Joseph Pirozzo99fecc02016-04-07 13:43:49 -0700538 if (DEBUG) {
539 Log.d(TAG,"Current Profiles" + profiles.toString());
540 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500541 profiles.clear();
542
543 if (uuids == null) {
544 return;
545 }
546
547 if (mHeadsetProfile != null) {
548 if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) &&
549 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
550 (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
551 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
552 profiles.add(mHeadsetProfile);
553 removedProfiles.remove(mHeadsetProfile);
554 }
555 }
556
Joseph Pirozzo99fecc02016-04-07 13:43:49 -0700557 if ((mHfpClientProfile != null) &&
558 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) &&
559 BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree)) {
560 profiles.add(mHfpClientProfile);
561 removedProfiles.remove(mHfpClientProfile);
562 }
563
Jason Monk7ce96b92015-02-02 11:27:58 -0500564 if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
565 mA2dpProfile != null) {
566 profiles.add(mA2dpProfile);
567 removedProfiles.remove(mA2dpProfile);
568 }
569
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700570 if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS) &&
571 mA2dpSinkProfile != null) {
572 profiles.add(mA2dpSinkProfile);
573 removedProfiles.remove(mA2dpSinkProfile);
Joseph Pirozzo99fecc02016-04-07 13:43:49 -0700574 }
Sanket Agarwal1bec6a52015-10-21 18:23:27 -0700575
Jason Monk7ce96b92015-02-02 11:27:58 -0500576 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
577 mOppProfile != null) {
578 profiles.add(mOppProfile);
579 removedProfiles.remove(mOppProfile);
580 }
581
582 if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) ||
583 BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) &&
584 mHidProfile != null) {
585 profiles.add(mHidProfile);
586 removedProfiles.remove(mHidProfile);
587 }
588
Hansong Zhang490d0202018-04-10 16:06:19 -0700589 if (mHidDeviceProfile != null && mHidDeviceProfile.getConnectionStatus(device)
Hansong Zhangdcae2932017-11-13 10:56:23 -0800590 != BluetoothProfile.STATE_DISCONNECTED) {
591 profiles.add(mHidDeviceProfile);
592 removedProfiles.remove(mHidDeviceProfile);
593 }
594
Jason Monk7ce96b92015-02-02 11:27:58 -0500595 if(isPanNapConnected)
596 if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists.");
597 if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
598 mPanProfile != null) || isPanNapConnected) {
599 profiles.add(mPanProfile);
600 removedProfiles.remove(mPanProfile);
601 }
602
603 if ((mMapProfile != null) &&
604 (mMapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
605 profiles.add(mMapProfile);
606 removedProfiles.remove(mMapProfile);
607 mMapProfile.setPreferred(device, true);
608 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500609
Hemant Guptadbc3d8d2017-05-12 21:14:44 +0530610 if ((mPbapProfile != null) &&
611 (mPbapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
612 profiles.add(mPbapProfile);
613 removedProfiles.remove(mPbapProfile);
614 mPbapProfile.setPreferred(device, true);
615 }
616
Joseph Pirozzo631768d2016-09-01 14:19:28 -0700617 if (mMapClientProfile != null) {
618 profiles.add(mMapClientProfile);
619 removedProfiles.remove(mMapClientProfile);
620 }
621
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700622 if (mUsePbapPce) {
623 profiles.add(mPbapClientProfile);
624 removedProfiles.remove(mPbapClientProfile);
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700625 }
Joseph Pirozzo99fecc02016-04-07 13:43:49 -0700626
Jakub Pawlowskiea580fa2017-11-22 11:02:34 -0800627 if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid) &&
628 mHearingAidProfile != null) {
629 profiles.add(mHearingAidProfile);
630 removedProfiles.remove(mHearingAidProfile);
631 }
632
Joseph Pirozzo99fecc02016-04-07 13:43:49 -0700633 if (DEBUG) {
634 Log.d(TAG,"New Profiles" + profiles.toString());
635 }
Joseph Pirozzo563c7002016-03-21 15:49:48 -0700636 }
Jason Monk7ce96b92015-02-02 11:27:58 -0500637}