• 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 android.bluetooth.BluetoothAdapter;
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothHeadset;
22 import android.bluetooth.BluetoothHearingAid;
23 import android.bluetooth.BluetoothLeAudio;
24 import android.bluetooth.BluetoothLeAudioCodecStatus;
25 import android.bluetooth.BluetoothProfile;
26 import android.bluetooth.BluetoothStatusCodes;
27 import android.content.Context;
28 import android.media.AudioManager;
29 import android.media.AudioDeviceInfo;
30 import android.media.audio.common.AudioDevice;
31 import android.telecom.Log;
32 import android.util.LocalLog;
33 
34 import com.android.internal.util.IndentingPrintWriter;
35 
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.Collections;
39 import java.util.concurrent.Executor;
40 import java.util.LinkedHashMap;
41 import java.util.LinkedHashSet;
42 import java.util.LinkedList;
43 import java.util.List;
44 import java.util.Objects;
45 import java.util.Set;
46 
47 public class BluetoothDeviceManager {
48 
49     public static final int DEVICE_TYPE_HEADSET = 0;
50     public static final int DEVICE_TYPE_HEARING_AID = 1;
51     public static final int DEVICE_TYPE_LE_AUDIO = 2;
52 
53     private BluetoothLeAudio.Callback mLeAudioCallbacks =
54         new BluetoothLeAudio.Callback() {
55             @Override
56             public void onCodecConfigChanged(int groupId, BluetoothLeAudioCodecStatus status) {}
57             @Override
58             public void onGroupStatusChanged(int groupId, int groupStatus) {}
59             @Override
60             public void onGroupNodeAdded(BluetoothDevice device, int groupId) {
61                 Log.i(this, device.getAddress() + " group added " + groupId);
62                 if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
63                     Log.w(this, "invalid parameter");
64                     return;
65                 }
66 
67                 synchronized (mLock) {
68                     mGroupsByDevice.put(device, groupId);
69                 }
70             }
71             @Override
72             public void onGroupNodeRemoved(BluetoothDevice device, int groupId) {
73                 if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) {
74                     Log.w(this, "invalid parameter");
75                     return;
76                 }
77 
78                 synchronized (mLock) {
79                     mGroupsByDevice.remove(device);
80                 }
81             }
82         };
83 
84     private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
85             new BluetoothProfile.ServiceListener() {
86                 @Override
87                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
88                     Log.startSession("BMSL.oSC");
89                     try {
90                         synchronized (mLock) {
91                             String logString;
92                             if (profile == BluetoothProfile.HEADSET) {
93                                 mBluetoothHeadset = (BluetoothHeadset) proxy;
94                                 logString = "Got BluetoothHeadset: " + mBluetoothHeadset;
95                             } else if (profile == BluetoothProfile.HEARING_AID) {
96                                 mBluetoothHearingAid = (BluetoothHearingAid) proxy;
97                                 logString = "Got BluetoothHearingAid: "
98                                         + mBluetoothHearingAid;
99                             } else if (profile == BluetoothProfile.LE_AUDIO) {
100                                 mBluetoothLeAudioService = (BluetoothLeAudio) proxy;
101                                 logString = "Got BluetoothLeAudio: "
102                                         + mBluetoothLeAudioService;
103                                 if (!mLeAudioCallbackRegistered) {
104                                     mBluetoothLeAudioService.registerCallback(
105                                                 mExecutor, mLeAudioCallbacks);
106                                     mLeAudioCallbackRegistered = true;
107                                 }
108                             } else {
109                                 logString = "Connected to non-requested bluetooth service." +
110                                         " Not changing bluetooth headset.";
111                             }
112                             Log.i(BluetoothDeviceManager.this, logString);
113                             mLocalLog.log(logString);
114                         }
115                     } finally {
116                         Log.endSession();
117                     }
118                 }
119 
120                 @Override
121                 public void onServiceDisconnected(int profile) {
122                     Log.startSession("BMSL.oSD");
123                     try {
124                         synchronized (mLock) {
125                             LinkedHashMap<String, BluetoothDevice> lostServiceDevices;
126                             String logString;
127                             if (profile == BluetoothProfile.HEADSET) {
128                                 mBluetoothHeadset = null;
129                                 lostServiceDevices = mHfpDevicesByAddress;
130                                 mBluetoothRouteManager.onActiveDeviceChanged(null,
131                                         DEVICE_TYPE_HEADSET);
132                                 logString = "Lost BluetoothHeadset service. " +
133                                         "Removing all tracked devices";
134                             } else if (profile == BluetoothProfile.HEARING_AID) {
135                                 mBluetoothHearingAid = null;
136                                 logString = "Lost BluetoothHearingAid service. " +
137                                         "Removing all tracked devices.";
138                                 lostServiceDevices = mHearingAidDevicesByAddress;
139                                 mBluetoothRouteManager.onActiveDeviceChanged(null,
140                                         DEVICE_TYPE_HEARING_AID);
141                             } else if (profile == BluetoothProfile.LE_AUDIO) {
142                                 mBluetoothLeAudioService = null;
143                                 logString = "Lost BluetoothLeAudio service. " +
144                                         "Removing all tracked devices.";
145                                 lostServiceDevices = mLeAudioDevicesByAddress;
146                                 mBluetoothRouteManager.onActiveDeviceChanged(null,
147                                         DEVICE_TYPE_LE_AUDIO);
148                             } else {
149                                 return;
150                             }
151                             Log.i(BluetoothDeviceManager.this, logString);
152                             mLocalLog.log(logString);
153 
154                             List<BluetoothDevice> devicesToRemove = new LinkedList<>(
155                                     lostServiceDevices.values());
156                             lostServiceDevices.clear();
157                             for (BluetoothDevice device : devicesToRemove) {
158                                 mBluetoothRouteManager.onDeviceLost(device.getAddress());
159                             }
160                         }
161                     } finally {
162                         Log.endSession();
163                     }
164                 }
165            };
166 
167     private final LinkedHashMap<String, BluetoothDevice> mHfpDevicesByAddress =
168             new LinkedHashMap<>();
169     private final LinkedHashMap<String, BluetoothDevice> mHearingAidDevicesByAddress =
170             new LinkedHashMap<>();
171     private final LinkedHashMap<BluetoothDevice, Long> mHearingAidDeviceSyncIds =
172             new LinkedHashMap<>();
173     private final LinkedHashMap<String, BluetoothDevice> mLeAudioDevicesByAddress =
174             new LinkedHashMap<>();
175     private final LinkedHashMap<BluetoothDevice, Integer> mGroupsByDevice =
176             new LinkedHashMap<>();
177     private int mGroupIdActive = BluetoothLeAudio.GROUP_ID_INVALID;
178     private int mGroupIdPending = BluetoothLeAudio.GROUP_ID_INVALID;
179     private final LocalLog mLocalLog = new LocalLog(20);
180 
181     // This lock only protects internal state -- it doesn't lock on anything going into Telecom.
182     private final Object mLock = new Object();
183 
184     private BluetoothRouteManager mBluetoothRouteManager;
185     private BluetoothHeadset mBluetoothHeadset;
186     private BluetoothHearingAid mBluetoothHearingAid;
187     private boolean mLeAudioCallbackRegistered = false;
188     private BluetoothLeAudio mBluetoothLeAudioService;
189     private boolean mLeAudioSetAsCommunicationDevice = false;
190     private String mLeAudioDevice;
191     private String mHearingAidDevice;
192     private boolean mHearingAidSetAsCommunicationDevice = false;
193     private BluetoothDevice mBluetoothHearingAidActiveDeviceCache;
194     private BluetoothAdapter mBluetoothAdapter;
195     private AudioManager mAudioManager;
196     private Executor mExecutor;
197 
BluetoothDeviceManager(Context context, BluetoothAdapter bluetoothAdapter)198     public BluetoothDeviceManager(Context context, BluetoothAdapter bluetoothAdapter) {
199         if (bluetoothAdapter != null) {
200             mBluetoothAdapter = bluetoothAdapter;
201             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
202                     BluetoothProfile.HEADSET);
203             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
204                     BluetoothProfile.HEARING_AID);
205             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
206                     BluetoothProfile.LE_AUDIO);
207             mAudioManager = context.getSystemService(AudioManager.class);
208             mExecutor = context.getMainExecutor();
209         }
210     }
211 
setBluetoothRouteManager(BluetoothRouteManager brm)212     public void setBluetoothRouteManager(BluetoothRouteManager brm) {
213         mBluetoothRouteManager = brm;
214     }
215 
getLeAudioConnectedDevices()216     private List<BluetoothDevice> getLeAudioConnectedDevices() {
217         synchronized (mLock) {
218             // Let's get devices which are a group leaders
219             ArrayList<BluetoothDevice> devices = new ArrayList<>();
220 
221             if (mGroupsByDevice.isEmpty() || mBluetoothLeAudioService == null) {
222                 return devices;
223             }
224 
225             for (LinkedHashMap.Entry<BluetoothDevice, Integer> entry : mGroupsByDevice.entrySet()) {
226                if (Objects.equals(entry.getKey(),
227                         mBluetoothLeAudioService.getConnectedGroupLeadDevice(entry.getValue()))) {
228                    devices.add(entry.getKey());
229                }
230             }
231             devices.removeIf(device -> !mLeAudioDevicesByAddress.containsValue(device));
232             return devices;
233         }
234     }
235 
getNumConnectedDevices()236     public int getNumConnectedDevices() {
237         synchronized (mLock) {
238             return mHfpDevicesByAddress.size() +
239                     mHearingAidDevicesByAddress.size() +
240                     getLeAudioConnectedDevices().size();
241         }
242     }
243 
getConnectedDevices()244     public Collection<BluetoothDevice> getConnectedDevices() {
245         synchronized (mLock) {
246             ArrayList<BluetoothDevice> result = new ArrayList<>(mHfpDevicesByAddress.values());
247             result.addAll(mHearingAidDevicesByAddress.values());
248             result.addAll(getLeAudioConnectedDevices());
249             return Collections.unmodifiableCollection(result);
250         }
251     }
252 
253     // Same as getConnectedDevices except it filters out the hearing aid devices that are linked
254     // together by their hiSyncId.
getUniqueConnectedDevices()255     public Collection<BluetoothDevice> getUniqueConnectedDevices() {
256         ArrayList<BluetoothDevice> result;
257         synchronized (mLock) {
258             result = new ArrayList<>(mHfpDevicesByAddress.values());
259         }
260         Set<Long> seenHiSyncIds = new LinkedHashSet<>();
261         // Add the left-most active device to the seen list so that we match up with the list
262         // generated in BluetoothRouteManager.
263         if (mBluetoothAdapter != null) {
264             for (BluetoothDevice device : mBluetoothAdapter.getActiveDevices(
265                         BluetoothProfile.HEARING_AID)) {
266                 if (device != null) {
267                     result.add(device);
268                     seenHiSyncIds.add(mHearingAidDeviceSyncIds.getOrDefault(device, -1L));
269                     break;
270                 }
271             }
272         }
273         synchronized (mLock) {
274             for (BluetoothDevice d : mHearingAidDevicesByAddress.values()) {
275                 long hiSyncId = mHearingAidDeviceSyncIds.getOrDefault(d, -1L);
276                 if (seenHiSyncIds.contains(hiSyncId)) {
277                     continue;
278                 }
279                 result.add(d);
280                 seenHiSyncIds.add(hiSyncId);
281             }
282         }
283 
284         if (mBluetoothLeAudioService != null) {
285             result.addAll(getLeAudioConnectedDevices());
286         }
287 
288         return Collections.unmodifiableCollection(result);
289     }
290 
getBluetoothHeadset()291     public BluetoothHeadset getBluetoothHeadset() {
292         return mBluetoothHeadset;
293     }
294 
getBluetoothAdapter()295     public BluetoothAdapter getBluetoothAdapter() {
296         return mBluetoothAdapter;
297     }
298 
getBluetoothHearingAid()299     public BluetoothHearingAid getBluetoothHearingAid() {
300         return mBluetoothHearingAid;
301     }
302 
getLeAudioService()303     public BluetoothLeAudio getLeAudioService() {
304         return mBluetoothLeAudioService;
305     }
306 
setHeadsetServiceForTesting(BluetoothHeadset bluetoothHeadset)307     public void setHeadsetServiceForTesting(BluetoothHeadset bluetoothHeadset) {
308         mBluetoothHeadset = bluetoothHeadset;
309     }
310 
setHearingAidServiceForTesting(BluetoothHearingAid bluetoothHearingAid)311     public void setHearingAidServiceForTesting(BluetoothHearingAid bluetoothHearingAid) {
312         mBluetoothHearingAid = bluetoothHearingAid;
313     }
314 
setLeAudioServiceForTesting(BluetoothLeAudio bluetoothLeAudio)315     public void setLeAudioServiceForTesting(BluetoothLeAudio bluetoothLeAudio) {
316         mBluetoothLeAudioService = bluetoothLeAudio;
317         mBluetoothLeAudioService.registerCallback(mExecutor, mLeAudioCallbacks);
318     }
319 
getDeviceTypeString(int deviceType)320     public static String getDeviceTypeString(int deviceType) {
321         switch (deviceType) {
322             case DEVICE_TYPE_LE_AUDIO:
323                 return "LeAudio";
324             case DEVICE_TYPE_HEARING_AID:
325                 return "HearingAid";
326             case DEVICE_TYPE_HEADSET:
327                 return "HFP";
328             default:
329                 return "unknown type";
330         }
331     }
332 
onDeviceConnected(BluetoothDevice device, int deviceType)333     void onDeviceConnected(BluetoothDevice device, int deviceType) {
334         synchronized (mLock) {
335             LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
336             if (deviceType == DEVICE_TYPE_LE_AUDIO) {
337                 if (mBluetoothLeAudioService == null) {
338                     Log.w(this, "LE audio service null when receiving device added broadcast");
339                     return;
340                 }
341                 /* Check if group is known. */
342                 if (!mGroupsByDevice.containsKey(device)) {
343                     int groupId = mBluetoothLeAudioService.getGroupId(device);
344                     /* If it is not yet assigned, then it will be provided in the callback */
345                     if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
346                         mGroupsByDevice.put(device, groupId);
347                     }
348                 }
349                 targetDeviceMap = mLeAudioDevicesByAddress;
350             } else if (deviceType == DEVICE_TYPE_HEARING_AID) {
351                 if (mBluetoothHearingAid == null) {
352                     Log.w(this, "Hearing aid service null when receiving device added broadcast");
353                     return;
354                 }
355                 long hiSyncId = mBluetoothHearingAid.getHiSyncId(device);
356                 mHearingAidDeviceSyncIds.put(device, hiSyncId);
357                 targetDeviceMap = mHearingAidDevicesByAddress;
358             } else if (deviceType == DEVICE_TYPE_HEADSET) {
359                 if (mBluetoothHeadset == null) {
360                     Log.w(this, "Headset service null when receiving device added broadcast");
361                     return;
362                 }
363                 targetDeviceMap = mHfpDevicesByAddress;
364             } else {
365                 Log.w(this, "Device: " + device.getAddress() + " with invalid type: "
366                             + getDeviceTypeString(deviceType));
367                 return;
368             }
369             if (!targetDeviceMap.containsKey(device.getAddress())) {
370                 targetDeviceMap.put(device.getAddress(), device);
371                 mBluetoothRouteManager.onDeviceAdded(device.getAddress());
372             }
373         }
374     }
375 
onDeviceDisconnected(BluetoothDevice device, int deviceType)376     void onDeviceDisconnected(BluetoothDevice device, int deviceType) {
377         mLocalLog.log("Device disconnected -- address: " + device.getAddress() + " deviceType: "
378                 + deviceType);
379         synchronized (mLock) {
380             LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
381             if (deviceType == DEVICE_TYPE_LE_AUDIO) {
382                 targetDeviceMap = mLeAudioDevicesByAddress;
383             } else if (deviceType == DEVICE_TYPE_HEARING_AID) {
384                 mHearingAidDeviceSyncIds.remove(device);
385                 targetDeviceMap = mHearingAidDevicesByAddress;
386             } else if (deviceType == DEVICE_TYPE_HEADSET) {
387                 targetDeviceMap = mHfpDevicesByAddress;
388             } else {
389                 Log.w(this, "Device: " + device.getAddress() + " with invalid type: "
390                             + getDeviceTypeString(deviceType));
391                 return;
392             }
393             if (targetDeviceMap.containsKey(device.getAddress())) {
394                 targetDeviceMap.remove(device.getAddress());
395                 mBluetoothRouteManager.onDeviceLost(device.getAddress());
396             }
397         }
398     }
399 
disconnectAudio()400     public void disconnectAudio() {
401         disconnectSco();
402         clearLeAudioCommunicationDevice();
403         clearHearingAidCommunicationDevice();
404     }
405 
disconnectSco()406     public void disconnectSco() {
407         if (mBluetoothHeadset == null) {
408             Log.w(this, "Trying to disconnect audio but no headset service exists.");
409         } else {
410             mBluetoothHeadset.disconnectAudio();
411         }
412     }
413 
isLeAudioCommunicationDevice()414     public boolean isLeAudioCommunicationDevice() {
415         return mLeAudioSetAsCommunicationDevice;
416     }
417 
isHearingAidSetAsCommunicationDevice()418     public boolean isHearingAidSetAsCommunicationDevice() {
419         return mHearingAidSetAsCommunicationDevice;
420     }
421 
clearLeAudioCommunicationDevice()422     public void clearLeAudioCommunicationDevice() {
423         Log.i(this, "clearLeAudioCommunicationDevice: mLeAudioSetAsCommunicationDevice = " +
424                 mLeAudioSetAsCommunicationDevice + " device = " + mLeAudioDevice);
425         if (!mLeAudioSetAsCommunicationDevice) {
426             return;
427         }
428         mLeAudioSetAsCommunicationDevice = false;
429         if (mLeAudioDevice != null) {
430             mBluetoothRouteManager.onAudioLost(mLeAudioDevice);
431             mLeAudioDevice = null;
432         }
433 
434         if (mAudioManager == null) {
435             Log.i(this, "clearLeAudioCommunicationDevice: mAudioManager is null");
436             return;
437         }
438 
439         AudioDeviceInfo audioDeviceInfo = mAudioManager.getCommunicationDevice();
440         if (audioDeviceInfo != null && audioDeviceInfo.getType()
441                 == AudioDeviceInfo.TYPE_BLE_HEADSET) {
442             mAudioManager.clearCommunicationDevice();
443         }
444     }
445 
clearHearingAidCommunicationDevice()446     public void clearHearingAidCommunicationDevice() {
447         Log.i(this, "clearHearingAidCommunicationDevice: mHearingAidSetAsCommunicationDevice = "
448                 + mHearingAidSetAsCommunicationDevice);
449         if (!mHearingAidSetAsCommunicationDevice) {
450             return;
451         }
452         mHearingAidSetAsCommunicationDevice = false;
453         if (mHearingAidDevice != null) {
454             mBluetoothRouteManager.onAudioLost(mHearingAidDevice);
455             mHearingAidDevice = null;
456         }
457 
458         if (mAudioManager == null) {
459             Log.i(this, "clearHearingAidCommunicationDevice: mAudioManager is null");
460             return;
461         }
462 
463         AudioDeviceInfo audioDeviceInfo = mAudioManager.getCommunicationDevice();
464         if (audioDeviceInfo != null && audioDeviceInfo.getType()
465                 == AudioDeviceInfo.TYPE_HEARING_AID) {
466             mAudioManager.clearCommunicationDevice();
467             mHearingAidSetAsCommunicationDevice = false;
468         }
469         mHearingAidSetAsCommunicationDevice = false;
470     }
471 
setLeAudioCommunicationDevice()472     public boolean setLeAudioCommunicationDevice() {
473         Log.i(this, "setLeAudioCommunicationDevice");
474 
475         if (mLeAudioSetAsCommunicationDevice) {
476             Log.i(this, "setLeAudioCommunicationDevice already set");
477             return true;
478         }
479 
480         if (mAudioManager == null) {
481             Log.w(this, " mAudioManager is null");
482             return false;
483         }
484 
485         AudioDeviceInfo bleHeadset = null;
486         List<AudioDeviceInfo> devices = mAudioManager.getAvailableCommunicationDevices();
487         if (devices.size() == 0) {
488             Log.w(this, " No communication devices available.");
489             return false;
490         }
491 
492         for (AudioDeviceInfo device : devices) {
493             Log.i(this, " Available device type:  " + device.getType());
494             if (device.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET) {
495                 bleHeadset = device;
496                 break;
497             }
498         }
499 
500         if (bleHeadset == null) {
501             Log.w(this, " No bleHeadset device available");
502             return false;
503         }
504 
505         // clear hearing aid communication device if set
506         clearHearingAidCommunicationDevice();
507 
508         // Turn BLE_OUT_HEADSET ON.
509         boolean result = mAudioManager.setCommunicationDevice(bleHeadset);
510         if (!result) {
511             Log.w(this, " Could not set bleHeadset device");
512         } else {
513             Log.i(this, " bleHeadset device set");
514             mBluetoothRouteManager.onAudioOn(bleHeadset.getAddress());
515             mLeAudioSetAsCommunicationDevice = true;
516             mLeAudioDevice = bleHeadset.getAddress();
517         }
518         return result;
519     }
520 
setHearingAidCommunicationDevice()521     public boolean setHearingAidCommunicationDevice() {
522         Log.i(this, "setHearingAidCommunicationDevice");
523 
524         if (mHearingAidSetAsCommunicationDevice) {
525             Log.i(this, "mHearingAidSetAsCommunicationDevice already set");
526             return true;
527         }
528 
529         if (mAudioManager == null) {
530             Log.w(this, " mAudioManager is null");
531             return false;
532         }
533 
534         AudioDeviceInfo hearingAid = null;
535         List<AudioDeviceInfo> devices = mAudioManager.getAvailableCommunicationDevices();
536         if (devices.size() == 0) {
537             Log.w(this, " No communication devices available.");
538             return false;
539         }
540 
541         for (AudioDeviceInfo device : devices) {
542             Log.i(this, " Available device type:  " + device.getType());
543             if (device.getType() == AudioDeviceInfo.TYPE_HEARING_AID) {
544                 hearingAid = device;
545                 break;
546             }
547         }
548 
549         if (hearingAid == null) {
550             Log.w(this, " No hearingAid device available");
551             return false;
552         }
553 
554         // clear LE audio communication device if set
555         clearLeAudioCommunicationDevice();
556 
557         // Turn hearing aid ON.
558         boolean result = mAudioManager.setCommunicationDevice(hearingAid);
559         if (!result) {
560             Log.w(this, " Could not set hearingAid device");
561         } else {
562             Log.i(this, " hearingAid device set");
563             mHearingAidDevice = hearingAid.getAddress();
564             mHearingAidSetAsCommunicationDevice = true;
565         }
566         return result;
567     }
568 
569     // Connect audio to the bluetooth device at address, checking to see whether it's
570     // le audio, hearing aid or a HFP device, and using the proper BT API.
connectAudio(String address, boolean switchingBtDevices)571     public boolean connectAudio(String address, boolean switchingBtDevices) {
572         if (mLeAudioDevicesByAddress.containsKey(address)) {
573             if (mBluetoothLeAudioService == null) {
574                 Log.w(this, "Attempting to turn on audio when the le audio service is null");
575                 return false;
576             }
577             BluetoothDevice device = mLeAudioDevicesByAddress.get(address);
578             if (mBluetoothAdapter.setActiveDevice(
579                     device, BluetoothAdapter.ACTIVE_DEVICE_ALL)) {
580 
581                 /* ACTION_ACTIVE_DEVICE_CHANGED intent will trigger setting communication device.
582                  * Only after receiving ACTION_ACTIVE_DEVICE_CHANGED it is known that device that
583                  * will be audio switched to is available to be choose as communication device */
584                 if (!switchingBtDevices) {
585                     return setLeAudioCommunicationDevice();
586                 }
587 
588                 return true;
589             }
590             return false;
591         } else if (mHearingAidDevicesByAddress.containsKey(address)) {
592             if (mBluetoothHearingAid == null) {
593                 Log.w(this, "Attempting to turn on audio when the hearing aid service is null");
594                 return false;
595             }
596             if (mBluetoothAdapter.setActiveDevice(
597                     mHearingAidDevicesByAddress.get(address),
598                     BluetoothAdapter.ACTIVE_DEVICE_ALL)) {
599 
600                 /* ACTION_ACTIVE_DEVICE_CHANGED intent will trigger setting communication device.
601                  * Only after receiving ACTION_ACTIVE_DEVICE_CHANGED it is known that device that
602                  * will be audio switched to is available to be choose as communication device */
603                 if (!switchingBtDevices) {
604                     return setHearingAidCommunicationDevice();
605                 }
606 
607                 return true;
608             }
609             return false;
610         } else if (mHfpDevicesByAddress.containsKey(address)) {
611             BluetoothDevice device = mHfpDevicesByAddress.get(address);
612             if (mBluetoothHeadset == null) {
613                 Log.w(this, "Attempting to turn on audio when the headset service is null");
614                 return false;
615             }
616             boolean success = mBluetoothAdapter.setActiveDevice(device,
617                 BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL);
618             if (!success) {
619                 Log.w(this, "Couldn't set active device to %s", address);
620                 return false;
621             }
622             int scoConnectionRequest = mBluetoothHeadset.connectAudio();
623             return scoConnectionRequest == BluetoothStatusCodes.SUCCESS ||
624                 scoConnectionRequest == BluetoothStatusCodes.ERROR_AUDIO_DEVICE_ALREADY_CONNECTED;
625         } else {
626             Log.w(this, "Attempting to turn on audio for a disconnected device");
627             return false;
628         }
629     }
630 
cacheHearingAidDevice()631     public void cacheHearingAidDevice() {
632         if (mBluetoothAdapter != null) {
633             for (BluetoothDevice device : mBluetoothAdapter.getActiveDevices(
634                         BluetoothProfile.HEARING_AID)) {
635                 if (device != null) {
636                     mBluetoothHearingAidActiveDeviceCache = device;
637                 }
638             }
639         }
640     }
641 
restoreHearingAidDevice()642     public void restoreHearingAidDevice() {
643         if (mBluetoothHearingAidActiveDeviceCache != null) {
644             mBluetoothAdapter.setActiveDevice(mBluetoothHearingAidActiveDeviceCache,
645                     BluetoothAdapter.ACTIVE_DEVICE_ALL);
646             mBluetoothHearingAidActiveDeviceCache = null;
647         }
648     }
649 
dump(IndentingPrintWriter pw)650     public void dump(IndentingPrintWriter pw) {
651         mLocalLog.dump(pw);
652     }
653 }
654