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