1 /* 2 * Copyright 2020 HIMSA II K/S - www.himsa.com. 3 * Represented by EHIMA - www.ehima.com 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.bluetooth.le_audio; 19 20 import static android.Manifest.permission.BLUETOOTH_CONNECT; 21 import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; 22 23 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; 24 25 import android.annotation.RequiresPermission; 26 import android.annotation.SuppressLint; 27 import android.bluetooth.BluetoothAdapter; 28 import android.bluetooth.BluetoothDevice; 29 import android.bluetooth.BluetoothLeAudio; 30 import android.bluetooth.BluetoothLeAudioCodecConfig; 31 import android.bluetooth.BluetoothLeAudioCodecStatus; 32 import android.bluetooth.BluetoothLeAudioContentMetadata; 33 import android.bluetooth.BluetoothLeBroadcastMetadata; 34 import android.bluetooth.BluetoothLeBroadcastSettings; 35 import android.bluetooth.BluetoothLeBroadcastSubgroupSettings; 36 import android.bluetooth.BluetoothProfile; 37 import android.bluetooth.BluetoothStatusCodes; 38 import android.bluetooth.BluetoothUuid; 39 import android.bluetooth.IBluetoothLeAudio; 40 import android.bluetooth.IBluetoothLeAudioCallback; 41 import android.bluetooth.IBluetoothLeBroadcastCallback; 42 import android.bluetooth.IBluetoothVolumeControl; 43 import android.bluetooth.le.BluetoothLeScanner; 44 import android.bluetooth.le.ScanCallback; 45 import android.bluetooth.le.ScanFilter; 46 import android.bluetooth.le.ScanResult; 47 import android.bluetooth.le.ScanSettings; 48 import android.content.AttributionSource; 49 import android.content.BroadcastReceiver; 50 import android.content.Context; 51 import android.content.Intent; 52 import android.content.IntentFilter; 53 import android.media.AudioDeviceCallback; 54 import android.media.AudioDeviceInfo; 55 import android.media.AudioManager; 56 import android.media.BluetoothProfileConnectionInfo; 57 import android.os.Handler; 58 import android.os.HandlerThread; 59 import android.os.Looper; 60 import android.os.Parcel; 61 import android.os.ParcelUuid; 62 import android.os.RemoteCallbackList; 63 import android.os.RemoteException; 64 import android.sysprop.BluetoothProperties; 65 import android.util.Log; 66 import android.util.Pair; 67 68 import com.android.bluetooth.Utils; 69 import com.android.bluetooth.btservice.AdapterService; 70 import com.android.bluetooth.btservice.ProfileService; 71 import com.android.bluetooth.btservice.ServiceFactory; 72 import com.android.bluetooth.btservice.storage.DatabaseManager; 73 import com.android.bluetooth.csip.CsipSetCoordinatorService; 74 import com.android.bluetooth.hfp.HeadsetService; 75 import com.android.bluetooth.mcp.McpService; 76 import com.android.bluetooth.tbs.TbsGatt; 77 import com.android.bluetooth.tbs.TbsService; 78 import com.android.bluetooth.vc.VolumeControlService; 79 import com.android.internal.annotations.GuardedBy; 80 import com.android.internal.annotations.VisibleForTesting; 81 import com.android.modules.utils.SynchronousResultReceiver; 82 83 import java.util.ArrayList; 84 import java.util.Collections; 85 import java.util.HashMap; 86 import java.util.LinkedHashMap; 87 import java.util.List; 88 import java.util.Map; 89 import java.util.Objects; 90 91 /** 92 * Provides Bluetooth LeAudio profile, as a service in the Bluetooth application. 93 * @hide 94 */ 95 public class LeAudioService extends ProfileService { 96 private static final boolean DBG = true; 97 private static final String TAG = "LeAudioService"; 98 99 // Timeout for state machine thread join, to prevent potential ANR. 100 private static final int SM_THREAD_JOIN_TIMEOUT_MS = 1000; 101 102 // Upper limit of all LeAudio devices: Bonded or Connected 103 private static final int MAX_LE_AUDIO_DEVICES = 10; 104 private static LeAudioService sLeAudioService; 105 106 /** 107 * Indicates group audio support for none direction 108 */ 109 private static final int AUDIO_DIRECTION_NONE = 0x00; 110 111 /** 112 * Indicates group audio support for output direction 113 */ 114 private static final int AUDIO_DIRECTION_OUTPUT_BIT = 0x01; 115 116 /** 117 * Indicates group audio support for input direction 118 */ 119 private static final int AUDIO_DIRECTION_INPUT_BIT = 0x02; 120 121 private AdapterService mAdapterService; 122 private DatabaseManager mDatabaseManager; 123 private HandlerThread mStateMachinesThread; 124 private volatile BluetoothDevice mActiveAudioOutDevice; 125 private volatile BluetoothDevice mActiveAudioInDevice; 126 private BluetoothDevice mExposedActiveDevice; 127 private LeAudioCodecConfig mLeAudioCodecConfig; 128 private final Object mGroupLock = new Object(); 129 ServiceFactory mServiceFactory = new ServiceFactory(); 130 131 LeAudioNativeInterface mLeAudioNativeInterface; 132 boolean mLeAudioNativeIsInitialized = false; 133 boolean mLeAudioInbandRingtoneSupportedByPlatform = true; 134 boolean mBluetoothEnabled = false; 135 BluetoothDevice mHfpHandoverDevice = null; 136 LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface = null; 137 @VisibleForTesting 138 AudioManager mAudioManager; 139 LeAudioTmapGattServer mTmapGattServer; 140 int mTmapRoleMask; 141 boolean mTmapStarted = false; 142 143 @VisibleForTesting 144 TbsService mTbsService; 145 146 @VisibleForTesting 147 McpService mMcpService; 148 149 @VisibleForTesting 150 VolumeControlService mVolumeControlService; 151 152 @VisibleForTesting 153 CsipSetCoordinatorService mCsipSetCoordinatorService; 154 155 @VisibleForTesting 156 RemoteCallbackList<IBluetoothLeBroadcastCallback> mBroadcastCallbacks; 157 158 @VisibleForTesting 159 RemoteCallbackList<IBluetoothLeAudioCallback> mLeAudioCallbacks; 160 161 BluetoothLeScanner mAudioServersScanner; 162 /* When mScanCallback is not null, it means scan is started. */ 163 ScanCallback mScanCallback; 164 165 private class LeAudioGroupDescriptor { LeAudioGroupDescriptor(boolean isInbandRingtonEnabled)166 LeAudioGroupDescriptor(boolean isInbandRingtonEnabled) { 167 mIsConnected = false; 168 mIsActive = false; 169 mDirection = AUDIO_DIRECTION_NONE; 170 mCodecStatus = null; 171 mLostLeadDeviceWhileStreaming = null; 172 mInbandRingtoneEnabled = isInbandRingtonEnabled; 173 mAvailableContexts = 0; 174 } 175 176 public Boolean mIsConnected; 177 public Boolean mIsActive; 178 public Integer mDirection; 179 public BluetoothLeAudioCodecStatus mCodecStatus; 180 /* This can be non empty only for the streaming time */ 181 BluetoothDevice mLostLeadDeviceWhileStreaming; 182 Boolean mInbandRingtoneEnabled; 183 Integer mAvailableContexts; 184 } 185 186 private static class LeAudioDeviceDescriptor { LeAudioDeviceDescriptor(boolean isInbandRingtonEnabled)187 LeAudioDeviceDescriptor(boolean isInbandRingtonEnabled) { 188 mStateMachine = null; 189 mGroupId = LE_AUDIO_GROUP_ID_INVALID; 190 mSinkAudioLocation = BluetoothLeAudio.AUDIO_LOCATION_INVALID; 191 mDirection = AUDIO_DIRECTION_NONE; 192 mDevInbandRingtoneEnabled = isInbandRingtonEnabled; 193 } 194 195 public LeAudioStateMachine mStateMachine; 196 public Integer mGroupId; 197 public Integer mSinkAudioLocation; 198 public Integer mDirection; 199 Boolean mDevInbandRingtoneEnabled; 200 } 201 202 List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>(); 203 List<BluetoothLeAudioCodecConfig> mOutputLocalCodecCapabilities = new ArrayList<>(); 204 205 @GuardedBy("mGroupLock") 206 private final Map<Integer, LeAudioGroupDescriptor> mGroupDescriptors = new LinkedHashMap<>(); 207 private final Map<BluetoothDevice, LeAudioDeviceDescriptor> mDeviceDescriptors = 208 new LinkedHashMap<>(); 209 210 private BroadcastReceiver mBondStateChangedReceiver; 211 private Handler mHandler = new Handler(Looper.getMainLooper()); 212 private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback = 213 new AudioManagerAudioDeviceCallback(); 214 215 private final Map<Integer, Integer> mBroadcastStateMap = new HashMap<>(); 216 private final Map<Integer, Boolean> mBroadcastsPlaybackMap = new HashMap<>(); 217 private final Map<Integer, BluetoothLeBroadcastMetadata> mBroadcastMetadataList = 218 new HashMap<>(); 219 220 @Override initBinder()221 protected IProfileServiceBinder initBinder() { 222 return new BluetoothLeAudioBinder(this); 223 } 224 isEnabled()225 public static boolean isEnabled() { 226 return BluetoothProperties.isProfileBapUnicastClientEnabled().orElse(false); 227 } 228 isBroadcastEnabled()229 public static boolean isBroadcastEnabled() { 230 return BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false); 231 } 232 233 @Override create()234 protected void create() { 235 Log.i(TAG, "create()"); 236 } 237 registerTmap()238 private boolean registerTmap() { 239 if (mTmapGattServer != null) { 240 throw new IllegalStateException("TMAP GATT server started before start() is called"); 241 } 242 mTmapGattServer = LeAudioObjectsFactory.getInstance().getTmapGattServer(this); 243 244 try { 245 mTmapGattServer.start(mTmapRoleMask); 246 } catch (IllegalStateException e) { 247 Log.e(TAG, "Fail to start TmapGattServer", e); 248 return false; 249 } 250 251 return true; 252 } 253 254 @Override start()255 protected boolean start() { 256 Log.i(TAG, "start()"); 257 if (sLeAudioService != null) { 258 throw new IllegalStateException("start() called twice"); 259 } 260 261 mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), 262 "AdapterService cannot be null when LeAudioService starts"); 263 mLeAudioNativeInterface = Objects.requireNonNull(LeAudioNativeInterface.getInstance(), 264 "LeAudioNativeInterface cannot be null when LeAudioService starts"); 265 mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(), 266 "DatabaseManager cannot be null when LeAudioService starts"); 267 268 mAudioManager = getSystemService(AudioManager.class); 269 Objects.requireNonNull(mAudioManager, 270 "AudioManager cannot be null when LeAudioService starts"); 271 272 // Start handler thread for state machines 273 mStateMachinesThread = new HandlerThread("LeAudioService.StateMachines"); 274 mStateMachinesThread.start(); 275 276 mBroadcastStateMap.clear(); 277 mBroadcastMetadataList.clear(); 278 mBroadcastsPlaybackMap.clear(); 279 280 synchronized (mGroupLock) { 281 mDeviceDescriptors.clear(); 282 mGroupDescriptors.clear(); 283 } 284 285 // Setup broadcast receivers 286 IntentFilter filter = new IntentFilter(); 287 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 288 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 289 mBondStateChangedReceiver = new BondStateChangedReceiver(); 290 registerReceiver(mBondStateChangedReceiver, filter); 291 292 mLeAudioCallbacks = new RemoteCallbackList<IBluetoothLeAudioCallback>(); 293 294 mTmapRoleMask = 295 LeAudioTmapGattServer.TMAP_ROLE_FLAG_CG | LeAudioTmapGattServer.TMAP_ROLE_FLAG_UMS; 296 297 // Initialize Broadcast native interface 298 if ((mAdapterService.getSupportedProfilesBitMask() 299 & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) != 0) { 300 Log.i(TAG, "Init Le Audio broadcaster"); 301 mBroadcastCallbacks = new RemoteCallbackList<IBluetoothLeBroadcastCallback>(); 302 mLeAudioBroadcasterNativeInterface = Objects.requireNonNull( 303 LeAudioBroadcasterNativeInterface.getInstance(), 304 "LeAudioBroadcasterNativeInterface cannot be null when LeAudioService starts"); 305 mLeAudioBroadcasterNativeInterface.init(); 306 mTmapRoleMask |= LeAudioTmapGattServer.TMAP_ROLE_FLAG_BMS; 307 } else { 308 Log.w(TAG, "Le Audio Broadcasts not supported."); 309 } 310 311 mTmapStarted = registerTmap(); 312 313 mLeAudioInbandRingtoneSupportedByPlatform = 314 BluetoothProperties.isLeAudioInbandRingtoneSupported().orElse(true); 315 316 mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, 317 mHandler); 318 319 // Mark service as started 320 setLeAudioService(this); 321 322 // Setup codec config 323 mLeAudioCodecConfig = new LeAudioCodecConfig(this); 324 325 // Delay the call to init by posting it. This ensures TBS and MCS are fully initialized 326 // before we start accepting connections 327 mHandler.post(this::init); 328 329 return true; 330 } 331 init()332 private void init() { 333 if (!mTmapStarted) { 334 mTmapStarted = registerTmap(); 335 } 336 337 LeAudioNativeInterface nativeInterface = mLeAudioNativeInterface; 338 if (nativeInterface == null) { 339 Log.w(TAG, "the service is stopped. ignore init()"); 340 return; 341 } 342 nativeInterface.init(mLeAudioCodecConfig.getCodecConfigOffloading()); 343 } 344 345 @Override stop()346 protected boolean stop() { 347 Log.i(TAG, "stop()"); 348 if (sLeAudioService == null) { 349 Log.w(TAG, "stop() called before start()"); 350 return true; 351 } 352 353 mHandler.removeCallbacks(this::init); 354 removeActiveDevice(false); 355 356 if (mTmapGattServer == null) { 357 Log.w(TAG, "TMAP GATT server should never be null before stop() is called"); 358 } else { 359 mTmapGattServer.stop(); 360 mTmapGattServer = null; 361 mTmapStarted = false; 362 } 363 364 stopAudioServersBackgroundScan(); 365 mAudioServersScanner = null; 366 367 //Don't wait for async call with INACTIVE group status, clean active 368 //device for active group. 369 synchronized (mGroupLock) { 370 for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : mGroupDescriptors.entrySet()) { 371 LeAudioGroupDescriptor descriptor = entry.getValue(); 372 Integer group_id = entry.getKey(); 373 if (descriptor.mIsActive) { 374 descriptor.mIsActive = false; 375 updateActiveDevices(group_id, descriptor.mDirection, AUDIO_DIRECTION_NONE, 376 descriptor.mIsActive, false); 377 break; 378 } 379 } 380 381 // Destroy state machines and stop handler thread 382 for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) { 383 LeAudioStateMachine sm = descriptor.mStateMachine; 384 if (sm == null) { 385 continue; 386 } 387 sm.quit(); 388 sm.cleanup(); 389 } 390 391 mDeviceDescriptors.clear(); 392 mGroupDescriptors.clear(); 393 } 394 395 // Cleanup native interfaces 396 mLeAudioNativeInterface.cleanup(); 397 mLeAudioNativeInterface = null; 398 mLeAudioNativeIsInitialized = false; 399 mBluetoothEnabled = false; 400 mHfpHandoverDevice = null; 401 402 mActiveAudioOutDevice = null; 403 mActiveAudioInDevice = null; 404 mExposedActiveDevice = null; 405 mLeAudioCodecConfig = null; 406 407 // Set the service and BLE devices as inactive 408 setLeAudioService(null); 409 410 // Unregister broadcast receivers 411 unregisterReceiver(mBondStateChangedReceiver); 412 mBondStateChangedReceiver = null; 413 414 if (mBroadcastCallbacks != null) { 415 mBroadcastCallbacks.kill(); 416 } 417 418 if (mLeAudioCallbacks != null) { 419 mLeAudioCallbacks.kill(); 420 } 421 422 mBroadcastStateMap.clear(); 423 mBroadcastsPlaybackMap.clear(); 424 mBroadcastMetadataList.clear(); 425 426 if (mLeAudioBroadcasterNativeInterface != null) { 427 mLeAudioBroadcasterNativeInterface.cleanup(); 428 mLeAudioBroadcasterNativeInterface = null; 429 } 430 431 if (mStateMachinesThread != null) { 432 try { 433 mStateMachinesThread.quitSafely(); 434 mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); 435 mStateMachinesThread = null; 436 } catch (InterruptedException e) { 437 // Do not rethrow as we are shutting down anyway 438 } 439 } 440 441 mAudioManager.unregisterAudioDeviceCallback(mAudioManagerAudioDeviceCallback); 442 443 mAdapterService = null; 444 mAudioManager = null; 445 mMcpService = null; 446 mTbsService = null; 447 mVolumeControlService = null; 448 mCsipSetCoordinatorService = null; 449 450 return true; 451 } 452 453 @Override cleanup()454 protected void cleanup() { 455 Log.i(TAG, "cleanup()"); 456 } 457 getLeAudioService()458 public static synchronized LeAudioService getLeAudioService() { 459 if (sLeAudioService == null) { 460 Log.w(TAG, "getLeAudioService(): service is NULL"); 461 return null; 462 } 463 if (!sLeAudioService.isAvailable()) { 464 Log.w(TAG, "getLeAudioService(): service is not available"); 465 return null; 466 } 467 return sLeAudioService; 468 } 469 470 @VisibleForTesting setLeAudioService(LeAudioService instance)471 static synchronized void setLeAudioService(LeAudioService instance) { 472 if (DBG) { 473 Log.d(TAG, "setLeAudioService(): set to: " + instance); 474 } 475 sLeAudioService = instance; 476 } 477 478 @VisibleForTesting getAudioDeviceGroupVolume(int groupId)479 int getAudioDeviceGroupVolume(int groupId) { 480 if (mVolumeControlService == null) { 481 mVolumeControlService = mServiceFactory.getVolumeControlService(); 482 if (mVolumeControlService == null) { 483 Log.e(TAG, "Volume control service is not available"); 484 return IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME; 485 } 486 } 487 488 return mVolumeControlService.getAudioDeviceGroupVolume(groupId); 489 } 490 createDeviceDescriptor(BluetoothDevice device, boolean isInbandRingtoneEnabled)491 LeAudioDeviceDescriptor createDeviceDescriptor(BluetoothDevice device, 492 boolean isInbandRingtoneEnabled) { 493 LeAudioDeviceDescriptor descriptor = mDeviceDescriptors.get(device); 494 if (descriptor == null) { 495 496 // Limit the maximum number of devices to avoid DoS attack 497 if (mDeviceDescriptors.size() >= MAX_LE_AUDIO_DEVICES) { 498 Log.e(TAG, "Maximum number of LeAudio state machines reached: " 499 + MAX_LE_AUDIO_DEVICES); 500 return null; 501 } 502 503 mDeviceDescriptors.put(device, new LeAudioDeviceDescriptor(isInbandRingtoneEnabled)); 504 descriptor = mDeviceDescriptors.get(device); 505 Log.d(TAG, "Created descriptor for device: " + device); 506 } else { 507 Log.w(TAG, "Device: " + device + ", already exists"); 508 } 509 510 return descriptor; 511 } 512 setEnabledState(BluetoothDevice device, boolean enabled)513 private void setEnabledState(BluetoothDevice device, boolean enabled) { 514 if (DBG) { 515 Log.d(TAG, "setEnabledState: address:" + device + " enabled: " + enabled); 516 } 517 if (!mLeAudioNativeIsInitialized) { 518 Log.e(TAG, "setEnabledState, mLeAudioNativeIsInitialized is not initialized"); 519 return; 520 } 521 mLeAudioNativeInterface.setEnableState(device, enabled); 522 } 523 connect(BluetoothDevice device)524 public boolean connect(BluetoothDevice device) { 525 if (DBG) { 526 Log.d(TAG, "connect(): " + device); 527 } 528 529 if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 530 Log.e(TAG, "Cannot connect to " + device + " : CONNECTION_POLICY_FORBIDDEN"); 531 return false; 532 } 533 ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 534 if (!Utils.arrayContains(featureUuids, BluetoothUuid.LE_AUDIO)) { 535 Log.e(TAG, "Cannot connect to " + device + " : Remote does not have LE_AUDIO UUID"); 536 return false; 537 } 538 539 synchronized (mGroupLock) { 540 boolean isInbandRingtoneEnabled = false; 541 int groupId = getGroupId(device); 542 if (groupId != LE_AUDIO_GROUP_ID_INVALID) { 543 isInbandRingtoneEnabled = getGroupDescriptor(groupId).mInbandRingtoneEnabled; 544 } 545 546 if (createDeviceDescriptor(device, isInbandRingtoneEnabled) == null) { 547 return false; 548 } 549 550 LeAudioStateMachine sm = getOrCreateStateMachine(device); 551 if (sm == null) { 552 Log.e(TAG, "Ignored connect request for " + device + " : no state machine"); 553 return false; 554 } 555 sm.sendMessage(LeAudioStateMachine.CONNECT); 556 } 557 558 return true; 559 } 560 561 /** 562 * Disconnects LE Audio for the remote bluetooth device 563 * 564 * @param device is the device with which we would like to disconnect LE Audio 565 * @return true if profile disconnected, false if device not connected over LE Audio 566 */ disconnect(BluetoothDevice device)567 public boolean disconnect(BluetoothDevice device) { 568 if (DBG) { 569 Log.d(TAG, "disconnect(): " + device); 570 } 571 572 synchronized (mGroupLock) { 573 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 574 if (descriptor == null) { 575 Log.e(TAG, "disconnect: No valid descriptor for device: " + device); 576 return false; 577 } 578 579 LeAudioStateMachine sm = descriptor.mStateMachine; 580 if (sm == null) { 581 Log.e(TAG, "Ignored disconnect request for " + device 582 + " : no state machine"); 583 return false; 584 } 585 sm.sendMessage(LeAudioStateMachine.DISCONNECT); 586 } 587 588 return true; 589 } 590 getConnectedDevices()591 public List<BluetoothDevice> getConnectedDevices() { 592 synchronized (mGroupLock) { 593 List<BluetoothDevice> devices = new ArrayList<>(); 594 for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) { 595 LeAudioStateMachine sm = descriptor.mStateMachine; 596 if (sm != null && sm.isConnected()) { 597 devices.add(sm.getDevice()); 598 } 599 } 600 return devices; 601 } 602 } 603 getConnectedGroupLeadDevice(int groupId)604 BluetoothDevice getConnectedGroupLeadDevice(int groupId) { 605 BluetoothDevice device = null; 606 607 if (mActiveAudioOutDevice != null 608 && getGroupId(mActiveAudioOutDevice) == groupId) { 609 device = mActiveAudioOutDevice; 610 } else { 611 device = getFirstDeviceFromGroup(groupId); 612 } 613 614 if (device == null) { 615 return device; 616 } 617 618 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 619 if (descriptor == null) { 620 Log.e(TAG, "getConnectedGroupLeadDevice: No valid descriptor for device: " + device); 621 return null; 622 } 623 624 LeAudioStateMachine sm = descriptor.mStateMachine; 625 if (sm != null && sm.getConnectionState() == BluetoothProfile.STATE_CONNECTED) { 626 return device; 627 } 628 629 return null; 630 } 631 getDevicesMatchingConnectionStates(int[] states)632 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 633 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 634 if (states == null) { 635 return devices; 636 } 637 final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 638 if (bondedDevices == null) { 639 return devices; 640 } 641 synchronized (mGroupLock) { 642 for (BluetoothDevice device : bondedDevices) { 643 final ParcelUuid[] featureUuids = device.getUuids(); 644 if (!Utils.arrayContains(featureUuids, BluetoothUuid.LE_AUDIO)) { 645 continue; 646 } 647 int connectionState = BluetoothProfile.STATE_DISCONNECTED; 648 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 649 if (descriptor == null) { 650 Log.e(TAG, "getDevicesMatchingConnectionStates: " 651 + "No valid descriptor for device: " + device); 652 return null; 653 } 654 655 LeAudioStateMachine sm = descriptor.mStateMachine; 656 if (sm != null) { 657 connectionState = sm.getConnectionState(); 658 } 659 for (int state : states) { 660 if (connectionState == state) { 661 devices.add(device); 662 break; 663 } 664 } 665 } 666 return devices; 667 } 668 } 669 670 /** 671 * Get the list of devices that have state machines. 672 * 673 * @return the list of devices that have state machines 674 */ 675 @VisibleForTesting getDevices()676 List<BluetoothDevice> getDevices() { 677 List<BluetoothDevice> devices = new ArrayList<>(); 678 synchronized (mGroupLock) { 679 for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) { 680 if (descriptor.mStateMachine != null) { 681 devices.add(descriptor.mStateMachine.getDevice()); 682 } 683 } 684 return devices; 685 } 686 } 687 688 /** 689 * Get the current connection state of the profile 690 * 691 * @param device is the remote bluetooth device 692 * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected, 693 * {@link BluetoothProfile#STATE_CONNECTING} if this profile is being connected, 694 * {@link BluetoothProfile#STATE_CONNECTED} if this profile is connected, or 695 * {@link BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected 696 */ getConnectionState(BluetoothDevice device)697 public int getConnectionState(BluetoothDevice device) { 698 synchronized (mGroupLock) { 699 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 700 if (descriptor == null) { 701 return BluetoothProfile.STATE_DISCONNECTED; 702 } 703 704 LeAudioStateMachine sm = descriptor.mStateMachine; 705 if (sm == null) { 706 return BluetoothProfile.STATE_DISCONNECTED; 707 } 708 return sm.getConnectionState(); 709 } 710 } 711 712 /** 713 * Add device to the given group. 714 * @param groupId group ID the device is being added to 715 * @param device the active device 716 * @return true on success, otherwise false 717 */ groupAddNode(int groupId, BluetoothDevice device)718 boolean groupAddNode(int groupId, BluetoothDevice device) { 719 if (!mLeAudioNativeIsInitialized) { 720 Log.e(TAG, "Le Audio not initialized properly."); 721 return false; 722 } 723 return mLeAudioNativeInterface.groupAddNode(groupId, device); 724 } 725 726 /** 727 * Remove device from a given group. 728 * @param groupId group ID the device is being removed from 729 * @param device the active device 730 * @return true on success, otherwise false 731 */ groupRemoveNode(int groupId, BluetoothDevice device)732 boolean groupRemoveNode(int groupId, BluetoothDevice device) { 733 if (!mLeAudioNativeIsInitialized) { 734 Log.e(TAG, "Le Audio not initialized properly."); 735 return false; 736 } 737 return mLeAudioNativeInterface.groupRemoveNode(groupId, device); 738 } 739 740 /** 741 * Checks if given group exists. 742 * @param group_id group Id to verify 743 * @return true given group exists, otherwise false 744 */ isValidDeviceGroup(int groupId)745 public boolean isValidDeviceGroup(int groupId) { 746 synchronized (mGroupLock) { 747 return groupId != LE_AUDIO_GROUP_ID_INVALID && mGroupDescriptors.containsKey(groupId); 748 } 749 } 750 751 /** 752 * Get all the devices within a given group. 753 * @param groupId group id to get devices 754 * @return all devices within a given group or empty list 755 */ getGroupDevices(int groupId)756 public List<BluetoothDevice> getGroupDevices(int groupId) { 757 List<BluetoothDevice> result = new ArrayList<>(); 758 759 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 760 return result; 761 } 762 763 synchronized (mGroupLock) { 764 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry 765 : mDeviceDescriptors.entrySet()) { 766 if (entry.getValue().mGroupId == groupId) { 767 result.add(entry.getKey()); 768 } 769 } 770 } 771 return result; 772 } 773 774 /** 775 * Get all the devices within a given group. 776 * @param device the device for which we want to get all devices in its group 777 * @return all devices within a given group or empty list 778 */ getGroupDevices(BluetoothDevice device)779 public List<BluetoothDevice> getGroupDevices(BluetoothDevice device) { 780 List<BluetoothDevice> result = new ArrayList<>(); 781 int groupId = getGroupId(device); 782 783 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 784 return result; 785 } 786 787 synchronized (mGroupLock) { 788 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry 789 : mDeviceDescriptors.entrySet()) { 790 if (entry.getValue().mGroupId == groupId) { 791 result.add(entry.getKey()); 792 } 793 } 794 } 795 return result; 796 } 797 798 /** 799 * Get the active device group id 800 */ getActiveGroupId()801 public Integer getActiveGroupId() { 802 synchronized (mGroupLock) { 803 for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : mGroupDescriptors.entrySet()) { 804 LeAudioGroupDescriptor descriptor = entry.getValue(); 805 if (descriptor.mIsActive) { 806 return entry.getKey(); 807 } 808 } 809 } 810 return LE_AUDIO_GROUP_ID_INVALID; 811 } 812 813 /** 814 * Creates LeAudio Broadcast instance with BluetoothLeBroadcastSettings. 815 * 816 * @param broadcastSettings broadcast settings for this broadcast source 817 */ createBroadcast(BluetoothLeBroadcastSettings broadcastSettings)818 public void createBroadcast(BluetoothLeBroadcastSettings broadcastSettings) { 819 if (mLeAudioBroadcasterNativeInterface == null) { 820 Log.w(TAG, "Native interface not available."); 821 return; 822 } 823 824 byte[] broadcastCode = broadcastSettings.getBroadcastCode(); 825 boolean isEncrypted = (broadcastCode != null) && (broadcastCode.length != 0); 826 if (isEncrypted) { 827 if ((broadcastCode.length > 16) || (broadcastCode.length < 4)) { 828 Log.e(TAG, "Invalid broadcast code length. Should be from 4 to 16 octets long."); 829 return; 830 } 831 } 832 833 List<BluetoothLeBroadcastSubgroupSettings> settingsList = 834 broadcastSettings.getSubgroupSettings(); 835 if (settingsList == null || settingsList.size() < 1) { 836 Log.d(TAG, "subgroup settings is not valid value"); 837 return; 838 } 839 840 BluetoothLeAudioContentMetadata publicMetadata = 841 broadcastSettings.getPublicBroadcastMetadata(); 842 843 Log.i(TAG, "createBroadcast: isEncrypted=" + (isEncrypted ? "true" : "false")); 844 mLeAudioBroadcasterNativeInterface.createBroadcast(broadcastSettings.isPublicBroadcast(), 845 broadcastSettings.getBroadcastName(), broadcastCode, 846 publicMetadata == null ? null : publicMetadata.getRawMetadata(), 847 settingsList.stream() 848 .mapToInt(s -> s.getPreferredQuality()).toArray(), 849 settingsList.stream() 850 .map(s -> s.getContentMetadata().getRawMetadata()) 851 .toArray(byte[][]::new)); 852 } 853 854 /** 855 * Start LeAudio Broadcast instance. 856 * @param broadcastId broadcast instance identifier 857 */ startBroadcast(int broadcastId)858 public void startBroadcast(int broadcastId) { 859 if (mLeAudioBroadcasterNativeInterface == null) { 860 Log.w(TAG, "Native interface not available."); 861 return; 862 } 863 if (DBG) Log.d(TAG, "startBroadcast"); 864 mLeAudioBroadcasterNativeInterface.startBroadcast(broadcastId); 865 } 866 867 /** 868 * Updates LeAudio broadcast instance metadata. 869 * 870 * @param broadcastId broadcast instance identifier 871 * @param broadcastSettings broadcast settings for this broadcast source 872 */ updateBroadcast(int broadcastId, BluetoothLeBroadcastSettings broadcastSettings)873 public void updateBroadcast(int broadcastId, BluetoothLeBroadcastSettings broadcastSettings) { 874 if (mLeAudioBroadcasterNativeInterface == null) { 875 Log.w(TAG, "Native interface not available."); 876 return; 877 } 878 if (!mBroadcastStateMap.containsKey(broadcastId)) { 879 notifyBroadcastUpdateFailed( 880 broadcastId, BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID); 881 return; 882 } 883 884 List<BluetoothLeBroadcastSubgroupSettings> settingsList = 885 broadcastSettings.getSubgroupSettings(); 886 if (settingsList == null || settingsList.size() < 1) { 887 Log.d(TAG, "subgroup settings is not valid value"); 888 return; 889 } 890 891 BluetoothLeAudioContentMetadata publicMetadata = 892 broadcastSettings.getPublicBroadcastMetadata(); 893 894 if (DBG) Log.d(TAG, "updateBroadcast"); 895 mLeAudioBroadcasterNativeInterface.updateMetadata(broadcastId, 896 broadcastSettings.getBroadcastName(), 897 publicMetadata == null ? null : publicMetadata.getRawMetadata(), 898 settingsList.stream() 899 .map(s -> s.getContentMetadata().getRawMetadata()) 900 .toArray(byte[][]::new)); 901 } 902 903 /** 904 * Stop LeAudio Broadcast instance. 905 * @param broadcastId broadcast instance identifier 906 */ stopBroadcast(Integer broadcastId)907 public void stopBroadcast(Integer broadcastId) { 908 if (mLeAudioBroadcasterNativeInterface == null) { 909 Log.w(TAG, "Native interface not available."); 910 return; 911 } 912 if (!mBroadcastStateMap.containsKey(broadcastId)) { 913 notifyOnBroadcastStopFailed( 914 BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID); 915 return; 916 } 917 918 if (DBG) Log.d(TAG, "stopBroadcast"); 919 mLeAudioBroadcasterNativeInterface.stopBroadcast(broadcastId); 920 } 921 922 /** 923 * Destroy LeAudio Broadcast instance. 924 * @param broadcastId broadcast instance identifier 925 */ destroyBroadcast(int broadcastId)926 public void destroyBroadcast(int broadcastId) { 927 if (mLeAudioBroadcasterNativeInterface == null) { 928 Log.w(TAG, "Native interface not available."); 929 return; 930 } 931 if (!mBroadcastStateMap.containsKey(broadcastId)) { 932 notifyOnBroadcastStopFailed( 933 BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID); 934 return; 935 } 936 937 if (DBG) Log.d(TAG, "destroyBroadcast"); 938 mLeAudioBroadcasterNativeInterface.destroyBroadcast(broadcastId); 939 } 940 941 /** 942 * Checks if Broadcast instance is playing. 943 * @param broadcastId broadcast instance identifier 944 * @return true if if broadcast is playing, false otherwise 945 */ isPlaying(int broadcastId)946 public boolean isPlaying(int broadcastId) { 947 return mBroadcastsPlaybackMap.getOrDefault(broadcastId, false); 948 } 949 950 /** 951 * Get all broadcast metadata. 952 * @return list of all know Broadcast metadata 953 */ getAllBroadcastMetadata()954 public List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() { 955 return new ArrayList<BluetoothLeBroadcastMetadata>(mBroadcastMetadataList.values()); 956 } 957 958 /** 959 * Get the maximum number of supported simultaneous broadcasts. 960 * @return number of supported simultaneous broadcasts 961 */ getMaximumNumberOfBroadcasts()962 public int getMaximumNumberOfBroadcasts() { 963 /* TODO: This is currently fixed to 1 */ 964 return 1; 965 } 966 967 /** 968 * Get the maximum number of supported streams per broadcast. 969 * 970 * @return number of supported streams per broadcast 971 */ getMaximumStreamsPerBroadcast()972 public int getMaximumStreamsPerBroadcast() { 973 /* TODO: This is currently fixed to 1 */ 974 return 1; 975 } 976 977 /** 978 * Get the maximum number of supported subgroups per broadcast. 979 * 980 * @return number of supported subgroups per broadcast 981 */ getMaximumSubgroupsPerBroadcast()982 public int getMaximumSubgroupsPerBroadcast() { 983 /* TODO: This is currently fixed to 1 */ 984 return 1; 985 } 986 getFirstDeviceFromGroup(Integer groupId)987 private BluetoothDevice getFirstDeviceFromGroup(Integer groupId) { 988 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 989 return null; 990 } 991 synchronized (mGroupLock) { 992 for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) { 993 if (!descriptor.mGroupId.equals(groupId)) { 994 continue; 995 } 996 997 LeAudioStateMachine sm = descriptor.mStateMachine; 998 if (sm == null || sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { 999 continue; 1000 } 1001 return sm.getDevice(); 1002 } 1003 } 1004 return null; 1005 } 1006 updateActiveInDevice(BluetoothDevice device, Integer groupId, Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections)1007 private boolean updateActiveInDevice(BluetoothDevice device, Integer groupId, 1008 Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections) { 1009 boolean oldSupportedByDeviceInput = (oldSupportedAudioDirections 1010 & AUDIO_DIRECTION_INPUT_BIT) != 0; 1011 boolean newSupportedByDeviceInput = (newSupportedAudioDirections 1012 & AUDIO_DIRECTION_INPUT_BIT) != 0; 1013 1014 /* 1015 * Do not update input if neither previous nor current device support input 1016 */ 1017 if (!oldSupportedByDeviceInput && !newSupportedByDeviceInput) { 1018 Log.d(TAG, "updateActiveInDevice: Device does not support input."); 1019 return false; 1020 } 1021 1022 if (device != null && mActiveAudioInDevice != null) { 1023 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 1024 if (deviceDescriptor == null) { 1025 Log.e(TAG, "updateActiveInDevice: No valid descriptor for device: " + device); 1026 return false; 1027 } 1028 1029 if (deviceDescriptor.mGroupId.equals(groupId)) { 1030 /* This is thes same group as aleady notified to the system. 1031 * Therefore do not change the device we have connected to the group, 1032 * unless, previous one is disconnected now 1033 */ 1034 if (mActiveAudioInDevice.isConnected()) { 1035 device = mActiveAudioInDevice; 1036 } 1037 } else if (deviceDescriptor.mGroupId != LE_AUDIO_GROUP_ID_INVALID) { 1038 /* Mark old group as no active */ 1039 LeAudioGroupDescriptor descriptor = getGroupDescriptor(deviceDescriptor.mGroupId); 1040 if (descriptor != null) { 1041 descriptor.mIsActive = false; 1042 } 1043 } 1044 } 1045 1046 BluetoothDevice previousInDevice = mActiveAudioInDevice; 1047 1048 /* 1049 * Update input if: 1050 * - Device changed 1051 * OR 1052 * - Device stops / starts supporting input 1053 */ 1054 if (!Objects.equals(device, previousInDevice) 1055 || (oldSupportedByDeviceInput != newSupportedByDeviceInput)) { 1056 mActiveAudioInDevice = newSupportedByDeviceInput ? device : null; 1057 if (DBG) { 1058 Log.d(TAG, " handleBluetoothActiveDeviceChanged previousInDevice: " 1059 + previousInDevice + ", mActiveAudioInDevice" + mActiveAudioInDevice 1060 + " isLeOutput: false"); 1061 } 1062 1063 return true; 1064 } 1065 Log.d(TAG, "updateActiveInDevice: Nothing to do."); 1066 return false; 1067 } 1068 updateActiveOutDevice(BluetoothDevice device, Integer groupId, Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections)1069 private boolean updateActiveOutDevice(BluetoothDevice device, Integer groupId, 1070 Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections) { 1071 boolean oldSupportedByDeviceOutput = (oldSupportedAudioDirections 1072 & AUDIO_DIRECTION_OUTPUT_BIT) != 0; 1073 boolean newSupportedByDeviceOutput = (newSupportedAudioDirections 1074 & AUDIO_DIRECTION_OUTPUT_BIT) != 0; 1075 1076 /* 1077 * Do not update output if neither previous nor current device support output 1078 */ 1079 if (!oldSupportedByDeviceOutput && !newSupportedByDeviceOutput) { 1080 Log.d(TAG, "updateActiveOutDevice: Device does not support output."); 1081 return false; 1082 } 1083 1084 if (device != null && mActiveAudioOutDevice != null) { 1085 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 1086 if (deviceDescriptor == null) { 1087 Log.e(TAG, "updateActiveOutDevice: No valid descriptor for device: " + device); 1088 return false; 1089 } 1090 1091 if (deviceDescriptor.mGroupId.equals(groupId)) { 1092 /* This is the same group as already notified to the system. 1093 * Therefore do not change the device we have connected to the group, 1094 * unless, previous one is disconnected now 1095 */ 1096 if (mActiveAudioOutDevice.isConnected()) { 1097 device = mActiveAudioOutDevice; 1098 } 1099 } else if (deviceDescriptor.mGroupId != LE_AUDIO_GROUP_ID_INVALID) { 1100 Log.i(TAG, " Switching active group from " + deviceDescriptor.mGroupId + " to " 1101 + groupId); 1102 /* Mark old group as no active */ 1103 LeAudioGroupDescriptor descriptor = getGroupDescriptor(deviceDescriptor.mGroupId); 1104 if (descriptor != null) { 1105 descriptor.mIsActive = false; 1106 } 1107 } 1108 } 1109 1110 BluetoothDevice previousOutDevice = mActiveAudioOutDevice; 1111 1112 /* 1113 * Update output if: 1114 * - Device changed 1115 * OR 1116 * - Device stops / starts supporting output 1117 */ 1118 if (!Objects.equals(device, previousOutDevice) 1119 || (oldSupportedByDeviceOutput != newSupportedByDeviceOutput)) { 1120 mActiveAudioOutDevice = newSupportedByDeviceOutput ? device : null; 1121 if (DBG) { 1122 Log.d(TAG, " handleBluetoothActiveDeviceChanged previousOutDevice: " 1123 + previousOutDevice + ", mActiveOutDevice: " + mActiveAudioOutDevice 1124 + " isLeOutput: true"); 1125 } 1126 return true; 1127 } 1128 Log.d(TAG, "updateActiveOutDevice: Nothing to do."); 1129 return false; 1130 } 1131 1132 /** 1133 * Send broadcast intent about LeAudio active device. 1134 * This is called when AudioManager confirms, LeAudio device 1135 * is added or removed. 1136 */ 1137 @VisibleForTesting notifyActiveDeviceChanged(BluetoothDevice device)1138 void notifyActiveDeviceChanged(BluetoothDevice device) { 1139 if (DBG) { 1140 Log.d(TAG, "Notify Active device changed." + device 1141 + ". Currently active device is " + mActiveAudioOutDevice); 1142 } 1143 1144 Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); 1145 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1146 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1147 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1148 sendBroadcast(intent, BLUETOOTH_CONNECT); 1149 } 1150 isScannerNeeded()1151 boolean isScannerNeeded() { 1152 if (mDeviceDescriptors.isEmpty() || !mBluetoothEnabled) { 1153 if (DBG) { 1154 Log.d(TAG, "isScannerNeeded: false, mBluetoothEnabled: " + mBluetoothEnabled); 1155 } 1156 return false; 1157 } 1158 1159 if (DBG) { 1160 Log.d(TAG, "isScannerNeeded: true"); 1161 } 1162 return true; 1163 } 1164 1165 private class AudioServerScanCallback extends ScanCallback { 1166 int mMaxScanRetires = 10; 1167 int mScanRetries = 0; 1168 1169 @Override onScanResult(int callbackType, ScanResult result)1170 public void onScanResult(int callbackType, ScanResult result) { 1171 /* Filter is set in the way, that there will be no results found. 1172 * We just need a scanner to be running for the APCF filtering defined in native 1173 */ 1174 } 1175 1176 @Override onBatchScanResults(List<ScanResult> results)1177 public void onBatchScanResults(List<ScanResult> results) { 1178 /* Filter is set in the way, that there will be no results found. 1179 * We just need a scanner to be running for the APCF filtering defined in native 1180 */ 1181 } 1182 1183 @Override onScanFailed(int errorCode)1184 public void onScanFailed(int errorCode) { 1185 Log.w(TAG, "Scan failed err: " + errorCode + " scan retries: " + mScanRetries); 1186 switch(errorCode) { 1187 case SCAN_FAILED_INTERNAL_ERROR: 1188 case SCAN_FAILED_APPLICATION_REGISTRATION_FAILED: 1189 if (mScanRetries < mMaxScanRetires) { 1190 mScanRetries++; 1191 Log.w(TAG, "Failed to start. Let's retry"); 1192 mHandler.post(() -> startAudioServersBackgroundScan(/* retry = */ true)); 1193 } 1194 break; 1195 default: 1196 /* Indicate scan is no running */ 1197 mScanCallback = null; 1198 break; 1199 } 1200 } 1201 } 1202 1203 /* Notifications of audio device connection/disconn events. */ 1204 private class AudioManagerAudioDeviceCallback extends AudioDeviceCallback { 1205 @Override onAudioDevicesAdded(AudioDeviceInfo[] addedDevices)1206 public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { 1207 if (mAudioManager == null || mAdapterService == null) { 1208 Log.e(TAG, "Callback called when LeAudioService is stopped"); 1209 return; 1210 } 1211 1212 for (AudioDeviceInfo deviceInfo : addedDevices) { 1213 if ((deviceInfo.getType() != AudioDeviceInfo.TYPE_BLE_HEADSET) 1214 && (deviceInfo.getType() != AudioDeviceInfo.TYPE_BLE_SPEAKER)) { 1215 continue; 1216 } 1217 1218 String address = deviceInfo.getAddress(); 1219 if (address.equals("00:00:00:00:00:00")) { 1220 continue; 1221 } 1222 1223 byte[] addressBytes = Utils.getBytesFromAddress(address); 1224 BluetoothDevice device = mAdapterService.getDeviceFromByte(addressBytes); 1225 1226 if (DBG) { 1227 Log.d(TAG, " onAudioDevicesAdded: " + device + ", device type: " 1228 + deviceInfo.getType() + ", isSink: " + deviceInfo.isSink() 1229 + " isSource: " + deviceInfo.isSource()); 1230 } 1231 1232 /* Don't expose already exposed active device */ 1233 if (device.equals(mExposedActiveDevice)) { 1234 if (DBG) { 1235 Log.d(TAG, " onAudioDevicesAdded: " + device + " is already exposed"); 1236 } 1237 return; 1238 } 1239 1240 1241 if ((deviceInfo.isSink() && !device.equals(mActiveAudioOutDevice)) 1242 || (deviceInfo.isSource() && !device.equals(mActiveAudioInDevice))) { 1243 Log.e(TAG, "Added device does not match to the one activated here. (" 1244 + device + " != " + mActiveAudioOutDevice 1245 + " / " + mActiveAudioInDevice + ")"); 1246 continue; 1247 } 1248 1249 mExposedActiveDevice = device; 1250 notifyActiveDeviceChanged(device); 1251 return; 1252 } 1253 } 1254 1255 @Override onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices)1256 public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) { 1257 if (mAudioManager == null || mAdapterService == null) { 1258 Log.e(TAG, "Callback called when LeAudioService is stopped"); 1259 return; 1260 } 1261 1262 for (AudioDeviceInfo deviceInfo : removedDevices) { 1263 if ((deviceInfo.getType() != AudioDeviceInfo.TYPE_BLE_HEADSET) 1264 && (deviceInfo.getType() != AudioDeviceInfo.TYPE_BLE_SPEAKER)) { 1265 continue; 1266 } 1267 1268 String address = deviceInfo.getAddress(); 1269 if (address.equals("00:00:00:00:00:00")) { 1270 continue; 1271 } 1272 1273 mExposedActiveDevice = null; 1274 1275 if (DBG) { 1276 Log.d(TAG, " onAudioDevicesRemoved: " + address + ", device type: " 1277 + deviceInfo.getType() + ", isSink: " + deviceInfo.isSink() 1278 + " isSource: " + deviceInfo.isSource() 1279 + ", mActiveAudioInDevice: " + mActiveAudioInDevice 1280 + ", mActiveAudioOutDevice: " + mActiveAudioOutDevice); 1281 } 1282 } 1283 } 1284 } 1285 1286 /** 1287 * Report the active devices change to the active device manager and the media framework. 1288 * @param groupId id of group which devices should be updated 1289 * @param newSupportedAudioDirections new supported audio directions for group of devices 1290 * @param oldSupportedAudioDirections old supported audio directions for group of devices 1291 * @param isActive if there is new active group 1292 * @param hasFallbackDevice whether any fallback device exists when deactivating 1293 * the current active device. 1294 * @return true if group is active after change false otherwise. 1295 */ updateActiveDevices(Integer groupId, Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections, boolean isActive, boolean hasFallbackDevice)1296 private boolean updateActiveDevices(Integer groupId, Integer oldSupportedAudioDirections, 1297 Integer newSupportedAudioDirections, boolean isActive, boolean hasFallbackDevice) { 1298 BluetoothDevice device = null; 1299 BluetoothDevice previousActiveOutDevice = mActiveAudioOutDevice; 1300 BluetoothDevice previousActiveInDevice = mActiveAudioInDevice; 1301 1302 if (isActive) { 1303 device = getFirstDeviceFromGroup(groupId); 1304 } 1305 1306 boolean isNewActiveOutDevice = updateActiveOutDevice(device, groupId, 1307 oldSupportedAudioDirections, newSupportedAudioDirections); 1308 boolean isNewActiveInDevice = updateActiveInDevice(device, groupId, 1309 oldSupportedAudioDirections, newSupportedAudioDirections); 1310 1311 if (DBG) { 1312 Log.d(TAG, " isNewActiveOutDevice: " + isNewActiveOutDevice + ", " 1313 + mActiveAudioOutDevice + ", isNewActiveInDevice: " + isNewActiveInDevice 1314 + ", " + mActiveAudioInDevice); 1315 } 1316 1317 if (isNewActiveOutDevice) { 1318 int volume = IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME; 1319 1320 if (mActiveAudioOutDevice != null) { 1321 volume = getAudioDeviceGroupVolume(groupId); 1322 } 1323 1324 final boolean suppressNoisyIntent = hasFallbackDevice || mActiveAudioOutDevice != null 1325 || (getConnectionState(previousActiveOutDevice) 1326 == BluetoothProfile.STATE_CONNECTED); 1327 1328 mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioOutDevice, 1329 previousActiveOutDevice, BluetoothProfileConnectionInfo.createLeAudioOutputInfo( 1330 suppressNoisyIntent, volume)); 1331 } 1332 1333 if (isNewActiveInDevice) { 1334 mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioInDevice, 1335 previousActiveInDevice, BluetoothProfileConnectionInfo.createLeAudioInfo(false, 1336 false)); 1337 } 1338 1339 if ((mActiveAudioOutDevice == null) && (mActiveAudioInDevice == null)) { 1340 /* Notify about inactive device as soon as possible. 1341 * When adding new device, wait with notification until AudioManager is ready 1342 * with adding the device. 1343 */ 1344 notifyActiveDeviceChanged(null); 1345 } 1346 1347 return mActiveAudioOutDevice != null; 1348 } 1349 1350 /** 1351 * Set the active device group. 1352 * 1353 * @param hasFallbackDevice hasFallbackDevice whether any fallback device exists when 1354 * {@code device} is null. 1355 */ setActiveGroupWithDevice(BluetoothDevice device, boolean hasFallbackDevice)1356 private void setActiveGroupWithDevice(BluetoothDevice device, boolean hasFallbackDevice) { 1357 int groupId = LE_AUDIO_GROUP_ID_INVALID; 1358 1359 if (device != null) { 1360 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 1361 if (descriptor == null) { 1362 Log.e(TAG, "setActiveGroupWithDevice: No valid descriptor for device: " + device); 1363 return; 1364 } 1365 1366 groupId = descriptor.mGroupId; 1367 } 1368 1369 int currentlyActiveGroupId = getActiveGroupId(); 1370 if (DBG) { 1371 Log.d(TAG, "setActiveGroupWithDevice = " + groupId 1372 + ", currentlyActiveGroupId = " + currentlyActiveGroupId 1373 + ", device: " + device); 1374 } 1375 1376 if (groupId == currentlyActiveGroupId) { 1377 if (groupId != LE_AUDIO_GROUP_ID_INVALID) { 1378 Log.w(TAG, "group is already active: device=" + device + ", groupId = " + groupId); 1379 } 1380 return; 1381 } 1382 1383 if (!mLeAudioNativeIsInitialized) { 1384 Log.e(TAG, "Le Audio not initialized properly."); 1385 return; 1386 } 1387 mLeAudioNativeInterface.groupSetActive(groupId); 1388 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 1389 /* Native will clear its states and send us group Inactive. 1390 * However we would like to notify audio framework that LeAudio is not 1391 * active anymore and does not want to get more audio data. 1392 */ 1393 handleGroupTransitToInactive(currentlyActiveGroupId, hasFallbackDevice); 1394 } 1395 } 1396 1397 /** 1398 * Remove the current active group. 1399 * 1400 * @param hasFallbackDevice whether any fallback device exists when deactivating 1401 * the current active device. 1402 * @return true on success, otherwise false 1403 */ removeActiveDevice(boolean hasFallbackDevice)1404 public boolean removeActiveDevice(boolean hasFallbackDevice) { 1405 /* Clear active group */ 1406 setActiveGroupWithDevice(null, hasFallbackDevice); 1407 return true; 1408 } 1409 1410 /** 1411 * Set the active group represented by device. 1412 * 1413 * @param device the new active device. Should not be null. 1414 * @return true on success, otherwise false 1415 */ setActiveDevice(BluetoothDevice device)1416 public boolean setActiveDevice(BluetoothDevice device) { 1417 Log.i(TAG, "setActiveDevice: device=" + device + ", current out=" 1418 + mActiveAudioOutDevice + ", current in=" + mActiveAudioInDevice); 1419 /* Clear active group */ 1420 if (device == null) { 1421 Log.e(TAG, "device should not be null!"); 1422 return removeActiveDevice(false); 1423 } 1424 if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { 1425 Log.e(TAG, "setActiveDevice(" + device + "): failed because group device is not " 1426 + "connected"); 1427 return false; 1428 } 1429 1430 if (Utils.isDualModeAudioEnabled()) { 1431 if (!mAdapterService.isAllSupportedClassicAudioProfilesActive(device)) { 1432 Log.e(TAG, "setActiveDevice(" + device + "): failed because the device is not " 1433 + "active for all supported classic audio profiles"); 1434 return false; 1435 } 1436 } 1437 setActiveGroupWithDevice(device, false); 1438 return true; 1439 } 1440 1441 /** 1442 * Get the active LE audio devices. 1443 * 1444 * Note: When LE audio group is active, one of the Bluetooth device address 1445 * which belongs to the group, represents the active LE audio group - it is called 1446 * Lead device. 1447 * Internally, this address is translated to LE audio group id. 1448 * 1449 * @return List of active group members. First element is a Lead device. 1450 */ getActiveDevices()1451 public List<BluetoothDevice> getActiveDevices() { 1452 if (DBG) { 1453 Log.d(TAG, "getActiveDevices"); 1454 } 1455 ArrayList<BluetoothDevice> activeDevices = new ArrayList<>(2); 1456 activeDevices.add(null); 1457 activeDevices.add(null); 1458 1459 int currentlyActiveGroupId = getActiveGroupId(); 1460 if (currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID) { 1461 return activeDevices; 1462 } 1463 1464 BluetoothDevice leadDevice = getConnectedGroupLeadDevice(currentlyActiveGroupId); 1465 activeDevices.set(0, leadDevice); 1466 1467 int i = 1; 1468 for (BluetoothDevice dev : getGroupDevices(currentlyActiveGroupId)) { 1469 if (Objects.equals(dev, leadDevice)) { 1470 continue; 1471 } 1472 if (i == 1) { 1473 /* Already has a spot for first member */ 1474 activeDevices.set(i++, dev); 1475 } else { 1476 /* Extend list with other members */ 1477 activeDevices.add(dev); 1478 } 1479 } 1480 return activeDevices; 1481 } 1482 connectSet(BluetoothDevice device)1483 void connectSet(BluetoothDevice device) { 1484 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 1485 if (descriptor == null) { 1486 Log.e(TAG, "connectSet: No valid descriptor for device: " + device); 1487 return; 1488 } 1489 if (descriptor.mGroupId == LE_AUDIO_GROUP_ID_INVALID) { 1490 return; 1491 } 1492 1493 if (DBG) { 1494 Log.d(TAG, "connect() others from group id: " + descriptor.mGroupId); 1495 } 1496 1497 Integer setGroupId = descriptor.mGroupId; 1498 1499 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry 1500 : mDeviceDescriptors.entrySet()) { 1501 BluetoothDevice storedDevice = entry.getKey(); 1502 descriptor = entry.getValue(); 1503 if (device.equals(storedDevice)) { 1504 continue; 1505 } 1506 1507 if (!descriptor.mGroupId.equals(setGroupId)) { 1508 continue; 1509 } 1510 1511 if (DBG) { 1512 Log.d(TAG, "connect(): " + storedDevice); 1513 } 1514 1515 synchronized (mGroupLock) { 1516 LeAudioStateMachine sm = getOrCreateStateMachine(storedDevice); 1517 if (sm == null) { 1518 Log.e(TAG, "Ignored connect request for " + storedDevice 1519 + " : no state machine"); 1520 continue; 1521 } 1522 sm.sendMessage(LeAudioStateMachine.CONNECT); 1523 } 1524 } 1525 } 1526 getBroadcastProfile(boolean suppressNoisyIntent)1527 BluetoothProfileConnectionInfo getBroadcastProfile(boolean suppressNoisyIntent) { 1528 Parcel parcel = Parcel.obtain(); 1529 parcel.writeInt(BluetoothProfile.LE_AUDIO_BROADCAST); 1530 parcel.writeBoolean(suppressNoisyIntent); 1531 parcel.writeInt(-1 /* mVolume */); 1532 parcel.writeBoolean(true /* mIsLeOutput */); 1533 parcel.setDataPosition(0); 1534 1535 BluetoothProfileConnectionInfo profileInfo = 1536 BluetoothProfileConnectionInfo.CREATOR.createFromParcel(parcel); 1537 parcel.recycle(); 1538 return profileInfo; 1539 } 1540 clearLostDevicesWhileStreaming(LeAudioGroupDescriptor descriptor)1541 private void clearLostDevicesWhileStreaming(LeAudioGroupDescriptor descriptor) { 1542 synchronized (mGroupLock) { 1543 if (DBG) { 1544 Log.d(TAG, "Clearing lost dev: " + descriptor.mLostLeadDeviceWhileStreaming); 1545 } 1546 1547 LeAudioDeviceDescriptor deviceDescriptor = 1548 getDeviceDescriptor(descriptor.mLostLeadDeviceWhileStreaming); 1549 if (deviceDescriptor == null) { 1550 Log.e(TAG, "clearLostDevicesWhileStreaming: No valid descriptor for device: " 1551 + descriptor.mLostLeadDeviceWhileStreaming); 1552 return; 1553 } 1554 1555 LeAudioStateMachine sm = deviceDescriptor.mStateMachine; 1556 if (sm != null) { 1557 LeAudioStackEvent stackEvent = 1558 new LeAudioStackEvent( 1559 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 1560 stackEvent.device = descriptor.mLostLeadDeviceWhileStreaming; 1561 stackEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED; 1562 sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent); 1563 } 1564 descriptor.mLostLeadDeviceWhileStreaming = null; 1565 } 1566 } 1567 handleGroupTransitToActive(int groupId)1568 private void handleGroupTransitToActive(int groupId) { 1569 synchronized (mGroupLock) { 1570 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 1571 if (descriptor == null || descriptor.mIsActive) { 1572 Log.e(TAG, "handleGroupTransitToActive: no descriptors for group: " + groupId 1573 + " or group already active"); 1574 return; 1575 } 1576 1577 descriptor.mIsActive = updateActiveDevices(groupId, AUDIO_DIRECTION_NONE, 1578 descriptor.mDirection, true, false); 1579 1580 if (descriptor.mIsActive) { 1581 notifyGroupStatusChanged(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE); 1582 updateInbandRingtoneForTheGroup(groupId); 1583 } 1584 } 1585 } 1586 handleGroupTransitToInactive(int groupId, boolean hasFallbackDevice)1587 private void handleGroupTransitToInactive(int groupId, boolean hasFallbackDevice) { 1588 synchronized (mGroupLock) { 1589 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 1590 if (descriptor == null || !descriptor.mIsActive) { 1591 Log.e(TAG, "handleGroupTransitToInactive: no descriptors for group: " + groupId 1592 + " or group already inactive"); 1593 return; 1594 } 1595 1596 descriptor.mIsActive = false; 1597 updateActiveDevices(groupId, descriptor.mDirection, AUDIO_DIRECTION_NONE, 1598 descriptor.mIsActive, hasFallbackDevice); 1599 /* Clear lost devices */ 1600 if (DBG) Log.d(TAG, "Clear for group: " + groupId); 1601 clearLostDevicesWhileStreaming(descriptor); 1602 notifyGroupStatusChanged(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE); 1603 updateInbandRingtoneForTheGroup(groupId); 1604 } 1605 } 1606 1607 @VisibleForTesting handleGroupIdleDuringCall()1608 void handleGroupIdleDuringCall() { 1609 if (mHfpHandoverDevice == null) { 1610 if (DBG) { 1611 Log.d(TAG, "There is no HFP handover"); 1612 } 1613 return; 1614 } 1615 HeadsetService headsetService = mServiceFactory.getHeadsetService(); 1616 if (headsetService == null) { 1617 if (DBG) { 1618 Log.d(TAG, "There is no HFP service available"); 1619 } 1620 return; 1621 } 1622 1623 BluetoothDevice activeHfpDevice = headsetService.getActiveDevice(); 1624 if (activeHfpDevice == null) { 1625 if (DBG) { 1626 Log.d(TAG, "Make " + mHfpHandoverDevice + " active again "); 1627 } 1628 headsetService.setActiveDevice(mHfpHandoverDevice); 1629 } else { 1630 if (DBG) { 1631 Log.d(TAG, "Connect audio to " + activeHfpDevice); 1632 } 1633 headsetService.connectAudio(); 1634 } 1635 mHfpHandoverDevice = null; 1636 } 1637 updateInbandRingtoneForTheGroup(int groupId)1638 void updateInbandRingtoneForTheGroup(int groupId) { 1639 if (!mLeAudioInbandRingtoneSupportedByPlatform) { 1640 if (DBG) { 1641 Log.d(TAG, "Platform does not support inband ringtone"); 1642 } 1643 return; 1644 } 1645 1646 synchronized (mGroupLock) { 1647 LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); 1648 if (groupDescriptor == null) { 1649 Log.e(TAG, "group descriptor for " + groupId + " does not exist"); 1650 return; 1651 } 1652 1653 boolean ringtoneContextAvailable = 1654 ((groupDescriptor.mAvailableContexts 1655 & BluetoothLeAudio.CONTEXT_TYPE_RINGTONE) != 0); 1656 if (DBG) { 1657 Log.d(TAG, "groupId active: " + groupDescriptor.mIsActive 1658 + " ringtone supported: " + ringtoneContextAvailable); 1659 } 1660 1661 boolean isRingtoneEnabled = (groupDescriptor.mIsActive && ringtoneContextAvailable); 1662 1663 if (DBG) { 1664 Log.d(TAG, "updateInbandRingtoneForTheGroup old: " 1665 + groupDescriptor.mInbandRingtoneEnabled + " new: " + isRingtoneEnabled); 1666 } 1667 1668 /* If at least one device from the group removes the Ringtone from available 1669 * context types, the inband ringtone will be removed 1670 */ 1671 groupDescriptor.mInbandRingtoneEnabled = isRingtoneEnabled; 1672 TbsService tbsService = getTbsService(); 1673 if (tbsService == null) { 1674 Log.w(TAG, "updateInbandRingtoneForTheGroup, tbsService not available"); 1675 return; 1676 } 1677 1678 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry : 1679 mDeviceDescriptors.entrySet()) { 1680 if (entry.getValue().mGroupId == groupId) { 1681 BluetoothDevice device = entry.getKey(); 1682 LeAudioDeviceDescriptor deviceDescriptor = entry.getValue(); 1683 Log.i(TAG, "updateInbandRingtoneForTheGroup, setting inband ringtone to: " 1684 + groupDescriptor.mInbandRingtoneEnabled + " for " + device 1685 + " " + deviceDescriptor.mDevInbandRingtoneEnabled); 1686 if (groupDescriptor.mInbandRingtoneEnabled 1687 == deviceDescriptor.mDevInbandRingtoneEnabled) { 1688 if (DBG) { 1689 Log.d(TAG, "Device " + device + " has already set inband ringtone to " 1690 + groupDescriptor.mInbandRingtoneEnabled); 1691 } 1692 continue; 1693 } 1694 1695 deviceDescriptor.mDevInbandRingtoneEnabled = 1696 groupDescriptor.mInbandRingtoneEnabled; 1697 if (deviceDescriptor.mDevInbandRingtoneEnabled) { 1698 tbsService.setInbandRingtoneSupport(device); 1699 } else { 1700 tbsService.clearInbandRingtoneSupport(device); 1701 } 1702 } 1703 } 1704 } 1705 } 1706 stopAudioServersBackgroundScan()1707 void stopAudioServersBackgroundScan() { 1708 if (DBG) { 1709 Log.d(TAG, "stopAudioServersBackgroundScan"); 1710 } 1711 1712 if (mAudioServersScanner == null || mScanCallback == null) { 1713 if (DBG) { 1714 Log.d(TAG, "stopAudioServersBackgroundScan: already stopped"); 1715 return; 1716 } 1717 } 1718 1719 mAudioServersScanner.stopScan(mScanCallback); 1720 1721 /* Callback is the indicator for scanning being enabled */ 1722 mScanCallback = null; 1723 } 1724 startAudioServersBackgroundScan(boolean retry)1725 void startAudioServersBackgroundScan(boolean retry) { 1726 if (DBG) { 1727 Log.d(TAG, "startAudioServersBackgroundScan, retry: " + retry); 1728 } 1729 1730 if (!isScannerNeeded()) { 1731 return; 1732 } 1733 1734 if (mAudioServersScanner == null) { 1735 mAudioServersScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner(); 1736 if (mAudioServersScanner == null) { 1737 Log.e(TAG, "startAudioServersBackgroundScan: Could not get scanner"); 1738 return; 1739 } 1740 } 1741 1742 if (!retry) { 1743 if (mScanCallback != null) { 1744 if (DBG) { 1745 Log.d(TAG, "startAudioServersBackgroundScan: Scanning already enabled"); 1746 return; 1747 } 1748 } 1749 mScanCallback = new AudioServerScanCallback(); 1750 } 1751 1752 /* Filter we are building here will not match to anything. 1753 * Eventually we should be able to start scan from native when 1754 * b/276350722 is done 1755 */ 1756 byte[] serviceData = new byte[]{0x11}; 1757 1758 ArrayList filterList = new ArrayList<ScanFilter>(); 1759 ScanFilter filter = new ScanFilter.Builder() 1760 .setServiceData(BluetoothUuid.LE_AUDIO, serviceData) 1761 .build(); 1762 filterList.add(filter); 1763 1764 ScanSettings settings = new ScanSettings.Builder() 1765 .setLegacy(false) 1766 .setScanMode(ScanSettings.SCAN_MODE_BALANCED) 1767 .build(); 1768 1769 mAudioServersScanner.startScan(filterList, settings, mScanCallback); 1770 } 1771 1772 // Suppressed since this is part of a local process 1773 @SuppressLint("AndroidFrameworkRequiresPermission") messageFromNative(LeAudioStackEvent stackEvent)1774 void messageFromNative(LeAudioStackEvent stackEvent) { 1775 Log.d(TAG, "Message from native: " + stackEvent); 1776 BluetoothDevice device = stackEvent.device; 1777 1778 if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { 1779 // Some events require device state machine 1780 synchronized (mGroupLock) { 1781 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 1782 if (deviceDescriptor == null) { 1783 Log.e(TAG, "messageFromNative: No valid descriptor for device: " + device); 1784 return; 1785 } 1786 1787 LeAudioStateMachine sm = deviceDescriptor.mStateMachine; 1788 if (sm != null) { 1789 /* 1790 * To improve scenario when lead Le Audio device is disconnected for the 1791 * streaming group, while there are still other devices streaming, 1792 * LeAudioService will not notify audio framework or other users about 1793 * Le Audio lead device disconnection. Instead we try to reconnect under 1794 * the hood and keep using lead device as a audio device indetifier in 1795 * the audio framework in order to not stop the stream. 1796 */ 1797 int groupId = deviceDescriptor.mGroupId; 1798 LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId); 1799 switch (stackEvent.valueInt1) { 1800 case LeAudioStackEvent.CONNECTION_STATE_DISCONNECTING: 1801 case LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED: 1802 startAudioServersBackgroundScan(/* retry = */ false); 1803 1804 boolean disconnectDueToUnbond = 1805 (BluetoothDevice.BOND_NONE 1806 == mAdapterService.getBondState(device)); 1807 if (descriptor != null && (Objects.equals(device, 1808 mActiveAudioOutDevice) 1809 || Objects.equals(device, mActiveAudioInDevice)) 1810 && (getConnectedPeerDevices(groupId).size() > 1) 1811 && !disconnectDueToUnbond) { 1812 1813 if (DBG) Log.d(TAG, "Adding to lost devices : " + device); 1814 descriptor.mLostLeadDeviceWhileStreaming = device; 1815 return; 1816 } 1817 break; 1818 case LeAudioStackEvent.CONNECTION_STATE_CONNECTED: 1819 case LeAudioStackEvent.CONNECTION_STATE_CONNECTING: 1820 if (descriptor != null 1821 && Objects.equals( 1822 descriptor.mLostLeadDeviceWhileStreaming, 1823 device)) { 1824 if (DBG) { 1825 Log.d(TAG, "Removing from lost devices : " + device); 1826 } 1827 descriptor.mLostLeadDeviceWhileStreaming = null; 1828 /* Try to connect other devices from the group */ 1829 connectSet(device); 1830 } 1831 break; 1832 } 1833 } else { 1834 /* state machine does not exist yet */ 1835 switch (stackEvent.valueInt1) { 1836 case LeAudioStackEvent.CONNECTION_STATE_CONNECTED: 1837 case LeAudioStackEvent.CONNECTION_STATE_CONNECTING: 1838 sm = getOrCreateStateMachine(device); 1839 /* Incoming connection try to connect other devices from the group */ 1840 connectSet(device); 1841 break; 1842 default: 1843 break; 1844 } 1845 1846 if (sm == null) { 1847 Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent); 1848 return; 1849 } 1850 } 1851 1852 sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent); 1853 return; 1854 } 1855 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED) { 1856 int groupId = stackEvent.valueInt1; 1857 int nodeStatus = stackEvent.valueInt2; 1858 1859 Objects.requireNonNull(stackEvent.device, 1860 "Device should never be null, event: " + stackEvent); 1861 1862 switch (nodeStatus) { 1863 case LeAudioStackEvent.GROUP_NODE_ADDED: 1864 handleGroupNodeAdded(device, groupId); 1865 break; 1866 case LeAudioStackEvent.GROUP_NODE_REMOVED: 1867 handleGroupNodeRemoved(device, groupId); 1868 break; 1869 default: 1870 break; 1871 } 1872 } else if (stackEvent.type 1873 == LeAudioStackEvent.EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED) { 1874 mInputLocalCodecCapabilities = stackEvent.valueCodecList1; 1875 mOutputLocalCodecCapabilities = stackEvent.valueCodecList2; 1876 } else if (stackEvent.type 1877 == LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED) { 1878 int groupId = stackEvent.valueInt1; 1879 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 1880 if (descriptor == null) { 1881 Log.e(TAG, " Group not found " + groupId); 1882 return; 1883 } 1884 1885 BluetoothLeAudioCodecStatus status = 1886 new BluetoothLeAudioCodecStatus(stackEvent.valueCodec1, 1887 stackEvent.valueCodec2, mInputLocalCodecCapabilities, 1888 mOutputLocalCodecCapabilities, 1889 stackEvent.valueCodecList1, 1890 stackEvent.valueCodecList2); 1891 1892 if (DBG) { 1893 if (descriptor.mCodecStatus != null) { 1894 Log.d(TAG, " Replacing codec status for group: " + groupId); 1895 } else { 1896 Log.d(TAG, " New codec status for group: " + groupId); 1897 } 1898 } 1899 1900 descriptor.mCodecStatus = status; 1901 notifyUnicastCodecConfigChanged(groupId, status); 1902 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED) { 1903 int direction = stackEvent.valueInt1; 1904 int groupId = stackEvent.valueInt2; 1905 int snk_audio_location = stackEvent.valueInt3; 1906 int src_audio_location = stackEvent.valueInt4; 1907 int available_contexts = stackEvent.valueInt5; 1908 1909 synchronized (mGroupLock) { 1910 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 1911 if (descriptor != null) { 1912 if (descriptor.mIsActive) { 1913 descriptor.mIsActive = 1914 updateActiveDevices(groupId, descriptor.mDirection, direction, 1915 descriptor.mIsActive, false); 1916 if (!descriptor.mIsActive) { 1917 notifyGroupStatusChanged(groupId, 1918 BluetoothLeAudio.GROUP_STATUS_INACTIVE); 1919 } 1920 } 1921 descriptor.mDirection = direction; 1922 descriptor.mAvailableContexts = available_contexts; 1923 updateInbandRingtoneForTheGroup(groupId); 1924 } else { 1925 Log.e(TAG, "messageFromNative: no descriptors for group: " + groupId); 1926 } 1927 } 1928 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE) { 1929 Objects.requireNonNull(stackEvent.device, 1930 "Device should never be null, event: " + stackEvent); 1931 1932 int sink_audio_location = stackEvent.valueInt1; 1933 1934 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 1935 if (descriptor == null) { 1936 Log.e(TAG, "messageFromNative: No valid descriptor for device: " + device); 1937 return; 1938 } 1939 1940 descriptor.mSinkAudioLocation = sink_audio_location; 1941 1942 if (DBG) { 1943 Log.i(TAG, "EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE:" + device 1944 + " audio location:" + sink_audio_location); 1945 } 1946 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED) { 1947 int groupId = stackEvent.valueInt1; 1948 int groupStatus = stackEvent.valueInt2; 1949 1950 switch (groupStatus) { 1951 case LeAudioStackEvent.GROUP_STATUS_ACTIVE: { 1952 handleGroupTransitToActive(groupId); 1953 break; 1954 } 1955 case LeAudioStackEvent.GROUP_STATUS_INACTIVE: { 1956 handleGroupTransitToInactive(groupId, false); 1957 break; 1958 } 1959 case LeAudioStackEvent.GROUP_STATUS_TURNED_IDLE_DURING_CALL: { 1960 handleGroupIdleDuringCall(); 1961 break; 1962 } 1963 default: 1964 break; 1965 } 1966 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED) { 1967 int broadcastId = stackEvent.valueInt1; 1968 boolean success = stackEvent.valueBool1; 1969 if (success) { 1970 Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " created."); 1971 notifyBroadcastStarted(broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 1972 1973 // Start sending the actual stream 1974 startBroadcast(broadcastId); 1975 } else { 1976 // TODO: Improve reason reporting or extend the native stack event with reason code 1977 notifyBroadcastStartFailed(broadcastId, BluetoothStatusCodes.ERROR_UNKNOWN); 1978 } 1979 1980 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_DESTROYED) { 1981 Integer broadcastId = stackEvent.valueInt1; 1982 1983 // TODO: Improve reason reporting or extend the native stack event with reason code 1984 notifyOnBroadcastStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 1985 1986 mBroadcastsPlaybackMap.remove(broadcastId); 1987 mBroadcastStateMap.remove(broadcastId); 1988 mBroadcastMetadataList.remove(broadcastId); 1989 1990 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE) { 1991 int broadcastId = stackEvent.valueInt1; 1992 int state = stackEvent.valueInt2; 1993 1994 /* Request broadcast details if not known yet */ 1995 if (!mBroadcastStateMap.containsKey(broadcastId)) { 1996 mLeAudioBroadcasterNativeInterface.getBroadcastMetadata(broadcastId); 1997 } 1998 mBroadcastStateMap.put(broadcastId, state); 1999 2000 if (state == LeAudioStackEvent.BROADCAST_STATE_STOPPED) { 2001 if (DBG) Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopped."); 2002 2003 // Playback stopped 2004 mBroadcastsPlaybackMap.put(broadcastId, false); 2005 notifyPlaybackStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 2006 2007 // Notify audio manager 2008 if (Collections.frequency(mBroadcastsPlaybackMap.values(), true) == 0) { 2009 if (Objects.equals(device, mActiveAudioOutDevice)) { 2010 BluetoothDevice previousDevice = mActiveAudioOutDevice; 2011 mActiveAudioOutDevice = null; 2012 mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioOutDevice, 2013 previousDevice, 2014 getBroadcastProfile(true)); 2015 } 2016 } 2017 2018 destroyBroadcast(broadcastId); 2019 2020 } else if (state == LeAudioStackEvent.BROADCAST_STATE_CONFIGURING) { 2021 if (DBG) Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " configuring."); 2022 2023 } else if (state == LeAudioStackEvent.BROADCAST_STATE_PAUSED) { 2024 if (DBG) Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " paused."); 2025 2026 // Playback paused 2027 mBroadcastsPlaybackMap.put(broadcastId, false); 2028 notifyPlaybackStopped(broadcastId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST); 2029 2030 } else if (state == LeAudioStackEvent.BROADCAST_STATE_STOPPING) { 2031 if (DBG) Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopping."); 2032 2033 } else if (state == LeAudioStackEvent.BROADCAST_STATE_STREAMING) { 2034 if (DBG) Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " streaming."); 2035 2036 // Stream resumed 2037 mBroadcastsPlaybackMap.put(broadcastId, true); 2038 notifyPlaybackStarted(broadcastId, BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST); 2039 2040 // Notify audio manager 2041 if (Collections.frequency(mBroadcastsPlaybackMap.values(), true) == 1) { 2042 if (!Objects.equals(device, mActiveAudioOutDevice)) { 2043 BluetoothDevice previousDevice = mActiveAudioOutDevice; 2044 mActiveAudioOutDevice = device; 2045 mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioOutDevice, 2046 previousDevice, 2047 getBroadcastProfile(false)); 2048 } 2049 } 2050 } 2051 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_METADATA_CHANGED) { 2052 int broadcastId = stackEvent.valueInt1; 2053 if (stackEvent.broadcastMetadata == null) { 2054 Log.e(TAG, "Missing Broadcast metadata for broadcastId: " + broadcastId); 2055 } else { 2056 mBroadcastMetadataList.put(broadcastId, stackEvent.broadcastMetadata); 2057 notifyBroadcastMetadataChanged(broadcastId, stackEvent.broadcastMetadata); 2058 } 2059 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED) { 2060 mLeAudioNativeIsInitialized = true; 2061 for (Map.Entry<ParcelUuid, Pair<Integer, Integer>> entry : 2062 ContentControlIdKeeper.getUuidToCcidContextPairMap().entrySet()) { 2063 ParcelUuid userUuid = entry.getKey(); 2064 Pair<Integer, Integer> ccidInformation = entry.getValue(); 2065 setCcidInformation(userUuid, ccidInformation.first, ccidInformation.second); 2066 } 2067 if (!mTmapStarted) { 2068 mTmapStarted = registerTmap(); 2069 } 2070 } 2071 } 2072 getOrCreateStateMachine(BluetoothDevice device)2073 private LeAudioStateMachine getOrCreateStateMachine(BluetoothDevice device) { 2074 if (device == null) { 2075 Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null"); 2076 return null; 2077 } 2078 2079 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 2080 if (descriptor == null) { 2081 Log.e(TAG, "getOrCreateStateMachine: No valid descriptor for device: " + device); 2082 return null; 2083 } 2084 2085 LeAudioStateMachine sm = descriptor.mStateMachine; 2086 if (sm != null) { 2087 return sm; 2088 } 2089 2090 if (DBG) { 2091 Log.d(TAG, "Creating a new state machine for " + device); 2092 } 2093 2094 sm = LeAudioStateMachine.make(device, this, 2095 mLeAudioNativeInterface, mStateMachinesThread.getLooper()); 2096 descriptor.mStateMachine = sm; 2097 return sm; 2098 } 2099 2100 // Remove state machine if the bonding for a device is removed 2101 private class BondStateChangedReceiver extends BroadcastReceiver { 2102 @Override onReceive(Context context, Intent intent)2103 public void onReceive(Context context, Intent intent) { 2104 if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) { 2105 return; 2106 } 2107 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 2108 BluetoothDevice.ERROR); 2109 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2110 Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 2111 bondStateChanged(device, state); 2112 } 2113 } 2114 2115 /** 2116 * Process a change in the bonding state for a device. 2117 * 2118 * @param device the device whose bonding state has changed 2119 * @param bondState the new bond state for the device. Possible values are: 2120 * {@link BluetoothDevice#BOND_NONE}, 2121 * {@link BluetoothDevice#BOND_BONDING}, 2122 * {@link BluetoothDevice#BOND_BONDED}. 2123 */ 2124 @VisibleForTesting bondStateChanged(BluetoothDevice device, int bondState)2125 void bondStateChanged(BluetoothDevice device, int bondState) { 2126 if (DBG) { 2127 Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState); 2128 } 2129 // Remove state machine if the bonding for a device is removed 2130 if (bondState != BluetoothDevice.BOND_NONE) { 2131 return; 2132 } 2133 2134 synchronized (mGroupLock) { 2135 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 2136 if (descriptor == null) { 2137 Log.e(TAG, "bondStateChanged: No valid descriptor for device: " + device); 2138 return; 2139 } 2140 2141 if (descriptor.mGroupId != LE_AUDIO_GROUP_ID_INVALID) { 2142 /* In case device is still in the group, let's remove it */ 2143 mLeAudioNativeInterface.groupRemoveNode(descriptor.mGroupId, device); 2144 } 2145 2146 descriptor.mGroupId = LE_AUDIO_GROUP_ID_INVALID; 2147 descriptor.mSinkAudioLocation = BluetoothLeAudio.AUDIO_LOCATION_INVALID; 2148 descriptor.mDirection = AUDIO_DIRECTION_NONE; 2149 2150 LeAudioStateMachine sm = descriptor.mStateMachine; 2151 if (sm == null) { 2152 return; 2153 } 2154 if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { 2155 Log.w(TAG, "Device is not disconnected yet."); 2156 disconnect(device); 2157 return; 2158 } 2159 removeStateMachine(device); 2160 } 2161 } 2162 removeStateMachine(BluetoothDevice device)2163 private void removeStateMachine(BluetoothDevice device) { 2164 synchronized (mGroupLock) { 2165 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 2166 if (descriptor == null) { 2167 Log.e(TAG, "removeStateMachine: No valid descriptor for device: " + device); 2168 return; 2169 } 2170 2171 LeAudioStateMachine sm = descriptor.mStateMachine; 2172 if (sm == null) { 2173 Log.w(TAG, "removeStateMachine: device " + device 2174 + " does not have a state machine"); 2175 return; 2176 } 2177 Log.i(TAG, "removeStateMachine: removing state machine for device: " + device); 2178 sm.quit(); 2179 sm.cleanup(); 2180 descriptor.mStateMachine = null; 2181 2182 mDeviceDescriptors.remove(device); 2183 if (!isScannerNeeded()) { 2184 stopAudioServersBackgroundScan(); 2185 } 2186 } 2187 } 2188 2189 @VisibleForTesting getConnectedPeerDevices(int groupId)2190 List<BluetoothDevice> getConnectedPeerDevices(int groupId) { 2191 List<BluetoothDevice> result = new ArrayList<>(); 2192 for (BluetoothDevice peerDevice : getConnectedDevices()) { 2193 if (getGroupId(peerDevice) == groupId) { 2194 result.add(peerDevice); 2195 } 2196 } 2197 return result; 2198 } 2199 2200 /** 2201 * Process a change for connection of a device. 2202 */ deviceConnected(BluetoothDevice device)2203 public synchronized void deviceConnected(BluetoothDevice device) { 2204 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 2205 if (deviceDescriptor == null) { 2206 Log.e(TAG, "deviceConnected: No valid descriptor for device: " + device); 2207 return; 2208 } 2209 2210 if (deviceDescriptor.mGroupId == LE_AUDIO_GROUP_ID_INVALID 2211 || getConnectedPeerDevices(deviceDescriptor.mGroupId).size() == 1) { 2212 // Log LE Audio connection event if we are the first device in a set 2213 // Or when the GroupId has not been found 2214 // MetricsLogger.logProfileConnectionEvent( 2215 // BluetoothMetricsProto.ProfileId.LE_AUDIO); 2216 } 2217 2218 LeAudioGroupDescriptor descriptor = getGroupDescriptor(deviceDescriptor.mGroupId); 2219 if (descriptor != null) { 2220 descriptor.mIsConnected = true; 2221 } else { 2222 Log.e(TAG, "deviceConnected: no descriptors for group: " 2223 + deviceDescriptor.mGroupId); 2224 } 2225 2226 if (!isScannerNeeded()) { 2227 stopAudioServersBackgroundScan(); 2228 } 2229 } 2230 2231 /** 2232 * Process a change for disconnection of a device. 2233 */ deviceDisconnected(BluetoothDevice device, boolean hasFallbackDevice)2234 public synchronized void deviceDisconnected(BluetoothDevice device, boolean hasFallbackDevice) { 2235 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 2236 if (deviceDescriptor == null) { 2237 Log.e(TAG, "deviceDisconnected: No valid descriptor for device: " + device); 2238 return; 2239 } 2240 2241 int bondState = mAdapterService.getBondState(device); 2242 if (bondState == BluetoothDevice.BOND_NONE) { 2243 if (DBG) { 2244 Log.d(TAG, device + " is unbond. Remove state machine"); 2245 } 2246 removeStateMachine(device); 2247 } 2248 2249 if (!isScannerNeeded()) { 2250 stopAudioServersBackgroundScan(); 2251 } 2252 2253 synchronized (mGroupLock) { 2254 LeAudioGroupDescriptor descriptor = getGroupDescriptor(deviceDescriptor.mGroupId); 2255 if (descriptor == null) { 2256 Log.e(TAG, "deviceDisconnected: no descriptors for group: " 2257 + deviceDescriptor.mGroupId); 2258 return; 2259 } 2260 2261 List<BluetoothDevice> connectedDevices = 2262 getConnectedPeerDevices(deviceDescriptor.mGroupId); 2263 /* Let's check if the last connected device is really connected */ 2264 if (connectedDevices.size() == 1 && Objects.equals( 2265 connectedDevices.get(0), descriptor.mLostLeadDeviceWhileStreaming)) { 2266 clearLostDevicesWhileStreaming(descriptor); 2267 return; 2268 } 2269 2270 if (getConnectedPeerDevices(deviceDescriptor.mGroupId).isEmpty()) { 2271 descriptor.mIsConnected = false; 2272 if (descriptor.mIsActive) { 2273 /* Notify Native layer */ 2274 removeActiveDevice(hasFallbackDevice); 2275 descriptor.mIsActive = false; 2276 /* Update audio framework */ 2277 updateActiveDevices(deviceDescriptor.mGroupId, 2278 descriptor.mDirection, 2279 descriptor.mDirection, 2280 descriptor.mIsActive, 2281 hasFallbackDevice); 2282 return; 2283 } 2284 } 2285 2286 if (descriptor.mIsActive) { 2287 updateActiveDevices(deviceDescriptor.mGroupId, 2288 descriptor.mDirection, 2289 descriptor.mDirection, 2290 descriptor.mIsActive, 2291 hasFallbackDevice); 2292 } 2293 } 2294 } 2295 2296 /** 2297 * Check whether can connect to a peer device. 2298 * The check considers a number of factors during the evaluation. 2299 * 2300 * @param device the peer device to connect to 2301 * @return true if connection is allowed, otherwise false 2302 */ okToConnect(BluetoothDevice device)2303 public boolean okToConnect(BluetoothDevice device) { 2304 // Check if this is an incoming connection in Quiet mode. 2305 if (mAdapterService.isQuietModeEnabled()) { 2306 Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled"); 2307 return false; 2308 } 2309 // Check connectionPolicy and accept or reject the connection. 2310 int connectionPolicy = getConnectionPolicy(device); 2311 int bondState = mAdapterService.getBondState(device); 2312 // Allow this connection only if the device is bonded. Any attempt to connect while 2313 // bonding would potentially lead to an unauthorized connection. 2314 if (bondState != BluetoothDevice.BOND_BONDED) { 2315 Log.w(TAG, "okToConnect: return false, bondState=" + bondState); 2316 return false; 2317 } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN 2318 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 2319 // Otherwise, reject the connection if connectionPolicy is not valid. 2320 Log.w(TAG, "okToConnect: return false, connectionPolicy=" + connectionPolicy); 2321 return false; 2322 } 2323 return true; 2324 } 2325 2326 /** 2327 * Get device audio location. 2328 * @param device LE Audio capable device 2329 * @return the sink audioi location that this device currently exposed 2330 */ getAudioLocation(BluetoothDevice device)2331 public int getAudioLocation(BluetoothDevice device) { 2332 if (device == null) { 2333 return BluetoothLeAudio.AUDIO_LOCATION_INVALID; 2334 } 2335 2336 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 2337 if (descriptor == null) { 2338 Log.e(TAG, "getAudioLocation: No valid descriptor for device: " + device); 2339 return BluetoothLeAudio.AUDIO_LOCATION_INVALID; 2340 } 2341 2342 return descriptor.mSinkAudioLocation; 2343 } 2344 2345 /** 2346 * Check if inband ringtone is enabled by the LE Audio group. 2347 * Group id for the device can be found with {@link BluetoothLeAudio#getGroupId}. 2348 * @param groupId LE Audio group id 2349 * @return true if inband ringtone is enabled, false otherwise 2350 */ isInbandRingtoneEnabled(int groupId)2351 public boolean isInbandRingtoneEnabled(int groupId) { 2352 if (!mLeAudioInbandRingtoneSupportedByPlatform) { 2353 return mLeAudioInbandRingtoneSupportedByPlatform; 2354 } 2355 2356 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 2357 if (descriptor == null) { 2358 return false; 2359 } 2360 2361 return descriptor.mInbandRingtoneEnabled; 2362 } 2363 2364 /** 2365 * Set In Call state 2366 * @param inCall True if device in call (any state), false otherwise. 2367 */ setInCall(boolean inCall)2368 public void setInCall(boolean inCall) { 2369 if (!mLeAudioNativeIsInitialized) { 2370 Log.e(TAG, "Le Audio not initialized properly."); 2371 return; 2372 } 2373 mLeAudioNativeInterface.setInCall(inCall); 2374 } 2375 2376 /** 2377 * Sends the preferred audio profiles for a dual mode audio device to the native stack. 2378 * 2379 * @param groupId is the group id of the device which had a preference change 2380 * @param isOutputPreferenceLeAudio {@code true} if {@link BluetoothProfile#LE_AUDIO} is 2381 * preferred for {@link BluetoothAdapter#AUDIO_MODE_OUTPUT_ONLY}, {@code false} if it is 2382 * {@link BluetoothProfile#A2DP} 2383 * @param isDuplexPreferenceLeAudio {@code true} if {@link BluetoothProfile#LE_AUDIO} is 2384 * preferred for {@link BluetoothAdapter#AUDIO_MODE_DUPLEX}, {@code false} if it is 2385 * {@link BluetoothProfile#HEADSET} 2386 */ sendAudioProfilePreferencesToNative(int groupId, boolean isOutputPreferenceLeAudio, boolean isDuplexPreferenceLeAudio)2387 public void sendAudioProfilePreferencesToNative(int groupId, boolean isOutputPreferenceLeAudio, 2388 boolean isDuplexPreferenceLeAudio) { 2389 if (!mLeAudioNativeIsInitialized) { 2390 Log.e(TAG, "Le Audio not initialized properly."); 2391 return; 2392 } 2393 mLeAudioNativeInterface.sendAudioProfilePreferences(groupId, isOutputPreferenceLeAudio, 2394 isDuplexPreferenceLeAudio); 2395 } 2396 2397 /** 2398 * Set Inactive by HFP during handover 2399 */ setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice)2400 public void setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice) { 2401 if (!mLeAudioNativeIsInitialized) { 2402 Log.e(TAG, "Le Audio not initialized properly."); 2403 return; 2404 } 2405 if (getActiveGroupId() != LE_AUDIO_GROUP_ID_INVALID) { 2406 mHfpHandoverDevice = hfpHandoverDevice; 2407 removeActiveDevice(true); 2408 } 2409 } 2410 2411 /** 2412 * Set connection policy of the profile and connects it if connectionPolicy is 2413 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is 2414 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 2415 * 2416 * <p> The device should already be paired. 2417 * Connection policy can be one of: 2418 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 2419 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 2420 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 2421 * 2422 * @param device the remote device 2423 * @param connectionPolicy is the connection policy to set to for this profile 2424 * @return true on success, otherwise false 2425 */ 2426 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) setConnectionPolicy(BluetoothDevice device, int connectionPolicy)2427 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 2428 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 2429 "Need BLUETOOTH_PRIVILEGED permission"); 2430 if (DBG) { 2431 Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 2432 } 2433 2434 if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO, 2435 connectionPolicy)) { 2436 return false; 2437 } 2438 2439 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 2440 setEnabledState(device, /* enabled = */ true); 2441 // Authorizes LEA GATT server services if already assigned to a group 2442 int groupId = getGroupId(device); 2443 if (groupId != LE_AUDIO_GROUP_ID_INVALID) { 2444 setAuthorizationForRelatedProfiles(device, true); 2445 } 2446 connect(device); 2447 } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 2448 setEnabledState(device, /* enabled = */ false); 2449 // Remove authorization for LEA GATT server services 2450 setAuthorizationForRelatedProfiles(device, false); 2451 disconnect(device); 2452 } 2453 setLeAudioGattClientProfilesPolicy(device, connectionPolicy); 2454 return true; 2455 } 2456 2457 /** 2458 * Sets the connection policy for LE Audio GATT client profiles 2459 * @param device is the remote device 2460 * @param connectionPolicy is the connection policy we wish to set 2461 */ setLeAudioGattClientProfilesPolicy(BluetoothDevice device, int connectionPolicy)2462 private void setLeAudioGattClientProfilesPolicy(BluetoothDevice device, int connectionPolicy) { 2463 if (DBG) { 2464 Log.d(TAG, "setLeAudioGattClientProfilesPolicy for device " + device + " to policy=" 2465 + connectionPolicy); 2466 } 2467 if (mVolumeControlService == null) { 2468 mVolumeControlService = mServiceFactory.getVolumeControlService(); 2469 } 2470 if (mVolumeControlService != null) { 2471 mVolumeControlService.setConnectionPolicy(device, connectionPolicy); 2472 } 2473 2474 if (mCsipSetCoordinatorService == null) { 2475 mCsipSetCoordinatorService = mServiceFactory.getCsipSetCoordinatorService(); 2476 } 2477 2478 // Disallow setting CSIP to forbidden until characteristic reads are complete 2479 if (mCsipSetCoordinatorService != null) { 2480 mCsipSetCoordinatorService.setConnectionPolicy(device, connectionPolicy); 2481 } 2482 } 2483 2484 /** 2485 * Get the connection policy of the profile. 2486 * 2487 * <p> The connection policy can be any of: 2488 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 2489 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 2490 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 2491 * 2492 * @param device Bluetooth device 2493 * @return connection policy of the device 2494 * @hide 2495 */ getConnectionPolicy(BluetoothDevice device)2496 public int getConnectionPolicy(BluetoothDevice device) { 2497 int connection_policy = mDatabaseManager 2498 .getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO); 2499 if (DBG) { 2500 Log.d(TAG, device + " connection policy = " + connection_policy); 2501 } 2502 return connection_policy; 2503 } 2504 2505 /** 2506 * Get device group id. Devices with same group id belong to same group (i.e left and right 2507 * earbud) 2508 * @param device LE Audio capable device 2509 * @return group id that this device currently belongs to 2510 */ getGroupId(BluetoothDevice device)2511 public int getGroupId(BluetoothDevice device) { 2512 if (device == null) { 2513 return LE_AUDIO_GROUP_ID_INVALID; 2514 } 2515 2516 synchronized (mGroupLock) { 2517 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 2518 if (descriptor == null) { 2519 Log.e(TAG, "getGroupId: No valid descriptor for device: " + device); 2520 return LE_AUDIO_GROUP_ID_INVALID; 2521 } 2522 2523 return descriptor.mGroupId; 2524 } 2525 } 2526 2527 /** 2528 * Set the user application ccid along with used context type 2529 * @param userUuid user uuid 2530 * @param ccid content control id 2531 * @param contextType context type 2532 */ setCcidInformation(ParcelUuid userUuid, int ccid, int contextType)2533 public void setCcidInformation(ParcelUuid userUuid, int ccid, int contextType) { 2534 /* for the moment we care only for GMCS and GTBS */ 2535 if (userUuid != BluetoothUuid.GENERIC_MEDIA_CONTROL 2536 && userUuid.getUuid() != TbsGatt.UUID_GTBS) { 2537 return; 2538 } 2539 if (!mLeAudioNativeIsInitialized) { 2540 Log.e(TAG, "Le Audio not initialized properly."); 2541 return; 2542 } 2543 mLeAudioNativeInterface.setCcidInformation(ccid, contextType); 2544 } 2545 2546 /** 2547 * Set volume for streaming devices 2548 * @param volume volume to set 2549 */ setVolume(int volume)2550 public void setVolume(int volume) { 2551 if (DBG) { 2552 Log.d(TAG, "SetVolume " + volume); 2553 } 2554 2555 int currentlyActiveGroupId = getActiveGroupId(); 2556 if (currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID) { 2557 Log.e(TAG, "There is no active group "); 2558 return; 2559 } 2560 2561 if (mVolumeControlService == null) { 2562 mVolumeControlService = mServiceFactory.getVolumeControlService(); 2563 } 2564 if (mVolumeControlService != null) { 2565 mVolumeControlService.setGroupVolume(currentlyActiveGroupId, volume); 2566 } 2567 } 2568 getTbsService()2569 TbsService getTbsService() { 2570 if (mTbsService != null) { 2571 return mTbsService; 2572 } 2573 2574 mTbsService = mServiceFactory.getTbsService(); 2575 return mTbsService; 2576 } 2577 getMcpService()2578 McpService getMcpService() { 2579 if (mMcpService != null) { 2580 return mMcpService; 2581 } 2582 2583 mMcpService = mServiceFactory.getMcpService(); 2584 return mMcpService; 2585 } 2586 setAuthorizationForRelatedProfiles(BluetoothDevice device, boolean authorize)2587 void setAuthorizationForRelatedProfiles(BluetoothDevice device, boolean authorize) { 2588 McpService mcpService = getMcpService(); 2589 if (mcpService != null) { 2590 mcpService.setDeviceAuthorized(device, authorize); 2591 } 2592 2593 TbsService tbsService = getTbsService(); 2594 if (tbsService != null) { 2595 tbsService.setDeviceAuthorized(device, authorize); 2596 } 2597 } 2598 2599 /** 2600 * This function is called when the framework registers a callback with the service for this 2601 * first time. This is used as an indication that Bluetooth has been enabled. 2602 * 2603 * <p>It is used to authorize all known LeAudio devices in the services which requires that e.g. 2604 * GMCS 2605 */ 2606 @VisibleForTesting handleBluetoothEnabled()2607 void handleBluetoothEnabled() { 2608 if (DBG) { 2609 Log.d(TAG, "handleBluetoothEnabled "); 2610 } 2611 2612 mBluetoothEnabled = true; 2613 2614 synchronized (mGroupLock) { 2615 if (mDeviceDescriptors.isEmpty()) { 2616 return; 2617 } 2618 } 2619 2620 synchronized (mGroupLock) { 2621 for (BluetoothDevice device : mDeviceDescriptors.keySet()) { 2622 if (getConnectionPolicy(device) != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 2623 setAuthorizationForRelatedProfiles(device, true); 2624 } 2625 } 2626 } 2627 2628 startAudioServersBackgroundScan(/* retry = */ false); 2629 } 2630 getGroupDescriptor(int groupId)2631 private LeAudioGroupDescriptor getGroupDescriptor(int groupId) { 2632 synchronized (mGroupLock) { 2633 return mGroupDescriptors.get(groupId); 2634 } 2635 } 2636 getDeviceDescriptor(BluetoothDevice device)2637 private LeAudioDeviceDescriptor getDeviceDescriptor(BluetoothDevice device) { 2638 synchronized (mGroupLock) { 2639 return mDeviceDescriptors.get(device); 2640 } 2641 } 2642 handleGroupNodeAdded(BluetoothDevice device, int groupId)2643 private void handleGroupNodeAdded(BluetoothDevice device, int groupId) { 2644 synchronized (mGroupLock) { 2645 if (DBG) { 2646 Log.d(TAG, "Device " + device + " added to group " + groupId); 2647 } 2648 2649 LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); 2650 if (groupDescriptor == null) { 2651 mGroupDescriptors.put(groupId, 2652 new LeAudioGroupDescriptor(false)); 2653 } 2654 groupDescriptor = getGroupDescriptor(groupId); 2655 if (groupDescriptor == null) { 2656 Log.e(TAG, "Could not create group description"); 2657 return; 2658 } 2659 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 2660 if (deviceDescriptor == null) { 2661 deviceDescriptor = createDeviceDescriptor(device, 2662 groupDescriptor.mInbandRingtoneEnabled); 2663 if (deviceDescriptor == null) { 2664 Log.e(TAG, "handleGroupNodeAdded: Can't create descriptor for added from" 2665 + " storage device: " + device); 2666 return; 2667 } 2668 2669 LeAudioStateMachine sm = getOrCreateStateMachine(device); 2670 if (getOrCreateStateMachine(device) == null) { 2671 Log.e(TAG, "Can't get state machine for device: " + device); 2672 return; 2673 } 2674 } 2675 deviceDescriptor.mGroupId = groupId; 2676 2677 notifyGroupNodeAdded(device, groupId); 2678 } 2679 2680 if (mBluetoothEnabled) { 2681 setAuthorizationForRelatedProfiles(device, true); 2682 startAudioServersBackgroundScan(/* retry = */ false); 2683 } 2684 } 2685 notifyGroupNodeAdded(BluetoothDevice device, int groupId)2686 private void notifyGroupNodeAdded(BluetoothDevice device, int groupId) { 2687 if (mVolumeControlService == null) { 2688 mVolumeControlService = mServiceFactory.getVolumeControlService(); 2689 } 2690 if (mVolumeControlService != null) { 2691 mVolumeControlService.handleGroupNodeAdded(groupId, device); 2692 } 2693 2694 if (mLeAudioCallbacks != null) { 2695 int n = mLeAudioCallbacks.beginBroadcast(); 2696 for (int i = 0; i < n; i++) { 2697 try { 2698 mLeAudioCallbacks.getBroadcastItem(i).onGroupNodeAdded(device, groupId); 2699 } catch (RemoteException e) { 2700 continue; 2701 } 2702 } 2703 mLeAudioCallbacks.finishBroadcast(); 2704 } 2705 } 2706 handleGroupNodeRemoved(BluetoothDevice device, int groupId)2707 private void handleGroupNodeRemoved(BluetoothDevice device, int groupId) { 2708 if (DBG) { 2709 Log.d(TAG, "Removing device " + device + " grom group " + groupId); 2710 } 2711 2712 synchronized (mGroupLock) { 2713 LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); 2714 if (groupDescriptor == null) { 2715 Log.e(TAG, "handleGroupNodeRemoved: No valid descriptor for group: " + groupId); 2716 return; 2717 } 2718 if (DBG) { 2719 Log.d(TAG, "Lost lead device is " + groupDescriptor.mLostLeadDeviceWhileStreaming); 2720 } 2721 if (Objects.equals(device, groupDescriptor.mLostLeadDeviceWhileStreaming)) { 2722 clearLostDevicesWhileStreaming(groupDescriptor); 2723 } 2724 2725 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 2726 if (deviceDescriptor == null) { 2727 Log.e(TAG, "handleGroupNodeRemoved: No valid descriptor for device: " + device); 2728 return; 2729 } 2730 deviceDescriptor.mGroupId = LE_AUDIO_GROUP_ID_INVALID; 2731 2732 boolean isGroupEmpty = true; 2733 2734 for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) { 2735 if (descriptor.mGroupId == groupId) { 2736 isGroupEmpty = false; 2737 break; 2738 } 2739 } 2740 2741 if (isGroupEmpty) { 2742 /* Device is currently an active device. Group needs to be inactivated before 2743 * removing 2744 */ 2745 if (Objects.equals(device, mActiveAudioOutDevice) 2746 || Objects.equals(device, mActiveAudioInDevice)) { 2747 handleGroupTransitToInactive(groupId, false); 2748 } 2749 mGroupDescriptors.remove(groupId); 2750 } 2751 notifyGroupNodeRemoved(device, groupId); 2752 } 2753 2754 setAuthorizationForRelatedProfiles(device, false); 2755 } 2756 notifyGroupNodeRemoved(BluetoothDevice device, int groupId)2757 private void notifyGroupNodeRemoved(BluetoothDevice device, int groupId) { 2758 if (mLeAudioCallbacks != null) { 2759 int n = mLeAudioCallbacks.beginBroadcast(); 2760 for (int i = 0; i < n; i++) { 2761 try { 2762 mLeAudioCallbacks.getBroadcastItem(i).onGroupNodeRemoved(device, groupId); 2763 } catch (RemoteException e) { 2764 continue; 2765 } 2766 } 2767 mLeAudioCallbacks.finishBroadcast(); 2768 } 2769 } 2770 notifyGroupStatusChanged(int groupId, int status)2771 private void notifyGroupStatusChanged(int groupId, int status) { 2772 if (mLeAudioCallbacks != null) { 2773 int n = mLeAudioCallbacks.beginBroadcast(); 2774 for (int i = 0; i < n; i++) { 2775 try { 2776 mLeAudioCallbacks.getBroadcastItem(i).onGroupStatusChanged(groupId, status); 2777 } catch (RemoteException e) { 2778 continue; 2779 } 2780 } 2781 mLeAudioCallbacks.finishBroadcast(); 2782 } 2783 } 2784 notifyUnicastCodecConfigChanged(int groupId, BluetoothLeAudioCodecStatus status)2785 private void notifyUnicastCodecConfigChanged(int groupId, BluetoothLeAudioCodecStatus status) { 2786 if (mLeAudioCallbacks != null) { 2787 int n = mLeAudioCallbacks.beginBroadcast(); 2788 for (int i = 0; i < n; i++) { 2789 try { 2790 mLeAudioCallbacks.getBroadcastItem(i).onCodecConfigChanged(groupId, status); 2791 } catch (RemoteException e) { 2792 continue; 2793 } 2794 } 2795 mLeAudioCallbacks.finishBroadcast(); 2796 } 2797 } 2798 notifyBroadcastStarted(Integer broadcastId, int reason)2799 private void notifyBroadcastStarted(Integer broadcastId, int reason) { 2800 if (mBroadcastCallbacks != null) { 2801 int n = mBroadcastCallbacks.beginBroadcast(); 2802 for (int i = 0; i < n; i++) { 2803 try { 2804 mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStarted(reason, broadcastId); 2805 } catch (RemoteException e) { 2806 continue; 2807 } 2808 } 2809 mBroadcastCallbacks.finishBroadcast(); 2810 } 2811 } 2812 notifyBroadcastStartFailed(Integer broadcastId, int reason)2813 private void notifyBroadcastStartFailed(Integer broadcastId, int reason) { 2814 if (mBroadcastCallbacks != null) { 2815 int n = mBroadcastCallbacks.beginBroadcast(); 2816 for (int i = 0; i < n; i++) { 2817 try { 2818 mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStartFailed(reason); 2819 } catch (RemoteException e) { 2820 continue; 2821 } 2822 } 2823 mBroadcastCallbacks.finishBroadcast(); 2824 } 2825 } 2826 notifyOnBroadcastStopped(Integer broadcastId, int reason)2827 private void notifyOnBroadcastStopped(Integer broadcastId, int reason) { 2828 if (mBroadcastCallbacks != null) { 2829 int n = mBroadcastCallbacks.beginBroadcast(); 2830 for (int i = 0; i < n; i++) { 2831 try { 2832 mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStopped(reason, broadcastId); 2833 } catch (RemoteException e) { 2834 continue; 2835 } 2836 } 2837 mBroadcastCallbacks.finishBroadcast(); 2838 } 2839 } 2840 notifyOnBroadcastStopFailed(int reason)2841 private void notifyOnBroadcastStopFailed(int reason) { 2842 if (mBroadcastCallbacks != null) { 2843 int n = mBroadcastCallbacks.beginBroadcast(); 2844 for (int i = 0; i < n; i++) { 2845 try { 2846 mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStopFailed(reason); 2847 } catch (RemoteException e) { 2848 continue; 2849 } 2850 } 2851 mBroadcastCallbacks.finishBroadcast(); 2852 } 2853 } 2854 notifyPlaybackStarted(Integer broadcastId, int reason)2855 private void notifyPlaybackStarted(Integer broadcastId, int reason) { 2856 if (mBroadcastCallbacks != null) { 2857 int n = mBroadcastCallbacks.beginBroadcast(); 2858 for (int i = 0; i < n; i++) { 2859 try { 2860 mBroadcastCallbacks.getBroadcastItem(i).onPlaybackStarted(reason, broadcastId); 2861 } catch (RemoteException e) { 2862 continue; 2863 } 2864 } 2865 mBroadcastCallbacks.finishBroadcast(); 2866 } 2867 } 2868 notifyPlaybackStopped(Integer broadcastId, int reason)2869 private void notifyPlaybackStopped(Integer broadcastId, int reason) { 2870 if (mBroadcastCallbacks != null) { 2871 int n = mBroadcastCallbacks.beginBroadcast(); 2872 for (int i = 0; i < n; i++) { 2873 try { 2874 mBroadcastCallbacks.getBroadcastItem(i).onPlaybackStopped(reason, broadcastId); 2875 } catch (RemoteException e) { 2876 continue; 2877 } 2878 } 2879 mBroadcastCallbacks.finishBroadcast(); 2880 } 2881 } 2882 notifyBroadcastUpdated(int broadcastId, int reason)2883 private void notifyBroadcastUpdated(int broadcastId, int reason) { 2884 if (mBroadcastCallbacks != null) { 2885 int n = mBroadcastCallbacks.beginBroadcast(); 2886 for (int i = 0; i < n; i++) { 2887 try { 2888 mBroadcastCallbacks.getBroadcastItem(i).onBroadcastUpdated(reason, broadcastId); 2889 } catch (RemoteException e) { 2890 continue; 2891 } 2892 } 2893 mBroadcastCallbacks.finishBroadcast(); 2894 } 2895 } 2896 notifyBroadcastUpdateFailed(int broadcastId, int reason)2897 private void notifyBroadcastUpdateFailed(int broadcastId, int reason) { 2898 if (mBroadcastCallbacks != null) { 2899 int n = mBroadcastCallbacks.beginBroadcast(); 2900 for (int i = 0; i < n; i++) { 2901 try { 2902 mBroadcastCallbacks.getBroadcastItem(i) 2903 .onBroadcastUpdateFailed(reason, broadcastId); 2904 } catch (RemoteException e) { 2905 continue; 2906 } 2907 } 2908 mBroadcastCallbacks.finishBroadcast(); 2909 } 2910 } 2911 notifyBroadcastMetadataChanged(int broadcastId, BluetoothLeBroadcastMetadata metadata)2912 private void notifyBroadcastMetadataChanged(int broadcastId, 2913 BluetoothLeBroadcastMetadata metadata) { 2914 if (mBroadcastCallbacks != null) { 2915 int n = mBroadcastCallbacks.beginBroadcast(); 2916 for (int i = 0; i < n; i++) { 2917 try { 2918 mBroadcastCallbacks.getBroadcastItem(i) 2919 .onBroadcastMetadataChanged(broadcastId, metadata); 2920 } catch (RemoteException e) { 2921 continue; 2922 } 2923 } 2924 mBroadcastCallbacks.finishBroadcast(); 2925 } 2926 } 2927 2928 /** 2929 * Gets the current codec status (configuration and capability). 2930 * 2931 * @param groupId the group id 2932 * @return the current codec status 2933 * @hide 2934 */ getCodecStatus(int groupId)2935 public BluetoothLeAudioCodecStatus getCodecStatus(int groupId) { 2936 if (DBG) { 2937 Log.d(TAG, "getCodecStatus(" + groupId + ")"); 2938 } 2939 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 2940 if (descriptor != null) { 2941 return descriptor.mCodecStatus; 2942 } 2943 return null; 2944 } 2945 2946 /** 2947 * Sets the codec configuration preference. 2948 * 2949 * @param groupId the group id 2950 * @param inputCodecConfig the input codec configuration preference 2951 * @param outputCodecConfig the output codec configuration preference 2952 * @hide 2953 */ setCodecConfigPreference(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig)2954 public void setCodecConfigPreference(int groupId, 2955 BluetoothLeAudioCodecConfig inputCodecConfig, 2956 BluetoothLeAudioCodecConfig outputCodecConfig) { 2957 if (DBG) { 2958 Log.d(TAG, "setCodecConfigPreference(" + groupId + "): " 2959 + Objects.toString(inputCodecConfig) 2960 + Objects.toString(outputCodecConfig)); 2961 } 2962 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 2963 if (descriptor == null) { 2964 Log.e(TAG, "setCodecConfigPreference: Invalid groupId, " + groupId); 2965 return; 2966 } 2967 2968 if (inputCodecConfig == null || outputCodecConfig == null) { 2969 Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); 2970 return; 2971 } 2972 2973 /* We support different configuration for input and output but codec type 2974 * shall be same */ 2975 if (inputCodecConfig.getCodecType() != outputCodecConfig.getCodecType()) { 2976 Log.e(TAG, "setCodecConfigPreference: Input codec type: " 2977 + inputCodecConfig.getCodecType() 2978 + "does not match output codec type: " + outputCodecConfig.getCodecType()); 2979 return; 2980 } 2981 2982 if (descriptor.mCodecStatus == null) { 2983 Log.e(TAG, "setCodecConfigPreference: Codec status is null"); 2984 return; 2985 } 2986 2987 if (!mLeAudioNativeIsInitialized) { 2988 Log.e(TAG, "Le Audio not initialized properly."); 2989 return; 2990 } 2991 2992 mLeAudioNativeInterface.setCodecConfigPreference( 2993 groupId, inputCodecConfig, outputCodecConfig); 2994 } 2995 2996 /** 2997 * Checks if the remote device supports LE Audio duplex (output and input). 2998 * @param device the remote device to check 2999 * @return {@code true} if LE Audio duplex is supported, {@code false} otherwise 3000 */ isLeAudioDuplexSupported(BluetoothDevice device)3001 public boolean isLeAudioDuplexSupported(BluetoothDevice device) { 3002 int groupId = getGroupId(device); 3003 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 3004 return false; 3005 } 3006 3007 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 3008 if (descriptor == null) { 3009 return false; 3010 } 3011 return (descriptor.mDirection & AUDIO_DIRECTION_OUTPUT_BIT) != 0 3012 && (descriptor.mDirection & AUDIO_DIRECTION_INPUT_BIT) != 0; 3013 } 3014 3015 /** 3016 * Checks if the remote device supports LE Audio output 3017 * @param device the remote device to check 3018 * @return {@code true} if LE Audio output is supported, {@code false} otherwise 3019 */ isLeAudioOutputSupported(BluetoothDevice device)3020 public boolean isLeAudioOutputSupported(BluetoothDevice device) { 3021 int groupId = getGroupId(device); 3022 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 3023 return false; 3024 } 3025 3026 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 3027 if (descriptor == null) { 3028 return false; 3029 } 3030 return (descriptor.mDirection & AUDIO_DIRECTION_OUTPUT_BIT) != 0; 3031 } 3032 3033 /** 3034 * Gets the lead device for the CSIP group containing the provided device 3035 * @param device the remote device whose CSIP group lead device we want to find 3036 * @return the lead device of the CSIP group or {@code null} if the group does not exist 3037 */ getLeadDevice(BluetoothDevice device)3038 public BluetoothDevice getLeadDevice(BluetoothDevice device) { 3039 int groupId = getGroupId(device); 3040 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 3041 return null; 3042 } 3043 return getConnectedGroupLeadDevice(groupId); 3044 } 3045 3046 /** 3047 * Sends the preferred audio profile change requested from a call to 3048 * {@link BluetoothAdapter#setPreferredAudioProfiles(BluetoothDevice, Bundle)} to the audio 3049 * framework to apply the change. The audio framework will call 3050 * {@link BluetoothAdapter#notifyActiveDeviceChangeApplied(BluetoothDevice)} once the 3051 * change is successfully applied. 3052 * 3053 * @return the number of requests sent to the audio framework 3054 */ sendPreferredAudioProfileChangeToAudioFramework()3055 public int sendPreferredAudioProfileChangeToAudioFramework() { 3056 if (mActiveAudioOutDevice == null && mActiveAudioInDevice == null) { 3057 Log.e(TAG, "sendPreferredAudioProfileChangeToAudioFramework: no active device"); 3058 return 0; 3059 } 3060 3061 int audioFrameworkCalls = 0; 3062 3063 if (mActiveAudioOutDevice != null) { 3064 int volume = getAudioDeviceGroupVolume(getGroupId(mActiveAudioOutDevice)); 3065 final boolean suppressNoisyIntent = mActiveAudioOutDevice != null; 3066 Log.i(TAG, "Sending LE Audio Output active device changed for preferred profile " 3067 + "change with volume=" + volume + " and suppressNoisyIntent=" 3068 + suppressNoisyIntent); 3069 mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioOutDevice, 3070 mActiveAudioOutDevice, BluetoothProfileConnectionInfo.createLeAudioOutputInfo( 3071 suppressNoisyIntent, volume)); 3072 audioFrameworkCalls++; 3073 } 3074 3075 if (mActiveAudioInDevice != null) { 3076 Log.i(TAG, "Sending LE Audio Input active device changed for audio profile change"); 3077 mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioInDevice, 3078 mActiveAudioInDevice, BluetoothProfileConnectionInfo.createLeAudioInfo(false, 3079 false)); 3080 audioFrameworkCalls++; 3081 } 3082 3083 return audioFrameworkCalls; 3084 } 3085 3086 /** 3087 * Binder object: must be a static class or memory leak may occur 3088 */ 3089 @VisibleForTesting 3090 static class BluetoothLeAudioBinder extends IBluetoothLeAudio.Stub 3091 implements IProfileServiceBinder { 3092 private LeAudioService mService; 3093 3094 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)3095 private LeAudioService getService(AttributionSource source) { 3096 if (Utils.isInstrumentationTestMode()) { 3097 return mService; 3098 } 3099 if (!Utils.checkServiceAvailable(mService, TAG) 3100 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 3101 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 3102 return null; 3103 } 3104 return mService; 3105 } 3106 BluetoothLeAudioBinder(LeAudioService svc)3107 BluetoothLeAudioBinder(LeAudioService svc) { 3108 mService = svc; 3109 } 3110 3111 @Override cleanup()3112 public void cleanup() { 3113 mService = null; 3114 } 3115 3116 @Override connect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)3117 public void connect(BluetoothDevice device, AttributionSource source, 3118 SynchronousResultReceiver receiver) { 3119 try { 3120 Objects.requireNonNull(device, "device cannot be null"); 3121 Objects.requireNonNull(source, "source cannot be null"); 3122 Objects.requireNonNull(receiver, "receiver cannot be null"); 3123 3124 LeAudioService service = getService(source); 3125 boolean result = false; 3126 if (service != null) { 3127 result = service.connect(device); 3128 } 3129 receiver.send(result); 3130 } catch (RuntimeException e) { 3131 receiver.propagateException(e); 3132 } 3133 } 3134 3135 @Override disconnect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)3136 public void disconnect(BluetoothDevice device, AttributionSource source, 3137 SynchronousResultReceiver receiver) { 3138 try { 3139 Objects.requireNonNull(device, "device cannot be null"); 3140 Objects.requireNonNull(source, "source cannot be null"); 3141 Objects.requireNonNull(receiver, "receiver cannot be null"); 3142 3143 LeAudioService service = getService(source); 3144 boolean result = false; 3145 if (service != null) { 3146 result = service.disconnect(device); 3147 } 3148 receiver.send(result); 3149 } catch (RuntimeException e) { 3150 receiver.propagateException(e); 3151 } 3152 } 3153 3154 @Override getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver)3155 public void getConnectedDevices(AttributionSource source, 3156 SynchronousResultReceiver receiver) { 3157 try { 3158 Objects.requireNonNull(source, "source cannot be null"); 3159 Objects.requireNonNull(receiver, "receiver cannot be null"); 3160 3161 LeAudioService service = getService(source); 3162 List<BluetoothDevice> result = new ArrayList<>(0); 3163 if (service != null) { 3164 result = service.getConnectedDevices(); 3165 } 3166 receiver.send(result); 3167 } catch (RuntimeException e) { 3168 receiver.propagateException(e); 3169 } 3170 } 3171 3172 @Override getConnectedGroupLeadDevice(int groupId, AttributionSource source, SynchronousResultReceiver receiver)3173 public void getConnectedGroupLeadDevice(int groupId, AttributionSource source, 3174 SynchronousResultReceiver receiver) { 3175 try { 3176 Objects.requireNonNull(source, "source cannot be null"); 3177 Objects.requireNonNull(receiver, "receiver cannot be null"); 3178 3179 LeAudioService service = getService(source); 3180 BluetoothDevice result = null; 3181 if (service != null) { 3182 result = service.getConnectedGroupLeadDevice(groupId); 3183 } 3184 receiver.send(result); 3185 } catch (RuntimeException e) { 3186 receiver.propagateException(e); 3187 } 3188 } 3189 3190 @Override getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver)3191 public void getDevicesMatchingConnectionStates(int[] states, 3192 AttributionSource source, SynchronousResultReceiver receiver) { 3193 try { 3194 Objects.requireNonNull(source, "source cannot be null"); 3195 Objects.requireNonNull(receiver, "receiver cannot be null"); 3196 3197 LeAudioService service = getService(source); 3198 List<BluetoothDevice> result = new ArrayList<>(0); 3199 if (service != null) { 3200 result = service.getDevicesMatchingConnectionStates(states); 3201 } 3202 receiver.send(result); 3203 } catch (RuntimeException e) { 3204 receiver.propagateException(e); 3205 } 3206 } 3207 3208 @Override getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)3209 public void getConnectionState(BluetoothDevice device, AttributionSource source, 3210 SynchronousResultReceiver receiver) { 3211 try { 3212 Objects.requireNonNull(device, "device cannot be null"); 3213 Objects.requireNonNull(source, "source cannot be null"); 3214 Objects.requireNonNull(receiver, "receiver cannot be null"); 3215 3216 LeAudioService service = getService(source); 3217 int result = BluetoothProfile.STATE_DISCONNECTED; 3218 if (service != null) { 3219 result = service.getConnectionState(device); 3220 } 3221 receiver.send(result); 3222 } catch (RuntimeException e) { 3223 receiver.propagateException(e); 3224 } 3225 } 3226 3227 @Override setActiveDevice(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)3228 public void setActiveDevice(BluetoothDevice device, AttributionSource source, 3229 SynchronousResultReceiver receiver) { 3230 try { 3231 Objects.requireNonNull(source, "source cannot be null"); 3232 Objects.requireNonNull(receiver, "receiver cannot be null"); 3233 3234 LeAudioService service = getService(source); 3235 boolean result = false; 3236 if (service != null) { 3237 if (device == null) { 3238 result = service.removeActiveDevice(true); 3239 } else { 3240 result = service.setActiveDevice(device); 3241 } 3242 } 3243 receiver.send(result); 3244 } catch (RuntimeException e) { 3245 receiver.propagateException(e); 3246 } 3247 } 3248 3249 @Override getActiveDevices(AttributionSource source, SynchronousResultReceiver receiver)3250 public void getActiveDevices(AttributionSource source, SynchronousResultReceiver receiver) { 3251 try { 3252 Objects.requireNonNull(source, "source cannot be null"); 3253 Objects.requireNonNull(receiver, "receiver cannot be null"); 3254 3255 LeAudioService service = getService(source); 3256 List<BluetoothDevice> result = new ArrayList<>(); 3257 if (service != null) { 3258 result = service.getActiveDevices(); 3259 } 3260 receiver.send(result); 3261 } catch (RuntimeException e) { 3262 receiver.propagateException(e); 3263 } 3264 } 3265 3266 @Override getAudioLocation(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)3267 public void getAudioLocation(BluetoothDevice device, AttributionSource source, 3268 SynchronousResultReceiver receiver) { 3269 try { 3270 Objects.requireNonNull(device, "device cannot be null"); 3271 Objects.requireNonNull(source, "source cannot be null"); 3272 Objects.requireNonNull(receiver, "receiver cannot be null"); 3273 3274 LeAudioService service = getService(source); 3275 int result = BluetoothLeAudio.AUDIO_LOCATION_INVALID; 3276 if (service != null) { 3277 enforceBluetoothPrivilegedPermission(service); 3278 result = service.getAudioLocation(device); 3279 } 3280 receiver.send(result); 3281 } catch (RuntimeException e) { 3282 receiver.propagateException(e); 3283 } 3284 } 3285 3286 @Override isInbandRingtoneEnabled(AttributionSource source, SynchronousResultReceiver receiver, int groupId)3287 public void isInbandRingtoneEnabled(AttributionSource source, 3288 SynchronousResultReceiver receiver, int groupId) { 3289 try { 3290 Objects.requireNonNull(source, "source cannot be null"); 3291 Objects.requireNonNull(receiver, "receiver cannot be null"); 3292 3293 LeAudioService service = getService(source); 3294 boolean result = false; 3295 if (service != null) { 3296 enforceBluetoothPrivilegedPermission(service); 3297 result = service.isInbandRingtoneEnabled(groupId); 3298 } 3299 receiver.send(result); 3300 } catch (RuntimeException e) { 3301 receiver.propagateException(e); 3302 } 3303 } 3304 3305 @Override setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver)3306 public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, 3307 AttributionSource source, SynchronousResultReceiver receiver) { 3308 Objects.requireNonNull(device, "device cannot be null"); 3309 Objects.requireNonNull(source, "source cannot be null"); 3310 Objects.requireNonNull(receiver, "receiver cannot be null"); 3311 3312 try { 3313 LeAudioService service = getService(source); 3314 boolean result = false; 3315 if (service != null) { 3316 enforceBluetoothPrivilegedPermission(service); 3317 result = service.setConnectionPolicy(device, connectionPolicy); 3318 } 3319 receiver.send(result); 3320 } catch (RuntimeException e) { 3321 receiver.propagateException(e); 3322 } 3323 } 3324 3325 @Override getConnectionPolicy(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)3326 public void getConnectionPolicy(BluetoothDevice device, AttributionSource source, 3327 SynchronousResultReceiver receiver) { 3328 try { 3329 Objects.requireNonNull(device, "device cannot be null"); 3330 Objects.requireNonNull(source, "source cannot be null"); 3331 Objects.requireNonNull(receiver, "receiver cannot be null"); 3332 3333 LeAudioService service = getService(source); 3334 int result = BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 3335 if (service == null) { 3336 throw new IllegalStateException("service is null"); 3337 } 3338 enforceBluetoothPrivilegedPermission(service); 3339 result = service.getConnectionPolicy(device); 3340 receiver.send(result); 3341 } catch (RuntimeException e) { 3342 receiver.propagateException(e); 3343 } 3344 } 3345 3346 @Override setCcidInformation(ParcelUuid userUuid, int ccid, int contextType, AttributionSource source, SynchronousResultReceiver receiver)3347 public void setCcidInformation(ParcelUuid userUuid, int ccid, int contextType, 3348 AttributionSource source, 3349 SynchronousResultReceiver receiver) { 3350 try { 3351 Objects.requireNonNull(userUuid, "userUuid cannot be null"); 3352 Objects.requireNonNull(source, "source cannot be null"); 3353 Objects.requireNonNull(receiver, "receiver cannot be null"); 3354 3355 LeAudioService service = getService(source); 3356 if (service == null) { 3357 throw new IllegalStateException("service is null"); 3358 } 3359 enforceBluetoothPrivilegedPermission(service); 3360 service.setCcidInformation(userUuid, ccid, contextType); 3361 receiver.send(null); 3362 } catch (RuntimeException e) { 3363 receiver.propagateException(e); 3364 } 3365 } 3366 3367 @Override getGroupId(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)3368 public void getGroupId(BluetoothDevice device, AttributionSource source, 3369 SynchronousResultReceiver receiver) { 3370 try { 3371 Objects.requireNonNull(device, "device cannot be null"); 3372 Objects.requireNonNull(source, "source cannot be null"); 3373 Objects.requireNonNull(receiver, "receiver cannot be null"); 3374 3375 LeAudioService service = getService(source); 3376 int result = LE_AUDIO_GROUP_ID_INVALID; 3377 if (service == null) { 3378 throw new IllegalStateException("service is null"); 3379 } 3380 result = service.getGroupId(device); 3381 receiver.send(result); 3382 } catch (RuntimeException e) { 3383 receiver.propagateException(e); 3384 } 3385 } 3386 3387 @Override groupAddNode(int group_id, BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)3388 public void groupAddNode(int group_id, BluetoothDevice device, 3389 AttributionSource source, SynchronousResultReceiver receiver) { 3390 try { 3391 Objects.requireNonNull(device, "device cannot be null"); 3392 Objects.requireNonNull(source, "source cannot be null"); 3393 Objects.requireNonNull(receiver, "receiver cannot be null"); 3394 3395 LeAudioService service = getService(source); 3396 boolean result = false; 3397 if (service == null) { 3398 throw new IllegalStateException("service is null"); 3399 } 3400 enforceBluetoothPrivilegedPermission(service); 3401 result = service.groupAddNode(group_id, device); 3402 receiver.send(result); 3403 } catch (RuntimeException e) { 3404 receiver.propagateException(e); 3405 } 3406 } 3407 3408 @Override setInCall(boolean inCall, AttributionSource source, SynchronousResultReceiver receiver)3409 public void setInCall(boolean inCall, AttributionSource source, 3410 SynchronousResultReceiver receiver) { 3411 try { 3412 Objects.requireNonNull(source, "source cannot be null"); 3413 Objects.requireNonNull(receiver, "receiver cannot be null"); 3414 3415 LeAudioService service = getService(source); 3416 if (service == null) { 3417 throw new IllegalStateException("service is null"); 3418 } 3419 enforceBluetoothPrivilegedPermission(service); 3420 service.setInCall(inCall); 3421 receiver.send(null); 3422 } catch (RuntimeException e) { 3423 receiver.propagateException(e); 3424 } 3425 } 3426 3427 @Override setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice, AttributionSource source, SynchronousResultReceiver receiver)3428 public void setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice, 3429 AttributionSource source, 3430 SynchronousResultReceiver receiver) { 3431 try { 3432 Objects.requireNonNull(source, "source cannot be null"); 3433 Objects.requireNonNull(receiver, "receiver cannot be null"); 3434 3435 LeAudioService service = getService(source); 3436 if (service == null) { 3437 throw new IllegalStateException("service is null"); 3438 } 3439 enforceBluetoothPrivilegedPermission(service); 3440 service.setInactiveForHfpHandover(hfpHandoverDevice); 3441 receiver.send(null); 3442 } catch (RuntimeException e) { 3443 receiver.propagateException(e); 3444 } 3445 } 3446 3447 @Override groupRemoveNode(int groupId, BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)3448 public void groupRemoveNode(int groupId, BluetoothDevice device, 3449 AttributionSource source, SynchronousResultReceiver receiver) { 3450 try { 3451 Objects.requireNonNull(device, "device cannot be null"); 3452 Objects.requireNonNull(source, "source cannot be null"); 3453 Objects.requireNonNull(receiver, "receiver cannot be null"); 3454 3455 LeAudioService service = getService(source); 3456 boolean result = false; 3457 if (service == null) { 3458 throw new IllegalStateException("service is null"); 3459 } 3460 enforceBluetoothPrivilegedPermission(service); 3461 result = service.groupRemoveNode(groupId, device); 3462 receiver.send(result); 3463 } catch (RuntimeException e) { 3464 receiver.propagateException(e); 3465 } 3466 } 3467 3468 @Override setVolume(int volume, AttributionSource source, SynchronousResultReceiver receiver)3469 public void setVolume(int volume, AttributionSource source, 3470 SynchronousResultReceiver receiver) { 3471 try { 3472 Objects.requireNonNull(source, "source cannot be null"); 3473 Objects.requireNonNull(receiver, "receiver cannot be null"); 3474 3475 LeAudioService service = getService(source); 3476 if (service == null) { 3477 throw new IllegalStateException("service is null"); 3478 } 3479 enforceBluetoothPrivilegedPermission(service); 3480 service.setVolume(volume); 3481 receiver.send(null); 3482 } catch (RuntimeException e) { 3483 receiver.propagateException(e); 3484 } 3485 } 3486 3487 @Override registerCallback(IBluetoothLeAudioCallback callback, AttributionSource source, SynchronousResultReceiver receiver)3488 public void registerCallback(IBluetoothLeAudioCallback callback, 3489 AttributionSource source, SynchronousResultReceiver receiver) { 3490 try { 3491 Objects.requireNonNull(callback, "callback cannot be null"); 3492 Objects.requireNonNull(source, "source cannot be null"); 3493 Objects.requireNonNull(receiver, "receiver cannot be null"); 3494 3495 LeAudioService service = getService(source); 3496 if ((service == null) || (service.mLeAudioCallbacks == null)) { 3497 throw new IllegalStateException("Service is unavailable: " + service); 3498 } 3499 3500 enforceBluetoothPrivilegedPermission(service); 3501 service.mLeAudioCallbacks.register(callback); 3502 if (!service.mBluetoothEnabled) { 3503 service.handleBluetoothEnabled(); 3504 } 3505 receiver.send(null); 3506 } catch (RuntimeException e) { 3507 receiver.propagateException(e); 3508 } 3509 } 3510 3511 @Override unregisterCallback(IBluetoothLeAudioCallback callback, AttributionSource source, SynchronousResultReceiver receiver)3512 public void unregisterCallback(IBluetoothLeAudioCallback callback, 3513 AttributionSource source, SynchronousResultReceiver receiver) { 3514 try { 3515 Objects.requireNonNull(callback, "callback cannot be null"); 3516 Objects.requireNonNull(source, "source cannot be null"); 3517 Objects.requireNonNull(receiver, "receiver cannot be null"); 3518 3519 LeAudioService service = getService(source); 3520 if ((service == null) || (service.mLeAudioCallbacks == null)) { 3521 throw new IllegalStateException("Service is unavailable"); 3522 } 3523 3524 enforceBluetoothPrivilegedPermission(service); 3525 3526 service.mLeAudioCallbacks.unregister(callback); 3527 receiver.send(null); 3528 } catch (RuntimeException e) { 3529 receiver.propagateException(e); 3530 } 3531 } 3532 3533 @Override registerLeBroadcastCallback(IBluetoothLeBroadcastCallback callback, AttributionSource source, SynchronousResultReceiver receiver)3534 public void registerLeBroadcastCallback(IBluetoothLeBroadcastCallback callback, 3535 AttributionSource source, SynchronousResultReceiver receiver) { 3536 try { 3537 Objects.requireNonNull(callback, "callback cannot be null"); 3538 Objects.requireNonNull(source, "source cannot be null"); 3539 Objects.requireNonNull(receiver, "receiver cannot be null"); 3540 3541 LeAudioService service = getService(source); 3542 if ((service == null) || (service.mBroadcastCallbacks == null)) { 3543 throw new IllegalStateException("Service is unavailable"); 3544 } 3545 3546 enforceBluetoothPrivilegedPermission(service); 3547 3548 service.mBroadcastCallbacks.register(callback); 3549 receiver.send(null); 3550 } catch (RuntimeException e) { 3551 receiver.propagateException(e); 3552 } 3553 } 3554 3555 @Override unregisterLeBroadcastCallback(IBluetoothLeBroadcastCallback callback, AttributionSource source, SynchronousResultReceiver receiver)3556 public void unregisterLeBroadcastCallback(IBluetoothLeBroadcastCallback callback, 3557 AttributionSource source, SynchronousResultReceiver receiver) { 3558 try { 3559 LeAudioService service = getService(source); 3560 if ((service == null) || (service.mBroadcastCallbacks == null)) { 3561 throw new IllegalStateException("Service is unavailable"); 3562 } 3563 3564 enforceBluetoothPrivilegedPermission(service); 3565 Objects.requireNonNull(callback, "callback cannot be null"); 3566 Objects.requireNonNull(source, "source cannot be null"); 3567 Objects.requireNonNull(receiver, "receiver cannot be null"); 3568 3569 service.mBroadcastCallbacks.unregister(callback); 3570 receiver.send(null); 3571 } catch (RuntimeException e) { 3572 receiver.propagateException(e); 3573 } 3574 } 3575 3576 @Override startBroadcast( BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source, SynchronousResultReceiver receiver)3577 public void startBroadcast( 3578 BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source, 3579 SynchronousResultReceiver receiver) { 3580 try { 3581 LeAudioService service = getService(source); 3582 if (service != null) { 3583 enforceBluetoothPrivilegedPermission(service); 3584 service.createBroadcast(broadcastSettings); 3585 } 3586 receiver.send(null); 3587 } catch (RuntimeException e) { 3588 receiver.propagateException(e); 3589 } 3590 } 3591 3592 @Override stopBroadcast(int broadcastId, AttributionSource source, SynchronousResultReceiver receiver)3593 public void stopBroadcast(int broadcastId, AttributionSource source, 3594 SynchronousResultReceiver receiver) { 3595 try { 3596 LeAudioService service = getService(source); 3597 if (service != null) { 3598 enforceBluetoothPrivilegedPermission(service); 3599 service.stopBroadcast(broadcastId); 3600 } 3601 receiver.send(null); 3602 } catch (RuntimeException e) { 3603 receiver.propagateException(e); 3604 } 3605 } 3606 3607 @Override updateBroadcast( int broadcastId, BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source, SynchronousResultReceiver receiver)3608 public void updateBroadcast( 3609 int broadcastId, 3610 BluetoothLeBroadcastSettings broadcastSettings, 3611 AttributionSource source, 3612 SynchronousResultReceiver receiver) { 3613 try { 3614 LeAudioService service = getService(source); 3615 if (service != null) { 3616 enforceBluetoothPrivilegedPermission(service); 3617 service.updateBroadcast(broadcastId, broadcastSettings); 3618 } 3619 receiver.send(null); 3620 } catch (RuntimeException e) { 3621 receiver.propagateException(e); 3622 } 3623 } 3624 3625 @Override isPlaying(int broadcastId, AttributionSource source, SynchronousResultReceiver receiver)3626 public void isPlaying(int broadcastId, AttributionSource source, 3627 SynchronousResultReceiver receiver) { 3628 try { 3629 boolean result = false; 3630 LeAudioService service = getService(source); 3631 if (service != null) { 3632 enforceBluetoothPrivilegedPermission(service); 3633 result = service.isPlaying(broadcastId); 3634 } 3635 receiver.send(result); 3636 } catch (RuntimeException e) { 3637 receiver.propagateException(e); 3638 } 3639 } 3640 3641 @Override getAllBroadcastMetadata(AttributionSource source, SynchronousResultReceiver receiver)3642 public void getAllBroadcastMetadata(AttributionSource source, 3643 SynchronousResultReceiver receiver) { 3644 try { 3645 List<BluetoothLeBroadcastMetadata> result = new ArrayList<>(); 3646 LeAudioService service = getService(source); 3647 if (service != null) { 3648 enforceBluetoothPrivilegedPermission(service); 3649 result = service.getAllBroadcastMetadata(); 3650 } 3651 receiver.send(result); 3652 } catch (RuntimeException e) { 3653 receiver.propagateException(e); 3654 } 3655 } 3656 3657 @Override getMaximumNumberOfBroadcasts(AttributionSource source, SynchronousResultReceiver receiver)3658 public void getMaximumNumberOfBroadcasts(AttributionSource source, 3659 SynchronousResultReceiver receiver) { 3660 try { 3661 int result = 0; 3662 LeAudioService service = getService(source); 3663 if (service != null) { 3664 enforceBluetoothPrivilegedPermission(service); 3665 result = service.getMaximumNumberOfBroadcasts(); 3666 } 3667 receiver.send(result); 3668 } catch (RuntimeException e) { 3669 receiver.propagateException(e); 3670 } 3671 } 3672 3673 @Override getMaximumStreamsPerBroadcast( AttributionSource source, SynchronousResultReceiver receiver)3674 public void getMaximumStreamsPerBroadcast( 3675 AttributionSource source, SynchronousResultReceiver receiver) { 3676 try { 3677 int result = 0; 3678 LeAudioService service = getService(source); 3679 if (service != null) { 3680 enforceBluetoothPrivilegedPermission(service); 3681 result = service.getMaximumStreamsPerBroadcast(); 3682 } 3683 receiver.send(result); 3684 } catch (RuntimeException e) { 3685 receiver.propagateException(e); 3686 } 3687 } 3688 3689 @Override getMaximumSubgroupsPerBroadcast( AttributionSource source, SynchronousResultReceiver receiver)3690 public void getMaximumSubgroupsPerBroadcast( 3691 AttributionSource source, SynchronousResultReceiver receiver) { 3692 try { 3693 int result = 0; 3694 LeAudioService service = getService(source); 3695 if (service != null) { 3696 enforceBluetoothPrivilegedPermission(service); 3697 result = service.getMaximumSubgroupsPerBroadcast(); 3698 } 3699 receiver.send(result); 3700 } catch (RuntimeException e) { 3701 receiver.propagateException(e); 3702 } 3703 } 3704 3705 @Override getCodecStatus(int groupId, AttributionSource source, SynchronousResultReceiver receiver)3706 public void getCodecStatus(int groupId, 3707 AttributionSource source, SynchronousResultReceiver receiver) { 3708 try { 3709 LeAudioService service = getService(source); 3710 BluetoothLeAudioCodecStatus codecStatus = null; 3711 if (service != null) { 3712 enforceBluetoothPrivilegedPermission(service); 3713 codecStatus = service.getCodecStatus(groupId); 3714 } 3715 receiver.send(codecStatus); 3716 } catch (RuntimeException e) { 3717 receiver.propagateException(e); 3718 } 3719 } 3720 3721 @Override setCodecConfigPreference(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig, AttributionSource source)3722 public void setCodecConfigPreference(int groupId, 3723 BluetoothLeAudioCodecConfig inputCodecConfig, 3724 BluetoothLeAudioCodecConfig outputCodecConfig, 3725 AttributionSource source) { 3726 LeAudioService service = getService(source); 3727 if (service == null) { 3728 return; 3729 } 3730 3731 enforceBluetoothPrivilegedPermission(service); 3732 service.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig); 3733 } 3734 } 3735 3736 @Override dump(StringBuilder sb)3737 public void dump(StringBuilder sb) { 3738 super.dump(sb); 3739 ProfileService.println(sb, "isDualModeAudioEnabled: " + Utils.isDualModeAudioEnabled()); 3740 ProfileService.println(sb, "Active Groups information: "); 3741 ProfileService.println(sb, " currentlyActiveGroupId: " + getActiveGroupId()); 3742 ProfileService.println(sb, " mActiveAudioOutDevice: " + mActiveAudioOutDevice); 3743 ProfileService.println(sb, " mActiveAudioInDevice: " + mActiveAudioInDevice); 3744 ProfileService.println(sb, " mExposedActiveDevice: " + mExposedActiveDevice); 3745 ProfileService.println(sb, " mHfpHandoverDevice:" + mHfpHandoverDevice); 3746 ProfileService.println(sb, " mLeAudioIsInbandRingtoneSupported:" 3747 + mLeAudioInbandRingtoneSupportedByPlatform); 3748 3749 int numberOfUngroupedDevs = 0; 3750 synchronized (mGroupLock) { 3751 for (Map.Entry<Integer, LeAudioGroupDescriptor> groupEntry 3752 : mGroupDescriptors.entrySet()) { 3753 LeAudioGroupDescriptor groupDescriptor = groupEntry.getValue(); 3754 Integer groupId = groupEntry.getKey(); 3755 BluetoothDevice leadDevice = getConnectedGroupLeadDevice(groupId); 3756 ProfileService.println(sb, "Group: " + groupId); 3757 ProfileService.println(sb, " isActive: " + groupDescriptor.mIsActive); 3758 ProfileService.println(sb, " isConnected: " + groupDescriptor.mIsConnected); 3759 ProfileService.println(sb, " mDirection: " + groupDescriptor.mDirection); 3760 ProfileService.println(sb, " group lead: " + leadDevice); 3761 ProfileService.println(sb, " first device: " + getFirstDeviceFromGroup(groupId)); 3762 ProfileService.println(sb, " lost lead device: " 3763 + groupDescriptor.mLostLeadDeviceWhileStreaming); 3764 ProfileService.println(sb, " mInbandRingtoneEnabled: " 3765 + groupDescriptor.mInbandRingtoneEnabled); 3766 3767 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> deviceEntry 3768 : mDeviceDescriptors.entrySet()) { 3769 LeAudioDeviceDescriptor deviceDescriptor = deviceEntry.getValue(); 3770 if (deviceDescriptor.mGroupId != groupId) { 3771 if (deviceDescriptor.mGroupId == LE_AUDIO_GROUP_ID_INVALID) { 3772 numberOfUngroupedDevs++; 3773 } 3774 continue; 3775 } 3776 3777 if (deviceDescriptor.mStateMachine != null) { 3778 deviceDescriptor.mStateMachine.dump(sb); 3779 } else { 3780 ProfileService.println(sb, "state machine is null"); 3781 } 3782 3783 ProfileService.println(sb, " mDevInbandRingtoneEnabled: " 3784 + deviceDescriptor.mDevInbandRingtoneEnabled); 3785 ProfileService.println(sb, " mSinkAudioLocation: " 3786 + deviceDescriptor.mSinkAudioLocation); 3787 ProfileService.println(sb, " mDirection: " + deviceDescriptor.mDirection); 3788 } 3789 } 3790 } 3791 3792 if (numberOfUngroupedDevs > 0) { 3793 ProfileService.println(sb, "UnGroup devices:"); 3794 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry 3795 : mDeviceDescriptors.entrySet()) { 3796 LeAudioDeviceDescriptor deviceDescriptor = entry.getValue(); 3797 if (deviceDescriptor.mGroupId != LE_AUDIO_GROUP_ID_INVALID) { 3798 continue; 3799 } 3800 3801 deviceDescriptor.mStateMachine.dump(sb); 3802 ProfileService.println(sb, " mDevInbandRingtoneEnabled: " 3803 + deviceDescriptor.mDevInbandRingtoneEnabled); 3804 ProfileService.println(sb, " mSinkAudioLocation: " 3805 + deviceDescriptor.mSinkAudioLocation); 3806 ProfileService.println(sb, " mDirection: " + deviceDescriptor.mDirection); 3807 } 3808 } 3809 } 3810 } 3811