• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.server.telecom.bluetooth;
18 
19 import static com.android.server.telecom.AudioRoute.TYPE_BLUETOOTH_HA;
20 import static com.android.server.telecom.AudioRoute.TYPE_BLUETOOTH_SCO;
21 import static com.android.server.telecom.CallAudioRouteAdapter.BT_DEVICE_REMOVED;
22 import static com.android.server.telecom.CallAudioRouteAdapter.SWITCH_BASELINE_ROUTE;
23 import static com.android.server.telecom.CallAudioRouteController.INCLUDE_BLUETOOTH_IN_BASELINE;
24 
25 import android.bluetooth.BluetoothAdapter;
26 import android.bluetooth.BluetoothDevice;
27 import android.bluetooth.BluetoothHeadset;
28 import android.bluetooth.BluetoothHearingAid;
29 import android.bluetooth.BluetoothLeAudio;
30 import android.bluetooth.BluetoothLeAudioCodecStatus;
31 import android.bluetooth.BluetoothProfile;
32 import android.bluetooth.BluetoothStatusCodes;
33 import android.content.Context;
34 import android.media.AudioDeviceInfo;
35 import android.media.AudioManager;
36 import android.os.Bundle;
37 import android.telecom.CallAudioState;
38 import android.telecom.Log;
39 import android.util.ArraySet;
40 import android.util.LocalLog;
41 import android.util.Pair;
42 
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.util.IndentingPrintWriter;
45 import com.android.server.telecom.AudioRoute;
46 import com.android.server.telecom.CallAudioCommunicationDeviceTracker;
47 import com.android.server.telecom.CallAudioRouteAdapter;
48 import com.android.server.telecom.CallAudioRouteController;
49 import com.android.server.telecom.flags.FeatureFlags;
50 
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.Collections;
54 import java.util.HashMap;
55 import java.util.LinkedHashMap;
56 import java.util.LinkedHashSet;
57 import java.util.LinkedList;
58 import java.util.List;
59 import java.util.Map;
60 import java.util.Objects;
61 import java.util.Set;
62 import java.util.concurrent.CompletableFuture;
63 import java.util.concurrent.ExecutionException;
64 import java.util.concurrent.Executor;
65 import java.util.concurrent.TimeUnit;
66 import java.util.concurrent.TimeoutException;
67 
68 public class BluetoothDeviceManager {
69 
70     public static final int DEVICE_TYPE_HEADSET = 0;
71     public static final int DEVICE_TYPE_HEARING_AID = 1;
72     public static final int DEVICE_TYPE_LE_AUDIO = 2;
73 
74     private static final Map<Integer, Integer> PROFILE_TO_AUDIO_ROUTE_MAP = new HashMap<>();
75     static {
PROFILE_TO_AUDIO_ROUTE_MAP.put(BluetoothProfile.HEADSET, AudioRoute.TYPE_BLUETOOTH_SCO)76         PROFILE_TO_AUDIO_ROUTE_MAP.put(BluetoothProfile.HEADSET,
77                 AudioRoute.TYPE_BLUETOOTH_SCO);
PROFILE_TO_AUDIO_ROUTE_MAP.put(BluetoothProfile.LE_AUDIO, AudioRoute.TYPE_BLUETOOTH_LE)78         PROFILE_TO_AUDIO_ROUTE_MAP.put(BluetoothProfile.LE_AUDIO,
79                 AudioRoute.TYPE_BLUETOOTH_LE);
PROFILE_TO_AUDIO_ROUTE_MAP.put(BluetoothProfile.HEARING_AID, TYPE_BLUETOOTH_HA)80         PROFILE_TO_AUDIO_ROUTE_MAP.put(BluetoothProfile.HEARING_AID,
81                 TYPE_BLUETOOTH_HA);
82     }
83 
84     private BluetoothLeAudio.Callback mLeAudioCallbacks =
85         new BluetoothLeAudio.Callback() {
86             @Override
87             public void onCodecConfigChanged(int groupId, BluetoothLeAudioCodecStatus status) {}
88             @Override
89             public void onGroupStatusChanged(int groupId, int groupStatus) {}
90             @Override
91             public void onGroupNodeAdded(BluetoothDevice device, int groupId) {
92                 Log.i(this, (device == null ? "device is null" : device.getAddress())
93                         + " group added " + groupId);
94                 if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
95                     Log.w(this, "invalid parameter");
96                     return;
97                 }
98 
99                 synchronized (mLock) {
100                     mGroupsByDevice.put(device, groupId);
101                 }
102             }
103             @Override
104             public void onGroupNodeRemoved(BluetoothDevice device, int groupId) {
105                 Log.i(this, (device == null ? "device is null" : device.getAddress())
106                         + " group removed " + groupId);
107                 if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
108                     Log.w(this, "invalid parameter");
109                     return;
110                 }
111 
112                 synchronized (mLock) {
113                     mGroupsByDevice.remove(device);
114                 }
115             }
116         };
117 
118     private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
119             new BluetoothProfile.ServiceListener() {
120                 @Override
121                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
122                     Log.startSession("BPSL.oSC");
123                     try {
124                         synchronized (mLock) {
125                             String logString;
126                             if (profile == BluetoothProfile.HEADSET) {
127                                 if (mFeatureFlags.useRefactoredAudioRouteSwitching()) {
128                                     mBluetoothHeadsetFuture.complete((BluetoothHeadset) proxy);
129                                 }
130                                 mBluetoothHeadset = (BluetoothHeadset) proxy;
131                                 logString = "Got BluetoothHeadset: " + mBluetoothHeadset;
132                             } else if (profile == BluetoothProfile.HEARING_AID) {
133                                 mBluetoothHearingAid = (BluetoothHearingAid) proxy;
134                                 logString = "Got BluetoothHearingAid: "
135                                         + mBluetoothHearingAid;
136                             } else if (profile == BluetoothProfile.LE_AUDIO) {
137                                 mBluetoothLeAudioService = (BluetoothLeAudio) proxy;
138                                 logString = ("Got BluetoothLeAudio: " + mBluetoothLeAudioService )
139                                         + (", mLeAudioCallbackRegistered: "
140                                         + mLeAudioCallbackRegistered);
141                                 if (!mLeAudioCallbackRegistered) {
142                                     if (mFeatureFlags.postponeRegisterToLeaudio()) {
143                                         mExecutor.execute(this::registerToLeAudio);
144                                     } else {
145                                         registerToLeAudio();
146                                     }
147                                 }
148                             } else {
149                                 logString = "Connected to non-requested bluetooth service." +
150                                         " Not changing bluetooth headset.";
151                             }
152                             Log.i(BluetoothDeviceManager.this, logString);
153                             mLocalLog.log(logString);
154                         }
155                     } finally {
156                         Log.endSession();
157                     }
158                 }
159 
160                 private void registerToLeAudio() {
161                     synchronized (mLock) {
162                         String logString = "Register to leAudio";
163 
164                         if (mLeAudioCallbackRegistered) {
165                             logString +=  ", but already registered";
166                             Log.i(BluetoothDeviceManager.this, logString);
167                             mLocalLog.log(logString);
168                             return;
169                         }
170                         if (mBluetoothLeAudioService == null) {
171                             logString += ", but leAudio service is unavailable";
172                             Log.i(BluetoothDeviceManager.this, logString);
173                             mLocalLog.log(logString);
174                             return;
175                         }
176                         try {
177                             mLeAudioCallbackRegistered = true;
178                             mBluetoothLeAudioService.registerCallback(
179                                             mExecutor, mLeAudioCallbacks);
180                         } catch (IllegalStateException e) {
181                             mLeAudioCallbackRegistered = false;
182                             logString += ", but failed: " + e;
183                         }
184                         Log.i(BluetoothDeviceManager.this, logString);
185                         mLocalLog.log(logString);
186                     }
187                 }
188 
189                 @Override
190                 public void onServiceDisconnected(int profile) {
191                     Log.startSession("BPSL.oSD");
192                     try {
193                         synchronized (mLock) {
194                             LinkedHashMap<String, BluetoothDevice> lostServiceDevices;
195                             String logString;
196                             if (profile == BluetoothProfile.HEADSET) {
197                                 if (mFeatureFlags.useRefactoredAudioRouteSwitching()) {
198                                     mBluetoothHeadsetFuture.complete(null);
199                                 }
200                                 mBluetoothHeadset = null;
201                                 lostServiceDevices = mHfpDevicesByAddress;
202                                 mBluetoothRouteManager.onActiveDeviceChanged(null,
203                                         DEVICE_TYPE_HEADSET);
204                                 logString = "Lost BluetoothHeadset service. " +
205                                         "Removing all tracked devices";
206                             } else if (profile == BluetoothProfile.HEARING_AID) {
207                                 mBluetoothHearingAid = null;
208                                 logString = "Lost BluetoothHearingAid service. " +
209                                         "Removing all tracked devices.";
210                                 lostServiceDevices = mHearingAidDevicesByAddress;
211                                 mBluetoothRouteManager.onActiveDeviceChanged(null,
212                                         DEVICE_TYPE_HEARING_AID);
213                             } else if (profile == BluetoothProfile.LE_AUDIO) {
214                                 mBluetoothLeAudioService = null;
215                                 logString = "Lost BluetoothLeAudio service. " +
216                                         "Removing all tracked devices.";
217                                 lostServiceDevices = mLeAudioDevicesByAddress;
218                                 mBluetoothRouteManager.onActiveDeviceChanged(null,
219                                         DEVICE_TYPE_LE_AUDIO);
220                             } else {
221                                 return;
222                             }
223                             Log.i(BluetoothDeviceManager.this, logString);
224                             mLocalLog.log(logString);
225 
226                             if (mFeatureFlags.useRefactoredAudioRouteSwitching()) {
227                                 handleAudioRefactoringServiceDisconnected(profile);
228                             } else {
229                                 List<BluetoothDevice> devicesToRemove = new LinkedList<>(
230                                         lostServiceDevices.values());
231                                 lostServiceDevices.clear();
232                                 for (BluetoothDevice device : devicesToRemove) {
233                                     mBluetoothRouteManager.onDeviceLost(device.getAddress());
234                                 }
235                             }
236                         }
237                     } finally {
238                         Log.endSession();
239                     }
240                 }
241            };
242 
243     @VisibleForTesting
handleAudioRefactoringServiceDisconnected(int profile)244     public void handleAudioRefactoringServiceDisconnected(int profile) {
245         CallAudioRouteController controller = (CallAudioRouteController)
246                 mCallAudioRouteAdapter;
247         Map<AudioRoute, BluetoothDevice> btRoutes = controller
248                 .getBluetoothRoutes();
249         List<Pair<AudioRoute, BluetoothDevice>> btRoutesToRemove =
250                 new ArrayList<>();
251         // Prevent concurrent modification exception by just iterating
252         //through keys instead of simultaneously removing them. Ensure that
253         // we synchronize on the map while we traverse via an Iterator.
254         synchronized (btRoutes) {
255             for (AudioRoute route: btRoutes.keySet()) {
256                 if (route.getType() != PROFILE_TO_AUDIO_ROUTE_MAP.get(profile)) {
257                     continue;
258                 }
259                 BluetoothDevice device = btRoutes.get(route);
260                 btRoutesToRemove.add(new Pair<>(route, device));
261             }
262         }
263 
264         for (Pair<AudioRoute, BluetoothDevice> routeToRemove:
265                 btRoutesToRemove) {
266             AudioRoute route = routeToRemove.first;
267             BluetoothDevice device = routeToRemove.second;
268             mCallAudioRouteAdapter.sendMessageWithSessionInfo(
269                     BT_DEVICE_REMOVED, route.getType(), device);
270         }
271 
272         if (mFeatureFlags.skipBaselineSwitchWhenRouteNotBluetooth()) {
273             CallAudioState currentAudioState = controller.getCurrentCallAudioState();
274             int currentRoute = currentAudioState.getRoute();
275             if (currentRoute == CallAudioState.ROUTE_BLUETOOTH) {
276                 Log.d(this, "handleAudioRefactoringServiceDisconnected: call audio "
277                         + "is currently routed to BT so switching back to baseline");
278                 mCallAudioRouteAdapter.sendMessageWithSessionInfo(
279                         SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE, (String) null);
280             } else {
281                 Log.d(this, "handleAudioRefactoringServiceDisconnected: call audio "
282                         + "is not currently routed to BT so skipping switch to baseline");
283             }
284         } else {
285             mCallAudioRouteAdapter.sendMessageWithSessionInfo(
286                     SWITCH_BASELINE_ROUTE, INCLUDE_BLUETOOTH_IN_BASELINE, (String) null);
287         }
288     }
289 
290     private final LinkedHashMap<String, BluetoothDevice> mHfpDevicesByAddress =
291             new LinkedHashMap<>();
292     private final LinkedHashMap<String, BluetoothDevice> mHearingAidDevicesByAddress =
293             new LinkedHashMap<>();
294     private final LinkedHashMap<BluetoothDevice, Long> mHearingAidDeviceSyncIds =
295             new LinkedHashMap<>();
296     private final LinkedHashMap<String, BluetoothDevice> mLeAudioDevicesByAddress =
297             new LinkedHashMap<>();
298     private final LinkedHashMap<BluetoothDevice, Integer> mGroupsByDevice =
299             new LinkedHashMap<>();
300     private final ArrayList<LinkedHashMap<String, BluetoothDevice>>
301             mDevicesByAddressMaps = new ArrayList<LinkedHashMap<String, BluetoothDevice>>(); {
302         mDevicesByAddressMaps.add(mHfpDevicesByAddress);
303         mDevicesByAddressMaps.add(mHearingAidDevicesByAddress);
304         mDevicesByAddressMaps.add(mLeAudioDevicesByAddress);
305     }
306     private int mGroupIdActive = BluetoothLeAudio.GROUP_ID_INVALID;
307     private int mGroupIdPending = BluetoothLeAudio.GROUP_ID_INVALID;
308     private final LocalLog mLocalLog = new LocalLog(20);
309 
310     // This lock only protects internal state -- it doesn't lock on anything going into Telecom.
311     private final Object mLock = new Object();
312 
313     private BluetoothRouteManager mBluetoothRouteManager;
314     private BluetoothHeadset mBluetoothHeadset;
315     private CompletableFuture<BluetoothHeadset> mBluetoothHeadsetFuture;
316     private BluetoothHearingAid mBluetoothHearingAid;
317     private boolean mLeAudioCallbackRegistered = false;
318     private BluetoothLeAudio mBluetoothLeAudioService;
319     private boolean mLeAudioSetAsCommunicationDevice = false;
320     private String mLeAudioDevice;
321     private String mHearingAidDevice;
322     private boolean mHearingAidSetAsCommunicationDevice = false;
323     private BluetoothDevice mBluetoothHearingAidActiveDeviceCache;
324     private BluetoothAdapter mBluetoothAdapter;
325     private AudioManager mAudioManager;
326     private Executor mExecutor;
327     private CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker;
328     private CallAudioRouteAdapter mCallAudioRouteAdapter;
329     private FeatureFlags mFeatureFlags;
330 
BluetoothDeviceManager(Context context, BluetoothAdapter bluetoothAdapter, CallAudioCommunicationDeviceTracker communicationDeviceTracker, FeatureFlags featureFlags)331     public BluetoothDeviceManager(Context context, BluetoothAdapter bluetoothAdapter,
332             CallAudioCommunicationDeviceTracker communicationDeviceTracker,
333             FeatureFlags featureFlags) {
334         mFeatureFlags = featureFlags;
335         if (bluetoothAdapter != null) {
336             mBluetoothAdapter = bluetoothAdapter;
337             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
338                     BluetoothProfile.HEADSET);
339             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
340                     BluetoothProfile.HEARING_AID);
341             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
342                     BluetoothProfile.LE_AUDIO);
343         }
344         if (mFeatureFlags.useRefactoredAudioRouteSwitching()) {
345             mBluetoothHeadsetFuture = new CompletableFuture<>();
346         }
347         mAudioManager = context.getSystemService(AudioManager.class);
348         mExecutor = context.getMainExecutor();
349         mCommunicationDeviceTracker = communicationDeviceTracker;
350     }
351 
setBluetoothRouteManager(BluetoothRouteManager brm)352     public void setBluetoothRouteManager(BluetoothRouteManager brm) {
353         mBluetoothRouteManager = brm;
354     }
355 
getLeAudioConnectedDevices()356     private List<BluetoothDevice> getLeAudioConnectedDevices() {
357         synchronized (mLock) {
358             // Let's get devices which are a group leaders
359             ArrayList<BluetoothDevice> devices = new ArrayList<>();
360 
361             if (mGroupsByDevice.isEmpty() || mBluetoothLeAudioService == null) {
362                 return devices;
363             }
364 
365             for (LinkedHashMap.Entry<BluetoothDevice, Integer> entry : mGroupsByDevice.entrySet()) {
366                 if (Objects.equals(entry.getKey(),
367                         mBluetoothLeAudioService.getConnectedGroupLeadDevice(entry.getValue()))) {
368                     devices.add(entry.getKey());
369                 }
370             }
371             devices.removeIf(device -> !mLeAudioDevicesByAddress.containsValue(device));
372             return devices;
373         }
374     }
375 
getNumConnectedDevices()376     public int getNumConnectedDevices() {
377         return getConnectedDevices().size();
378     }
379 
getConnectedDevices()380     public Collection<BluetoothDevice> getConnectedDevices() {
381         synchronized (mLock) {
382             ArraySet<BluetoothDevice> result = new ArraySet<>();
383 
384             // Set storing the group ids of all dual mode audio devices to de-dupe them
385             Set<Integer> dualModeGroupIds = new ArraySet<>();
386             for (BluetoothDevice hfpDevice: mHfpDevicesByAddress.values()) {
387                 result.add(hfpDevice);
388                 if (mBluetoothLeAudioService == null) {
389                     continue;
390                 }
391                 int groupId = mBluetoothLeAudioService.getGroupId(hfpDevice);
392                 if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
393                     dualModeGroupIds.add(groupId);
394                 }
395             }
396 
397             result.addAll(mHearingAidDevicesByAddress.values());
398             if (mBluetoothLeAudioService == null) {
399                 return Collections.unmodifiableCollection(result);
400             }
401             for (BluetoothDevice leAudioDevice: getLeAudioConnectedDevices()) {
402                 // Exclude dual mode audio devices included from the HFP devices list
403                 int groupId = mBluetoothLeAudioService.getGroupId(leAudioDevice);
404                 if (groupId != BluetoothLeAudio.GROUP_ID_INVALID
405                         && !dualModeGroupIds.contains(groupId)) {
406                     result.add(leAudioDevice);
407                 }
408             }
409             return Collections.unmodifiableCollection(result);
410         }
411     }
412 
413     // Same as getConnectedDevices except it filters out the hearing aid devices that are linked
414     // together by their hiSyncId.
getUniqueConnectedDevices()415     public Collection<BluetoothDevice> getUniqueConnectedDevices() {
416         ArraySet<BluetoothDevice> result;
417         synchronized (mLock) {
418             result = new ArraySet<>(mHfpDevicesByAddress.values());
419         }
420         Set<Long> seenHiSyncIds = new LinkedHashSet<>();
421         // Add the left-most active device to the seen list so that we match up with the list
422         // generated in BluetoothRouteManager.
423         if (mBluetoothAdapter != null) {
424             for (BluetoothDevice device : mBluetoothAdapter.getActiveDevices(
425                         BluetoothProfile.HEARING_AID)) {
426                 if (device != null) {
427                     result.add(device);
428                     seenHiSyncIds.add(mHearingAidDeviceSyncIds.getOrDefault(device, -1L));
429                     break;
430                 }
431             }
432         }
433         synchronized (mLock) {
434             for (BluetoothDevice d : mHearingAidDevicesByAddress.values()) {
435                 long hiSyncId = mHearingAidDeviceSyncIds.getOrDefault(d, -1L);
436                 if (seenHiSyncIds.contains(hiSyncId)) {
437                     continue;
438                 }
439                 result.add(d);
440                 seenHiSyncIds.add(hiSyncId);
441             }
442         }
443 
444         if (mBluetoothLeAudioService != null) {
445             result.addAll(getLeAudioConnectedDevices());
446         }
447 
448         return Collections.unmodifiableCollection(result);
449     }
450 
getBluetoothHeadset()451     public BluetoothHeadset getBluetoothHeadset() {
452         if (mFeatureFlags.useRefactoredAudioRouteSwitching()) {
453             try {
454                 mBluetoothHeadset = mBluetoothHeadsetFuture.get(500L,
455                         TimeUnit.MILLISECONDS);
456                 return mBluetoothHeadset;
457             } catch (TimeoutException | InterruptedException | ExecutionException e) {
458                 // ignore
459                 Log.w(this, "getBluetoothHeadset: Acquire BluetoothHeadset service failed due to: "
460                         + e);
461                 return null;
462             }
463         } else {
464             return mBluetoothHeadset;
465         }
466     }
467 
getBluetoothAdapter()468     public BluetoothAdapter getBluetoothAdapter() {
469         return mBluetoothAdapter;
470     }
471 
getBluetoothHearingAid()472     public BluetoothHearingAid getBluetoothHearingAid() {
473         return mBluetoothHearingAid;
474     }
475 
getLeAudioService()476     public BluetoothLeAudio getLeAudioService() {
477         return mBluetoothLeAudioService;
478     }
479 
setHeadsetServiceForTesting(BluetoothHeadset bluetoothHeadset)480     public void setHeadsetServiceForTesting(BluetoothHeadset bluetoothHeadset) {
481         mBluetoothHeadset = bluetoothHeadset;
482     }
483 
setHearingAidServiceForTesting(BluetoothHearingAid bluetoothHearingAid)484     public void setHearingAidServiceForTesting(BluetoothHearingAid bluetoothHearingAid) {
485         mBluetoothHearingAid = bluetoothHearingAid;
486     }
487 
setLeAudioServiceForTesting(BluetoothLeAudio bluetoothLeAudio)488     public void setLeAudioServiceForTesting(BluetoothLeAudio bluetoothLeAudio) {
489         mBluetoothLeAudioService = bluetoothLeAudio;
490         mBluetoothLeAudioService.registerCallback(mExecutor, mLeAudioCallbacks);
491     }
492 
getDeviceTypeString(int deviceType)493     public static String getDeviceTypeString(int deviceType) {
494         switch (deviceType) {
495             case DEVICE_TYPE_LE_AUDIO:
496                 return "LeAudio";
497             case DEVICE_TYPE_HEARING_AID:
498                 return "HearingAid";
499             case DEVICE_TYPE_HEADSET:
500                 return "HFP";
501             default:
502                 return "unknown type";
503         }
504     }
505 
506     @VisibleForTesting
onDeviceConnected(BluetoothDevice device, int deviceType)507     public void onDeviceConnected(BluetoothDevice device, int deviceType) {
508         synchronized (mLock) {
509             clearDeviceFromDeviceMaps(device.getAddress());
510             LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
511             if (deviceType == DEVICE_TYPE_LE_AUDIO) {
512                 if (mBluetoothLeAudioService == null) {
513                     Log.w(this, "onDeviceConnected: LE audio service null");
514                     return;
515                 }
516                 /* Check if group is known. */
517                 if (!mGroupsByDevice.containsKey(device)) {
518                     int groupId = mBluetoothLeAudioService.getGroupId(device);
519                     /* If it is not yet assigned, then it will be provided in the callback */
520                     if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
521                         mGroupsByDevice.put(device, groupId);
522                     }
523                 }
524                 targetDeviceMap = mLeAudioDevicesByAddress;
525             } else if (deviceType == DEVICE_TYPE_HEARING_AID) {
526                 if (mBluetoothHearingAid == null) {
527                     Log.w(this, "onDeviceConnected: Hearing aid service null");
528                     return;
529                 }
530                 long hiSyncId = mBluetoothHearingAid.getHiSyncId(device);
531                 mHearingAidDeviceSyncIds.put(device, hiSyncId);
532                 targetDeviceMap = mHearingAidDevicesByAddress;
533             } else if (deviceType == DEVICE_TYPE_HEADSET) {
534                 if (getBluetoothHeadset() == null) {
535                     Log.w(this, "onDeviceConnected: Headset service null");
536                     return;
537                 }
538                 targetDeviceMap = mHfpDevicesByAddress;
539             } else {
540                 Log.w(this, "onDeviceConnected: Device: %s; invalid type %s", device.getAddress(),
541                         getDeviceTypeString(deviceType));
542                 return;
543             }
544             if (!targetDeviceMap.containsKey(device.getAddress())) {
545                 Log.i(this, "onDeviceConnected: Adding device with address: %s and devicetype=%s",
546                         device, getDeviceTypeString(deviceType));
547                 targetDeviceMap.put(device.getAddress(), device);
548                 if (!mFeatureFlags.keepBluetoothDevicesCacheUpdated()
549                         || !mFeatureFlags.useRefactoredAudioRouteSwitching()) {
550                     mBluetoothRouteManager.onDeviceAdded(device.getAddress());
551                 }
552             }
553         }
554     }
555 
clearDeviceFromDeviceMaps(String deviceAddress)556     void clearDeviceFromDeviceMaps(String deviceAddress) {
557         for (LinkedHashMap<String, BluetoothDevice> deviceMap : mDevicesByAddressMaps) {
558             deviceMap.remove(deviceAddress);
559         }
560     }
561 
onDeviceDisconnected(BluetoothDevice device, int deviceType)562     void onDeviceDisconnected(BluetoothDevice device, int deviceType) {
563         mLocalLog.log("Device disconnected -- address: " + device.getAddress() + " deviceType: "
564                 + deviceType);
565         synchronized (mLock) {
566             LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
567             if (deviceType == DEVICE_TYPE_LE_AUDIO) {
568                 targetDeviceMap = mLeAudioDevicesByAddress;
569             } else if (deviceType == DEVICE_TYPE_HEARING_AID) {
570                 mHearingAidDeviceSyncIds.remove(device);
571                 targetDeviceMap = mHearingAidDevicesByAddress;
572             } else if (deviceType == DEVICE_TYPE_HEADSET) {
573                 targetDeviceMap = mHfpDevicesByAddress;
574             } else {
575                 Log.w(this, "onDeviceDisconnected: Device: %s with invalid type: %s",
576                         device.getAddress(), getDeviceTypeString(deviceType));
577                 return;
578             }
579             if (targetDeviceMap.containsKey(device.getAddress())) {
580                 Log.i(this, "onDeviceDisconnected: Removing device with address: %s, devicetype=%s",
581                         device, getDeviceTypeString(deviceType));
582                 targetDeviceMap.remove(device.getAddress());
583                 if (!mFeatureFlags.keepBluetoothDevicesCacheUpdated()
584                         || !mFeatureFlags.useRefactoredAudioRouteSwitching()) {
585                     mBluetoothRouteManager.onDeviceLost(device.getAddress());
586                 }
587             }
588         }
589     }
590 
disconnectAudio()591     public void disconnectAudio() {
592         if (mFeatureFlags.callAudioCommunicationDeviceRefactor()) {
593             mCommunicationDeviceTracker.clearBtCommunicationDevice();
594             disconnectSco();
595         } else {
596             disconnectSco();
597             clearLeAudioCommunicationDevice();
598             clearHearingAidCommunicationDevice();
599         }
600     }
601 
disconnectSco()602     public int disconnectSco() {
603         int result = BluetoothStatusCodes.ERROR_UNKNOWN;
604         if (getBluetoothHeadset() == null) {
605             Log.w(this, "disconnectSco: Trying to disconnect audio but no headset service exists.");
606         } else {
607             result = mBluetoothHeadset.disconnectAudio();
608             Log.i(this, "disconnectSco: BluetoothHeadset#disconnectAudio()=%s",
609                     btCodeToString(result));
610         }
611         return result;
612     }
613 
isLeAudioCommunicationDevice()614     public boolean isLeAudioCommunicationDevice() {
615         return mLeAudioSetAsCommunicationDevice;
616     }
617 
isHearingAidSetAsCommunicationDevice()618     public boolean isHearingAidSetAsCommunicationDevice() {
619         return mHearingAidSetAsCommunicationDevice;
620     }
621 
clearLeAudioCommunicationDevice()622     public void clearLeAudioCommunicationDevice() {
623         Log.i(this, "clearLeAudioCommunicationDevice: mLeAudioSetAsCommunicationDevice = " +
624                 mLeAudioSetAsCommunicationDevice + " device = " + mLeAudioDevice);
625         if (!mLeAudioSetAsCommunicationDevice) {
626             return;
627         }
628         mLeAudioSetAsCommunicationDevice = false;
629         if (mLeAudioDevice != null) {
630             mBluetoothRouteManager.onAudioLost(mLeAudioDevice);
631             mLeAudioDevice = null;
632         }
633 
634         if (mAudioManager == null) {
635             Log.i(this, "clearLeAudioCommunicationDevice: mAudioManager is null");
636             return;
637         }
638 
639         AudioDeviceInfo audioDeviceInfo = mAudioManager.getCommunicationDevice();
640         if (audioDeviceInfo != null && audioDeviceInfo.getType()
641                 == AudioDeviceInfo.TYPE_BLE_HEADSET) {
642             mBluetoothRouteManager.onAudioLost(audioDeviceInfo.getAddress());
643             Log.i(this, "clearLeAudioCommunicationDevice: audioManager#clearCommunicationDevice");
644             mAudioManager.clearCommunicationDevice();
645         }
646     }
647 
clearHearingAidCommunicationDevice()648     public void clearHearingAidCommunicationDevice() {
649         Log.i(this, "clearHearingAidCommunicationDevice: mHearingAidSetAsCommunicationDevice = "
650                 + mHearingAidSetAsCommunicationDevice);
651         if (!mHearingAidSetAsCommunicationDevice) {
652             return;
653         }
654         mHearingAidSetAsCommunicationDevice = false;
655         if (mHearingAidDevice != null) {
656             mBluetoothRouteManager.onAudioLost(mHearingAidDevice);
657             mHearingAidDevice = null;
658         }
659 
660         if (mAudioManager == null) {
661             Log.i(this, "clearHearingAidCommunicationDevice: mAudioManager is null");
662             return;
663         }
664 
665         AudioDeviceInfo audioDeviceInfo = mAudioManager.getCommunicationDevice();
666         if (audioDeviceInfo != null && audioDeviceInfo.getType()
667                 == AudioDeviceInfo.TYPE_HEARING_AID) {
668             Log.i(this, "clearHearingAidCommunicationDevice: "
669                     + "audioManager#clearCommunicationDevice");
670             mAudioManager.clearCommunicationDevice();
671         }
672     }
673 
setLeAudioCommunicationDevice()674     public boolean setLeAudioCommunicationDevice() {
675         if (mLeAudioSetAsCommunicationDevice) {
676             Log.i(this, "setLeAudioCommunicationDevice: already set");
677             return true;
678         }
679 
680         if (mAudioManager == null) {
681             Log.w(this, "setLeAudioCommunicationDevice: mAudioManager is null");
682             return false;
683         }
684 
685         AudioDeviceInfo bleHeadset = null;
686         List<AudioDeviceInfo> devices = mAudioManager.getAvailableCommunicationDevices();
687         if (devices.size() == 0) {
688             Log.w(this, "setLeAudioCommunicationDevice: No communication devices available.");
689             return false;
690         }
691 
692         for (AudioDeviceInfo device : devices) {
693             Log.d(this, "setLeAudioCommunicationDevice: Available device type:  "
694                     + device.getType());
695             if (device.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET) {
696                 bleHeadset = device;
697                 break;
698             }
699         }
700 
701         if (bleHeadset == null) {
702             Log.w(this, "setLeAudioCommunicationDevice: No bleHeadset device available");
703             return false;
704         }
705 
706         // clear hearing aid communication device if set
707         clearHearingAidCommunicationDevice();
708 
709         // Turn BLE_OUT_HEADSET ON.
710         boolean result = mAudioManager.setCommunicationDevice(bleHeadset);
711         if (!result) {
712             Log.w(this, "setLeAudioCommunicationDevice: AudioManager#setCommunicationDevice(%s)=%b;"
713                     + " Could not set bleHeadset device", bleHeadset, result);
714         } else {
715             Log.i(this, "setLeAudioCommunicationDevice: "
716                     + "AudioManager#setCommunicationDevice(%s)=%b", bleHeadset, result);
717             mBluetoothRouteManager.onAudioOn(bleHeadset.getAddress());
718             mLeAudioSetAsCommunicationDevice = true;
719             mLeAudioDevice = bleHeadset.getAddress();
720         }
721         return result;
722     }
723 
setHearingAidCommunicationDevice()724     public boolean setHearingAidCommunicationDevice() {
725         if (mHearingAidSetAsCommunicationDevice) {
726             Log.i(this, "setHearingAidCommunicationDevice: already set");
727             return true;
728         }
729 
730         if (mAudioManager == null) {
731             Log.w(this, "setHearingAidCommunicationDevice: mAudioManager is null");
732             return false;
733         }
734 
735         AudioDeviceInfo hearingAid = null;
736         List<AudioDeviceInfo> devices = mAudioManager.getAvailableCommunicationDevices();
737         if (devices.size() == 0) {
738             Log.w(this, "setHearingAidCommunicationDevice: No communication devices available.");
739             return false;
740         }
741 
742         for (AudioDeviceInfo device : devices) {
743             Log.d(this, "setHearingAidCommunicationDevice: Available device type: "
744                     + device.getType());
745             if (device.getType() == AudioDeviceInfo.TYPE_HEARING_AID) {
746                 hearingAid = device;
747                 break;
748             }
749         }
750 
751         if (hearingAid == null) {
752             Log.w(this, "setHearingAidCommunicationDevice: No hearingAid device available");
753             return false;
754         }
755 
756         // clear LE audio communication device if set
757         clearLeAudioCommunicationDevice();
758 
759         // Turn hearing aid ON.
760         boolean result = mAudioManager.setCommunicationDevice(hearingAid);
761         if (!result) {
762             Log.w(this, "setHearingAidCommunicationDevice: "
763                     + "AudioManager#setCommunicationDevice(%s)=%b; Could not set HA device",
764                     hearingAid, result);
765         } else {
766             Log.i(this, "setHearingAidCommunicationDevice: "
767                             + "AudioManager#setCommunicationDevice(%s)=%b", hearingAid, result);
768             mHearingAidDevice = hearingAid.getAddress();
769             mHearingAidSetAsCommunicationDevice = true;
770         }
771         return result;
772     }
773 
setCommunicationDeviceForAddress(String address)774     public boolean setCommunicationDeviceForAddress(String address) {
775         AudioDeviceInfo deviceInfo = null;
776         List<AudioDeviceInfo> devices = mAudioManager.getAvailableCommunicationDevices();
777         if (devices.size() == 0) {
778             Log.w(this, "setCommunicationDeviceForAddress: No communication devices available.");
779             return false;
780         }
781 
782         for (AudioDeviceInfo device : devices) {
783             Log.d(this, "setCommunicationDeviceForAddress: Available device type: "
784                     + device.getType());
785             if (device.getAddress().equals(address)) {
786                 deviceInfo = device;
787                 break;
788             }
789         }
790 
791         if (deviceInfo == null) {
792             Log.w(this, "setCommunicationDeviceForAddress: Device %s not found.", address);
793             return false;
794         }
795         if (deviceInfo.equals(mAudioManager.getCommunicationDevice())) {
796             Log.i(this, "setCommunicationDeviceForAddress: Device %s already active.", address);
797             return true;
798         }
799         boolean success = mAudioManager.setCommunicationDevice(deviceInfo);
800         Log.i(this, "setCommunicationDeviceForAddress: "
801                 + "AudioManager#setCommunicationDevice(%s)=%b", deviceInfo, success);
802         return success;
803     }
804 
805     // Connect audio to the bluetooth device at address, checking to see whether it's
806     // le audio, hearing aid or a HFP device, and using the proper BT API.
connectAudio(String address, boolean switchingBtDevices)807     public boolean connectAudio(String address, boolean switchingBtDevices) {
808         int callProfile = BluetoothProfile.LE_AUDIO;
809         BluetoothDevice device = null;
810         if (mLeAudioDevicesByAddress.containsKey(address)) {
811             Log.i(this, "connectAudio: found LE Audio device for address: %s", address);
812             if (mBluetoothLeAudioService == null) {
813                 Log.w(this, "connectAudio: Attempting to turn on audio when the le audio service "
814                         + "is null");
815                 return false;
816             }
817             device = mLeAudioDevicesByAddress.get(address);
818             callProfile = BluetoothProfile.LE_AUDIO;
819         } else if (mHearingAidDevicesByAddress.containsKey(address)) {
820             if (mBluetoothHearingAid == null) {
821                 Log.w(this, "connectAudio: Attempting to turn on audio when the hearing aid "
822                         + "service is null");
823                 return false;
824             }
825             Log.i(this, "connectAudio: found hearing aid device for address: %s", address);
826             device = mHearingAidDevicesByAddress.get(address);
827             callProfile = BluetoothProfile.HEARING_AID;
828         } else if (mHfpDevicesByAddress.containsKey(address)) {
829             if (getBluetoothHeadset() == null) {
830                 Log.w(this, "connectAudio: Attempting to turn on audio when the headset service "
831                         + "is null");
832                 return false;
833             }
834             Log.i(this, "connectAudio: found HFP device for address: %s", address);
835             device = mHfpDevicesByAddress.get(address);
836             callProfile = BluetoothProfile.HEADSET;
837         }
838 
839         if (device == null) {
840             Log.w(this, "No active profiles for Bluetooth address: %s", address);
841             return false;
842         }
843 
844         Bundle preferredAudioProfiles = mBluetoothAdapter.getPreferredAudioProfiles(device);
845         if (preferredAudioProfiles != null && !preferredAudioProfiles.isEmpty()
846             && preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX) != 0) {
847             Log.i(this, "connectAudio: Preferred duplex profile for device=% is %d", address,
848                 preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
849             callProfile = preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX);
850         }
851 
852         if (callProfile == BluetoothProfile.LE_AUDIO) {
853             if (mBluetoothAdapter.setActiveDevice(
854                     device, BluetoothAdapter.ACTIVE_DEVICE_ALL)) {
855                 Log.i(this, "connectAudio: BluetoothAdapter#setActiveDevice(%s)=true", address);
856                 /* ACTION_ACTIVE_DEVICE_CHANGED intent will trigger setting communication device.
857                  * Only after receiving ACTION_ACTIVE_DEVICE_CHANGED it is known that device that
858                  * will be audio switched to is available to be choose as communication device */
859                 if (!switchingBtDevices) {
860                     return mFeatureFlags.callAudioCommunicationDeviceRefactor() ?
861                             mCommunicationDeviceTracker.setCommunicationDevice(
862                                     AudioDeviceInfo.TYPE_BLE_HEADSET, device)
863                             : setLeAudioCommunicationDevice();
864                 }
865                 return true;
866             }
867             Log.i(this, "connectAudio: BluetoothAdapter#setActiveDevice(%s)=false", address);
868             return false;
869         } else if (callProfile == BluetoothProfile.HEARING_AID) {
870             if (mBluetoothAdapter.setActiveDevice(device, BluetoothAdapter.ACTIVE_DEVICE_ALL)) {
871                 Log.i(this, "connectAudio: BluetoothAdapter#setActiveDevice(%s)=true", address);
872                 /* ACTION_ACTIVE_DEVICE_CHANGED intent will trigger setting communication device.
873                  * Only after receiving ACTION_ACTIVE_DEVICE_CHANGED it is known that device that
874                  * will be audio switched to is available to be choose as communication device */
875                 if (!switchingBtDevices) {
876                     return mFeatureFlags.callAudioCommunicationDeviceRefactor() ?
877                             mCommunicationDeviceTracker.setCommunicationDevice(
878                                     AudioDeviceInfo.TYPE_HEARING_AID, null)
879                             : setHearingAidCommunicationDevice();
880                 }
881                 return true;
882             }
883             Log.i(this, "connectAudio: BluetoothAdapter#setActiveDevice(%s)=false", address);
884             return false;
885         } else if (callProfile == BluetoothProfile.HEADSET) {
886             boolean success = mBluetoothAdapter.setActiveDevice(device,
887                 BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL);
888             Log.i(this, "connectAudio: BluetoothAdapter#setActiveDevice(%s)=%b", address, success);
889             if (!success) {
890                 Log.w(this, "connectAudio: Couldn't set active device to %s", address);
891                 return false;
892             }
893             if (getBluetoothHeadset() != null) {
894                 int scoConnectionRequest = mBluetoothHeadset.connectAudio();
895                 Log.i(this, "connectAudio: BluetoothHeadset#connectAudio()=%s",
896                         btCodeToString(scoConnectionRequest));
897                 return scoConnectionRequest == BluetoothStatusCodes.SUCCESS ||
898                         scoConnectionRequest
899                                 == BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED;
900             } else {
901                 Log.w(this, "connectAudio: Couldn't find bluetooth headset service");
902                 return false;
903             }
904         } else {
905             Log.w(this, "connectAudio: Attempting to turn on audio for disconnected device %s",
906                     address);
907             return false;
908         }
909     }
910 
911     /**
912      * Used by CallAudioRouteController in order to connect the BT device.
913      * @param device {@link BluetoothDevice} to connect to.
914      * @param type {@link AudioRoute.AudioRouteType} associated with the device.
915      * @return {@code true} if device was successfully connected, {@code false} otherwise.
916      */
connectAudio(BluetoothDevice device, @AudioRoute.AudioRouteType int type, boolean isScoManagedByAudio)917     public boolean connectAudio(BluetoothDevice device, @AudioRoute.AudioRouteType int type,
918             boolean isScoManagedByAudio) {
919         String address = device.getAddress();
920         int callProfile = BluetoothProfile.LE_AUDIO;
921         if (type == TYPE_BLUETOOTH_SCO) {
922             callProfile = BluetoothProfile.HEADSET;
923         } else if (type == TYPE_BLUETOOTH_HA) {
924             callProfile = BluetoothProfile.HEARING_AID;
925         }
926 
927         Bundle preferredAudioProfiles = mBluetoothAdapter.getPreferredAudioProfiles(device);
928         if (preferredAudioProfiles != null && !preferredAudioProfiles.isEmpty()
929                 && preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX) != 0) {
930             Log.i(this, "connectAudio: Preferred duplex profile for device=%s is %d", address,
931                     preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
932             callProfile = preferredAudioProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX);
933         }
934 
935         if (callProfile == BluetoothProfile.LE_AUDIO
936                 || callProfile == BluetoothProfile.HEARING_AID || isScoManagedByAudio) {
937             boolean success = mBluetoothAdapter.setActiveDevice(device,
938                     BluetoothAdapter.ACTIVE_DEVICE_ALL);
939             Log.i(this, "connectAudio: BluetoothAdapter#setActiveDevice(%s)=%b", address, success);
940             return success;
941         } else if (callProfile == BluetoothProfile.HEADSET) {
942             boolean success = mBluetoothAdapter.setActiveDevice(device,
943                     BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL);
944             Log.i(this, "connectAudio: BluetoothAdapter#setActiveDevice(%s)=%b", address, success);
945             if (!success) {
946                 Log.w(this, "connectAudio: Couldn't set active device to %s", address);
947                 return false;
948             }
949             if (getBluetoothHeadset() != null) {
950                 int scoConnectionRequest = mBluetoothHeadset.connectAudio();
951                 Log.i(this, "connectAudio: BluetoothHeadset#connectAudio()=%s",
952                         btCodeToString(scoConnectionRequest));
953                 return scoConnectionRequest == BluetoothStatusCodes.SUCCESS ||
954                         scoConnectionRequest
955                                 == BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED;
956             } else {
957                 Log.w(this, "connectAudio: Couldn't find bluetooth headset service");
958                 return false;
959             }
960         } else {
961             Log.w(this, "connectAudio: Attempting to turn on audio for a disconnected device %s",
962                     address);
963             return false;
964         }
965     }
966 
cacheHearingAidDevice()967     public void cacheHearingAidDevice() {
968         if (mBluetoothAdapter != null) {
969             for (BluetoothDevice device : mBluetoothAdapter.getActiveDevices(
970                         BluetoothProfile.HEARING_AID)) {
971                 if (device != null) {
972                     mBluetoothHearingAidActiveDeviceCache = device;
973                 }
974             }
975         }
976     }
977 
restoreHearingAidDevice()978     public void restoreHearingAidDevice() {
979         if (mBluetoothHearingAidActiveDeviceCache != null) {
980             mBluetoothAdapter.setActiveDevice(mBluetoothHearingAidActiveDeviceCache,
981                     BluetoothAdapter.ACTIVE_DEVICE_ALL);
982             Log.i(this, "restoreHearingAidDevice: BluetoothAdapter#setActiveDevice(%s)",
983                     mBluetoothHearingAidActiveDeviceCache.getAddress());
984             mBluetoothHearingAidActiveDeviceCache = null;
985         }
986     }
987 
isInbandRingingEnabled()988     public boolean isInbandRingingEnabled() {
989         // Get the inband ringing enabled status of expected BT device to route call audio instead
990         // of using the address of currently connected device.
991         BluetoothDevice activeDevice = mBluetoothRouteManager.getMostRecentlyReportedActiveDevice();
992         return isInbandRingEnabled(activeDevice);
993     }
994 
995     /**
996      * Check if inband ringing is enabled for the specified BT device.
997      * This is intended for use by {@link CallAudioRouteController}.
998      * @param audioRouteType The BT device type.
999      * @param bluetoothDevice The BT device.
1000      * @return {@code true} if inband ringing is enabled, {@code false} otherwise.
1001      */
isInbandRingEnabled(@udioRoute.AudioRouteType int audioRouteType, BluetoothDevice bluetoothDevice)1002     public boolean isInbandRingEnabled(@AudioRoute.AudioRouteType int audioRouteType,
1003             BluetoothDevice bluetoothDevice) {
1004         if (audioRouteType == AudioRoute.TYPE_BLUETOOTH_LE) {
1005             if (mBluetoothLeAudioService == null) {
1006                 Log.i(this, "isInbandRingingEnabled: no leaudio service available.");
1007                 return false;
1008             }
1009             int groupId = mBluetoothLeAudioService.getGroupId(bluetoothDevice);
1010             return mBluetoothLeAudioService.isInbandRingtoneEnabled(groupId);
1011         } else {
1012             if (getBluetoothHeadset() == null) {
1013                 Log.i(this, "isInbandRingingEnabled: no headset service available.");
1014                 return false;
1015             }
1016             boolean isEnabled = mBluetoothHeadset.isInbandRingingEnabled();
1017             Log.i(this, "isInbandRingEnabled: device: %s, isEnabled: %b", bluetoothDevice,
1018                     isEnabled);
1019             return isEnabled;
1020         }
1021     }
1022 
isInbandRingEnabled(BluetoothDevice bluetoothDevice)1023     public boolean isInbandRingEnabled(BluetoothDevice bluetoothDevice) {
1024         if (mBluetoothRouteManager.isCachedLeAudioDevice(bluetoothDevice)) {
1025             if (mBluetoothLeAudioService == null) {
1026                 Log.i(this, "isInbandRingingEnabled: no leaudio service available.");
1027                 return false;
1028             }
1029             int groupId = mBluetoothLeAudioService.getGroupId(bluetoothDevice);
1030             return mBluetoothLeAudioService.isInbandRingtoneEnabled(groupId);
1031         } else {
1032             if (getBluetoothHeadset() == null) {
1033                 Log.i(this, "isInbandRingingEnabled: no headset service available.");
1034                 return false;
1035             }
1036             boolean isEnabled = mBluetoothHeadset.isInbandRingingEnabled();
1037             Log.i(this, "isInbandRingEnabled: device: %s, isEnabled: %b", bluetoothDevice,
1038                     isEnabled);
1039             return isEnabled;
1040         }
1041     }
1042 
setCallAudioRouteAdapter(CallAudioRouteAdapter adapter)1043     public void setCallAudioRouteAdapter(CallAudioRouteAdapter adapter) {
1044         mCallAudioRouteAdapter = adapter;
1045     }
1046 
dump(IndentingPrintWriter pw)1047     public void dump(IndentingPrintWriter pw) {
1048         mLocalLog.dump(pw);
1049     }
1050 
btCodeToString(int code)1051     private String btCodeToString(int code) {
1052         switch (code) {
1053             case BluetoothStatusCodes.SUCCESS:
1054                 return "SUCCESS";
1055             case BluetoothStatusCodes.ERROR_UNKNOWN:
1056                 return "ERROR_UNKNOWN";
1057             case BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND:
1058                 return "ERROR_PROFILE_SERVICE_NOT_BOUND";
1059             case BluetoothStatusCodes.ERROR_TIMEOUT:
1060                 return "ERROR_TIMEOUT";
1061             case BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED:
1062                 return "ERROR_AUDIO_DEVICE_ALREADY_CONNECTED";
1063             case BluetoothStatusCodes.ERROR_NO_ACTIVE_DEVICES:
1064                 return "ERROR_NO_ACTIVE_DEVICES";
1065             case BluetoothStatusCodes.ERROR_NOT_ACTIVE_DEVICE:
1066                 return "ERROR_NOT_ACTIVE_DEVICE";
1067             case BluetoothStatusCodes.ERROR_AUDIO_ROUTE_BLOCKED:
1068                 return "ERROR_AUDIO_ROUTE_BLOCKED";
1069             case BluetoothStatusCodes.ERROR_CALL_ACTIVE:
1070                 return "ERROR_CALL_ACTIVE";
1071             case BluetoothStatusCodes.ERROR_PROFILE_NOT_CONNECTED:
1072                 return "ERROR_PROFILE_NOT_CONNECTED";
1073             case BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED:
1074                 return "BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED";
1075             default:
1076                 return Integer.toString(code);
1077         }
1078     }
1079 }
1080