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