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 import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask; 25 import static com.android.bluetooth.flags.Flags.leaudioApiSynchronizedBlockFix; 26 import static com.android.bluetooth.flags.Flags.leaudioBroadcastFeatureSupport; 27 import static com.android.bluetooth.flags.Flags.leaudioUseAudioModeListener; 28 import static com.android.modules.utils.build.SdkLevel.isAtLeastU; 29 30 import android.annotation.RequiresPermission; 31 import android.annotation.SuppressLint; 32 import android.app.ActivityManager; 33 import android.bluetooth.BluetoothAdapter; 34 import android.bluetooth.BluetoothDevice; 35 import android.bluetooth.BluetoothLeAudio; 36 import android.bluetooth.BluetoothLeAudioCodecConfig; 37 import android.bluetooth.BluetoothLeAudioCodecStatus; 38 import android.bluetooth.BluetoothLeAudioContentMetadata; 39 import android.bluetooth.BluetoothLeBroadcastMetadata; 40 import android.bluetooth.BluetoothLeBroadcastSettings; 41 import android.bluetooth.BluetoothLeBroadcastSubgroupSettings; 42 import android.bluetooth.BluetoothProfile; 43 import android.bluetooth.BluetoothProtoEnums; 44 import android.bluetooth.BluetoothStatusCodes; 45 import android.bluetooth.BluetoothUuid; 46 import android.bluetooth.IBluetoothLeAudio; 47 import android.bluetooth.IBluetoothLeAudioCallback; 48 import android.bluetooth.IBluetoothLeBroadcastCallback; 49 import android.bluetooth.IBluetoothVolumeControl; 50 import android.bluetooth.le.BluetoothLeScanner; 51 import android.bluetooth.le.ScanCallback; 52 import android.bluetooth.le.ScanFilter; 53 import android.bluetooth.le.ScanResult; 54 import android.bluetooth.le.ScanSettings; 55 import android.content.AttributionSource; 56 import android.content.Context; 57 import android.content.Intent; 58 import android.media.AudioDeviceCallback; 59 import android.media.AudioDeviceInfo; 60 import android.media.AudioManager; 61 import android.media.BluetoothProfileConnectionInfo; 62 import android.os.Binder; 63 import android.os.Handler; 64 import android.os.HandlerThread; 65 import android.os.Looper; 66 import android.os.Parcel; 67 import android.os.ParcelUuid; 68 import android.os.RemoteCallbackList; 69 import android.os.RemoteException; 70 import android.os.UserHandle; 71 import android.provider.Settings; 72 import android.sysprop.BluetoothProperties; 73 import android.util.Log; 74 import android.util.Pair; 75 76 import com.android.bluetooth.Utils; 77 import com.android.bluetooth.bass_client.BassClientService; 78 import com.android.bluetooth.btservice.AdapterService; 79 import com.android.bluetooth.btservice.AudioRoutingManager; 80 import com.android.bluetooth.btservice.MetricsLogger; 81 import com.android.bluetooth.btservice.ProfileService; 82 import com.android.bluetooth.btservice.ServiceFactory; 83 import com.android.bluetooth.btservice.storage.DatabaseManager; 84 import com.android.bluetooth.csip.CsipSetCoordinatorService; 85 import com.android.bluetooth.flags.Flags; 86 import com.android.bluetooth.hap.HapClientService; 87 import com.android.bluetooth.hfp.HeadsetService; 88 import com.android.bluetooth.mcp.McpService; 89 import com.android.bluetooth.tbs.TbsGatt; 90 import com.android.bluetooth.tbs.TbsService; 91 import com.android.bluetooth.vc.VolumeControlService; 92 import com.android.internal.annotations.GuardedBy; 93 import com.android.internal.annotations.VisibleForTesting; 94 95 import java.util.ArrayList; 96 import java.util.Arrays; 97 import java.util.Collections; 98 import java.util.LinkedHashMap; 99 import java.util.LinkedList; 100 import java.util.List; 101 import java.util.Map; 102 import java.util.Objects; 103 import java.util.Optional; 104 import java.util.Set; 105 import java.util.concurrent.locks.Lock; 106 import java.util.concurrent.locks.ReentrantLock; 107 import java.util.concurrent.locks.ReentrantReadWriteLock; 108 import java.util.stream.Collectors; 109 110 /** Provides Bluetooth LeAudio profile, as a service in the Bluetooth application. */ 111 public class LeAudioService extends ProfileService { 112 private static final String TAG = "LeAudioService"; 113 114 // Timeout for state machine thread join, to prevent potential ANR. 115 private static final int SM_THREAD_JOIN_TIMEOUT_MS = 1000; 116 117 private static LeAudioService sLeAudioService; 118 119 /** Indicates group audio support for none direction */ 120 private static final int AUDIO_DIRECTION_NONE = 0x00; 121 122 /** Indicates group audio support for output direction */ 123 private static final int AUDIO_DIRECTION_OUTPUT_BIT = 0x01; 124 125 /** Indicates group audio support for input direction */ 126 private static final int AUDIO_DIRECTION_INPUT_BIT = 0x02; 127 128 /** Indicates group is not active */ 129 private static final int ACTIVE_STATE_INACTIVE = 0x00; 130 131 /** Indicates group is going to be activeted */ 132 private static final int ACTIVE_STATE_GETTING_ACTIVE = 0x01; 133 134 /** Indicates group is active */ 135 private static final int ACTIVE_STATE_ACTIVE = 0x02; 136 137 /** This is used by application read-only for checking the fallback active group id. */ 138 public static final String BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_GROUP_ID = 139 "bluetooth_le_broadcast_fallback_active_group_id"; 140 141 /** 142 * Per PBP 1.0 4.3. High Quality Public Broadcast Audio, Broadcast HIGH quality audio configs 143 * are with sampling frequency 48khz 144 */ 145 private static final BluetoothLeAudioCodecConfig BROADCAST_HIGH_QUALITY_CONFIG = 146 new BluetoothLeAudioCodecConfig.Builder() 147 .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3) 148 .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000) 149 .build(); 150 151 /* 5 seconds timeout for Broadcast streaming state transition */ 152 private static final int DIALING_OUT_TIMEOUT_MS = 5000; 153 154 private AdapterService mAdapterService; 155 private DatabaseManager mDatabaseManager; 156 private HandlerThread mStateMachinesThread; 157 private volatile BluetoothDevice mActiveAudioOutDevice; 158 private volatile BluetoothDevice mActiveAudioInDevice; 159 private volatile BluetoothDevice mActiveBroadcastAudioDevice; 160 private BluetoothDevice mExposedActiveDevice; 161 private LeAudioCodecConfig mLeAudioCodecConfig; 162 private final ReentrantLock mGroupLock = new ReentrantLock(); 163 private final ReentrantReadWriteLock mGroupReadWriteLock = new ReentrantReadWriteLock(); 164 private final Lock mGroupReadLock = 165 leaudioApiSynchronizedBlockFix() ? mGroupReadWriteLock.readLock() : mGroupLock; 166 private final Lock mGroupWriteLock = 167 leaudioApiSynchronizedBlockFix() ? mGroupReadWriteLock.writeLock() : mGroupLock; 168 ServiceFactory mServiceFactory = new ServiceFactory(); 169 170 private final LeAudioNativeInterface mNativeInterface; 171 boolean mLeAudioNativeIsInitialized = false; 172 boolean mLeAudioInbandRingtoneSupportedByPlatform = true; 173 boolean mBluetoothEnabled = false; 174 175 /** 176 * During a call that has LE Audio -> HFP handover, the HFP device that is going to connect SCO 177 * after LE Audio group becomes idle 178 */ 179 BluetoothDevice mHfpHandoverDevice = null; 180 181 /** LE audio active device that was removed from active because of HFP handover */ 182 BluetoothDevice mLeAudioDeviceInactivatedForHfpHandover = null; 183 184 LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface = null; 185 private DialingOutTimeoutEvent mDialingOutTimeoutEvent = null; 186 @VisibleForTesting AudioManager mAudioManager; 187 LeAudioTmapGattServer mTmapGattServer; 188 int mTmapRoleMask; 189 int mUnicastGroupIdDeactivatedForBroadcastTransition = LE_AUDIO_GROUP_ID_INVALID; 190 int mCurrentAudioMode = AudioManager.MODE_NORMAL; 191 Optional<Integer> mBroadcastIdDeactivatedForUnicastTransition = Optional.empty(); 192 Optional<Boolean> mQueuedInCallValue = Optional.empty(); 193 boolean mTmapStarted = false; 194 private boolean mAwaitingBroadcastCreateResponse = false; 195 private final LinkedList<BluetoothLeBroadcastSettings> mCreateBroadcastQueue = 196 new LinkedList<>(); 197 boolean mIsSourceStreamMonitorModeEnabled = false; 198 199 @VisibleForTesting TbsService mTbsService; 200 201 @VisibleForTesting McpService mMcpService; 202 203 @VisibleForTesting VolumeControlService mVolumeControlService; 204 205 @VisibleForTesting HapClientService mHapClientService; 206 207 @VisibleForTesting CsipSetCoordinatorService mCsipSetCoordinatorService; 208 209 @VisibleForTesting BassClientService mBassClientService; 210 211 @VisibleForTesting RemoteCallbackList<IBluetoothLeBroadcastCallback> mBroadcastCallbacks; 212 213 @VisibleForTesting RemoteCallbackList<IBluetoothLeAudioCallback> mLeAudioCallbacks; 214 215 BluetoothLeScanner mAudioServersScanner; 216 /* When mScanCallback is not null, it means scan is started. */ 217 ScanCallback mScanCallback; 218 LeAudioService(Context ctx)219 public LeAudioService(Context ctx) { 220 this(ctx, LeAudioNativeInterface.getInstance()); 221 } 222 223 @VisibleForTesting LeAudioService(Context ctx, LeAudioNativeInterface nativeInterface)224 LeAudioService(Context ctx, LeAudioNativeInterface nativeInterface) { 225 super(ctx); 226 mNativeInterface = Objects.requireNonNull(nativeInterface); 227 } 228 229 private static class LeAudioGroupDescriptor { LeAudioGroupDescriptor(boolean isInbandRingtonEnabled)230 LeAudioGroupDescriptor(boolean isInbandRingtonEnabled) { 231 mIsConnected = false; 232 mActiveState = ACTIVE_STATE_INACTIVE; 233 mAllowedSinkContexts = BluetoothLeAudio.CONTEXTS_ALL; 234 mAllowedSourceContexts = BluetoothLeAudio.CONTEXTS_ALL; 235 mHasFallbackDeviceWhenGettingInactive = false; 236 mDirection = AUDIO_DIRECTION_NONE; 237 mCodecStatus = null; 238 mLostLeadDeviceWhileStreaming = null; 239 mCurrentLeadDevice = null; 240 mInbandRingtoneEnabled = isInbandRingtonEnabled; 241 mAvailableContexts = 0; 242 mInputSelectableConfig = new ArrayList<>(); 243 mOutputSelectableConfig = new ArrayList<>(); 244 mInactivatedDueToContextType = false; 245 } 246 247 Boolean mIsConnected; 248 Boolean mHasFallbackDeviceWhenGettingInactive; 249 Integer mDirection; 250 BluetoothLeAudioCodecStatus mCodecStatus; 251 /* This can be non empty only for the streaming time */ 252 BluetoothDevice mLostLeadDeviceWhileStreaming; 253 BluetoothDevice mCurrentLeadDevice; 254 Boolean mInbandRingtoneEnabled; 255 Integer mAvailableContexts; 256 List<BluetoothLeAudioCodecConfig> mInputSelectableConfig; 257 List<BluetoothLeAudioCodecConfig> mOutputSelectableConfig; 258 Boolean mInactivatedDueToContextType; 259 260 private Integer mActiveState; 261 private Integer mAllowedSinkContexts; 262 private Integer mAllowedSourceContexts; 263 isActive()264 boolean isActive() { 265 return mActiveState == ACTIVE_STATE_ACTIVE; 266 } 267 isInactive()268 boolean isInactive() { 269 return mActiveState == ACTIVE_STATE_INACTIVE; 270 } 271 isGettingActive()272 boolean isGettingActive() { 273 return mActiveState == ACTIVE_STATE_GETTING_ACTIVE; 274 } 275 setActiveState(int state)276 void setActiveState(int state) { 277 if ((state != ACTIVE_STATE_ACTIVE) 278 && (state != ACTIVE_STATE_INACTIVE) 279 && (state != ACTIVE_STATE_GETTING_ACTIVE)) { 280 Log.e(TAG, "LeAudioGroupDescriptor.setActiveState: Invalid state set: " + state); 281 return; 282 } 283 284 Log.d(TAG, "LeAudioGroupDescriptor.setActiveState: " + mActiveState + " -> " + state); 285 mActiveState = state; 286 } 287 getActiveStateString()288 String getActiveStateString() { 289 switch (mActiveState) { 290 case ACTIVE_STATE_ACTIVE: 291 return "ACTIVE_STATE_ACTIVE"; 292 case ACTIVE_STATE_INACTIVE: 293 return "ACTIVE_STATE_INACTIVE"; 294 case ACTIVE_STATE_GETTING_ACTIVE: 295 return "ACTIVE_STATE_GETTING_ACTIVE"; 296 default: 297 return "INVALID"; 298 } 299 } 300 updateAllowedContexts(Integer allowedSinkContexts, Integer allowedSourceContexts)301 void updateAllowedContexts(Integer allowedSinkContexts, Integer allowedSourceContexts) { 302 Log.d( 303 TAG, 304 "LeAudioGroupDescriptor.mAllowedSinkContexts: " 305 + mAllowedSinkContexts 306 + " -> " 307 + allowedSinkContexts 308 + ", LeAudioGroupDescriptor.mAllowedSourceContexts: " 309 + mAllowedSourceContexts 310 + " -> " 311 + allowedSourceContexts); 312 313 mAllowedSinkContexts = allowedSinkContexts; 314 mAllowedSourceContexts = allowedSourceContexts; 315 } 316 getAllowedSinkContexts()317 Integer getAllowedSinkContexts() { 318 return mAllowedSinkContexts; 319 } 320 getAllowedSourceContexts()321 Integer getAllowedSourceContexts() { 322 return mAllowedSourceContexts; 323 } 324 areAllowedContextsModified()325 boolean areAllowedContextsModified() { 326 return (mAllowedSinkContexts != BluetoothLeAudio.CONTEXTS_ALL) 327 || (mAllowedSourceContexts != BluetoothLeAudio.CONTEXTS_ALL); 328 } 329 } 330 331 private static class LeAudioDeviceDescriptor { LeAudioDeviceDescriptor(boolean isInbandRingtonEnabled)332 LeAudioDeviceDescriptor(boolean isInbandRingtonEnabled) { 333 mAclConnected = false; 334 mStateMachine = null; 335 mGroupId = LE_AUDIO_GROUP_ID_INVALID; 336 mSinkAudioLocation = BluetoothLeAudio.AUDIO_LOCATION_INVALID; 337 mDirection = AUDIO_DIRECTION_NONE; 338 mDevInbandRingtoneEnabled = isInbandRingtonEnabled; 339 } 340 341 public boolean mAclConnected; 342 public LeAudioStateMachine mStateMachine; 343 public Integer mGroupId; 344 public Integer mSinkAudioLocation; 345 public Integer mDirection; 346 Boolean mDevInbandRingtoneEnabled; 347 } 348 349 private static class LeAudioBroadcastDescriptor { LeAudioBroadcastDescriptor()350 LeAudioBroadcastDescriptor() { 351 mState = LeAudioStackEvent.BROADCAST_STATE_STOPPED; 352 mMetadata = null; 353 mRequestedForDetails = false; 354 } 355 356 public Integer mState; 357 public BluetoothLeBroadcastMetadata mMetadata; 358 public Boolean mRequestedForDetails; 359 } 360 361 List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>(); 362 List<BluetoothLeAudioCodecConfig> mOutputLocalCodecCapabilities = new ArrayList<>(); 363 364 @GuardedBy("mGroupWriteLock") 365 private final Map<Integer, LeAudioGroupDescriptor> mGroupDescriptors = new LinkedHashMap<>(); 366 367 @GuardedBy("mGroupReadLock") 368 private final Map<Integer, LeAudioGroupDescriptor> mGroupDescriptorsView = 369 Collections.unmodifiableMap(mGroupDescriptors); 370 371 private final Map<BluetoothDevice, LeAudioDeviceDescriptor> mDeviceDescriptors = 372 new LinkedHashMap<>(); 373 private final Map<Integer, LeAudioBroadcastDescriptor> mBroadcastDescriptors = 374 new LinkedHashMap<>(); 375 376 private Handler mHandler = new Handler(Looper.getMainLooper()); 377 private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback = 378 new AudioManagerAudioDeviceCallback(); 379 private final AudioModeChangeListener mAudioModeChangeListener = new AudioModeChangeListener(); 380 381 @Override initBinder()382 protected IProfileServiceBinder initBinder() { 383 return new BluetoothLeAudioBinder(this); 384 } 385 isEnabled()386 public static boolean isEnabled() { 387 return BluetoothProperties.isProfileBapUnicastClientEnabled().orElse(false); 388 } 389 isBroadcastEnabled()390 public static boolean isBroadcastEnabled() { 391 return leaudioBroadcastFeatureSupport() 392 && BluetoothProperties.isProfileBapBroadcastSourceEnabled().orElse(false); 393 } 394 registerTmap()395 private boolean registerTmap() { 396 if (mTmapGattServer != null) { 397 throw new IllegalStateException("TMAP GATT server started before start() is called"); 398 } 399 mTmapGattServer = LeAudioObjectsFactory.getInstance().getTmapGattServer(this); 400 401 try { 402 mTmapGattServer.start(mTmapRoleMask); 403 } catch (IllegalStateException e) { 404 Log.e(TAG, "Fail to start TmapGattServer", e); 405 mTmapGattServer = null; 406 return false; 407 } 408 409 return true; 410 } 411 412 @Override start()413 public void start() { 414 Log.i(TAG, "start()"); 415 if (sLeAudioService != null) { 416 throw new IllegalStateException("start() called twice"); 417 } 418 419 mAdapterService = 420 Objects.requireNonNull( 421 AdapterService.getAdapterService(), 422 "AdapterService cannot be null when LeAudioService starts"); 423 mDatabaseManager = 424 Objects.requireNonNull( 425 mAdapterService.getDatabase(), 426 "DatabaseManager cannot be null when LeAudioService starts"); 427 428 mAudioManager = getSystemService(AudioManager.class); 429 Objects.requireNonNull( 430 mAudioManager, "AudioManager cannot be null when LeAudioService starts"); 431 432 // Start handler thread for state machines 433 mStateMachinesThread = new HandlerThread("LeAudioService.StateMachines"); 434 mStateMachinesThread.start(); 435 436 mBroadcastDescriptors.clear(); 437 438 mGroupWriteLock.lock(); 439 try { 440 mDeviceDescriptors.clear(); 441 mGroupDescriptors.clear(); 442 } finally { 443 mGroupWriteLock.unlock(); 444 } 445 446 // Setup broadcast callbacks 447 mLeAudioCallbacks = new RemoteCallbackList<IBluetoothLeAudioCallback>(); 448 449 mTmapRoleMask = 450 LeAudioTmapGattServer.TMAP_ROLE_FLAG_CG | LeAudioTmapGattServer.TMAP_ROLE_FLAG_UMS; 451 452 // Initialize Broadcast native interface 453 if ((mAdapterService.getSupportedProfilesBitMask() 454 & (1 << BluetoothProfile.LE_AUDIO_BROADCAST)) 455 != 0) { 456 Log.i(TAG, "Init Le Audio broadcaster"); 457 mBroadcastCallbacks = new RemoteCallbackList<IBluetoothLeBroadcastCallback>(); 458 mLeAudioBroadcasterNativeInterface = 459 Objects.requireNonNull( 460 LeAudioBroadcasterNativeInterface.getInstance(), 461 "LeAudioBroadcasterNativeInterface cannot be null when LeAudioService" 462 + " starts"); 463 mLeAudioBroadcasterNativeInterface.init(); 464 mTmapRoleMask |= LeAudioTmapGattServer.TMAP_ROLE_FLAG_BMS; 465 } else { 466 Log.w(TAG, "Le Audio Broadcasts not supported."); 467 } 468 469 mTmapStarted = registerTmap(); 470 471 mLeAudioInbandRingtoneSupportedByPlatform = 472 BluetoothProperties.isLeAudioInbandRingtoneSupported().orElse(true); 473 474 mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler); 475 476 // Mark service as started 477 setLeAudioService(this); 478 479 // Setup codec config 480 mLeAudioCodecConfig = new LeAudioCodecConfig(this); 481 if (!Flags.leaudioSynchronizeStart()) { 482 // Delay the call to init by posting it. This ensures TBS and MCS are fully initialized 483 // before we start accepting connections 484 mHandler.post(this::init); 485 return; 486 } 487 mNativeInterface.init(mLeAudioCodecConfig.getCodecConfigOffloading()); 488 489 if (leaudioUseAudioModeListener()) { 490 mAudioManager.addOnModeChangedListener(getMainExecutor(), mAudioModeChangeListener); 491 } 492 } 493 494 // TODO: b/341385684 -- Delete the init method as it has been inlined in start init()495 private void init() { 496 if (!isAvailable()) { 497 Log.e(TAG, " Service disabled before init"); 498 return; 499 } 500 501 if (!mTmapStarted) { 502 mTmapStarted = registerTmap(); 503 } 504 505 mNativeInterface.init(mLeAudioCodecConfig.getCodecConfigOffloading()); 506 507 if (leaudioUseAudioModeListener()) { 508 mAudioManager.addOnModeChangedListener(getMainExecutor(), mAudioModeChangeListener); 509 } 510 } 511 512 @Override stop()513 public void stop() { 514 Log.i(TAG, "stop()"); 515 if (sLeAudioService == null) { 516 Log.w(TAG, "stop() called before start()"); 517 return; 518 } 519 520 mQueuedInCallValue = Optional.empty(); 521 if (leaudioUseAudioModeListener()) { 522 mAudioManager.removeOnModeChangedListener(mAudioModeChangeListener); 523 } 524 525 mCreateBroadcastQueue.clear(); 526 mAwaitingBroadcastCreateResponse = false; 527 mIsSourceStreamMonitorModeEnabled = false; 528 529 clearBroadcastTimeoutCallback(); 530 531 if (!Flags.leaudioSynchronizeStart()) { 532 mHandler.removeCallbacks(this::init); 533 } 534 removeActiveDevice(false); 535 536 if (mTmapGattServer == null) { 537 Log.w(TAG, "TMAP GATT server should never be null before stop() is called"); 538 } else { 539 mTmapGattServer.stop(); 540 mTmapGattServer = null; 541 mTmapStarted = false; 542 } 543 544 stopAudioServersBackgroundScan(); 545 mAudioServersScanner = null; 546 547 // Don't wait for async call with INACTIVE group status, clean active 548 // device for active group. 549 mGroupReadLock.lock(); 550 try { 551 try { 552 for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : 553 mGroupDescriptorsView.entrySet()) { 554 LeAudioGroupDescriptor descriptor = entry.getValue(); 555 Integer groupId = entry.getKey(); 556 if (descriptor.isActive()) { 557 descriptor.setActiveState(ACTIVE_STATE_INACTIVE); 558 updateActiveDevices( 559 groupId, 560 descriptor.mDirection, 561 AUDIO_DIRECTION_NONE, 562 false, 563 false, 564 false); 565 break; 566 } 567 } 568 569 // Destroy state machines and stop handler thread 570 for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) { 571 LeAudioStateMachine sm = descriptor.mStateMachine; 572 if (sm == null) { 573 continue; 574 } 575 sm.quit(); 576 sm.cleanup(); 577 } 578 } finally { 579 if (Flags.leaudioApiSynchronizedBlockFix()) { 580 // Upgrade to write lock 581 mGroupReadLock.unlock(); 582 mGroupWriteLock.lock(); 583 } 584 } 585 mDeviceDescriptors.clear(); 586 mGroupDescriptors.clear(); 587 } finally { 588 mGroupWriteLock.unlock(); 589 } 590 591 // Cleanup native interfaces 592 mNativeInterface.cleanup(); 593 mLeAudioNativeIsInitialized = false; 594 mBluetoothEnabled = false; 595 mHfpHandoverDevice = null; 596 mLeAudioDeviceInactivatedForHfpHandover = null; 597 598 mActiveAudioOutDevice = null; 599 mActiveAudioInDevice = null; 600 mExposedActiveDevice = null; 601 mLeAudioCodecConfig = null; 602 603 // Set the service and BLE devices as inactive 604 setLeAudioService(null); 605 606 // Unregister broadcast callbacks 607 if (mBroadcastCallbacks != null) { 608 mBroadcastCallbacks.kill(); 609 } 610 611 if (mLeAudioCallbacks != null) { 612 mLeAudioCallbacks.kill(); 613 } 614 615 mBroadcastDescriptors.clear(); 616 617 if (mLeAudioBroadcasterNativeInterface != null) { 618 mLeAudioBroadcasterNativeInterface.cleanup(); 619 mLeAudioBroadcasterNativeInterface = null; 620 } 621 622 if (mStateMachinesThread != null) { 623 try { 624 mStateMachinesThread.quitSafely(); 625 mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); 626 mStateMachinesThread = null; 627 } catch (InterruptedException e) { 628 // Do not rethrow as we are shutting down anyway 629 } 630 } 631 632 mAudioManager.unregisterAudioDeviceCallback(mAudioManagerAudioDeviceCallback); 633 634 mAdapterService = null; 635 mAudioManager = null; 636 mMcpService = null; 637 mTbsService = null; 638 mVolumeControlService = null; 639 mCsipSetCoordinatorService = null; 640 mBassClientService = null; 641 } 642 643 @Override cleanup()644 public void cleanup() { 645 Log.i(TAG, "cleanup()"); 646 } 647 getLeAudioService()648 public static synchronized LeAudioService getLeAudioService() { 649 if (sLeAudioService == null) { 650 Log.w(TAG, "getLeAudioService(): service is NULL"); 651 return null; 652 } 653 if (!sLeAudioService.isAvailable()) { 654 Log.w(TAG, "getLeAudioService(): service is not available"); 655 return null; 656 } 657 return sLeAudioService; 658 } 659 660 @VisibleForTesting setLeAudioService(LeAudioService instance)661 static synchronized void setLeAudioService(LeAudioService instance) { 662 Log.d(TAG, "setLeAudioService(): set to: " + instance); 663 sLeAudioService = instance; 664 } 665 getVolumeControlService()666 VolumeControlService getVolumeControlService() { 667 if (mVolumeControlService == null) { 668 mVolumeControlService = mServiceFactory.getVolumeControlService(); 669 if (mVolumeControlService == null) { 670 Log.e(TAG, "Volume control service is not available"); 671 } 672 } 673 return mVolumeControlService; 674 } 675 getBassClientService()676 BassClientService getBassClientService() { 677 if (mBassClientService == null) { 678 mBassClientService = mServiceFactory.getBassClientService(); 679 if (mBassClientService == null) { 680 Log.e(TAG, "BASS service is not available"); 681 } 682 } 683 return mBassClientService; 684 } 685 686 @VisibleForTesting getAudioDeviceGroupVolume(int groupId)687 int getAudioDeviceGroupVolume(int groupId) { 688 VolumeControlService volumeControlService = getVolumeControlService(); 689 if (volumeControlService == null) { 690 return IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME; 691 } 692 return volumeControlService.getAudioDeviceGroupVolume(groupId); 693 } 694 createDeviceDescriptor( BluetoothDevice device, boolean isInbandRingtoneEnabled)695 LeAudioDeviceDescriptor createDeviceDescriptor( 696 BluetoothDevice device, boolean isInbandRingtoneEnabled) { 697 LeAudioDeviceDescriptor descriptor = mDeviceDescriptors.get(device); 698 if (descriptor == null) { 699 mDeviceDescriptors.put(device, new LeAudioDeviceDescriptor(isInbandRingtoneEnabled)); 700 descriptor = mDeviceDescriptors.get(device); 701 Log.d(TAG, "Created descriptor for device: " + device); 702 } else { 703 Log.w(TAG, "Device: " + device + ", already exists"); 704 } 705 706 return descriptor; 707 } 708 setEnabledState(BluetoothDevice device, boolean enabled)709 private void setEnabledState(BluetoothDevice device, boolean enabled) { 710 Log.d(TAG, "setEnabledState: address:" + device + " enabled: " + enabled); 711 if (!mLeAudioNativeIsInitialized) { 712 Log.e(TAG, "setEnabledState, mLeAudioNativeIsInitialized is not initialized"); 713 return; 714 } 715 mNativeInterface.setEnableState(device, enabled); 716 } 717 connect(BluetoothDevice device)718 public boolean connect(BluetoothDevice device) { 719 Log.d(TAG, "connect(): " + device); 720 721 if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 722 Log.e(TAG, "Cannot connect to " + device + " : CONNECTION_POLICY_FORBIDDEN"); 723 return false; 724 } 725 ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 726 if (!Utils.arrayContains(featureUuids, BluetoothUuid.LE_AUDIO)) { 727 Log.e(TAG, "Cannot connect to " + device + " : Remote does not have LE_AUDIO UUID"); 728 return false; 729 } 730 731 LeAudioStateMachine sm = null; 732 733 mGroupWriteLock.lock(); 734 try { 735 boolean isInbandRingtoneEnabled = false; 736 int groupId = getGroupId(device); 737 if (groupId != LE_AUDIO_GROUP_ID_INVALID) { 738 isInbandRingtoneEnabled = getGroupDescriptor(groupId).mInbandRingtoneEnabled; 739 } 740 741 if (createDeviceDescriptor(device, isInbandRingtoneEnabled) == null) { 742 return false; 743 } 744 745 sm = getOrCreateStateMachine(device); 746 if (sm == null) { 747 Log.e(TAG, "Ignored connect request for " + device + " : no state machine"); 748 return false; 749 } 750 751 if (!Flags.leaudioApiSynchronizedBlockFix()) { 752 sm.sendMessage(LeAudioStateMachine.CONNECT); 753 } 754 755 } finally { 756 mGroupWriteLock.unlock(); 757 } 758 759 if (Flags.leaudioApiSynchronizedBlockFix()) { 760 sm.sendMessage(LeAudioStateMachine.CONNECT); 761 } 762 763 return true; 764 } 765 766 /** 767 * Disconnects LE Audio for the remote bluetooth device 768 * 769 * @param device is the device with which we would like to disconnect LE Audio 770 * @return true if profile disconnected, false if device not connected over LE Audio 771 */ disconnectV2(BluetoothDevice device)772 boolean disconnectV2(BluetoothDevice device) { 773 Log.d(TAG, "disconnectV2(): " + device); 774 775 LeAudioStateMachine sm = null; 776 777 mGroupReadLock.lock(); 778 try { 779 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 780 if (descriptor == null) { 781 Log.e(TAG, "disconnect: No valid descriptor for device: " + device); 782 return false; 783 } 784 sm = descriptor.mStateMachine; 785 } finally { 786 mGroupReadLock.unlock(); 787 } 788 789 if (sm == null) { 790 Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine"); 791 return false; 792 } 793 794 sm.sendMessage(LeAudioStateMachine.DISCONNECT); 795 796 return true; 797 } 798 799 /** 800 * Disconnects LE Audio for the remote bluetooth device 801 * 802 * @param device is the device with which we would like to disconnect LE Audio 803 * @return true if profile disconnected, false if device not connected over LE Audio 804 */ disconnect(BluetoothDevice device)805 public boolean disconnect(BluetoothDevice device) { 806 if (Flags.leaudioApiSynchronizedBlockFix()) { 807 return disconnectV2(device); 808 } 809 810 Log.d(TAG, "disconnect(): " + device); 811 812 mGroupReadLock.lock(); 813 try { 814 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 815 if (descriptor == null) { 816 Log.e(TAG, "disconnect: No valid descriptor for device: " + device); 817 return false; 818 } 819 820 LeAudioStateMachine sm = descriptor.mStateMachine; 821 if (sm == null) { 822 Log.e(TAG, "Ignored disconnect request for " + device + " : no state machine"); 823 return false; 824 } 825 826 sm.sendMessage(LeAudioStateMachine.DISCONNECT); 827 } finally { 828 mGroupReadLock.unlock(); 829 } 830 831 return true; 832 } 833 getConnectedDevices()834 public List<BluetoothDevice> getConnectedDevices() { 835 mGroupReadLock.lock(); 836 try { 837 List<BluetoothDevice> devices = new ArrayList<>(); 838 for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) { 839 LeAudioStateMachine sm = descriptor.mStateMachine; 840 if (sm != null && sm.isConnected()) { 841 devices.add(sm.getDevice()); 842 } 843 } 844 return devices; 845 } finally { 846 mGroupReadLock.unlock(); 847 } 848 } 849 getConnectedGroupLeadDevice(int groupId)850 BluetoothDevice getConnectedGroupLeadDevice(int groupId) { 851 if (getGroupId(mActiveAudioOutDevice) == groupId) { 852 return mActiveAudioOutDevice; 853 } 854 855 return getLeadDeviceForTheGroup(groupId); 856 } 857 getDevicesMatchingConnectionStates(int[] states)858 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 859 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 860 if (states == null) { 861 return devices; 862 } 863 final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 864 if (bondedDevices == null) { 865 return devices; 866 } 867 mGroupReadLock.lock(); 868 try { 869 for (BluetoothDevice device : bondedDevices) { 870 final ParcelUuid[] featureUuids = device.getUuids(); 871 if (!Utils.arrayContains(featureUuids, BluetoothUuid.LE_AUDIO)) { 872 continue; 873 } 874 int connectionState = BluetoothProfile.STATE_DISCONNECTED; 875 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 876 if (descriptor == null) { 877 Log.e( 878 TAG, 879 "getDevicesMatchingConnectionStates: " 880 + "No valid descriptor for device: " 881 + device); 882 return null; 883 } 884 885 LeAudioStateMachine sm = descriptor.mStateMachine; 886 if (sm != null) { 887 connectionState = sm.getConnectionState(); 888 } 889 for (int state : states) { 890 if (connectionState == state) { 891 devices.add(device); 892 break; 893 } 894 } 895 } 896 return devices; 897 } finally { 898 mGroupReadLock.unlock(); 899 } 900 } 901 902 /** 903 * Get the list of devices that have state machines. 904 * 905 * @return the list of devices that have state machines 906 */ 907 @VisibleForTesting getDevices()908 List<BluetoothDevice> getDevices() { 909 List<BluetoothDevice> devices = new ArrayList<>(); 910 mGroupReadLock.lock(); 911 try { 912 for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) { 913 if (descriptor.mStateMachine != null) { 914 devices.add(descriptor.mStateMachine.getDevice()); 915 } 916 } 917 return devices; 918 } finally { 919 mGroupReadLock.unlock(); 920 } 921 } 922 923 /** 924 * Get the current connection state of the profile 925 * 926 * @param device is the remote bluetooth device 927 * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected, {@link 928 * BluetoothProfile#STATE_CONNECTING} if this profile is being connected, {@link 929 * BluetoothProfile#STATE_CONNECTED} if this profile is connected, or {@link 930 * BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected 931 */ getConnectionState(BluetoothDevice device)932 public int getConnectionState(BluetoothDevice device) { 933 mGroupReadLock.lock(); 934 try { 935 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 936 if (descriptor == null) { 937 return BluetoothProfile.STATE_DISCONNECTED; 938 } 939 940 LeAudioStateMachine sm = descriptor.mStateMachine; 941 if (sm == null) { 942 return BluetoothProfile.STATE_DISCONNECTED; 943 } 944 return sm.getConnectionState(); 945 } finally { 946 mGroupReadLock.unlock(); 947 } 948 } 949 950 /** 951 * Add device to the given group. 952 * 953 * @param groupId group ID the device is being added to 954 * @param device the active device 955 * @return true on success, otherwise false 956 */ groupAddNode(int groupId, BluetoothDevice device)957 boolean groupAddNode(int groupId, BluetoothDevice device) { 958 if (!mLeAudioNativeIsInitialized) { 959 Log.e(TAG, "Le Audio not initialized properly."); 960 return false; 961 } 962 return mNativeInterface.groupAddNode(groupId, device); 963 } 964 965 /** 966 * Remove device from a given group. 967 * 968 * @param groupId group ID the device is being removed from 969 * @param device the active device 970 * @return true on success, otherwise false 971 */ groupRemoveNode(int groupId, BluetoothDevice device)972 boolean groupRemoveNode(int groupId, BluetoothDevice device) { 973 if (!mLeAudioNativeIsInitialized) { 974 Log.e(TAG, "Le Audio not initialized properly."); 975 return false; 976 } 977 return mNativeInterface.groupRemoveNode(groupId, device); 978 } 979 980 /** 981 * Checks if given group exists. 982 * 983 * @param groupId group Id to verify 984 * @return true given group exists, otherwise false 985 */ isValidDeviceGroup(int groupId)986 public boolean isValidDeviceGroup(int groupId) { 987 mGroupReadLock.lock(); 988 try { 989 return groupId != LE_AUDIO_GROUP_ID_INVALID 990 && mGroupDescriptorsView.containsKey(groupId); 991 } finally { 992 mGroupReadLock.unlock(); 993 } 994 } 995 996 /** 997 * Get all the devices within a given group. 998 * 999 * @param groupId group id to get devices 1000 * @return all devices within a given group or empty list 1001 */ getGroupDevices(int groupId)1002 public List<BluetoothDevice> getGroupDevices(int groupId) { 1003 List<BluetoothDevice> result = new ArrayList<>(); 1004 1005 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 1006 return result; 1007 } 1008 1009 mGroupReadLock.lock(); 1010 try { 1011 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry : 1012 mDeviceDescriptors.entrySet()) { 1013 if (entry.getValue().mGroupId == groupId) { 1014 result.add(entry.getKey()); 1015 } 1016 } 1017 } finally { 1018 mGroupReadLock.unlock(); 1019 } 1020 return result; 1021 } 1022 1023 /** 1024 * Get all the devices within a given group. 1025 * 1026 * @param device the device for which we want to get all devices in its group 1027 * @return all devices within a given group or empty list 1028 */ getGroupDevices(BluetoothDevice device)1029 public List<BluetoothDevice> getGroupDevices(BluetoothDevice device) { 1030 List<BluetoothDevice> result = new ArrayList<>(); 1031 int groupId = getGroupId(device); 1032 1033 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 1034 return result; 1035 } 1036 1037 mGroupReadLock.lock(); 1038 try { 1039 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry : 1040 mDeviceDescriptors.entrySet()) { 1041 if (entry.getValue().mGroupId == groupId) { 1042 result.add(entry.getKey()); 1043 } 1044 } 1045 } finally { 1046 mGroupReadLock.unlock(); 1047 } 1048 return result; 1049 } 1050 1051 /** Get the active device group id */ getActiveGroupId()1052 public Integer getActiveGroupId() { 1053 mGroupReadLock.lock(); 1054 try { 1055 for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : 1056 mGroupDescriptorsView.entrySet()) { 1057 LeAudioGroupDescriptor descriptor = entry.getValue(); 1058 if (descriptor.isActive()) { 1059 return entry.getKey(); 1060 } 1061 } 1062 } finally { 1063 mGroupReadLock.unlock(); 1064 } 1065 return LE_AUDIO_GROUP_ID_INVALID; 1066 } 1067 1068 /** 1069 * Creates LeAudio Broadcast instance with BluetoothLeBroadcastSettings. 1070 * 1071 * @param broadcastSettings broadcast settings for this broadcast source 1072 */ createBroadcast(BluetoothLeBroadcastSettings broadcastSettings)1073 public void createBroadcast(BluetoothLeBroadcastSettings broadcastSettings) { 1074 if (mBroadcastDescriptors.size() >= getMaximumNumberOfBroadcasts()) { 1075 Log.w( 1076 TAG, 1077 "createBroadcast reached maximum allowed broadcasts number: " 1078 + getMaximumNumberOfBroadcasts()); 1079 mHandler.post( 1080 () -> 1081 notifyBroadcastStartFailed( 1082 BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES)); 1083 return; 1084 } 1085 1086 if (mLeAudioBroadcasterNativeInterface == null) { 1087 Log.w(TAG, "Native interface not available."); 1088 return; 1089 } 1090 1091 if (mAwaitingBroadcastCreateResponse) { 1092 mCreateBroadcastQueue.add(broadcastSettings); 1093 Log.i(TAG, "Broadcast creation queued due to waiting for a previous request response."); 1094 return; 1095 } 1096 1097 if (!areAllGroupsInNotActiveState()) { 1098 /* Broadcast would be created once unicast group became inactive */ 1099 Log.i( 1100 TAG, 1101 "Unicast group is active, queueing Broadcast creation, while the Unicast" 1102 + " group is deactivated."); 1103 mCreateBroadcastQueue.add(broadcastSettings); 1104 if (Flags.leaudioBroadcastAudioHandoverPolicies()) { 1105 mNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SINK, true); 1106 } 1107 removeActiveDevice(true); 1108 1109 return; 1110 } 1111 1112 byte[] broadcastCode = broadcastSettings.getBroadcastCode(); 1113 boolean isEncrypted = (broadcastCode != null) && (broadcastCode.length != 0); 1114 if (isEncrypted) { 1115 if ((broadcastCode.length > 16) || (broadcastCode.length < 4)) { 1116 Log.e(TAG, "Invalid broadcast code length. Should be from 4 to 16 octets long."); 1117 return; 1118 } 1119 } 1120 1121 List<BluetoothLeBroadcastSubgroupSettings> settingsList = 1122 broadcastSettings.getSubgroupSettings(); 1123 if (settingsList == null || settingsList.size() < 1) { 1124 Log.d(TAG, "subgroup settings is not valid value"); 1125 return; 1126 } 1127 1128 BluetoothLeAudioContentMetadata publicMetadata = 1129 broadcastSettings.getPublicBroadcastMetadata(); 1130 1131 Log.i(TAG, "createBroadcast: isEncrypted=" + (isEncrypted ? "true" : "false")); 1132 1133 mAwaitingBroadcastCreateResponse = true; 1134 mLeAudioBroadcasterNativeInterface.createBroadcast( 1135 broadcastSettings.isPublicBroadcast(), 1136 broadcastSettings.getBroadcastName(), 1137 broadcastCode, 1138 publicMetadata == null ? null : publicMetadata.getRawMetadata(), 1139 getBroadcastAudioQualityPerSinkCapabilities(settingsList), 1140 settingsList.stream() 1141 .map(s -> s.getContentMetadata().getRawMetadata()) 1142 .toArray(byte[][]::new)); 1143 } 1144 getBroadcastAudioQualityPerSinkCapabilities( List<BluetoothLeBroadcastSubgroupSettings> settingsList)1145 private int[] getBroadcastAudioQualityPerSinkCapabilities( 1146 List<BluetoothLeBroadcastSubgroupSettings> settingsList) { 1147 int[] preferredQualityArray = 1148 settingsList.stream().mapToInt(s -> s.getPreferredQuality()).toArray(); 1149 1150 BassClientService bassClientService = getBassClientService(); 1151 if (bassClientService == null) { 1152 return preferredQualityArray; 1153 } 1154 1155 for (BluetoothDevice sink : bassClientService.getConnectedDevices()) { 1156 int groupId = getGroupId(sink); 1157 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 1158 continue; 1159 } 1160 1161 BluetoothLeAudioCodecStatus codecStatus = getCodecStatus(groupId); 1162 if (codecStatus != null 1163 && !codecStatus.isInputCodecConfigSelectable(BROADCAST_HIGH_QUALITY_CONFIG)) { 1164 // If any sink device does not support high quality audio config, 1165 // set all subgroup audio quality to standard quality for now before multi codec 1166 // config support is ready 1167 Log.i( 1168 TAG, 1169 "Sink device doesn't support HIGH broadcast audio quality, use STANDARD" 1170 + " quality"); 1171 Arrays.fill( 1172 preferredQualityArray, 1173 BluetoothLeBroadcastSubgroupSettings.QUALITY_STANDARD); 1174 break; 1175 } 1176 } 1177 return preferredQualityArray; 1178 } 1179 1180 /** 1181 * Start LeAudio Broadcast instance. 1182 * 1183 * @param broadcastId broadcast instance identifier 1184 */ startBroadcast(int broadcastId)1185 public void startBroadcast(int broadcastId) { 1186 if (mLeAudioBroadcasterNativeInterface == null) { 1187 Log.w(TAG, "Native interface not available."); 1188 return; 1189 } 1190 1191 Log.d(TAG, "startBroadcast"); 1192 1193 /* Start timeout to recover from stucked/error start Broadcast operation */ 1194 mDialingOutTimeoutEvent = new DialingOutTimeoutEvent(broadcastId); 1195 mHandler.postDelayed(mDialingOutTimeoutEvent, DIALING_OUT_TIMEOUT_MS); 1196 1197 mLeAudioBroadcasterNativeInterface.startBroadcast(broadcastId); 1198 } 1199 1200 /** 1201 * Updates LeAudio broadcast instance metadata. 1202 * 1203 * @param broadcastId broadcast instance identifier 1204 * @param broadcastSettings broadcast settings for this broadcast source 1205 */ updateBroadcast(int broadcastId, BluetoothLeBroadcastSettings broadcastSettings)1206 public void updateBroadcast(int broadcastId, BluetoothLeBroadcastSettings broadcastSettings) { 1207 if (mLeAudioBroadcasterNativeInterface == null) { 1208 Log.w(TAG, "Native interface not available."); 1209 return; 1210 } 1211 1212 LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); 1213 if (descriptor == null) { 1214 mHandler.post( 1215 () -> 1216 notifyBroadcastUpdateFailed( 1217 broadcastId, 1218 BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID)); 1219 Log.e(TAG, "updateBroadcast: No valid descriptor for broadcastId: " + broadcastId); 1220 return; 1221 } 1222 1223 List<BluetoothLeBroadcastSubgroupSettings> settingsList = 1224 broadcastSettings.getSubgroupSettings(); 1225 if (settingsList == null || settingsList.size() < 1) { 1226 Log.d(TAG, "subgroup settings is not valid value"); 1227 return; 1228 } 1229 1230 BluetoothLeAudioContentMetadata publicMetadata = 1231 broadcastSettings.getPublicBroadcastMetadata(); 1232 1233 Log.d(TAG, "updateBroadcast"); 1234 mLeAudioBroadcasterNativeInterface.updateMetadata( 1235 broadcastId, 1236 broadcastSettings.getBroadcastName(), 1237 publicMetadata == null ? null : publicMetadata.getRawMetadata(), 1238 settingsList.stream() 1239 .map(s -> s.getContentMetadata().getRawMetadata()) 1240 .toArray(byte[][]::new)); 1241 } 1242 1243 /** 1244 * Pause LeAudio Broadcast instance. 1245 * 1246 * @param broadcastId broadcast instance identifier 1247 */ pauseBroadcast(Integer broadcastId)1248 public void pauseBroadcast(Integer broadcastId) { 1249 if (mLeAudioBroadcasterNativeInterface == null) { 1250 Log.w(TAG, "Native interface not available."); 1251 return; 1252 } 1253 1254 LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); 1255 if (descriptor == null) { 1256 Log.e(TAG, "pauseBroadcast: No valid descriptor for broadcastId: " + broadcastId); 1257 return; 1258 } 1259 1260 if (Flags.leaudioBroadcastAssistantPeripheralEntrustment()) { 1261 if (!isPlaying(broadcastId)) { 1262 Log.d(TAG, "pauseBroadcast: Broadcast is not playing, skip pause request"); 1263 return; 1264 } 1265 1266 // Due to broadcast pause sinks may lose synchronization 1267 BassClientService bassClientService = getBassClientService(); 1268 if (bassClientService != null) { 1269 bassClientService.cacheSuspendingSources(broadcastId); 1270 } 1271 } 1272 1273 Log.d(TAG, "pauseBroadcast"); 1274 mLeAudioBroadcasterNativeInterface.pauseBroadcast(broadcastId); 1275 } 1276 1277 /** 1278 * Stop LeAudio Broadcast instance. 1279 * 1280 * @param broadcastId broadcast instance identifier 1281 */ stopBroadcast(Integer broadcastId)1282 public void stopBroadcast(Integer broadcastId) { 1283 if (mLeAudioBroadcasterNativeInterface == null) { 1284 Log.w(TAG, "Native interface not available."); 1285 return; 1286 } 1287 1288 LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); 1289 if (descriptor == null) { 1290 mHandler.post( 1291 () -> 1292 notifyOnBroadcastStopFailed( 1293 BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID)); 1294 Log.e(TAG, "stopBroadcast: No valid descriptor for broadcastId: " + broadcastId); 1295 return; 1296 } 1297 1298 Log.d(TAG, "stopBroadcast"); 1299 mLeAudioBroadcasterNativeInterface.stopBroadcast(broadcastId); 1300 } 1301 1302 /** 1303 * Destroy LeAudio Broadcast instance. 1304 * 1305 * @param broadcastId broadcast instance identifier 1306 */ destroyBroadcast(int broadcastId)1307 public void destroyBroadcast(int broadcastId) { 1308 if (mLeAudioBroadcasterNativeInterface == null) { 1309 Log.w(TAG, "Native interface not available."); 1310 return; 1311 } 1312 1313 LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); 1314 if (descriptor == null) { 1315 mHandler.post( 1316 () -> 1317 notifyOnBroadcastStopFailed( 1318 BluetoothStatusCodes.ERROR_LE_BROADCAST_INVALID_BROADCAST_ID)); 1319 Log.e(TAG, "destroyBroadcast: No valid descriptor for broadcastId: " + broadcastId); 1320 return; 1321 } 1322 1323 Log.d(TAG, "destroyBroadcast"); 1324 if (Flags.leaudioBroadcastAudioHandoverPolicies()) { 1325 mNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SINK, false); 1326 } 1327 mLeAudioBroadcasterNativeInterface.destroyBroadcast(broadcastId); 1328 } 1329 1330 /** 1331 * Checks if Broadcast instance is playing. 1332 * 1333 * @param broadcastId broadcast instance identifier 1334 * @return true if if broadcast is playing, false otherwise 1335 */ isPlaying(int broadcastId)1336 public boolean isPlaying(int broadcastId) { 1337 LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); 1338 if (descriptor == null) { 1339 Log.e(TAG, "isPlaying: No valid descriptor for broadcastId: " + broadcastId); 1340 return false; 1341 } 1342 1343 return descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING); 1344 } 1345 1346 /** 1347 * Get all broadcast metadata. 1348 * 1349 * @return list of all know Broadcast metadata 1350 */ getAllBroadcastMetadata()1351 public List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata() { 1352 return mBroadcastDescriptors.values().stream() 1353 .map(s -> s.mMetadata) 1354 .collect(Collectors.toList()); 1355 } 1356 1357 /** 1358 * Check if broadcast is active 1359 * 1360 * @return true if there is active broadcast, false otherwise 1361 */ isBroadcastActive()1362 public boolean isBroadcastActive() { 1363 return !mBroadcastDescriptors.isEmpty(); 1364 } 1365 1366 /** 1367 * Get the maximum number of supported simultaneous broadcasts. 1368 * 1369 * @return number of supported simultaneous broadcasts 1370 */ getMaximumNumberOfBroadcasts()1371 public int getMaximumNumberOfBroadcasts() { 1372 /* TODO: This is currently fixed to 1 */ 1373 return 1; 1374 } 1375 1376 /** 1377 * Get the maximum number of supported streams per broadcast. 1378 * 1379 * @return number of supported streams per broadcast 1380 */ getMaximumStreamsPerBroadcast()1381 public int getMaximumStreamsPerBroadcast() { 1382 /* TODO: This is currently fixed to 1 */ 1383 return 1; 1384 } 1385 1386 /** 1387 * Get the maximum number of supported subgroups per broadcast. 1388 * 1389 * @return number of supported subgroups per broadcast 1390 */ getMaximumSubgroupsPerBroadcast()1391 public int getMaximumSubgroupsPerBroadcast() { 1392 /* TODO: This is currently fixed to 1 */ 1393 return 1; 1394 } 1395 1396 /** Active Broadcast Assistant notification handler */ activeBroadcastAssistantNotification(boolean active)1397 public void activeBroadcastAssistantNotification(boolean active) { 1398 if (getBassClientService() == null) { 1399 Log.w(TAG, "Ignore active Broadcast Assistant notification"); 1400 return; 1401 } 1402 1403 if (active) { 1404 mIsSourceStreamMonitorModeEnabled = true; 1405 mNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SOURCE, true); 1406 } else { 1407 if (mIsSourceStreamMonitorModeEnabled) { 1408 mNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SOURCE, false); 1409 } 1410 1411 mIsSourceStreamMonitorModeEnabled = false; 1412 } 1413 } 1414 1415 /** Return true if device is primary - is active or was active before switch to broadcast */ isPrimaryDevice(BluetoothDevice device)1416 public boolean isPrimaryDevice(BluetoothDevice device) { 1417 LeAudioDeviceDescriptor descriptor = mDeviceDescriptors.get(device); 1418 if (descriptor == null) { 1419 return false; 1420 } 1421 1422 return descriptor.mGroupId == mUnicastGroupIdDeactivatedForBroadcastTransition; 1423 } 1424 areBroadcastsAllStopped()1425 private boolean areBroadcastsAllStopped() { 1426 if (mBroadcastDescriptors == null) { 1427 Log.e(TAG, "areBroadcastsAllStopped: Invalid Broadcast Descriptors"); 1428 return false; 1429 } 1430 1431 return mBroadcastDescriptors.values().stream() 1432 .allMatch(d -> d.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STOPPED)); 1433 } 1434 getFirstNotStoppedBroadcastId()1435 private Optional<Integer> getFirstNotStoppedBroadcastId() { 1436 if (mBroadcastDescriptors == null) { 1437 Log.e(TAG, "getFirstNotStoppedBroadcastId: Invalid Broadcast Descriptors"); 1438 return Optional.empty(); 1439 } 1440 1441 for (Map.Entry<Integer, LeAudioBroadcastDescriptor> entry : 1442 mBroadcastDescriptors.entrySet()) { 1443 if (!entry.getValue().mState.equals(LeAudioStackEvent.BROADCAST_STATE_STOPPED)) { 1444 return Optional.of(entry.getKey()); 1445 } 1446 } 1447 1448 return Optional.empty(); 1449 } 1450 areAllGroupsInNotActiveState()1451 private boolean areAllGroupsInNotActiveState() { 1452 mGroupReadLock.lock(); 1453 try { 1454 for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : 1455 mGroupDescriptorsView.entrySet()) { 1456 LeAudioGroupDescriptor descriptor = entry.getValue(); 1457 if (!descriptor.isInactive()) { 1458 return false; 1459 } 1460 } 1461 } finally { 1462 mGroupReadLock.unlock(); 1463 } 1464 return true; 1465 } 1466 areAllGroupsInNotGettingActiveState()1467 private boolean areAllGroupsInNotGettingActiveState() { 1468 mGroupReadLock.lock(); 1469 try { 1470 for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : 1471 mGroupDescriptorsView.entrySet()) { 1472 LeAudioGroupDescriptor descriptor = entry.getValue(); 1473 if (descriptor.isGettingActive()) { 1474 return false; 1475 } 1476 } 1477 } finally { 1478 mGroupReadLock.unlock(); 1479 } 1480 return true; 1481 } 1482 getFirstGroupIdInGettingActiveState()1483 private Integer getFirstGroupIdInGettingActiveState() { 1484 mGroupReadLock.lock(); 1485 try { 1486 for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : 1487 mGroupDescriptorsView.entrySet()) { 1488 LeAudioGroupDescriptor descriptor = entry.getValue(); 1489 if (descriptor.isGettingActive()) { 1490 return entry.getKey(); 1491 } 1492 } 1493 } finally { 1494 mGroupReadLock.unlock(); 1495 } 1496 return LE_AUDIO_GROUP_ID_INVALID; 1497 } 1498 getLeadDeviceForTheGroup(Integer groupId)1499 private BluetoothDevice getLeadDeviceForTheGroup(Integer groupId) { 1500 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 1501 return null; 1502 } 1503 mGroupReadLock.lock(); 1504 try { 1505 LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); 1506 if (groupDescriptor == null) { 1507 Log.e(TAG, "Group " + groupId + " does not exist"); 1508 return null; 1509 } 1510 1511 if (groupDescriptor.mCurrentLeadDevice != null 1512 && getConnectionState(groupDescriptor.mCurrentLeadDevice) 1513 == BluetoothProfile.STATE_CONNECTED) { 1514 return groupDescriptor.mCurrentLeadDevice; 1515 } 1516 1517 for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) { 1518 if (!descriptor.mGroupId.equals(groupId)) { 1519 continue; 1520 } 1521 1522 LeAudioStateMachine sm = descriptor.mStateMachine; 1523 if (sm == null || sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { 1524 continue; 1525 } 1526 groupDescriptor.mCurrentLeadDevice = sm.getDevice(); 1527 return groupDescriptor.mCurrentLeadDevice; 1528 } 1529 } finally { 1530 mGroupReadLock.unlock(); 1531 } 1532 return null; 1533 } 1534 updateActiveInDevice( BluetoothDevice device, Integer groupId, Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections)1535 private boolean updateActiveInDevice( 1536 BluetoothDevice device, 1537 Integer groupId, 1538 Integer oldSupportedAudioDirections, 1539 Integer newSupportedAudioDirections) { 1540 boolean oldSupportedByDeviceInput = 1541 (oldSupportedAudioDirections & AUDIO_DIRECTION_INPUT_BIT) != 0; 1542 boolean newSupportedByDeviceInput = 1543 (newSupportedAudioDirections & AUDIO_DIRECTION_INPUT_BIT) != 0; 1544 1545 /* 1546 * Do not update input if neither previous nor current device support input 1547 */ 1548 if (!oldSupportedByDeviceInput && !newSupportedByDeviceInput) { 1549 Log.d(TAG, "updateActiveInDevice: Device does not support input."); 1550 return false; 1551 } 1552 1553 if (device != null && mActiveAudioInDevice != null) { 1554 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 1555 if (deviceDescriptor == null) { 1556 Log.e(TAG, "updateActiveInDevice: No valid descriptor for device: " + device); 1557 return false; 1558 } 1559 1560 if (deviceDescriptor.mGroupId.equals(groupId)) { 1561 /* This is thes same group as aleady notified to the system. 1562 * Therefore do not change the device we have connected to the group, 1563 * unless, previous one is disconnected now 1564 */ 1565 if (mActiveAudioInDevice.isConnected()) { 1566 device = mActiveAudioInDevice; 1567 } 1568 } else if (deviceDescriptor.mGroupId != LE_AUDIO_GROUP_ID_INVALID) { 1569 /* Mark old group as no active */ 1570 LeAudioGroupDescriptor descriptor = getGroupDescriptor(deviceDescriptor.mGroupId); 1571 if (descriptor != null) { 1572 descriptor.setActiveState(ACTIVE_STATE_INACTIVE); 1573 } 1574 } 1575 } 1576 1577 BluetoothDevice previousInDevice = mActiveAudioInDevice; 1578 1579 /* 1580 * Update input if: 1581 * - Device changed 1582 * OR 1583 * - Device stops / starts supporting input 1584 */ 1585 if (!Objects.equals(device, previousInDevice) 1586 || (oldSupportedByDeviceInput != newSupportedByDeviceInput)) { 1587 mActiveAudioInDevice = newSupportedByDeviceInput ? device : null; 1588 Log.d( 1589 TAG, 1590 " handleBluetoothActiveDeviceChanged previousInDevice: " 1591 + previousInDevice 1592 + ", mActiveAudioInDevice: " 1593 + mActiveAudioInDevice 1594 + " isLeOutput: false"); 1595 1596 return true; 1597 } 1598 Log.d(TAG, "updateActiveInDevice: Nothing to do."); 1599 return false; 1600 } 1601 updateActiveOutDevice( BluetoothDevice device, Integer groupId, Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections)1602 private boolean updateActiveOutDevice( 1603 BluetoothDevice device, 1604 Integer groupId, 1605 Integer oldSupportedAudioDirections, 1606 Integer newSupportedAudioDirections) { 1607 boolean oldSupportedByDeviceOutput = 1608 (oldSupportedAudioDirections & AUDIO_DIRECTION_OUTPUT_BIT) != 0; 1609 boolean newSupportedByDeviceOutput = 1610 (newSupportedAudioDirections & AUDIO_DIRECTION_OUTPUT_BIT) != 0; 1611 1612 /* 1613 * Do not update output if neither previous nor current device support output 1614 */ 1615 if (!oldSupportedByDeviceOutput && !newSupportedByDeviceOutput) { 1616 Log.d(TAG, "updateActiveOutDevice: Device does not support output."); 1617 return false; 1618 } 1619 1620 if (device != null && mActiveAudioOutDevice != null) { 1621 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 1622 if (deviceDescriptor == null) { 1623 Log.e(TAG, "updateActiveOutDevice: No valid descriptor for device: " + device); 1624 return false; 1625 } 1626 1627 if (deviceDescriptor.mGroupId.equals(groupId)) { 1628 /* This is the same group as already notified to the system. 1629 * Therefore do not change the device we have connected to the group, 1630 * unless, previous one is disconnected now 1631 */ 1632 if (mActiveAudioOutDevice.isConnected()) { 1633 device = mActiveAudioOutDevice; 1634 } 1635 } else if (deviceDescriptor.mGroupId != LE_AUDIO_GROUP_ID_INVALID) { 1636 Log.i( 1637 TAG, 1638 " Switching active group from " 1639 + deviceDescriptor.mGroupId 1640 + " to " 1641 + groupId); 1642 /* Mark old group as no active */ 1643 LeAudioGroupDescriptor descriptor = getGroupDescriptor(deviceDescriptor.mGroupId); 1644 if (descriptor != null) { 1645 descriptor.setActiveState(ACTIVE_STATE_INACTIVE); 1646 } 1647 } 1648 } 1649 1650 BluetoothDevice previousOutDevice = mActiveAudioOutDevice; 1651 1652 /* 1653 * Update output if: 1654 * - Device changed 1655 * OR 1656 * - Device stops / starts supporting output 1657 */ 1658 if (!Objects.equals(device, previousOutDevice) 1659 || (oldSupportedByDeviceOutput != newSupportedByDeviceOutput)) { 1660 mActiveAudioOutDevice = newSupportedByDeviceOutput ? device : null; 1661 Log.d( 1662 TAG, 1663 " handleBluetoothActiveDeviceChanged previousOutDevice: " 1664 + previousOutDevice 1665 + ", mActiveAudioOutDevice: " 1666 + mActiveAudioOutDevice 1667 + " isLeOutput: true"); 1668 return true; 1669 } 1670 Log.d(TAG, "updateActiveOutDevice: Nothing to do."); 1671 return false; 1672 } 1673 1674 /** 1675 * Send broadcast intent about LeAudio connection state changed. This is called by 1676 * LeAudioStateMachine. 1677 */ notifyConnectionStateChanged(BluetoothDevice device, int newState, int prevState)1678 void notifyConnectionStateChanged(BluetoothDevice device, int newState, int prevState) { 1679 Log.d( 1680 TAG, 1681 "Notify connection state changed." 1682 + device 1683 + "(" 1684 + prevState 1685 + " -> " 1686 + newState 1687 + ")"); 1688 1689 mAdapterService.notifyProfileConnectionStateChangeToGatt( 1690 BluetoothProfile.LE_AUDIO, prevState, newState); 1691 mAdapterService.handleProfileConnectionStateChange( 1692 BluetoothProfile.LE_AUDIO, device, prevState, newState); 1693 mAdapterService 1694 .getActiveDeviceManager() 1695 .profileConnectionStateChanged( 1696 BluetoothProfile.LE_AUDIO, device, prevState, newState); 1697 mAdapterService.updateProfileConnectionAdapterProperties( 1698 device, BluetoothProfile.LE_AUDIO, newState, prevState); 1699 1700 Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); 1701 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 1702 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 1703 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1704 intent.addFlags( 1705 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1706 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1707 sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 1708 } 1709 sentActiveDeviceChangeIntent(BluetoothDevice device)1710 void sentActiveDeviceChangeIntent(BluetoothDevice device) { 1711 Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); 1712 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1713 intent.addFlags( 1714 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1715 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1716 sendBroadcast(intent, BLUETOOTH_CONNECT); 1717 } 1718 notifyVolumeControlServiceAboutActiveGroup(BluetoothDevice device)1719 void notifyVolumeControlServiceAboutActiveGroup(BluetoothDevice device) { 1720 VolumeControlService volumeControlService = getVolumeControlService(); 1721 if (volumeControlService == null) { 1722 return; 1723 } 1724 1725 if (mExposedActiveDevice != null) { 1726 volumeControlService.setGroupActive(getGroupId(mExposedActiveDevice), false); 1727 } 1728 1729 if (device != null) { 1730 volumeControlService.setGroupActive(getGroupId(device), true); 1731 } 1732 } 1733 1734 /** 1735 * Send broadcast intent about LeAudio active device. This is called when AudioManager confirms, 1736 * LeAudio device is added or removed. 1737 */ 1738 @VisibleForTesting notifyActiveDeviceChanged(BluetoothDevice device)1739 void notifyActiveDeviceChanged(BluetoothDevice device) { 1740 Log.d( 1741 TAG, 1742 "Notify Active device changed." 1743 + device 1744 + ". Currently active device is " 1745 + mActiveAudioOutDevice); 1746 1747 mAdapterService.handleActiveDeviceChange(BluetoothProfile.LE_AUDIO, device); 1748 sentActiveDeviceChangeIntent(device); 1749 notifyVolumeControlServiceAboutActiveGroup(device); 1750 mExposedActiveDevice = device; 1751 } 1752 isScannerNeeded()1753 boolean isScannerNeeded() { 1754 if (mDeviceDescriptors.isEmpty() || !mBluetoothEnabled) { 1755 Log.d(TAG, "isScannerNeeded: false, mBluetoothEnabled: " + mBluetoothEnabled); 1756 return false; 1757 } 1758 1759 if (allLeAudioDevicesConnected()) { 1760 Log.d(TAG, "isScannerNeeded: all devices connected, scanner not needed"); 1761 return false; 1762 } 1763 1764 Log.d(TAG, "isScannerNeeded: true"); 1765 return true; 1766 } 1767 allLeAudioDevicesConnected()1768 boolean allLeAudioDevicesConnected() { 1769 mGroupReadLock.lock(); 1770 try { 1771 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> deviceEntry : 1772 mDeviceDescriptors.entrySet()) { 1773 LeAudioDeviceDescriptor deviceDescriptor = deviceEntry.getValue(); 1774 1775 if (deviceDescriptor.mStateMachine == null) { 1776 /* Lack of state machine means device is not connected */ 1777 return false; 1778 } 1779 1780 if (!deviceDescriptor.mStateMachine.isConnected() 1781 || !deviceDescriptor.mAclConnected) { 1782 return false; 1783 } 1784 } 1785 } finally { 1786 mGroupReadLock.unlock(); 1787 } 1788 return true; 1789 } 1790 1791 private class AudioServerScanCallback extends ScanCallback { 1792 int mMaxScanRetires = 10; 1793 int mScanRetries = 0; 1794 1795 @Override onScanResult(int callbackType, ScanResult result)1796 public void onScanResult(int callbackType, ScanResult result) { 1797 /* Filter is set in the way, that there will be no results found. 1798 * We just need a scanner to be running for the APCF filtering defined in native 1799 */ 1800 } 1801 1802 @Override onBatchScanResults(List<ScanResult> results)1803 public void onBatchScanResults(List<ScanResult> results) { 1804 /* Filter is set in the way, that there will be no results found. 1805 * We just need a scanner to be running for the APCF filtering defined in native 1806 */ 1807 } 1808 1809 @Override onScanFailed(int errorCode)1810 public void onScanFailed(int errorCode) { 1811 Log.w(TAG, "Scan failed err: " + errorCode + " scan retries: " + mScanRetries); 1812 switch (errorCode) { 1813 case SCAN_FAILED_INTERNAL_ERROR: 1814 case SCAN_FAILED_APPLICATION_REGISTRATION_FAILED: 1815 if (mScanRetries < mMaxScanRetires) { 1816 mScanRetries++; 1817 Log.w(TAG, "Failed to start. Let's retry"); 1818 mHandler.post(() -> startAudioServersBackgroundScan(/* retry= */ true)); 1819 } 1820 break; 1821 default: 1822 /* Indicate scan is no running */ 1823 mScanCallback = null; 1824 break; 1825 } 1826 } 1827 } 1828 1829 @VisibleForTesting handleAudioDeviceAdded( BluetoothDevice device, int type, boolean isSink, boolean isSource)1830 boolean handleAudioDeviceAdded( 1831 BluetoothDevice device, int type, boolean isSink, boolean isSource) { 1832 Log.d( 1833 TAG, 1834 (" handleAudioDeviceAdded: " + device) 1835 + (", device type: " + type) 1836 + (", isSink: " + isSink) 1837 + (" isSource: " + isSource)); 1838 1839 /* Don't expose already exposed active device */ 1840 if (device.equals(mExposedActiveDevice)) { 1841 Log.d(TAG, " onAudioDevicesAdded: " + device + " is already exposed"); 1842 return true; 1843 } 1844 1845 if ((isSink && !device.equals(mActiveAudioOutDevice)) 1846 || (isSource && !device.equals(mActiveAudioInDevice))) { 1847 Log.e( 1848 TAG, 1849 "Added device does not match to the one activated here. (" 1850 + (device 1851 + " != " 1852 + mActiveAudioOutDevice 1853 + " / " 1854 + mActiveAudioInDevice 1855 + ")")); 1856 return false; 1857 } 1858 1859 notifyActiveDeviceChanged(device); 1860 return true; 1861 } 1862 1863 @VisibleForTesting handleAudioDeviceRemoved( BluetoothDevice device, int type, boolean isSink, boolean isSource)1864 void handleAudioDeviceRemoved( 1865 BluetoothDevice device, int type, boolean isSink, boolean isSource) { 1866 Log.d( 1867 TAG, 1868 (" handleAudioDeviceRemoved: " + device) 1869 + (", device type: " + type) 1870 + (", isSink: " + isSink) 1871 + (" isSource: " + isSource) 1872 + (", mActiveAudioInDevice: " + mActiveAudioInDevice) 1873 + (", mActiveAudioOutDevice: " + mActiveAudioOutDevice)); 1874 1875 if (!device.equals(mExposedActiveDevice)) { 1876 return; 1877 } 1878 1879 if ((isSource && mActiveAudioInDevice == null) 1880 || (isSink && mActiveAudioOutDevice == null)) { 1881 Log.d(TAG, "Expecting device removal"); 1882 if (mActiveAudioInDevice == null && mActiveAudioOutDevice == null) { 1883 mExposedActiveDevice = null; 1884 } 1885 return; 1886 } 1887 1888 Log.i(TAG, "Audio manager disactivate LeAudio device " + mExposedActiveDevice); 1889 mExposedActiveDevice = null; 1890 setActiveDevice(null); 1891 } 1892 1893 /* Notifications of audio device connection/disconn events. */ 1894 private class AudioManagerAudioDeviceCallback extends AudioDeviceCallback { 1895 @Override onAudioDevicesAdded(AudioDeviceInfo[] addedDevices)1896 public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) { 1897 if (mAudioManager == null || mAdapterService == null) { 1898 Log.e(TAG, "Callback called when LeAudioService is stopped"); 1899 return; 1900 } 1901 1902 for (AudioDeviceInfo deviceInfo : addedDevices) { 1903 if ((deviceInfo.getType() != AudioDeviceInfo.TYPE_BLE_HEADSET) 1904 && (deviceInfo.getType() != AudioDeviceInfo.TYPE_BLE_SPEAKER)) { 1905 continue; 1906 } 1907 1908 String address = deviceInfo.getAddress(); 1909 if (address.equals("00:00:00:00:00:00")) { 1910 continue; 1911 } 1912 1913 byte[] addressBytes = Utils.getBytesFromAddress(address); 1914 BluetoothDevice device = mAdapterService.getDeviceFromByte(addressBytes); 1915 1916 if (handleAudioDeviceAdded( 1917 device, deviceInfo.getType(), deviceInfo.isSink(), deviceInfo.isSource())) { 1918 return; 1919 } 1920 } 1921 } 1922 1923 @Override onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices)1924 public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) { 1925 if (mAudioManager == null || mAdapterService == null) { 1926 Log.e(TAG, "Callback called when LeAudioService is stopped"); 1927 return; 1928 } 1929 1930 for (AudioDeviceInfo deviceInfo : removedDevices) { 1931 if ((deviceInfo.getType() != AudioDeviceInfo.TYPE_BLE_HEADSET) 1932 && (deviceInfo.getType() != AudioDeviceInfo.TYPE_BLE_SPEAKER)) { 1933 continue; 1934 } 1935 1936 String address = deviceInfo.getAddress(); 1937 if (address.equals("00:00:00:00:00:00")) { 1938 continue; 1939 } 1940 1941 byte[] addressBytes = Utils.getBytesFromAddress(address); 1942 BluetoothDevice device = mAdapterService.getDeviceFromByte(addressBytes); 1943 1944 handleAudioDeviceRemoved( 1945 device, deviceInfo.getType(), deviceInfo.isSink(), deviceInfo.isSource()); 1946 } 1947 } 1948 } 1949 1950 /* 1951 * Report the active broadcast device change to the active device manager and the media 1952 * framework. 1953 * @param newDevice new supported broadcast audio device 1954 * @param previousDevice previous no longer supported broadcast audio device 1955 */ updateBroadcastActiveDevice( BluetoothDevice newDevice, BluetoothDevice previousDevice, boolean suppressNoisyIntent)1956 private void updateBroadcastActiveDevice( 1957 BluetoothDevice newDevice, 1958 BluetoothDevice previousDevice, 1959 boolean suppressNoisyIntent) { 1960 mActiveBroadcastAudioDevice = newDevice; 1961 Log.d( 1962 TAG, 1963 "updateBroadcastActiveDevice: newDevice: " 1964 + newDevice 1965 + ", previousDevice: " 1966 + previousDevice); 1967 mAudioManager.handleBluetoothActiveDeviceChanged( 1968 newDevice, previousDevice, getBroadcastProfile(suppressNoisyIntent)); 1969 } 1970 1971 /* 1972 * Listen mode is set when broadcast is queued, waiting for create response notification or 1973 * descriptor was created - idicate that create notification was received. 1974 */ wasSetSinkListeningMode()1975 private boolean wasSetSinkListeningMode() { 1976 return !mCreateBroadcastQueue.isEmpty() 1977 || mAwaitingBroadcastCreateResponse 1978 || !mBroadcastDescriptors.isEmpty(); 1979 } 1980 1981 /** 1982 * Report the active devices change to the active device manager and the media framework. 1983 * 1984 * @param groupId id of group which devices should be updated 1985 * @param newSupportedAudioDirections new supported audio directions for group of devices 1986 * @param oldSupportedAudioDirections old supported audio directions for group of devices 1987 * @param isActive if there is new active group 1988 * @param hasFallbackDevice whether any fallback device exists when deactivating the current 1989 * active device. 1990 * @param notifyAndUpdateInactiveOutDeviceOnly if only output device should be updated to 1991 * inactive devices (if new out device would be null device). 1992 * @return true if group is active after change false otherwise. 1993 */ updateActiveDevices( Integer groupId, Integer oldSupportedAudioDirections, Integer newSupportedAudioDirections, boolean isActive, boolean hasFallbackDevice, boolean notifyAndUpdateInactiveOutDeviceOnly)1994 private boolean updateActiveDevices( 1995 Integer groupId, 1996 Integer oldSupportedAudioDirections, 1997 Integer newSupportedAudioDirections, 1998 boolean isActive, 1999 boolean hasFallbackDevice, 2000 boolean notifyAndUpdateInactiveOutDeviceOnly) { 2001 BluetoothDevice newOutDevice = null; 2002 BluetoothDevice newInDevice = null; 2003 BluetoothDevice previousActiveOutDevice = mActiveAudioOutDevice; 2004 BluetoothDevice previousActiveInDevice = mActiveAudioInDevice; 2005 2006 if (isActive) { 2007 newOutDevice = getLeadDeviceForTheGroup(groupId); 2008 newInDevice = newOutDevice; 2009 } else { 2010 /* While broadcasting a input device needs to be connected to track Audio Framework 2011 * streaming requests. This would allow native to make a fallback to Unicast decision. 2012 */ 2013 if (notifyAndUpdateInactiveOutDeviceOnly 2014 && ((newSupportedAudioDirections & AUDIO_DIRECTION_INPUT_BIT) != 0)) { 2015 newInDevice = getLeadDeviceForTheGroup(groupId); 2016 } else if (Flags.leaudioBroadcastAudioHandoverPolicies() && wasSetSinkListeningMode()) { 2017 mNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SINK, false); 2018 } 2019 } 2020 2021 boolean isNewActiveOutDevice = 2022 updateActiveOutDevice( 2023 newOutDevice, 2024 groupId, 2025 oldSupportedAudioDirections, 2026 newSupportedAudioDirections); 2027 boolean isNewActiveInDevice = 2028 updateActiveInDevice( 2029 newInDevice, 2030 groupId, 2031 oldSupportedAudioDirections, 2032 newSupportedAudioDirections); 2033 2034 Log.d( 2035 TAG, 2036 " isNewActiveOutDevice: " 2037 + isNewActiveOutDevice 2038 + ", " 2039 + mActiveAudioOutDevice 2040 + ", isNewActiveInDevice: " 2041 + isNewActiveInDevice 2042 + ", " 2043 + mActiveAudioInDevice 2044 + ", notifyAndUpdateInactiveOutDeviceOnly: " 2045 + notifyAndUpdateInactiveOutDeviceOnly); 2046 2047 if (isNewActiveOutDevice) { 2048 int volume = IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME; 2049 2050 if (mActiveAudioOutDevice != null) { 2051 volume = getAudioDeviceGroupVolume(groupId); 2052 } 2053 2054 final boolean suppressNoisyIntent = hasFallbackDevice || mActiveAudioOutDevice != null; 2055 2056 Log.d( 2057 TAG, 2058 "suppressNoisyIntent: " 2059 + suppressNoisyIntent 2060 + ", hasFallbackDevice: " 2061 + hasFallbackDevice); 2062 final BluetoothProfileConnectionInfo connectionInfo; 2063 if (isAtLeastU()) { 2064 connectionInfo = 2065 BluetoothProfileConnectionInfo.createLeAudioOutputInfo( 2066 suppressNoisyIntent, volume); 2067 } else { 2068 connectionInfo = 2069 BluetoothProfileConnectionInfo.createLeAudioInfo(suppressNoisyIntent, true); 2070 } 2071 mAudioManager.handleBluetoothActiveDeviceChanged( 2072 mActiveAudioOutDevice, previousActiveOutDevice, connectionInfo); 2073 } 2074 2075 if (isNewActiveInDevice) { 2076 mAudioManager.handleBluetoothActiveDeviceChanged( 2077 mActiveAudioInDevice, 2078 previousActiveInDevice, 2079 BluetoothProfileConnectionInfo.createLeAudioInfo(false, false)); 2080 } 2081 2082 if ((mActiveAudioOutDevice == null) 2083 && (notifyAndUpdateInactiveOutDeviceOnly || (mActiveAudioInDevice == null))) { 2084 /* Notify about inactive device as soon as possible. 2085 * When adding new device, wait with notification until AudioManager is ready 2086 * with adding the device. 2087 */ 2088 notifyActiveDeviceChanged(null); 2089 } 2090 2091 return mActiveAudioOutDevice != null || mActiveAudioInDevice != null; 2092 } 2093 clearInactiveDueToContextTypeFlags()2094 private void clearInactiveDueToContextTypeFlags() { 2095 mGroupReadLock.lock(); 2096 try { 2097 for (Map.Entry<Integer, LeAudioGroupDescriptor> groupEntry : 2098 mGroupDescriptorsView.entrySet()) { 2099 LeAudioGroupDescriptor groupDescriptor = groupEntry.getValue(); 2100 if (groupDescriptor.mInactivatedDueToContextType) { 2101 Log.d(TAG, "clearInactiveDueToContextTypeFlags " + groupEntry.getKey()); 2102 groupDescriptor.mInactivatedDueToContextType = false; 2103 } 2104 } 2105 } finally { 2106 mGroupReadLock.unlock(); 2107 } 2108 } 2109 2110 /** 2111 * Set the active device group. 2112 * 2113 * @param hasFallbackDevice hasFallbackDevice whether any fallback device exists when {@code 2114 * device} is null. 2115 */ setActiveGroupWithDevice(BluetoothDevice device, boolean hasFallbackDevice)2116 private boolean setActiveGroupWithDevice(BluetoothDevice device, boolean hasFallbackDevice) { 2117 int groupId = LE_AUDIO_GROUP_ID_INVALID; 2118 2119 if (device != null) { 2120 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 2121 if (descriptor == null) { 2122 Log.e(TAG, "setActiveGroupWithDevice: No valid descriptor for device: " + device); 2123 return false; 2124 } 2125 2126 groupId = descriptor.mGroupId; 2127 2128 if (!isGroupAvailableForStream(groupId)) { 2129 Log.e( 2130 TAG, 2131 "setActiveGroupWithDevice: groupId " 2132 + groupId 2133 + " is not available for streaming"); 2134 return false; 2135 } 2136 2137 clearInactiveDueToContextTypeFlags(); 2138 } 2139 2140 int currentlyActiveGroupId = getActiveGroupId(); 2141 Log.d( 2142 TAG, 2143 "setActiveGroupWithDevice = " 2144 + groupId 2145 + ", currentlyActiveGroupId = " 2146 + currentlyActiveGroupId 2147 + ", device: " 2148 + device 2149 + ", hasFallbackDevice: " 2150 + hasFallbackDevice 2151 + ", mExposedActiveDevice: " 2152 + mExposedActiveDevice); 2153 2154 if (isBroadcastActive() 2155 && currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID 2156 && mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID 2157 && groupId != LE_AUDIO_GROUP_ID_INVALID) { 2158 // If broadcast is ongoing and need to update unicast fallback active group 2159 // we need to update the cached group id and skip changing the active device 2160 updateFallbackUnicastGroupIdForBroadcast(groupId); 2161 return true; 2162 } 2163 2164 LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(currentlyActiveGroupId); 2165 if (groupDescriptor != null && groupId == currentlyActiveGroupId) { 2166 /* Make sure active group is already exposed to audio framework. 2167 * If not, lets wait for it and don't sent additional intent. 2168 */ 2169 if (groupDescriptor.mCurrentLeadDevice == mExposedActiveDevice) { 2170 Log.w( 2171 TAG, 2172 "group is already active: device=" 2173 + device 2174 + ", groupId = " 2175 + groupId 2176 + ", exposedDevice: " 2177 + mExposedActiveDevice); 2178 sentActiveDeviceChangeIntent(mExposedActiveDevice); 2179 } 2180 return true; 2181 } 2182 2183 if (currentlyActiveGroupId != LE_AUDIO_GROUP_ID_INVALID 2184 && (groupId != LE_AUDIO_GROUP_ID_INVALID || hasFallbackDevice)) { 2185 Log.i(TAG, "Remember that device has FallbackDevice when become inactive active"); 2186 groupDescriptor.mHasFallbackDeviceWhenGettingInactive = true; 2187 } 2188 2189 if (!mLeAudioNativeIsInitialized) { 2190 Log.e(TAG, "Le Audio not initialized properly."); 2191 return false; 2192 } 2193 2194 if (Flags.leaudioGettingActiveStateSupport()) { 2195 mGroupReadLock.lock(); 2196 try { 2197 LeAudioGroupDescriptor descriptor = mGroupDescriptorsView.get(groupId); 2198 if (descriptor != null) { 2199 descriptor.setActiveState(ACTIVE_STATE_GETTING_ACTIVE); 2200 } 2201 } finally { 2202 mGroupReadLock.unlock(); 2203 } 2204 } 2205 2206 mNativeInterface.groupSetActive(groupId); 2207 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 2208 /* Native will clear its states and send us group Inactive. 2209 * However we would like to notify audio framework that LeAudio is not 2210 * active anymore and does not want to get more audio data. 2211 */ 2212 handleGroupTransitToInactive(currentlyActiveGroupId); 2213 } 2214 return true; 2215 } 2216 2217 /** 2218 * Remove the current active group. 2219 * 2220 * @param hasFallbackDevice whether any fallback device exists when deactivating the current 2221 * active device. 2222 * @return true on success, otherwise false 2223 */ removeActiveDevice(boolean hasFallbackDevice)2224 public boolean removeActiveDevice(boolean hasFallbackDevice) { 2225 /* Clear active group */ 2226 Log.d(TAG, "removeActiveDevice, hasFallbackDevice " + hasFallbackDevice); 2227 setActiveGroupWithDevice(null, hasFallbackDevice); 2228 return true; 2229 } 2230 2231 /** 2232 * Set the active group represented by device. 2233 * 2234 * @param device the new active device. Should not be null. 2235 * @return true on success, otherwise false 2236 */ setActiveDevice(BluetoothDevice device)2237 public boolean setActiveDevice(BluetoothDevice device) { 2238 Log.i( 2239 TAG, 2240 "setActiveDevice: device=" 2241 + device 2242 + ", current out=" 2243 + mActiveAudioOutDevice 2244 + ", current in=" 2245 + mActiveAudioInDevice); 2246 /* Clear active group */ 2247 if (device == null) { 2248 Log.e(TAG, "device should not be null!"); 2249 return removeActiveDevice(false); 2250 } 2251 if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { 2252 Log.e( 2253 TAG, 2254 "setActiveDevice(" 2255 + device 2256 + "): failed because group device is not " 2257 + "connected"); 2258 return false; 2259 } 2260 2261 if (!Flags.audioRoutingCentralization()) { 2262 // If AUDIO_ROUTING_CENTRALIZATION, this will be checked inside AudioRoutingManager. 2263 if (Utils.isDualModeAudioEnabled()) { 2264 if (!mAdapterService.isAllSupportedClassicAudioProfilesActive(device)) { 2265 Log.e( 2266 TAG, 2267 "setActiveDevice(" 2268 + device 2269 + "): failed because the device is not " 2270 + "active for all supported classic audio profiles"); 2271 return false; 2272 } 2273 } 2274 } 2275 return setActiveGroupWithDevice(device, false); 2276 } 2277 2278 /** 2279 * Get the active LE audio devices. 2280 * 2281 * <p>Note: When LE audio group is active, one of the Bluetooth device address which belongs to 2282 * the group, represents the active LE audio group - it is called Lead device. Internally, this 2283 * address is translated to LE audio group id. 2284 * 2285 * @return List of active group members. First element is a Lead device. 2286 */ getActiveDevices()2287 public List<BluetoothDevice> getActiveDevices() { 2288 Log.d(TAG, "getActiveDevices"); 2289 ArrayList<BluetoothDevice> activeDevices = new ArrayList<>(2); 2290 activeDevices.add(null); 2291 activeDevices.add(null); 2292 2293 int currentlyActiveGroupId = getActiveGroupId(); 2294 if (currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID) { 2295 return activeDevices; 2296 } 2297 2298 BluetoothDevice leadDevice = getConnectedGroupLeadDevice(currentlyActiveGroupId); 2299 activeDevices.set(0, leadDevice); 2300 2301 int i = 1; 2302 for (BluetoothDevice dev : getGroupDevices(currentlyActiveGroupId)) { 2303 if (Objects.equals(dev, leadDevice)) { 2304 continue; 2305 } 2306 if (i == 1) { 2307 /* Already has a spot for first member */ 2308 activeDevices.set(i++, dev); 2309 } else { 2310 /* Extend list with other members */ 2311 activeDevices.add(dev); 2312 } 2313 } 2314 return activeDevices; 2315 } 2316 connectSet(BluetoothDevice device)2317 void connectSet(BluetoothDevice device) { 2318 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 2319 if (descriptor == null) { 2320 Log.e(TAG, "connectSet: No valid descriptor for device: " + device); 2321 return; 2322 } 2323 if (descriptor.mGroupId == LE_AUDIO_GROUP_ID_INVALID) { 2324 return; 2325 } 2326 2327 Log.d(TAG, "connect() others from group id: " + descriptor.mGroupId); 2328 2329 Integer setGroupId = descriptor.mGroupId; 2330 2331 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry : 2332 mDeviceDescriptors.entrySet()) { 2333 BluetoothDevice storedDevice = entry.getKey(); 2334 descriptor = entry.getValue(); 2335 if (device.equals(storedDevice)) { 2336 continue; 2337 } 2338 2339 if (!descriptor.mGroupId.equals(setGroupId)) { 2340 continue; 2341 } 2342 2343 Log.d(TAG, "connect(): " + storedDevice); 2344 2345 mGroupReadLock.lock(); 2346 try { 2347 LeAudioStateMachine sm = getOrCreateStateMachine(storedDevice); 2348 if (sm == null) { 2349 Log.e( 2350 TAG, 2351 "Ignored connect request for " + storedDevice + " : no state machine"); 2352 continue; 2353 } 2354 sm.sendMessage(LeAudioStateMachine.CONNECT); 2355 } finally { 2356 mGroupReadLock.unlock(); 2357 } 2358 } 2359 } 2360 getBroadcastProfile(boolean suppressNoisyIntent)2361 BluetoothProfileConnectionInfo getBroadcastProfile(boolean suppressNoisyIntent) { 2362 Parcel parcel = Parcel.obtain(); 2363 parcel.writeInt(BluetoothProfile.LE_AUDIO_BROADCAST); 2364 parcel.writeBoolean(suppressNoisyIntent); 2365 parcel.writeInt(-1 /* mVolume */); 2366 parcel.writeBoolean(true /* mIsLeOutput */); 2367 parcel.setDataPosition(0); 2368 2369 BluetoothProfileConnectionInfo profileInfo = 2370 BluetoothProfileConnectionInfo.CREATOR.createFromParcel(parcel); 2371 parcel.recycle(); 2372 return profileInfo; 2373 } 2374 clearLostDevicesWhileStreaming(LeAudioGroupDescriptor descriptor)2375 private void clearLostDevicesWhileStreaming(LeAudioGroupDescriptor descriptor) { 2376 mGroupReadLock.lock(); 2377 try { 2378 Log.d(TAG, "Clearing lost dev: " + descriptor.mLostLeadDeviceWhileStreaming); 2379 2380 LeAudioDeviceDescriptor deviceDescriptor = 2381 getDeviceDescriptor(descriptor.mLostLeadDeviceWhileStreaming); 2382 if (deviceDescriptor == null) { 2383 Log.e( 2384 TAG, 2385 "clearLostDevicesWhileStreaming: No valid descriptor for device: " 2386 + descriptor.mLostLeadDeviceWhileStreaming); 2387 return; 2388 } 2389 2390 LeAudioStateMachine sm = deviceDescriptor.mStateMachine; 2391 if (sm != null) { 2392 LeAudioStackEvent stackEvent = 2393 new LeAudioStackEvent( 2394 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 2395 stackEvent.device = descriptor.mLostLeadDeviceWhileStreaming; 2396 stackEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED; 2397 sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent); 2398 } 2399 descriptor.mLostLeadDeviceWhileStreaming = null; 2400 } finally { 2401 mGroupReadLock.unlock(); 2402 } 2403 } 2404 handleDeviceHealthAction(BluetoothDevice device, int action)2405 private void handleDeviceHealthAction(BluetoothDevice device, int action) { 2406 Log.d( 2407 TAG, 2408 "handleDeviceHealthAction: device: " 2409 + device 2410 + " action: " 2411 + action 2412 + ", not implemented"); 2413 if (action == LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_DISABLE) { 2414 MetricsLogger.getInstance() 2415 .count( 2416 mAdapterService.isLeAudioAllowed(device) 2417 ? BluetoothProtoEnums 2418 .LE_AUDIO_ALLOWLIST_DEVICE_HEALTH_STATUS_BAD 2419 : BluetoothProtoEnums 2420 .LE_AUDIO_NONALLOWLIST_DEVICE_HEALTH_STATUS_BAD, 2421 1); 2422 } 2423 } 2424 handleGroupHealthAction(int groupId, int action)2425 private void handleGroupHealthAction(int groupId, int action) { 2426 Log.d( 2427 TAG, 2428 "handleGroupHealthAction: groupId: " 2429 + groupId 2430 + " action: " 2431 + action 2432 + ", not implemented"); 2433 BluetoothDevice device = getLeadDeviceForTheGroup(groupId); 2434 switch (action) { 2435 case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_DISABLE: 2436 MetricsLogger.getInstance() 2437 .count( 2438 mAdapterService.isLeAudioAllowed(device) 2439 ? BluetoothProtoEnums 2440 .LE_AUDIO_ALLOWLIST_GROUP_HEALTH_STATUS_BAD 2441 : BluetoothProtoEnums 2442 .LE_AUDIO_NONALLOWLIST_GROUP_HEALTH_STATUS_BAD, 2443 1); 2444 break; 2445 case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING: 2446 MetricsLogger.getInstance() 2447 .count( 2448 mAdapterService.isLeAudioAllowed(device) 2449 ? BluetoothProtoEnums 2450 .LE_AUDIO_ALLOWLIST_GROUP_HEALTH_STATUS_TRENDING_BAD 2451 : BluetoothProtoEnums 2452 .LE_AUDIO_NONALLOWLIST_GROUP_HEALTH_STATUS_TRENDING_BAD, 2453 1); 2454 break; 2455 case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP: 2456 if (Flags.leaudioUnicastInactivateDeviceBasedOnContext()) { 2457 LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); 2458 if (groupDescriptor != null 2459 && groupDescriptor.isActive() 2460 && !isGroupReceivingBroadcast(groupId)) { 2461 Log.i( 2462 TAG, 2463 "Group " 2464 + groupId 2465 + " is inactivated due to blocked media context"); 2466 groupDescriptor.mInactivatedDueToContextType = true; 2467 setActiveGroupWithDevice(null, false); 2468 } 2469 } 2470 default: 2471 break; 2472 } 2473 } 2474 handleGroupTransitToActive(int groupId)2475 private void handleGroupTransitToActive(int groupId) { 2476 mGroupReadLock.lock(); 2477 try { 2478 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 2479 if (descriptor == null || (descriptor.isActive())) { 2480 Log.e( 2481 TAG, 2482 "handleGroupTransitToActive: no descriptors for group: " 2483 + groupId 2484 + " or group already active"); 2485 return; 2486 } 2487 2488 if (updateActiveDevices( 2489 groupId, AUDIO_DIRECTION_NONE, descriptor.mDirection, true, false, false)) { 2490 descriptor.setActiveState(ACTIVE_STATE_ACTIVE); 2491 } else { 2492 descriptor.setActiveState(ACTIVE_STATE_INACTIVE); 2493 } 2494 2495 if (descriptor.isActive()) { 2496 mHandler.post( 2497 () -> 2498 notifyGroupStatusChanged( 2499 groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE)); 2500 updateInbandRingtoneForTheGroup(groupId); 2501 } 2502 } finally { 2503 mGroupReadLock.unlock(); 2504 } 2505 } 2506 isBroadcastAllowedToBeActivateInCurrentAudioMode()2507 private boolean isBroadcastAllowedToBeActivateInCurrentAudioMode() { 2508 switch (mCurrentAudioMode) { 2509 case AudioManager.MODE_NORMAL: 2510 return true; 2511 case AudioManager.MODE_RINGTONE: 2512 case AudioManager.MODE_IN_CALL: 2513 case AudioManager.MODE_IN_COMMUNICATION: 2514 default: 2515 return false; 2516 } 2517 } 2518 isBroadcastReadyToBeReActivated()2519 private boolean isBroadcastReadyToBeReActivated() { 2520 return areAllGroupsInNotGettingActiveState() 2521 && (!mCreateBroadcastQueue.isEmpty() 2522 || mBroadcastIdDeactivatedForUnicastTransition.isPresent()) 2523 && isBroadcastAllowedToBeActivateInCurrentAudioMode(); 2524 } 2525 handleGroupTransitToInactive(int groupId)2526 private void handleGroupTransitToInactive(int groupId) { 2527 mGroupReadLock.lock(); 2528 try { 2529 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 2530 if (descriptor == null || descriptor.isInactive()) { 2531 Log.e( 2532 TAG, 2533 "handleGroupTransitToInactive: no descriptors for group: " 2534 + groupId 2535 + " or group already inactive"); 2536 return; 2537 } 2538 2539 descriptor.setActiveState(ACTIVE_STATE_INACTIVE); 2540 2541 /* Group became inactive due to broadcast creation, check if input device should remain 2542 * connected to track streaming request on Unicast 2543 */ 2544 boolean leaveConnectedInputDevice = false; 2545 Integer newDirections = AUDIO_DIRECTION_NONE; 2546 if (Flags.leaudioBroadcastAudioHandoverPolicies() 2547 && isBroadcastReadyToBeReActivated()) { 2548 leaveConnectedInputDevice = true; 2549 newDirections |= AUDIO_DIRECTION_INPUT_BIT; 2550 2551 /* Update Broadcast device before streaming state in handover case to avoid switch 2552 * to non LE Audio device in Audio Manager e.g. Phone Speaker. 2553 */ 2554 BluetoothDevice device = 2555 mAdapterService.getDeviceFromByte( 2556 Utils.getBytesFromAddress("FF:FF:FF:FF:FF:FF")); 2557 if (!device.equals(mActiveBroadcastAudioDevice)) { 2558 updateBroadcastActiveDevice(device, mActiveBroadcastAudioDevice, true); 2559 } 2560 } 2561 2562 updateActiveDevices( 2563 groupId, 2564 descriptor.mDirection, 2565 newDirections, 2566 false, 2567 descriptor.mHasFallbackDeviceWhenGettingInactive, 2568 leaveConnectedInputDevice); 2569 /* Clear lost devices */ 2570 Log.d(TAG, "Clear for group: " + groupId); 2571 descriptor.mHasFallbackDeviceWhenGettingInactive = false; 2572 clearLostDevicesWhileStreaming(descriptor); 2573 mHandler.post( 2574 () -> 2575 notifyGroupStatusChanged( 2576 groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE)); 2577 updateInbandRingtoneForTheGroup(groupId); 2578 } finally { 2579 mGroupReadLock.unlock(); 2580 } 2581 } 2582 handleSinkStreamStatusChange(int status)2583 private void handleSinkStreamStatusChange(int status) { 2584 Log.d(TAG, "status: " + status); 2585 2586 /* Straming request of Unicast Sink stream should result in pausing broadcast and activating 2587 * Unicast group. 2588 * 2589 * When stream is suspended there should be a reverse handover. Active Unicast group should 2590 * become inactive and broadcast should be resumed grom paused state. 2591 */ 2592 if (status == LeAudioStackEvent.STATUS_LOCAL_STREAM_REQUESTED) { 2593 Optional<Integer> broadcastId = getFirstNotStoppedBroadcastId(); 2594 if (broadcastId.isEmpty() || (mBroadcastDescriptors.get(broadcastId.get()) == null)) { 2595 Log.e( 2596 TAG, 2597 "handleUnicastStreamStatusChange: Broadcast to Unicast handover not" 2598 + " possible"); 2599 return; 2600 } 2601 2602 mBroadcastIdDeactivatedForUnicastTransition = Optional.of(broadcastId.get()); 2603 pauseBroadcast(broadcastId.get()); 2604 } else if (status == LeAudioStackEvent.STATUS_LOCAL_STREAM_SUSPENDED) { 2605 /* Deactivate unicast device if there is some and broadcast is ready to be activated */ 2606 if (!areAllGroupsInNotActiveState() && isBroadcastReadyToBeReActivated()) { 2607 removeActiveDevice(true); 2608 } 2609 } 2610 } 2611 handleSourceStreamStatusChange(int status)2612 private void handleSourceStreamStatusChange(int status) { 2613 BassClientService bassClientService = getBassClientService(); 2614 if (bassClientService == null) { 2615 Log.e(TAG, "handleSourceStreamStatusChange: BASS Client service is not available"); 2616 2617 mNativeInterface.setUnicastMonitorMode(LeAudioStackEvent.DIRECTION_SOURCE, false); 2618 } 2619 2620 bassClientService.handleUnicastSourceStreamStatusChange(status); 2621 } 2622 handleUnicastStreamStatusChange(int direction, int status)2623 private void handleUnicastStreamStatusChange(int direction, int status) { 2624 if (direction == LeAudioStackEvent.DIRECTION_SINK) { 2625 handleSinkStreamStatusChange(status); 2626 } else if (direction == LeAudioStackEvent.DIRECTION_SOURCE) { 2627 handleSourceStreamStatusChange(status); 2628 } else { 2629 Log.e(TAG, "handleUnicastStreamStatusChange: invalid direction: " + direction); 2630 } 2631 } 2632 isGroupReceivingBroadcast(int groupId)2633 private boolean isGroupReceivingBroadcast(int groupId) { 2634 if (!Flags.leaudioBroadcastAudioHandoverPolicies()) { 2635 return false; 2636 } 2637 2638 BassClientService bassClientService = getBassClientService(); 2639 if (bassClientService == null) { 2640 return false; 2641 } 2642 2643 return bassClientService.isAnyReceiverReceivingBroadcast(getGroupDevices(groupId)); 2644 } 2645 notifyGroupStreamStatusChanged(int groupId, int groupStreamStatus)2646 private void notifyGroupStreamStatusChanged(int groupId, int groupStreamStatus) { 2647 if (mLeAudioCallbacks != null) { 2648 int n = mLeAudioCallbacks.beginBroadcast(); 2649 for (int i = 0; i < n; i++) { 2650 try { 2651 mLeAudioCallbacks 2652 .getBroadcastItem(i) 2653 .onGroupStreamStatusChanged(groupId, groupStreamStatus); 2654 } catch (RemoteException e) { 2655 continue; 2656 } 2657 } 2658 mLeAudioCallbacks.finishBroadcast(); 2659 } 2660 } 2661 setGroupAllowedContextMask( int groupId, int sinkContextTypes, int sourceContextTypes)2662 private void setGroupAllowedContextMask( 2663 int groupId, int sinkContextTypes, int sourceContextTypes) { 2664 if (!mLeAudioNativeIsInitialized) { 2665 Log.e(TAG, "Le Audio not initialized properly."); 2666 return; 2667 } 2668 2669 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 2670 Log.i(TAG, "setActiveGroupAllowedContextMask: no active group"); 2671 return; 2672 } 2673 2674 LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); 2675 if (groupDescriptor == null) { 2676 Log.e(TAG, "Group " + groupId + " does not exist"); 2677 return; 2678 } 2679 2680 groupDescriptor.updateAllowedContexts(sinkContextTypes, sourceContextTypes); 2681 2682 mNativeInterface.setGroupAllowedContextMask(groupId, sinkContextTypes, sourceContextTypes); 2683 } 2684 2685 @VisibleForTesting handleGroupIdleDuringCall()2686 void handleGroupIdleDuringCall() { 2687 if (mHfpHandoverDevice == null) { 2688 Log.d(TAG, "There is no HFP handover"); 2689 return; 2690 } 2691 HeadsetService headsetService = mServiceFactory.getHeadsetService(); 2692 if (headsetService == null) { 2693 Log.d(TAG, "There is no HFP service available"); 2694 return; 2695 } 2696 2697 BluetoothDevice activeHfpDevice = headsetService.getActiveDevice(); 2698 if (activeHfpDevice == null) { 2699 Log.d(TAG, "Make " + mHfpHandoverDevice + " active again "); 2700 headsetService.setActiveDevice(mHfpHandoverDevice); 2701 } else { 2702 Log.d(TAG, "Connect audio to " + activeHfpDevice); 2703 headsetService.connectAudio(); 2704 } 2705 mHfpHandoverDevice = null; 2706 } 2707 updateInbandRingtoneForTheGroup(int groupId)2708 void updateInbandRingtoneForTheGroup(int groupId) { 2709 if (!mLeAudioInbandRingtoneSupportedByPlatform) { 2710 Log.d(TAG, "Platform does not support inband ringtone"); 2711 return; 2712 } 2713 2714 mGroupReadLock.lock(); 2715 try { 2716 LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); 2717 if (groupDescriptor == null) { 2718 Log.e(TAG, "group descriptor for " + groupId + " does not exist"); 2719 return; 2720 } 2721 2722 boolean ringtoneContextAvailable = 2723 ((groupDescriptor.mAvailableContexts & BluetoothLeAudio.CONTEXT_TYPE_RINGTONE) 2724 != 0); 2725 2726 Log.d( 2727 TAG, 2728 "groupId active state: " 2729 + groupDescriptor.mActiveState 2730 + " ringtone supported: " 2731 + ringtoneContextAvailable); 2732 2733 boolean isRingtoneEnabled = (groupDescriptor.isActive() && ringtoneContextAvailable); 2734 2735 Log.d( 2736 TAG, 2737 "updateInbandRingtoneForTheGroup old: " 2738 + groupDescriptor.mInbandRingtoneEnabled 2739 + " new: " 2740 + isRingtoneEnabled); 2741 2742 /* If at least one device from the group removes the Ringtone from available 2743 * context types, the inband ringtone will be removed 2744 */ 2745 groupDescriptor.mInbandRingtoneEnabled = isRingtoneEnabled; 2746 TbsService tbsService = getTbsService(); 2747 if (tbsService == null) { 2748 Log.w(TAG, "updateInbandRingtoneForTheGroup, tbsService not available"); 2749 return; 2750 } 2751 2752 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry : 2753 mDeviceDescriptors.entrySet()) { 2754 if (entry.getValue().mGroupId == groupId) { 2755 BluetoothDevice device = entry.getKey(); 2756 LeAudioDeviceDescriptor deviceDescriptor = entry.getValue(); 2757 Log.i( 2758 TAG, 2759 "updateInbandRingtoneForTheGroup, setting inband ringtone to: " 2760 + groupDescriptor.mInbandRingtoneEnabled 2761 + " for " 2762 + device 2763 + " " 2764 + deviceDescriptor.mDevInbandRingtoneEnabled); 2765 if (Objects.equals( 2766 groupDescriptor.mInbandRingtoneEnabled, 2767 deviceDescriptor.mDevInbandRingtoneEnabled)) { 2768 Log.d( 2769 TAG, 2770 "Device " 2771 + device 2772 + " has already set inband ringtone to " 2773 + groupDescriptor.mInbandRingtoneEnabled); 2774 continue; 2775 } 2776 2777 deviceDescriptor.mDevInbandRingtoneEnabled = 2778 groupDescriptor.mInbandRingtoneEnabled; 2779 if (deviceDescriptor.mDevInbandRingtoneEnabled) { 2780 tbsService.setInbandRingtoneSupport(device); 2781 } else { 2782 tbsService.clearInbandRingtoneSupport(device); 2783 } 2784 } 2785 } 2786 } finally { 2787 mGroupReadLock.unlock(); 2788 } 2789 } 2790 stopAudioServersBackgroundScan()2791 void stopAudioServersBackgroundScan() { 2792 Log.d(TAG, "stopAudioServersBackgroundScan"); 2793 2794 if (mAudioServersScanner == null || mScanCallback == null) { 2795 Log.d(TAG, "stopAudioServersBackgroundScan: already stopped"); 2796 return; 2797 } 2798 2799 try { 2800 mAudioServersScanner.stopScan(mScanCallback); 2801 } catch (IllegalStateException e) { 2802 Log.e(TAG, "Fail to stop scanner, consider it stopped", e); 2803 } 2804 2805 /* Callback is the indicator for scanning being enabled */ 2806 mScanCallback = null; 2807 } 2808 startAudioServersBackgroundScan(boolean retry)2809 void startAudioServersBackgroundScan(boolean retry) { 2810 Log.d(TAG, "startAudioServersBackgroundScan, retry: " + retry); 2811 2812 if (!isScannerNeeded()) { 2813 return; 2814 } 2815 2816 if (mAudioServersScanner == null) { 2817 mAudioServersScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner(); 2818 if (mAudioServersScanner == null) { 2819 Log.e(TAG, "startAudioServersBackgroundScan: Could not get scanner"); 2820 return; 2821 } 2822 } 2823 2824 if (!retry) { 2825 if (mScanCallback != null) { 2826 Log.d(TAG, "startAudioServersBackgroundScan: Scanning already enabled"); 2827 return; 2828 } 2829 mScanCallback = new AudioServerScanCallback(); 2830 } 2831 2832 /* Filter we are building here will not match to anything. 2833 * Eventually we should be able to start scan from native when 2834 * b/276350722 is done 2835 */ 2836 byte[] serviceData = new byte[] {0x11}; 2837 2838 ArrayList filterList = new ArrayList<ScanFilter>(); 2839 ScanFilter filter = 2840 new ScanFilter.Builder() 2841 .setServiceData(BluetoothUuid.LE_AUDIO, serviceData) 2842 .build(); 2843 filterList.add(filter); 2844 2845 ScanSettings settings = 2846 new ScanSettings.Builder() 2847 .setLegacy(false) 2848 .setScanMode(ScanSettings.SCAN_MODE_BALANCED) 2849 .setPhy(BluetoothDevice.PHY_LE_1M) 2850 .build(); 2851 2852 try { 2853 mAudioServersScanner.startScan(filterList, settings, mScanCallback); 2854 } catch (IllegalStateException e) { 2855 Log.e(TAG, "Fail to start scanner, consider it stopped", e); 2856 mScanCallback = null; 2857 } 2858 } 2859 transitionFromBroadcastToUnicast()2860 void transitionFromBroadcastToUnicast() { 2861 if (mUnicastGroupIdDeactivatedForBroadcastTransition == LE_AUDIO_GROUP_ID_INVALID) { 2862 Log.d(TAG, "No deactivated group due for broadcast transmission"); 2863 // Notify audio manager 2864 if (mBroadcastDescriptors.values().stream() 2865 .noneMatch(d -> d.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING))) { 2866 updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); 2867 } 2868 return; 2869 } 2870 2871 if (!leaudioUseAudioModeListener()) { 2872 if (mQueuedInCallValue.isPresent()) { 2873 mNativeInterface.setInCall(mQueuedInCallValue.get()); 2874 mQueuedInCallValue = Optional.empty(); 2875 } 2876 } 2877 2878 BluetoothDevice unicastDevice = 2879 getLeadDeviceForTheGroup(mUnicastGroupIdDeactivatedForBroadcastTransition); 2880 if (unicastDevice == null) { 2881 /* All devices from group were disconnected in meantime */ 2882 Log.w( 2883 TAG, 2884 "transitionFromBroadcastToUnicast: No valid unicast device for group ID: " 2885 + mUnicastGroupIdDeactivatedForBroadcastTransition); 2886 updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID); 2887 updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); 2888 return; 2889 } 2890 2891 Log.d( 2892 TAG, 2893 "Transitioning to Unicast stream for group: " 2894 + mUnicastGroupIdDeactivatedForBroadcastTransition 2895 + ", with device: " 2896 + unicastDevice); 2897 2898 updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID); 2899 setActiveDevice(unicastDevice); 2900 } 2901 clearBroadcastTimeoutCallback()2902 void clearBroadcastTimeoutCallback() { 2903 if (mHandler == null) { 2904 Log.e(TAG, "No callback handler"); 2905 return; 2906 } 2907 2908 /* Timeout callback already cleared */ 2909 if (mDialingOutTimeoutEvent == null) { 2910 return; 2911 } 2912 2913 mHandler.removeCallbacks(mDialingOutTimeoutEvent); 2914 mDialingOutTimeoutEvent = null; 2915 } 2916 notifyAudioFrameworkForCodecConfigUpdate(int groupId, LeAudioGroupDescriptor descriptor)2917 void notifyAudioFrameworkForCodecConfigUpdate(int groupId, LeAudioGroupDescriptor descriptor) { 2918 Log.i(TAG, " notifyAudioFrameworkForCodecConfigUpdate groupId: " + groupId); 2919 2920 if (!Flags.leaudioCodecConfigCallbackOrderFix()) { 2921 Log.d(TAG, " leaudio_codec_config_callback_order_fix is not enabled"); 2922 return; 2923 } 2924 2925 if (mActiveAudioOutDevice != null) { 2926 int volume = getAudioDeviceGroupVolume(groupId); 2927 2928 final BluetoothProfileConnectionInfo connectionInfo; 2929 if (isAtLeastU()) { 2930 connectionInfo = 2931 BluetoothProfileConnectionInfo.createLeAudioOutputInfo(true, volume); 2932 } else { 2933 connectionInfo = BluetoothProfileConnectionInfo.createLeAudioInfo(true, true); 2934 } 2935 2936 mAudioManager.handleBluetoothActiveDeviceChanged( 2937 mActiveAudioOutDevice, mActiveAudioOutDevice, connectionInfo); 2938 } 2939 2940 if (mActiveAudioInDevice != null) { 2941 mAudioManager.handleBluetoothActiveDeviceChanged( 2942 mActiveAudioOutDevice, 2943 mActiveAudioOutDevice, 2944 BluetoothProfileConnectionInfo.createLeAudioInfo(false, false)); 2945 } 2946 } 2947 2948 // Suppressed since this is part of a local process 2949 @SuppressLint("AndroidFrameworkRequiresPermission") messageFromNative(LeAudioStackEvent stackEvent)2950 void messageFromNative(LeAudioStackEvent stackEvent) { 2951 Log.d(TAG, "Message from native: " + stackEvent); 2952 BluetoothDevice device = stackEvent.device; 2953 2954 if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) { 2955 // Some events require device state machine 2956 mGroupReadLock.lock(); 2957 try { 2958 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 2959 if (deviceDescriptor == null) { 2960 Log.e(TAG, "messageFromNative: No valid descriptor for device: " + device); 2961 return; 2962 } 2963 2964 LeAudioStateMachine sm = deviceDescriptor.mStateMachine; 2965 if (sm != null) { 2966 /* 2967 * To improve scenario when lead Le Audio device is disconnected for the 2968 * streaming group, while there are still other devices streaming, 2969 * LeAudioService will not notify audio framework or other users about 2970 * Le Audio lead device disconnection. Instead we try to reconnect under 2971 * the hood and keep using lead device as a audio device indetifier in 2972 * the audio framework in order to not stop the stream. 2973 */ 2974 int groupId = deviceDescriptor.mGroupId; 2975 LeAudioGroupDescriptor descriptor = mGroupDescriptorsView.get(groupId); 2976 switch (stackEvent.valueInt1) { 2977 case LeAudioStackEvent.CONNECTION_STATE_DISCONNECTING: 2978 case LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED: 2979 deviceDescriptor.mAclConnected = false; 2980 startAudioServersBackgroundScan(/* retry= */ false); 2981 2982 boolean disconnectDueToUnbond = 2983 (BluetoothDevice.BOND_NONE 2984 == mAdapterService.getBondState(device)); 2985 if (descriptor != null 2986 && (Objects.equals(device, mActiveAudioOutDevice) 2987 || Objects.equals(device, mActiveAudioInDevice)) 2988 && (getConnectedPeerDevices(groupId).size() > 1) 2989 && !disconnectDueToUnbond) { 2990 2991 Log.d(TAG, "Adding to lost devices : " + device); 2992 descriptor.mLostLeadDeviceWhileStreaming = device; 2993 return; 2994 } 2995 break; 2996 case LeAudioStackEvent.CONNECTION_STATE_CONNECTED: 2997 case LeAudioStackEvent.CONNECTION_STATE_CONNECTING: 2998 deviceDescriptor.mAclConnected = true; 2999 if (descriptor != null 3000 && Objects.equals( 3001 descriptor.mLostLeadDeviceWhileStreaming, device)) { 3002 Log.d(TAG, "Removing from lost devices : " + device); 3003 descriptor.mLostLeadDeviceWhileStreaming = null; 3004 /* Try to connect other devices from the group */ 3005 connectSet(device); 3006 } 3007 break; 3008 } 3009 } else { 3010 /* state machine does not exist yet */ 3011 switch (stackEvent.valueInt1) { 3012 case LeAudioStackEvent.CONNECTION_STATE_CONNECTED: 3013 case LeAudioStackEvent.CONNECTION_STATE_CONNECTING: 3014 deviceDescriptor.mAclConnected = true; 3015 sm = getOrCreateStateMachine(device); 3016 /* Incoming connection try to connect other devices from the group */ 3017 connectSet(device); 3018 break; 3019 default: 3020 break; 3021 } 3022 3023 if (sm == null) { 3024 Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent); 3025 return; 3026 } 3027 } 3028 3029 sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent); 3030 return; 3031 } finally { 3032 mGroupReadLock.unlock(); 3033 } 3034 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED) { 3035 int groupId = stackEvent.valueInt1; 3036 int nodeStatus = stackEvent.valueInt2; 3037 3038 Objects.requireNonNull( 3039 stackEvent.device, "Device should never be null, event: " + stackEvent); 3040 3041 switch (nodeStatus) { 3042 case LeAudioStackEvent.GROUP_NODE_ADDED: 3043 handleGroupNodeAdded(device, groupId); 3044 break; 3045 case LeAudioStackEvent.GROUP_NODE_REMOVED: 3046 handleGroupNodeRemoved(device, groupId); 3047 break; 3048 default: 3049 break; 3050 } 3051 } else if (stackEvent.type 3052 == LeAudioStackEvent.EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED) { 3053 mInputLocalCodecCapabilities = stackEvent.valueCodecList1; 3054 mOutputLocalCodecCapabilities = stackEvent.valueCodecList2; 3055 } else if (stackEvent.type 3056 == LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_SELECTABLE_CODEC_CONFIG_CHANGED) { 3057 int groupId = stackEvent.valueInt1; 3058 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 3059 if (descriptor == null) { 3060 Log.e(TAG, " Group not found " + groupId); 3061 return; 3062 } 3063 3064 descriptor.mInputSelectableConfig = new ArrayList<>(stackEvent.valueCodecList1); 3065 descriptor.mOutputSelectableConfig = new ArrayList<>(stackEvent.valueCodecList2); 3066 3067 BluetoothLeAudioCodecConfig emptyConfig = 3068 new BluetoothLeAudioCodecConfig.Builder().build(); 3069 3070 descriptor.mInputSelectableConfig.removeIf(n -> n.equals(emptyConfig)); 3071 descriptor.mOutputSelectableConfig.removeIf(n -> n.equals(emptyConfig)); 3072 3073 } else if (stackEvent.type 3074 == LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CURRENT_CODEC_CONFIG_CHANGED) { 3075 int groupId = stackEvent.valueInt1; 3076 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 3077 if (descriptor == null) { 3078 Log.e(TAG, " Group not found " + groupId); 3079 return; 3080 } 3081 BluetoothLeAudioCodecConfig emptyConfig = 3082 new BluetoothLeAudioCodecConfig.Builder().build(); 3083 3084 BluetoothLeAudioCodecStatus status = 3085 new BluetoothLeAudioCodecStatus( 3086 (stackEvent.valueCodec1.equals(emptyConfig) 3087 ? null 3088 : stackEvent.valueCodec1), 3089 (stackEvent.valueCodec2.equals(emptyConfig) 3090 ? null 3091 : stackEvent.valueCodec2), 3092 mInputLocalCodecCapabilities, 3093 mOutputLocalCodecCapabilities, 3094 descriptor.mInputSelectableConfig, 3095 descriptor.mOutputSelectableConfig); 3096 3097 if (descriptor.mCodecStatus != null) { 3098 Log.d(TAG, " Replacing codec status for group: " + groupId); 3099 } else { 3100 Log.d(TAG, " New codec status for group: " + groupId); 3101 } 3102 3103 descriptor.mCodecStatus = status; 3104 mHandler.post(() -> notifyUnicastCodecConfigChanged(groupId, status)); 3105 3106 if (descriptor.isActive()) { 3107 // Audio framework needs to be notified so it get new codec config 3108 notifyAudioFrameworkForCodecConfigUpdate(groupId, descriptor); 3109 } 3110 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED) { 3111 int direction = stackEvent.valueInt1; 3112 int groupId = stackEvent.valueInt2; 3113 int available_contexts = stackEvent.valueInt5; 3114 3115 mGroupReadLock.lock(); 3116 try { 3117 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 3118 if (descriptor != null) { 3119 if (descriptor.isActive()) { 3120 if (updateActiveDevices( 3121 groupId, descriptor.mDirection, direction, true, false, false)) { 3122 descriptor.setActiveState(ACTIVE_STATE_ACTIVE); 3123 } else { 3124 descriptor.setActiveState(ACTIVE_STATE_INACTIVE); 3125 } 3126 3127 if (descriptor.isInactive()) { 3128 mHandler.post( 3129 () -> 3130 notifyGroupStatusChanged( 3131 groupId, 3132 BluetoothLeAudio.GROUP_STATUS_INACTIVE)); 3133 } 3134 } 3135 boolean availableContextChanged = 3136 Integer.bitCount(descriptor.mAvailableContexts) 3137 != Integer.bitCount(available_contexts); 3138 3139 descriptor.mDirection = direction; 3140 descriptor.mAvailableContexts = available_contexts; 3141 updateInbandRingtoneForTheGroup(groupId); 3142 3143 if (!availableContextChanged) { 3144 Log.d( 3145 TAG, 3146 " Context did not changed for " 3147 + groupId 3148 + ": " 3149 + descriptor.mAvailableContexts); 3150 return; 3151 } 3152 3153 if (descriptor.mAvailableContexts == 0) { 3154 if (descriptor.isActive()) { 3155 Log.i( 3156 TAG, 3157 " Inactivating group " 3158 + groupId 3159 + " due to unavailable context types"); 3160 descriptor.mInactivatedDueToContextType = true; 3161 setActiveGroupWithDevice(null, false); 3162 } 3163 return; 3164 } 3165 3166 if (descriptor.mInactivatedDueToContextType) { 3167 Log.i( 3168 TAG, 3169 " Some context got available again for " 3170 + groupId 3171 + ", try it out: " 3172 + descriptor.mAvailableContexts); 3173 descriptor.mInactivatedDueToContextType = false; 3174 setActiveGroupWithDevice(getLeadDeviceForTheGroup(groupId), true); 3175 } 3176 } else { 3177 Log.e(TAG, "messageFromNative: no descriptors for group: " + groupId); 3178 } 3179 } finally { 3180 mGroupReadLock.unlock(); 3181 } 3182 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE) { 3183 Objects.requireNonNull( 3184 stackEvent.device, "Device should never be null, event: " + stackEvent); 3185 3186 int sink_audio_location = stackEvent.valueInt1; 3187 3188 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 3189 if (descriptor == null) { 3190 Log.e(TAG, "messageFromNative: No valid descriptor for device: " + device); 3191 return; 3192 } 3193 3194 descriptor.mSinkAudioLocation = sink_audio_location; 3195 3196 Log.i( 3197 TAG, 3198 "EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE:" 3199 + device 3200 + " audio location:" 3201 + sink_audio_location); 3202 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED) { 3203 int groupId = stackEvent.valueInt1; 3204 int groupStatus = stackEvent.valueInt2; 3205 3206 switch (groupStatus) { 3207 case LeAudioStackEvent.GROUP_STATUS_ACTIVE: 3208 { 3209 handleGroupTransitToActive(groupId); 3210 3211 /* Clear possible exposed broadcast device after activating unicast */ 3212 if (mActiveBroadcastAudioDevice != null) { 3213 updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, true); 3214 } 3215 break; 3216 } 3217 case LeAudioStackEvent.GROUP_STATUS_INACTIVE: 3218 { 3219 if (Flags.leaudioGettingActiveStateSupport()) { 3220 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 3221 if (descriptor == null) { 3222 Log.e( 3223 TAG, 3224 "deviceDisconnected: no descriptors for group: " + groupId); 3225 return; 3226 } 3227 3228 if (descriptor.isActive()) { 3229 handleGroupTransitToInactive(groupId); 3230 } 3231 3232 descriptor.setActiveState(ACTIVE_STATE_INACTIVE); 3233 3234 /* In case if group is inactivated due to switch to other */ 3235 Integer gettingActiveGroupId = getFirstGroupIdInGettingActiveState(); 3236 if (gettingActiveGroupId != LE_AUDIO_GROUP_ID_INVALID) { 3237 if (leaudioAllowedContextMask()) { 3238 /* Context were modified, apply mask to activating group */ 3239 if (descriptor.areAllowedContextsModified()) { 3240 setGroupAllowedContextMask( 3241 gettingActiveGroupId, 3242 descriptor.getAllowedSinkContexts(), 3243 descriptor.getAllowedSourceContexts()); 3244 setGroupAllowedContextMask( 3245 groupId, 3246 BluetoothLeAudio.CONTEXTS_ALL, 3247 BluetoothLeAudio.CONTEXTS_ALL); 3248 } 3249 } 3250 break; 3251 } 3252 3253 if (leaudioAllowedContextMask()) { 3254 /* Clear allowed context mask if there is no switch of group */ 3255 if (descriptor.areAllowedContextsModified()) { 3256 setGroupAllowedContextMask( 3257 groupId, 3258 BluetoothLeAudio.CONTEXTS_ALL, 3259 BluetoothLeAudio.CONTEXTS_ALL); 3260 } 3261 } 3262 } else { 3263 handleGroupTransitToInactive(groupId); 3264 } 3265 3266 if (isBroadcastAllowedToBeActivateInCurrentAudioMode()) { 3267 /* Check if broadcast was deactivated due to unicast */ 3268 if (mBroadcastIdDeactivatedForUnicastTransition.isPresent()) { 3269 updateFallbackUnicastGroupIdForBroadcast(groupId); 3270 if (!leaudioUseAudioModeListener()) { 3271 mQueuedInCallValue = Optional.empty(); 3272 } 3273 startBroadcast(mBroadcastIdDeactivatedForUnicastTransition.get()); 3274 mBroadcastIdDeactivatedForUnicastTransition = Optional.empty(); 3275 } 3276 3277 if (!mCreateBroadcastQueue.isEmpty()) { 3278 updateFallbackUnicastGroupIdForBroadcast(groupId); 3279 BluetoothLeBroadcastSettings settings = 3280 mCreateBroadcastQueue.remove(); 3281 createBroadcast(settings); 3282 } 3283 } 3284 break; 3285 } 3286 case LeAudioStackEvent.GROUP_STATUS_TURNED_IDLE_DURING_CALL: 3287 { 3288 handleGroupIdleDuringCall(); 3289 break; 3290 } 3291 default: 3292 break; 3293 } 3294 } else if (stackEvent.type 3295 == LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_DEV_RECOMMENDATION) { 3296 handleDeviceHealthAction(stackEvent.device, stackEvent.valueInt1); 3297 } else if (stackEvent.type 3298 == LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION) { 3299 handleGroupHealthAction(stackEvent.valueInt1, stackEvent.valueInt2); 3300 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED) { 3301 int broadcastId = stackEvent.valueInt1; 3302 boolean success = stackEvent.valueBool1; 3303 if (success) { 3304 Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " created."); 3305 mBroadcastDescriptors.put(broadcastId, new LeAudioBroadcastDescriptor()); 3306 mHandler.post( 3307 () -> 3308 notifyBroadcastStarted( 3309 broadcastId, 3310 BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 3311 // Start sending the actual stream 3312 startBroadcast(broadcastId); 3313 3314 } else { 3315 // TODO: Improve reason reporting or extend the native stack event with reason code 3316 Log.e( 3317 TAG, 3318 "EVENT_TYPE_BROADCAST_CREATED: Failed to create broadcast: " + broadcastId); 3319 3320 /* Disconnect Broadcast device which was connected to avoid non LE Audio sound 3321 * leak in handover scenario. 3322 */ 3323 if ((mUnicastGroupIdDeactivatedForBroadcastTransition != LE_AUDIO_GROUP_ID_INVALID) 3324 && mCreateBroadcastQueue.isEmpty() 3325 && (!Objects.equals(device, mActiveBroadcastAudioDevice))) { 3326 clearBroadcastTimeoutCallback(); 3327 updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); 3328 } 3329 3330 mHandler.post(() -> notifyBroadcastStartFailed(BluetoothStatusCodes.ERROR_UNKNOWN)); 3331 } 3332 3333 mAwaitingBroadcastCreateResponse = false; 3334 3335 // In case if there were additional calls to create broadcast 3336 if (!mCreateBroadcastQueue.isEmpty()) { 3337 BluetoothLeBroadcastSettings settings = mCreateBroadcastQueue.remove(); 3338 createBroadcast(settings); 3339 } 3340 3341 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_DESTROYED) { 3342 Integer broadcastId = stackEvent.valueInt1; 3343 LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); 3344 if (descriptor == null) { 3345 Log.e( 3346 TAG, 3347 "EVENT_TYPE_BROADCAST_DESTROYED: No valid descriptor for broadcastId: " 3348 + broadcastId); 3349 } else { 3350 mBroadcastDescriptors.remove(broadcastId); 3351 } 3352 3353 // TODO: Improve reason reporting or extend the native stack event with reason code 3354 mHandler.post( 3355 () -> 3356 notifyOnBroadcastStopped( 3357 broadcastId, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 3358 BassClientService bassClientService = getBassClientService(); 3359 if (bassClientService != null) { 3360 bassClientService.stopReceiversSourceSynchronization(broadcastId); 3361 } 3362 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE) { 3363 int broadcastId = stackEvent.valueInt1; 3364 int state = stackEvent.valueInt2; 3365 int previousState; 3366 3367 LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); 3368 if (descriptor == null) { 3369 Log.e( 3370 TAG, 3371 "EVENT_TYPE_BROADCAST_STATE: No valid descriptor for broadcastId: " 3372 + broadcastId); 3373 return; 3374 } 3375 3376 /* Request broadcast details if not known yet */ 3377 if (!descriptor.mRequestedForDetails) { 3378 mLeAudioBroadcasterNativeInterface.getBroadcastMetadata(broadcastId); 3379 descriptor.mRequestedForDetails = true; 3380 } 3381 previousState = descriptor.mState; 3382 descriptor.mState = state; 3383 BassClientService bassClientService = getBassClientService(); 3384 3385 switch (descriptor.mState) { 3386 case LeAudioStackEvent.BROADCAST_STATE_STOPPED: 3387 Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopped."); 3388 3389 // Playback stopped 3390 mHandler.post( 3391 () -> 3392 notifyPlaybackStopped( 3393 broadcastId, 3394 BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST)); 3395 3396 transitionFromBroadcastToUnicast(); 3397 destroyBroadcast(broadcastId); 3398 break; 3399 case LeAudioStackEvent.BROADCAST_STATE_CONFIGURING: 3400 Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " configuring."); 3401 break; 3402 case LeAudioStackEvent.BROADCAST_STATE_PAUSED: 3403 Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " paused."); 3404 3405 /* Stop here if Broadcast was not in Streaming state before */ 3406 if (previousState != LeAudioStackEvent.BROADCAST_STATE_STREAMING) { 3407 return; 3408 } 3409 3410 // Playback paused 3411 mHandler.post( 3412 () -> 3413 notifyPlaybackStopped( 3414 broadcastId, 3415 BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST)); 3416 3417 if (!Flags.leaudioBroadcastAssistantPeripheralEntrustment()) { 3418 if (bassClientService != null) { 3419 bassClientService.suspendReceiversSourceSynchronization(broadcastId); 3420 } 3421 } 3422 3423 transitionFromBroadcastToUnicast(); 3424 break; 3425 case LeAudioStackEvent.BROADCAST_STATE_STOPPING: 3426 Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopping."); 3427 break; 3428 case LeAudioStackEvent.BROADCAST_STATE_STREAMING: 3429 Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " streaming."); 3430 3431 // Stream resumed 3432 mHandler.post( 3433 () -> 3434 notifyPlaybackStarted( 3435 broadcastId, 3436 BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST)); 3437 3438 clearBroadcastTimeoutCallback(); 3439 3440 if (previousState == LeAudioStackEvent.BROADCAST_STATE_PAUSED) { 3441 if (bassClientService != null) { 3442 bassClientService.resumeReceiversSourceSynchronization(); 3443 } 3444 } 3445 3446 // Notify audio manager 3447 if (mBroadcastDescriptors.values().stream() 3448 .anyMatch( 3449 d -> 3450 d.mState.equals( 3451 LeAudioStackEvent.BROADCAST_STATE_STREAMING))) { 3452 if (!Objects.equals(device, mActiveBroadcastAudioDevice)) { 3453 updateBroadcastActiveDevice(device, mActiveBroadcastAudioDevice, true); 3454 } 3455 } 3456 break; 3457 default: 3458 Log.e(TAG, "Invalid state of broadcast: " + descriptor.mState); 3459 break; 3460 } 3461 3462 // Notify broadcast assistant 3463 if (Flags.leaudioBroadcastAudioHandoverPolicies()) { 3464 if (bassClientService != null) { 3465 bassClientService.notifyBroadcastStateChanged(descriptor.mState, broadcastId); 3466 } 3467 } 3468 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_BROADCAST_METADATA_CHANGED) { 3469 int broadcastId = stackEvent.valueInt1; 3470 if (stackEvent.broadcastMetadata == null) { 3471 Log.e(TAG, "Missing Broadcast metadata for broadcastId: " + broadcastId); 3472 } else { 3473 LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId); 3474 if (descriptor == null) { 3475 Log.e( 3476 TAG, 3477 "EVENT_TYPE_BROADCAST_METADATA_CHANGED: No valid descriptor for " 3478 + "broadcastId: " 3479 + broadcastId); 3480 return; 3481 } 3482 descriptor.mMetadata = stackEvent.broadcastMetadata; 3483 mHandler.post( 3484 () -> 3485 notifyBroadcastMetadataChanged( 3486 broadcastId, stackEvent.broadcastMetadata)); 3487 } 3488 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED) { 3489 mLeAudioNativeIsInitialized = true; 3490 for (Map.Entry<ParcelUuid, Pair<Integer, Integer>> entry : 3491 ContentControlIdKeeper.getUuidToCcidContextPairMap().entrySet()) { 3492 ParcelUuid userUuid = entry.getKey(); 3493 Pair<Integer, Integer> ccidInformation = entry.getValue(); 3494 setCcidInformation(userUuid, ccidInformation.first, ccidInformation.second); 3495 } 3496 if (!mTmapStarted) { 3497 mTmapStarted = registerTmap(); 3498 } 3499 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_UNICAST_MONITOR_MODE_STATUS) { 3500 handleUnicastStreamStatusChange(stackEvent.valueInt1, stackEvent.valueInt2); 3501 } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_STREAM_STATUS_CHANGED) { 3502 mHandler.post( 3503 () -> 3504 notifyGroupStreamStatusChanged( 3505 stackEvent.valueInt1, stackEvent.valueInt2)); 3506 } 3507 } 3508 getOrCreateStateMachine(BluetoothDevice device)3509 private LeAudioStateMachine getOrCreateStateMachine(BluetoothDevice device) { 3510 if (device == null) { 3511 Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null"); 3512 return null; 3513 } 3514 3515 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 3516 if (descriptor == null) { 3517 Log.e(TAG, "getOrCreateStateMachine: No valid descriptor for device: " + device); 3518 return null; 3519 } 3520 3521 LeAudioStateMachine sm = descriptor.mStateMachine; 3522 if (sm != null) { 3523 return sm; 3524 } 3525 3526 Log.d(TAG, "Creating a new state machine for " + device); 3527 3528 sm = 3529 LeAudioStateMachine.make( 3530 device, this, mNativeInterface, mStateMachinesThread.getLooper()); 3531 descriptor.mStateMachine = sm; 3532 return sm; 3533 } 3534 handleBondStateChanged(BluetoothDevice device, int fromState, int toState)3535 public void handleBondStateChanged(BluetoothDevice device, int fromState, int toState) { 3536 mHandler.post(() -> bondStateChanged(device, toState)); 3537 } 3538 3539 /** 3540 * Process a change in the bonding state for a device. 3541 * 3542 * @param device the device whose bonding state has changed 3543 * @param bondState the new bond state for the device. Possible values are: {@link 3544 * BluetoothDevice#BOND_NONE}, {@link BluetoothDevice#BOND_BONDING}, {@link 3545 * BluetoothDevice#BOND_BONDED}. 3546 */ 3547 @VisibleForTesting bondStateChanged(BluetoothDevice device, int bondState)3548 void bondStateChanged(BluetoothDevice device, int bondState) { 3549 Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState); 3550 // Remove state machine if the bonding for a device is removed 3551 if (bondState != BluetoothDevice.BOND_NONE) { 3552 return; 3553 } 3554 3555 mGroupReadLock.lock(); 3556 try { 3557 try { 3558 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 3559 if (descriptor == null) { 3560 Log.e(TAG, "bondStateChanged: No valid descriptor for device: " + device); 3561 return; 3562 } 3563 3564 if (descriptor.mGroupId != LE_AUDIO_GROUP_ID_INVALID) { 3565 /* In case device is still in the group, let's remove it */ 3566 mNativeInterface.groupRemoveNode(descriptor.mGroupId, device); 3567 } 3568 3569 descriptor.mGroupId = LE_AUDIO_GROUP_ID_INVALID; 3570 descriptor.mSinkAudioLocation = BluetoothLeAudio.AUDIO_LOCATION_INVALID; 3571 descriptor.mDirection = AUDIO_DIRECTION_NONE; 3572 3573 LeAudioStateMachine sm = descriptor.mStateMachine; 3574 if (sm == null) { 3575 return; 3576 } 3577 if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { 3578 Log.w(TAG, "Device is not disconnected yet."); 3579 disconnect(device); 3580 return; 3581 } 3582 } finally { 3583 // Reduce size of critical section when this feature is enabled 3584 if (Flags.leaudioApiSynchronizedBlockFix()) { 3585 mGroupReadLock.unlock(); 3586 } 3587 } 3588 removeStateMachine(device); 3589 removeAuthorizationInfoForRelatedProfiles(device); 3590 } finally { 3591 if (!Flags.leaudioApiSynchronizedBlockFix()) { 3592 mGroupReadLock.unlock(); 3593 } 3594 } 3595 } 3596 removeStateMachine(BluetoothDevice device)3597 private void removeStateMachine(BluetoothDevice device) { 3598 mGroupReadLock.lock(); 3599 try { 3600 try { 3601 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 3602 if (descriptor == null) { 3603 Log.e(TAG, "removeStateMachine: No valid descriptor for device: " + device); 3604 return; 3605 } 3606 3607 LeAudioStateMachine sm = descriptor.mStateMachine; 3608 if (sm == null) { 3609 Log.w( 3610 TAG, 3611 "removeStateMachine: device " 3612 + device 3613 + " does not have a state machine"); 3614 return; 3615 } 3616 Log.i(TAG, "removeStateMachine: removing state machine for device: " + device); 3617 sm.quit(); 3618 sm.cleanup(); 3619 descriptor.mStateMachine = null; 3620 } finally { 3621 if (Flags.leaudioApiSynchronizedBlockFix()) { 3622 // Upgrade to write lock 3623 mGroupReadLock.unlock(); 3624 mGroupWriteLock.lock(); 3625 } 3626 } 3627 mDeviceDescriptors.remove(device); 3628 if (!isScannerNeeded()) { 3629 stopAudioServersBackgroundScan(); 3630 } 3631 } finally { 3632 /* Note, when flag is disabled, mGroupWriteLock == mGroupReadLock */ 3633 mGroupWriteLock.unlock(); 3634 } 3635 } 3636 3637 @VisibleForTesting getConnectedPeerDevices(int groupId)3638 List<BluetoothDevice> getConnectedPeerDevices(int groupId) { 3639 List<BluetoothDevice> result = new ArrayList<>(); 3640 for (BluetoothDevice peerDevice : getConnectedDevices()) { 3641 if (getGroupId(peerDevice) == groupId) { 3642 result.add(peerDevice); 3643 } 3644 } 3645 return result; 3646 } 3647 3648 /** Process a change for connection of a device. */ deviceConnected(BluetoothDevice device)3649 public synchronized void deviceConnected(BluetoothDevice device) { 3650 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 3651 if (deviceDescriptor == null) { 3652 Log.e(TAG, "deviceConnected: No valid descriptor for device: " + device); 3653 return; 3654 } 3655 3656 if (deviceDescriptor.mGroupId == LE_AUDIO_GROUP_ID_INVALID 3657 || getConnectedPeerDevices(deviceDescriptor.mGroupId).size() == 1) { 3658 // Log LE Audio connection event if we are the first device in a set 3659 // Or when the GroupId has not been found 3660 // MetricsLogger.logProfileConnectionEvent( 3661 // BluetoothMetricsProto.ProfileId.LE_AUDIO); 3662 } 3663 3664 LeAudioGroupDescriptor descriptor = getGroupDescriptor(deviceDescriptor.mGroupId); 3665 if (descriptor != null) { 3666 descriptor.mIsConnected = true; 3667 } else { 3668 Log.e(TAG, "deviceConnected: no descriptors for group: " + deviceDescriptor.mGroupId); 3669 } 3670 3671 if (!isScannerNeeded()) { 3672 stopAudioServersBackgroundScan(); 3673 } 3674 } 3675 3676 /** Process a change for disconnection of a device. */ deviceDisconnectedV2(BluetoothDevice device, boolean hasFallbackDevice)3677 synchronized void deviceDisconnectedV2(BluetoothDevice device, boolean hasFallbackDevice) { 3678 Log.d(TAG, "deviceDisconnectedV2 " + device); 3679 3680 int groupId = LE_AUDIO_GROUP_ID_INVALID; 3681 mGroupReadLock.lock(); 3682 try { 3683 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 3684 if (deviceDescriptor == null) { 3685 Log.e(TAG, "deviceDisconnected: No valid descriptor for device: " + device); 3686 return; 3687 } 3688 groupId = deviceDescriptor.mGroupId; 3689 } finally { 3690 mGroupReadLock.unlock(); 3691 } 3692 3693 int bondState = mAdapterService.getBondState(device); 3694 if (bondState == BluetoothDevice.BOND_NONE) { 3695 Log.d(TAG, device + " is unbond. Remove state machine"); 3696 3697 removeStateMachine(device); 3698 removeAuthorizationInfoForRelatedProfiles(device); 3699 } 3700 3701 if (!isScannerNeeded()) { 3702 stopAudioServersBackgroundScan(); 3703 } 3704 3705 mGroupReadLock.lock(); 3706 try { 3707 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 3708 if (descriptor == null) { 3709 Log.e(TAG, "deviceDisconnected: no descriptors for group: " + groupId); 3710 return; 3711 } 3712 3713 List<BluetoothDevice> connectedDevices = getConnectedPeerDevices(groupId); 3714 /* Let's check if the last connected device is really connected */ 3715 if (connectedDevices.size() == 1 3716 && Objects.equals( 3717 connectedDevices.get(0), descriptor.mLostLeadDeviceWhileStreaming)) { 3718 clearLostDevicesWhileStreaming(descriptor); 3719 return; 3720 } 3721 3722 if (getConnectedPeerDevices(groupId).isEmpty()) { 3723 descriptor.mIsConnected = false; 3724 descriptor.mInactivatedDueToContextType = false; 3725 if (descriptor.isActive()) { 3726 /* Notify Native layer */ 3727 removeActiveDevice(hasFallbackDevice); 3728 descriptor.setActiveState(ACTIVE_STATE_INACTIVE); 3729 /* Update audio framework */ 3730 updateActiveDevices( 3731 groupId, 3732 descriptor.mDirection, 3733 descriptor.mDirection, 3734 false, 3735 hasFallbackDevice, 3736 false); 3737 return; 3738 } 3739 } 3740 3741 if (descriptor.isActive() 3742 || Objects.equals(mActiveAudioOutDevice, device) 3743 || Objects.equals(mActiveAudioInDevice, device)) { 3744 updateActiveDevices( 3745 groupId, 3746 descriptor.mDirection, 3747 descriptor.mDirection, 3748 descriptor.isActive(), 3749 hasFallbackDevice, 3750 false); 3751 } 3752 } finally { 3753 mGroupReadLock.unlock(); 3754 } 3755 } 3756 3757 /** Process a change for disconnection of a device. */ deviceDisconnected(BluetoothDevice device, boolean hasFallbackDevice)3758 public synchronized void deviceDisconnected(BluetoothDevice device, boolean hasFallbackDevice) { 3759 if (Flags.leaudioApiSynchronizedBlockFix()) { 3760 deviceDisconnectedV2(device, hasFallbackDevice); 3761 return; 3762 } 3763 3764 Log.d(TAG, "deviceDisconnected " + device); 3765 3766 mGroupReadLock.lock(); 3767 try { 3768 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 3769 if (deviceDescriptor == null) { 3770 Log.e(TAG, "deviceDisconnected: No valid descriptor for device: " + device); 3771 return; 3772 } 3773 3774 int bondState = mAdapterService.getBondState(device); 3775 if (bondState == BluetoothDevice.BOND_NONE) { 3776 Log.d(TAG, device + " is unbond. Remove state machine"); 3777 removeStateMachine(device); 3778 removeAuthorizationInfoForRelatedProfiles(device); 3779 } 3780 3781 if (!isScannerNeeded()) { 3782 stopAudioServersBackgroundScan(); 3783 } 3784 3785 LeAudioGroupDescriptor descriptor = getGroupDescriptor(deviceDescriptor.mGroupId); 3786 if (descriptor == null) { 3787 Log.e( 3788 TAG, 3789 "deviceDisconnected: no descriptors for group: " 3790 + deviceDescriptor.mGroupId); 3791 return; 3792 } 3793 3794 List<BluetoothDevice> connectedDevices = 3795 getConnectedPeerDevices(deviceDescriptor.mGroupId); 3796 /* Let's check if the last connected device is really connected */ 3797 if (connectedDevices.size() == 1 3798 && Objects.equals( 3799 connectedDevices.get(0), descriptor.mLostLeadDeviceWhileStreaming)) { 3800 clearLostDevicesWhileStreaming(descriptor); 3801 return; 3802 } 3803 3804 if (getConnectedPeerDevices(deviceDescriptor.mGroupId).isEmpty()) { 3805 descriptor.mIsConnected = false; 3806 descriptor.mInactivatedDueToContextType = false; 3807 if (descriptor.isActive()) { 3808 /* Notify Native layer */ 3809 removeActiveDevice(hasFallbackDevice); 3810 descriptor.setActiveState(ACTIVE_STATE_INACTIVE); 3811 /* Update audio framework */ 3812 updateActiveDevices( 3813 deviceDescriptor.mGroupId, 3814 descriptor.mDirection, 3815 descriptor.mDirection, 3816 false, 3817 hasFallbackDevice, 3818 false); 3819 return; 3820 } 3821 } 3822 3823 if (descriptor.isActive() 3824 || Objects.equals(mActiveAudioOutDevice, device) 3825 || Objects.equals(mActiveAudioInDevice, device)) { 3826 updateActiveDevices( 3827 deviceDescriptor.mGroupId, 3828 descriptor.mDirection, 3829 descriptor.mDirection, 3830 descriptor.isActive(), 3831 hasFallbackDevice, 3832 false); 3833 } 3834 } finally { 3835 mGroupReadLock.unlock(); 3836 } 3837 } 3838 3839 /** 3840 * Check whether can connect to a peer device. The check considers a number of factors during 3841 * the evaluation. 3842 * 3843 * @param device the peer device to connect to 3844 * @return true if connection is allowed, otherwise false 3845 */ okToConnect(BluetoothDevice device)3846 public boolean okToConnect(BluetoothDevice device) { 3847 // Check if this is an incoming connection in Quiet mode. 3848 if (mAdapterService.isQuietModeEnabled()) { 3849 Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled"); 3850 return false; 3851 } 3852 // Check connectionPolicy and accept or reject the connection. 3853 int connectionPolicy = getConnectionPolicy(device); 3854 int bondState = mAdapterService.getBondState(device); 3855 // Allow this connection only if the device is bonded. Any attempt to connect while 3856 // bonding would potentially lead to an unauthorized connection. 3857 if (bondState != BluetoothDevice.BOND_BONDED) { 3858 Log.w(TAG, "okToConnect: return false, bondState=" + bondState); 3859 return false; 3860 } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN 3861 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 3862 // Otherwise, reject the connection if connectionPolicy is not valid. 3863 Log.w(TAG, "okToConnect: return false, connectionPolicy=" + connectionPolicy); 3864 return false; 3865 } 3866 return true; 3867 } 3868 3869 /** 3870 * Get device audio location. 3871 * 3872 * @param device LE Audio capable device 3873 * @return the sink audioi location that this device currently exposed 3874 */ getAudioLocation(BluetoothDevice device)3875 public int getAudioLocation(BluetoothDevice device) { 3876 if (device == null) { 3877 return BluetoothLeAudio.AUDIO_LOCATION_INVALID; 3878 } 3879 3880 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 3881 if (descriptor == null) { 3882 Log.e(TAG, "getAudioLocation: No valid descriptor for device: " + device); 3883 return BluetoothLeAudio.AUDIO_LOCATION_INVALID; 3884 } 3885 3886 return descriptor.mSinkAudioLocation; 3887 } 3888 3889 /** 3890 * Check if inband ringtone is enabled by the LE Audio group. Group id for the device can be 3891 * found with {@link BluetoothLeAudio#getGroupId}. 3892 * 3893 * @param groupId LE Audio group id 3894 * @return true if inband ringtone is enabled, false otherwise 3895 */ isInbandRingtoneEnabled(int groupId)3896 public boolean isInbandRingtoneEnabled(int groupId) { 3897 if (!mLeAudioInbandRingtoneSupportedByPlatform) { 3898 return mLeAudioInbandRingtoneSupportedByPlatform; 3899 } 3900 3901 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 3902 if (descriptor == null) { 3903 return false; 3904 } 3905 3906 return descriptor.mInbandRingtoneEnabled; 3907 } 3908 3909 /** 3910 * Set In Call state 3911 * 3912 * @param inCall True if device in call (any state), false otherwise. 3913 */ setInCall(boolean inCall)3914 public void setInCall(boolean inCall) { 3915 if (!mLeAudioNativeIsInitialized) { 3916 Log.e(TAG, "Le Audio not initialized properly."); 3917 return; 3918 } 3919 3920 if (!leaudioUseAudioModeListener()) { 3921 /* For setting inCall mode */ 3922 if (Flags.leaudioBroadcastAudioHandoverPolicies() 3923 && inCall 3924 && !areBroadcastsAllStopped()) { 3925 mQueuedInCallValue = Optional.of(true); 3926 3927 /* Request activation of unicast group */ 3928 handleUnicastStreamStatusChange( 3929 LeAudioStackEvent.DIRECTION_SINK, 3930 LeAudioStackEvent.STATUS_LOCAL_STREAM_REQUESTED); 3931 return; 3932 } 3933 } 3934 3935 mNativeInterface.setInCall(inCall); 3936 3937 if (!leaudioUseAudioModeListener()) { 3938 /* For clearing inCall mode */ 3939 if (Flags.leaudioBroadcastAudioHandoverPolicies() 3940 && !inCall 3941 && mBroadcastIdDeactivatedForUnicastTransition.isPresent()) { 3942 handleUnicastStreamStatusChange( 3943 LeAudioStackEvent.DIRECTION_SINK, 3944 LeAudioStackEvent.STATUS_LOCAL_STREAM_SUSPENDED); 3945 } 3946 } 3947 } 3948 3949 /** 3950 * Sends the preferred audio profiles for a dual mode audio device to the native stack. 3951 * 3952 * @param groupId is the group id of the device which had a preference change 3953 * @param isOutputPreferenceLeAudio {@code true} if {@link BluetoothProfile#LE_AUDIO} is 3954 * preferred for {@link BluetoothAdapter#AUDIO_MODE_OUTPUT_ONLY}, {@code false} if it is 3955 * {@link BluetoothProfile#A2DP} 3956 * @param isDuplexPreferenceLeAudio {@code true} if {@link BluetoothProfile#LE_AUDIO} is 3957 * preferred for {@link BluetoothAdapter#AUDIO_MODE_DUPLEX}, {@code false} if it is {@link 3958 * BluetoothProfile#HEADSET} 3959 */ sendAudioProfilePreferencesToNative( int groupId, boolean isOutputPreferenceLeAudio, boolean isDuplexPreferenceLeAudio)3960 public void sendAudioProfilePreferencesToNative( 3961 int groupId, boolean isOutputPreferenceLeAudio, boolean isDuplexPreferenceLeAudio) { 3962 if (!mLeAudioNativeIsInitialized) { 3963 Log.e(TAG, "Le Audio not initialized properly."); 3964 return; 3965 } 3966 mNativeInterface.sendAudioProfilePreferences( 3967 groupId, isOutputPreferenceLeAudio, isDuplexPreferenceLeAudio); 3968 } 3969 3970 /** 3971 * Set allowed context which should be considered while Audio Framework would request streaming. 3972 * 3973 * @param sinkContextTypes sink context types that would be allowed to stream 3974 * @param sourceContextTypes source context types that would be allowed to stream 3975 */ setActiveGroupAllowedContextMask(int sinkContextTypes, int sourceContextTypes)3976 public void setActiveGroupAllowedContextMask(int sinkContextTypes, int sourceContextTypes) { 3977 setGroupAllowedContextMask(getActiveGroupId(), sinkContextTypes, sourceContextTypes); 3978 } 3979 3980 /** 3981 * Set Inactive by HFP during handover This is a work around to handle controllers that cannot 3982 * have SCO and CIS at the same time. So remove active device to tear down CIS, and re-connect 3983 * the SCO in {@link LeAudioService#handleGroupIdleDuringCall()} 3984 * 3985 * @param hfpHandoverDevice is the hfp device that was set to active 3986 */ setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice)3987 public void setInactiveForHfpHandover(BluetoothDevice hfpHandoverDevice) { 3988 if (!mLeAudioNativeIsInitialized) { 3989 Log.e(TAG, "Le Audio not initialized properly."); 3990 return; 3991 } 3992 if (getActiveGroupId() != LE_AUDIO_GROUP_ID_INVALID) { 3993 mHfpHandoverDevice = hfpHandoverDevice; 3994 if (Flags.leaudioResumeActiveAfterHfpHandover()) { 3995 // record the lead device 3996 mLeAudioDeviceInactivatedForHfpHandover = mExposedActiveDevice; 3997 } 3998 removeActiveDevice(true); 3999 } 4000 } 4001 4002 /** Resume prior active device after HFP phone call hand over */ setActiveAfterHfpHandover()4003 public void setActiveAfterHfpHandover() { 4004 if (!mLeAudioNativeIsInitialized) { 4005 Log.e(TAG, "Le Audio not initialized properly."); 4006 return; 4007 } 4008 if (mLeAudioDeviceInactivatedForHfpHandover != null) { 4009 Log.i(TAG, "handover to LE audio device=" + mLeAudioDeviceInactivatedForHfpHandover); 4010 setActiveDevice(mLeAudioDeviceInactivatedForHfpHandover); 4011 mLeAudioDeviceInactivatedForHfpHandover = null; 4012 } else { 4013 Log.d(TAG, "nothing to handover back"); 4014 } 4015 } 4016 4017 /** 4018 * Set connection policy of the profile and connects it if connectionPolicy is {@link 4019 * BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is {@link 4020 * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 4021 * 4022 * <p>The device should already be paired. Connection policy can be one of: {@link 4023 * BluetoothProfile#CONNECTION_POLICY_ALLOWED}, {@link 4024 * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link 4025 * BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 4026 * 4027 * @param device the remote device 4028 * @param connectionPolicy is the connection policy to set to for this profile 4029 * @return true on success, otherwise false 4030 */ 4031 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) setConnectionPolicy(BluetoothDevice device, int connectionPolicy)4032 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 4033 enforceCallingOrSelfPermission( 4034 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 4035 Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 4036 4037 if (!mDatabaseManager.setProfileConnectionPolicy( 4038 device, BluetoothProfile.LE_AUDIO, connectionPolicy)) { 4039 return false; 4040 } 4041 4042 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 4043 setEnabledState(device, /* enabled= */ true); 4044 // Authorizes LEA GATT server services if already assigned to a group 4045 int groupId = getGroupId(device); 4046 if (groupId != LE_AUDIO_GROUP_ID_INVALID) { 4047 setAuthorizationForRelatedProfiles(device, true); 4048 } 4049 connect(device); 4050 } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 4051 setEnabledState(device, /* enabled= */ false); 4052 // Remove authorization for LEA GATT server services 4053 setAuthorizationForRelatedProfiles(device, false); 4054 disconnect(device); 4055 } 4056 setLeAudioGattClientProfilesPolicy(device, connectionPolicy); 4057 return true; 4058 } 4059 4060 /** 4061 * Sets the connection policy for LE Audio GATT client profiles 4062 * 4063 * @param device is the remote device 4064 * @param connectionPolicy is the connection policy we wish to set 4065 */ setLeAudioGattClientProfilesPolicy(BluetoothDevice device, int connectionPolicy)4066 private void setLeAudioGattClientProfilesPolicy(BluetoothDevice device, int connectionPolicy) { 4067 Log.d( 4068 TAG, 4069 "setLeAudioGattClientProfilesPolicy for device " 4070 + device 4071 + " to policy=" 4072 + connectionPolicy); 4073 VolumeControlService volumeControlService = getVolumeControlService(); 4074 if (volumeControlService != null) { 4075 volumeControlService.setConnectionPolicy(device, connectionPolicy); 4076 } 4077 4078 if (mHapClientService == null) { 4079 mHapClientService = mServiceFactory.getHapClientService(); 4080 } 4081 if (mHapClientService != null) { 4082 mHapClientService.setConnectionPolicy(device, connectionPolicy); 4083 } 4084 4085 if (mCsipSetCoordinatorService == null) { 4086 mCsipSetCoordinatorService = mServiceFactory.getCsipSetCoordinatorService(); 4087 } 4088 4089 // Disallow setting CSIP to forbidden until characteristic reads are complete 4090 if (mCsipSetCoordinatorService != null) { 4091 mCsipSetCoordinatorService.setConnectionPolicy(device, connectionPolicy); 4092 } 4093 } 4094 4095 /** 4096 * Get the connection policy of the profile. 4097 * 4098 * <p>The connection policy can be any of: {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 4099 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link 4100 * BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 4101 * 4102 * @param device Bluetooth device 4103 * @return connection policy of the device 4104 */ getConnectionPolicy(BluetoothDevice device)4105 public int getConnectionPolicy(BluetoothDevice device) { 4106 int connection_policy = 4107 mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO); 4108 Log.d(TAG, device + " connection policy = " + connection_policy); 4109 return connection_policy; 4110 } 4111 4112 /** 4113 * Get device group id. Devices with same group id belong to same group (i.e left and right 4114 * earbud) 4115 * 4116 * @param device LE Audio capable device 4117 * @return group id that this device currently belongs to 4118 */ getGroupId(BluetoothDevice device)4119 public int getGroupId(BluetoothDevice device) { 4120 if (device == null) { 4121 return LE_AUDIO_GROUP_ID_INVALID; 4122 } 4123 4124 mGroupReadLock.lock(); 4125 try { 4126 LeAudioDeviceDescriptor descriptor = getDeviceDescriptor(device); 4127 if (descriptor == null) { 4128 Log.e(TAG, "getGroupId: No valid descriptor for device: " + device); 4129 return LE_AUDIO_GROUP_ID_INVALID; 4130 } 4131 4132 return descriptor.mGroupId; 4133 } finally { 4134 mGroupReadLock.unlock(); 4135 } 4136 } 4137 4138 /** 4139 * Check if group is available for streaming. If there is no available context types then group 4140 * is not available for streaming. 4141 * 4142 * @param groupId groupid 4143 * @return true if available, false otherwise 4144 */ isGroupAvailableForStream(int groupId)4145 public boolean isGroupAvailableForStream(int groupId) { 4146 mGroupReadLock.lock(); 4147 try { 4148 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 4149 if (descriptor == null) { 4150 Log.e(TAG, "getGroupId: No valid descriptor for groupId: " + groupId); 4151 return false; 4152 } 4153 return descriptor.mAvailableContexts != 0; 4154 } finally { 4155 mGroupReadLock.unlock(); 4156 } 4157 } 4158 4159 /** 4160 * Set the user application ccid along with used context type 4161 * 4162 * @param userUuid user uuid 4163 * @param ccid content control id 4164 * @param contextType context type 4165 */ setCcidInformation(ParcelUuid userUuid, int ccid, int contextType)4166 public void setCcidInformation(ParcelUuid userUuid, int ccid, int contextType) { 4167 /* for the moment we care only for GMCS and GTBS */ 4168 if (userUuid != BluetoothUuid.GENERIC_MEDIA_CONTROL 4169 && userUuid.getUuid() != TbsGatt.UUID_GTBS) { 4170 return; 4171 } 4172 if (!mLeAudioNativeIsInitialized) { 4173 Log.e(TAG, "Le Audio not initialized properly."); 4174 return; 4175 } 4176 mNativeInterface.setCcidInformation(ccid, contextType); 4177 } 4178 4179 /** 4180 * Set volume for streaming devices 4181 * 4182 * @param volume volume to set 4183 */ setVolume(int volume)4184 public void setVolume(int volume) { 4185 Log.d(TAG, "SetVolume " + volume); 4186 4187 int currentlyActiveGroupId = getActiveGroupId(); 4188 List<BluetoothDevice> activeBroadcastSinks = new ArrayList<>(); 4189 4190 if (currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID) { 4191 if (!Flags.leaudioBroadcastVolumeControlWithSetVolume()) { 4192 Log.e(TAG, "There is no active group "); 4193 return; 4194 } 4195 4196 BassClientService bassClientService = getBassClientService(); 4197 if (bassClientService != null) { 4198 activeBroadcastSinks = bassClientService.getActiveBroadcastSinks(); 4199 } 4200 4201 if (activeBroadcastSinks.isEmpty()) { 4202 Log.e(TAG, "There is no active streaming group or broadcast sinks"); 4203 return; 4204 } 4205 } 4206 4207 VolumeControlService volumeControlService = getVolumeControlService(); 4208 if (volumeControlService != null) { 4209 if (Flags.leaudioBroadcastVolumeControlWithSetVolume() 4210 && currentlyActiveGroupId == LE_AUDIO_GROUP_ID_INVALID 4211 && !activeBroadcastSinks.isEmpty()) { 4212 Set<Integer> broadcastGroups = 4213 activeBroadcastSinks.stream() 4214 .map(dev -> getGroupId(dev)) 4215 .filter(id -> id != IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID) 4216 .collect(Collectors.toSet()); 4217 4218 Log.d(TAG, "Setting volume for broadcast sink groups: " + broadcastGroups); 4219 broadcastGroups.forEach( 4220 groupId -> volumeControlService.setGroupVolume(groupId, volume)); 4221 } else { 4222 volumeControlService.setGroupVolume(currentlyActiveGroupId, volume); 4223 } 4224 } 4225 } 4226 getTbsService()4227 TbsService getTbsService() { 4228 if (mTbsService != null) { 4229 return mTbsService; 4230 } 4231 4232 mTbsService = mServiceFactory.getTbsService(); 4233 return mTbsService; 4234 } 4235 getMcpService()4236 McpService getMcpService() { 4237 if (mMcpService != null) { 4238 return mMcpService; 4239 } 4240 4241 mMcpService = mServiceFactory.getMcpService(); 4242 return mMcpService; 4243 } 4244 setAuthorizationForRelatedProfiles(BluetoothDevice device, boolean authorize)4245 void setAuthorizationForRelatedProfiles(BluetoothDevice device, boolean authorize) { 4246 McpService mcpService = getMcpService(); 4247 if (mcpService != null) { 4248 mcpService.setDeviceAuthorized(device, authorize); 4249 } 4250 4251 TbsService tbsService = getTbsService(); 4252 if (tbsService != null) { 4253 tbsService.setDeviceAuthorized(device, authorize); 4254 } 4255 } 4256 removeAuthorizationInfoForRelatedProfiles(BluetoothDevice device)4257 void removeAuthorizationInfoForRelatedProfiles(BluetoothDevice device) { 4258 if (!Flags.leaudioMcsTbsAuthorizationRebondFix()) { 4259 Log.i(TAG, "leaudio_mcs_tbs_authorization_rebond_fix is disabled"); 4260 return; 4261 } 4262 4263 McpService mcpService = getMcpService(); 4264 if (mcpService != null) { 4265 mcpService.removeDeviceAuthorizationInfo(device); 4266 } 4267 4268 TbsService tbsService = getTbsService(); 4269 if (tbsService != null) { 4270 tbsService.removeDeviceAuthorizationInfo(device); 4271 } 4272 } 4273 4274 /** 4275 * This function is called when the framework registers a callback with the service for this 4276 * first time. This is used as an indication that Bluetooth has been enabled. 4277 * 4278 * <p>It is used to authorize all known LeAudio devices in the services which requires that e.g. 4279 * GMCS 4280 */ 4281 @VisibleForTesting handleBluetoothEnabled()4282 void handleBluetoothEnabled() { 4283 Log.d(TAG, "handleBluetoothEnabled "); 4284 4285 mBluetoothEnabled = true; 4286 4287 mGroupReadLock.lock(); 4288 try { 4289 try { 4290 if (mDeviceDescriptors.isEmpty()) { 4291 return; 4292 } 4293 } finally { 4294 if (!Flags.leaudioApiSynchronizedBlockFix()) { 4295 // Keep previous behavior where a lock is released and acquired immediately 4296 mGroupReadLock.unlock(); 4297 mGroupReadLock.lock(); 4298 } 4299 } 4300 for (BluetoothDevice device : mDeviceDescriptors.keySet()) { 4301 if (getConnectionPolicy(device) != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 4302 setAuthorizationForRelatedProfiles(device, true); 4303 } 4304 } 4305 } finally { 4306 mGroupReadLock.unlock(); 4307 } 4308 4309 startAudioServersBackgroundScan(/* retry= */ false); 4310 } 4311 4312 @VisibleForTesting handleAudioModeChange(int mode)4313 void handleAudioModeChange(int mode) { 4314 Log.d(TAG, "Audio mode changed: " + mCurrentAudioMode + " -> " + mode); 4315 int previousAudioMode = mCurrentAudioMode; 4316 4317 mCurrentAudioMode = mode; 4318 4319 switch (mode) { 4320 case AudioManager.MODE_RINGTONE: 4321 case AudioManager.MODE_IN_CALL: 4322 case AudioManager.MODE_IN_COMMUNICATION: 4323 if (!areBroadcastsAllStopped()) { 4324 /* Request activation of unicast group */ 4325 handleUnicastStreamStatusChange( 4326 LeAudioStackEvent.DIRECTION_SINK, 4327 LeAudioStackEvent.STATUS_LOCAL_STREAM_REQUESTED); 4328 } 4329 break; 4330 case AudioManager.MODE_NORMAL: 4331 /* Remove broadcast if during handover active LE Audio device disappears 4332 * (switch to primary device or non LE Audio device) 4333 */ 4334 if (isBroadcastReadyToBeReActivated() 4335 && isAudioModeChangedFromCommunicationToNormal( 4336 previousAudioMode, mCurrentAudioMode) 4337 && (getActiveGroupId() == LE_AUDIO_GROUP_ID_INVALID)) { 4338 stopBroadcast(mBroadcastIdDeactivatedForUnicastTransition.get()); 4339 mBroadcastIdDeactivatedForUnicastTransition = Optional.empty(); 4340 break; 4341 } 4342 4343 if (mBroadcastIdDeactivatedForUnicastTransition.isPresent()) { 4344 handleUnicastStreamStatusChange( 4345 LeAudioStackEvent.DIRECTION_SINK, 4346 LeAudioStackEvent.STATUS_LOCAL_STREAM_SUSPENDED); 4347 } 4348 break; 4349 default: 4350 Log.d(TAG, "Not handled audio mode set: " + mode); 4351 break; 4352 } 4353 } 4354 getGroupDescriptor(int groupId)4355 private LeAudioGroupDescriptor getGroupDescriptor(int groupId) { 4356 mGroupReadLock.lock(); 4357 try { 4358 return mGroupDescriptorsView.get(groupId); 4359 } finally { 4360 mGroupReadLock.unlock(); 4361 } 4362 } 4363 getDeviceDescriptor(BluetoothDevice device)4364 private LeAudioDeviceDescriptor getDeviceDescriptor(BluetoothDevice device) { 4365 mGroupReadLock.lock(); 4366 try { 4367 return mDeviceDescriptors.get(device); 4368 } finally { 4369 mGroupReadLock.unlock(); 4370 } 4371 } 4372 handleGroupNodeAdded(BluetoothDevice device, int groupId)4373 private void handleGroupNodeAdded(BluetoothDevice device, int groupId) { 4374 mGroupWriteLock.lock(); 4375 try { 4376 Log.d(TAG, "Device " + device + " added to group " + groupId); 4377 4378 LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); 4379 if (groupDescriptor == null) { 4380 mGroupDescriptors.put(groupId, new LeAudioGroupDescriptor(false)); 4381 } 4382 groupDescriptor = getGroupDescriptor(groupId); 4383 if (groupDescriptor == null) { 4384 Log.e(TAG, "Could not create group description"); 4385 return; 4386 } 4387 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 4388 if (deviceDescriptor == null) { 4389 deviceDescriptor = 4390 createDeviceDescriptor(device, groupDescriptor.mInbandRingtoneEnabled); 4391 if (deviceDescriptor == null) { 4392 Log.e( 4393 TAG, 4394 "handleGroupNodeAdded: Can't create descriptor for added from" 4395 + " storage device: " 4396 + device); 4397 return; 4398 } 4399 4400 LeAudioStateMachine unused = getOrCreateStateMachine(device); 4401 if (getOrCreateStateMachine(device) == null) { 4402 Log.e(TAG, "Can't get state machine for device: " + device); 4403 return; 4404 } 4405 } 4406 deviceDescriptor.mGroupId = groupId; 4407 4408 mHandler.post(() -> notifyGroupNodeAdded(device, groupId)); 4409 } finally { 4410 mGroupWriteLock.unlock(); 4411 } 4412 4413 if (mBluetoothEnabled) { 4414 setAuthorizationForRelatedProfiles(device, true); 4415 startAudioServersBackgroundScan(/* retry= */ false); 4416 } 4417 } 4418 notifyGroupNodeAdded(BluetoothDevice device, int groupId)4419 private void notifyGroupNodeAdded(BluetoothDevice device, int groupId) { 4420 VolumeControlService volumeControlService = getVolumeControlService(); 4421 if (volumeControlService != null) { 4422 volumeControlService.handleGroupNodeAdded(groupId, device); 4423 } 4424 4425 if (mLeAudioCallbacks != null) { 4426 int n = mLeAudioCallbacks.beginBroadcast(); 4427 for (int i = 0; i < n; i++) { 4428 try { 4429 mLeAudioCallbacks.getBroadcastItem(i).onGroupNodeAdded(device, groupId); 4430 } catch (RemoteException e) { 4431 continue; 4432 } 4433 } 4434 mLeAudioCallbacks.finishBroadcast(); 4435 } 4436 } 4437 4438 // When leaudioApiSynchronizedBlockFix is false, mGroupDescriptors is used within a 4439 // mGroupReadLock (same as mGroupWriteLock). 4440 // TODO(b/326295400): Remove SuppressLint 4441 @SuppressLint("GuardedBy") handleGroupNodeRemoved(BluetoothDevice device, int groupId)4442 private void handleGroupNodeRemoved(BluetoothDevice device, int groupId) { 4443 Log.d(TAG, "Removing device " + device + " grom group " + groupId); 4444 4445 boolean isGroupEmpty = true; 4446 mGroupReadLock.lock(); 4447 try { 4448 LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId); 4449 if (groupDescriptor == null) { 4450 Log.e(TAG, "handleGroupNodeRemoved: No valid descriptor for group: " + groupId); 4451 return; 4452 } 4453 Log.d(TAG, "Lost lead device is " + groupDescriptor.mLostLeadDeviceWhileStreaming); 4454 if (Objects.equals(device, groupDescriptor.mLostLeadDeviceWhileStreaming)) { 4455 clearLostDevicesWhileStreaming(groupDescriptor); 4456 } 4457 4458 LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device); 4459 if (deviceDescriptor == null) { 4460 Log.e(TAG, "handleGroupNodeRemoved: No valid descriptor for device: " + device); 4461 return; 4462 } 4463 deviceDescriptor.mGroupId = LE_AUDIO_GROUP_ID_INVALID; 4464 4465 for (LeAudioDeviceDescriptor descriptor : mDeviceDescriptors.values()) { 4466 if (descriptor.mGroupId == groupId) { 4467 isGroupEmpty = false; 4468 break; 4469 } 4470 } 4471 4472 if (isGroupEmpty) { 4473 /* Device is currently an active device. Group needs to be inactivated before 4474 * removing 4475 */ 4476 if (Objects.equals(device, mActiveAudioOutDevice) 4477 || Objects.equals(device, mActiveAudioInDevice)) { 4478 handleGroupTransitToInactive(groupId); 4479 } 4480 if (!Flags.leaudioApiSynchronizedBlockFix()) { 4481 mGroupDescriptors.remove(groupId); 4482 } 4483 4484 if (mUnicastGroupIdDeactivatedForBroadcastTransition == groupId) { 4485 updateFallbackUnicastGroupIdForBroadcast(LE_AUDIO_GROUP_ID_INVALID); 4486 } 4487 } 4488 mHandler.post(() -> notifyGroupNodeRemoved(device, groupId)); 4489 } finally { 4490 mGroupReadLock.unlock(); 4491 } 4492 4493 if (isGroupEmpty && Flags.leaudioApiSynchronizedBlockFix()) { 4494 mGroupWriteLock.lock(); 4495 try { 4496 mGroupDescriptors.remove(groupId); 4497 } finally { 4498 mGroupWriteLock.unlock(); 4499 } 4500 } 4501 4502 setAuthorizationForRelatedProfiles(device, false); 4503 removeAuthorizationInfoForRelatedProfiles(device); 4504 } 4505 notifyGroupNodeRemoved(BluetoothDevice device, int groupId)4506 private void notifyGroupNodeRemoved(BluetoothDevice device, int groupId) { 4507 if (mLeAudioCallbacks != null) { 4508 int n = mLeAudioCallbacks.beginBroadcast(); 4509 for (int i = 0; i < n; i++) { 4510 try { 4511 mLeAudioCallbacks.getBroadcastItem(i).onGroupNodeRemoved(device, groupId); 4512 } catch (RemoteException e) { 4513 continue; 4514 } 4515 } 4516 mLeAudioCallbacks.finishBroadcast(); 4517 } 4518 } 4519 notifyGroupStatusChanged(int groupId, int status)4520 private void notifyGroupStatusChanged(int groupId, int status) { 4521 if (mLeAudioCallbacks != null) { 4522 int n = mLeAudioCallbacks.beginBroadcast(); 4523 for (int i = 0; i < n; i++) { 4524 try { 4525 mLeAudioCallbacks.getBroadcastItem(i).onGroupStatusChanged(groupId, status); 4526 } catch (RemoteException e) { 4527 continue; 4528 } 4529 } 4530 mLeAudioCallbacks.finishBroadcast(); 4531 } 4532 } 4533 notifyUnicastCodecConfigChanged(int groupId, BluetoothLeAudioCodecStatus status)4534 private void notifyUnicastCodecConfigChanged(int groupId, BluetoothLeAudioCodecStatus status) { 4535 if (mLeAudioCallbacks != null) { 4536 int n = mLeAudioCallbacks.beginBroadcast(); 4537 for (int i = 0; i < n; i++) { 4538 try { 4539 mLeAudioCallbacks.getBroadcastItem(i).onCodecConfigChanged(groupId, status); 4540 } catch (RemoteException e) { 4541 continue; 4542 } 4543 } 4544 mLeAudioCallbacks.finishBroadcast(); 4545 } 4546 } 4547 notifyBroadcastStarted(Integer broadcastId, int reason)4548 private void notifyBroadcastStarted(Integer broadcastId, int reason) { 4549 if (mBroadcastCallbacks != null) { 4550 int n = mBroadcastCallbacks.beginBroadcast(); 4551 for (int i = 0; i < n; i++) { 4552 try { 4553 mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStarted(reason, broadcastId); 4554 } catch (RemoteException e) { 4555 continue; 4556 } 4557 } 4558 mBroadcastCallbacks.finishBroadcast(); 4559 } 4560 } 4561 notifyBroadcastStartFailed(int reason)4562 private void notifyBroadcastStartFailed(int reason) { 4563 if (mBroadcastCallbacks != null) { 4564 int n = mBroadcastCallbacks.beginBroadcast(); 4565 for (int i = 0; i < n; i++) { 4566 try { 4567 mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStartFailed(reason); 4568 } catch (RemoteException e) { 4569 continue; 4570 } 4571 } 4572 mBroadcastCallbacks.finishBroadcast(); 4573 } 4574 } 4575 notifyOnBroadcastStopped(Integer broadcastId, int reason)4576 private void notifyOnBroadcastStopped(Integer broadcastId, int reason) { 4577 if (mBroadcastCallbacks != null) { 4578 int n = mBroadcastCallbacks.beginBroadcast(); 4579 for (int i = 0; i < n; i++) { 4580 try { 4581 mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStopped(reason, broadcastId); 4582 } catch (RemoteException e) { 4583 continue; 4584 } 4585 } 4586 mBroadcastCallbacks.finishBroadcast(); 4587 } 4588 } 4589 notifyOnBroadcastStopFailed(int reason)4590 private void notifyOnBroadcastStopFailed(int reason) { 4591 if (mBroadcastCallbacks != null) { 4592 int n = mBroadcastCallbacks.beginBroadcast(); 4593 for (int i = 0; i < n; i++) { 4594 try { 4595 mBroadcastCallbacks.getBroadcastItem(i).onBroadcastStopFailed(reason); 4596 } catch (RemoteException e) { 4597 continue; 4598 } 4599 } 4600 mBroadcastCallbacks.finishBroadcast(); 4601 } 4602 } 4603 notifyPlaybackStarted(Integer broadcastId, int reason)4604 private void notifyPlaybackStarted(Integer broadcastId, int reason) { 4605 if (mBroadcastCallbacks != null) { 4606 int n = mBroadcastCallbacks.beginBroadcast(); 4607 for (int i = 0; i < n; i++) { 4608 try { 4609 mBroadcastCallbacks.getBroadcastItem(i).onPlaybackStarted(reason, broadcastId); 4610 } catch (RemoteException e) { 4611 continue; 4612 } 4613 } 4614 mBroadcastCallbacks.finishBroadcast(); 4615 } 4616 } 4617 notifyPlaybackStopped(Integer broadcastId, int reason)4618 private void notifyPlaybackStopped(Integer broadcastId, int reason) { 4619 if (mBroadcastCallbacks != null) { 4620 int n = mBroadcastCallbacks.beginBroadcast(); 4621 for (int i = 0; i < n; i++) { 4622 try { 4623 mBroadcastCallbacks.getBroadcastItem(i).onPlaybackStopped(reason, broadcastId); 4624 } catch (RemoteException e) { 4625 continue; 4626 } 4627 } 4628 mBroadcastCallbacks.finishBroadcast(); 4629 } 4630 } 4631 notifyBroadcastUpdateFailed(int broadcastId, int reason)4632 private void notifyBroadcastUpdateFailed(int broadcastId, int reason) { 4633 if (mBroadcastCallbacks != null) { 4634 int n = mBroadcastCallbacks.beginBroadcast(); 4635 for (int i = 0; i < n; i++) { 4636 try { 4637 mBroadcastCallbacks 4638 .getBroadcastItem(i) 4639 .onBroadcastUpdateFailed(reason, broadcastId); 4640 } catch (RemoteException e) { 4641 continue; 4642 } 4643 } 4644 mBroadcastCallbacks.finishBroadcast(); 4645 } 4646 } 4647 notifyBroadcastMetadataChanged( int broadcastId, BluetoothLeBroadcastMetadata metadata)4648 private void notifyBroadcastMetadataChanged( 4649 int broadcastId, BluetoothLeBroadcastMetadata metadata) { 4650 if (mBroadcastCallbacks != null) { 4651 int n = mBroadcastCallbacks.beginBroadcast(); 4652 for (int i = 0; i < n; i++) { 4653 try { 4654 mBroadcastCallbacks 4655 .getBroadcastItem(i) 4656 .onBroadcastMetadataChanged(broadcastId, metadata); 4657 } catch (RemoteException e) { 4658 continue; 4659 } 4660 } 4661 mBroadcastCallbacks.finishBroadcast(); 4662 } 4663 } 4664 4665 /** 4666 * Update the fallback unicast group id during the handover to broadcast Also store the fallback 4667 * group id in Settings store. 4668 * 4669 * @param groupId group id to update 4670 */ updateFallbackUnicastGroupIdForBroadcast(int groupId)4671 private void updateFallbackUnicastGroupIdForBroadcast(int groupId) { 4672 Log.i( 4673 TAG, 4674 "Update unicast fallback active group from: " 4675 + mUnicastGroupIdDeactivatedForBroadcastTransition 4676 + " to : " 4677 + groupId); 4678 mUnicastGroupIdDeactivatedForBroadcastTransition = groupId; 4679 4680 // waive WRITE_SECURE_SETTINGS permission check 4681 final long callingIdentity = Binder.clearCallingIdentity(); 4682 try { 4683 Context userContext = 4684 getApplicationContext() 4685 .createContextAsUser( 4686 UserHandle.of(ActivityManager.getCurrentUser()), 0); 4687 Settings.Secure.putInt( 4688 userContext.getContentResolver(), 4689 BLUETOOTH_LE_BROADCAST_FALLBACK_ACTIVE_GROUP_ID, 4690 groupId); 4691 } finally { 4692 Binder.restoreCallingIdentity(callingIdentity); 4693 } 4694 } 4695 isAudioModeChangedFromCommunicationToNormal(int previousMode, int currentMode)4696 private boolean isAudioModeChangedFromCommunicationToNormal(int previousMode, int currentMode) { 4697 switch (previousMode) { 4698 case AudioManager.MODE_RINGTONE: 4699 case AudioManager.MODE_IN_CALL: 4700 case AudioManager.MODE_IN_COMMUNICATION: 4701 if (currentMode == AudioManager.MODE_NORMAL) { 4702 return true; 4703 } 4704 4705 return false; 4706 default: 4707 return false; 4708 } 4709 } 4710 4711 /** 4712 * Gets the current codec status (configuration and capability). 4713 * 4714 * @param groupId the group id 4715 * @return the current codec status 4716 */ getCodecStatus(int groupId)4717 public BluetoothLeAudioCodecStatus getCodecStatus(int groupId) { 4718 Log.d(TAG, "getCodecStatus(" + groupId + ")"); 4719 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 4720 if (descriptor != null) { 4721 return descriptor.mCodecStatus; 4722 } 4723 return null; 4724 } 4725 4726 /** 4727 * Sets the codec configuration preference. 4728 * 4729 * @param groupId the group id 4730 * @param inputCodecConfig the input codec configuration preference 4731 * @param outputCodecConfig the output codec configuration preference 4732 */ setCodecConfigPreference( int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig)4733 public void setCodecConfigPreference( 4734 int groupId, 4735 BluetoothLeAudioCodecConfig inputCodecConfig, 4736 BluetoothLeAudioCodecConfig outputCodecConfig) { 4737 Log.d( 4738 TAG, 4739 "setCodecConfigPreference(" 4740 + groupId 4741 + "): " 4742 + Objects.toString(inputCodecConfig) 4743 + Objects.toString(outputCodecConfig)); 4744 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 4745 if (descriptor == null) { 4746 Log.e(TAG, "setCodecConfigPreference: Invalid groupId, " + groupId); 4747 return; 4748 } 4749 4750 if (inputCodecConfig == null || outputCodecConfig == null) { 4751 Log.e(TAG, "setCodecConfigPreference: Codec config can't be null"); 4752 return; 4753 } 4754 4755 /* We support different configuration for input and output but codec type 4756 * shall be same */ 4757 if (inputCodecConfig.getCodecType() != outputCodecConfig.getCodecType()) { 4758 Log.e( 4759 TAG, 4760 "setCodecConfigPreference: Input codec type: " 4761 + inputCodecConfig.getCodecType() 4762 + "does not match output codec type: " 4763 + outputCodecConfig.getCodecType()); 4764 return; 4765 } 4766 4767 if (descriptor.mCodecStatus == null) { 4768 Log.e(TAG, "setCodecConfigPreference: Codec status is null"); 4769 return; 4770 } 4771 4772 if (!mLeAudioNativeIsInitialized) { 4773 Log.e(TAG, "Le Audio not initialized properly."); 4774 return; 4775 } 4776 4777 mNativeInterface.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig); 4778 } 4779 4780 /** 4781 * Checks if the remote device supports LE Audio duplex (output and input). 4782 * 4783 * @param device the remote device to check 4784 * @return {@code true} if LE Audio duplex is supported, {@code false} otherwise 4785 */ isLeAudioDuplexSupported(BluetoothDevice device)4786 public boolean isLeAudioDuplexSupported(BluetoothDevice device) { 4787 int groupId = getGroupId(device); 4788 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 4789 return false; 4790 } 4791 4792 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 4793 if (descriptor == null) { 4794 return false; 4795 } 4796 return (descriptor.mDirection & AUDIO_DIRECTION_OUTPUT_BIT) != 0 4797 && (descriptor.mDirection & AUDIO_DIRECTION_INPUT_BIT) != 0; 4798 } 4799 4800 /** 4801 * Checks if the remote device supports LE Audio output 4802 * 4803 * @param device the remote device to check 4804 * @return {@code true} if LE Audio output is supported, {@code false} otherwise 4805 */ isLeAudioOutputSupported(BluetoothDevice device)4806 public boolean isLeAudioOutputSupported(BluetoothDevice device) { 4807 int groupId = getGroupId(device); 4808 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 4809 return false; 4810 } 4811 4812 LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); 4813 if (descriptor == null) { 4814 return false; 4815 } 4816 return (descriptor.mDirection & AUDIO_DIRECTION_OUTPUT_BIT) != 0; 4817 } 4818 4819 /** 4820 * Gets the lead device for the CSIP group containing the provided device 4821 * 4822 * @param device the remote device whose CSIP group lead device we want to find 4823 * @return the lead device of the CSIP group or {@code null} if the group does not exist 4824 */ getLeadDevice(BluetoothDevice device)4825 public BluetoothDevice getLeadDevice(BluetoothDevice device) { 4826 int groupId = getGroupId(device); 4827 if (groupId == LE_AUDIO_GROUP_ID_INVALID) { 4828 return null; 4829 } 4830 return getConnectedGroupLeadDevice(groupId); 4831 } 4832 4833 /** 4834 * Sends the preferred audio profile change requested from a call to {@link 4835 * BluetoothAdapter#setPreferredAudioProfiles(BluetoothDevice, Bundle)} to the audio framework 4836 * to apply the change. The audio framework will call {@link 4837 * BluetoothAdapter#notifyActiveDeviceChangeApplied(BluetoothDevice)} once the change is 4838 * successfully applied. 4839 * 4840 * @return the number of requests sent to the audio framework 4841 */ sendPreferredAudioProfileChangeToAudioFramework()4842 public int sendPreferredAudioProfileChangeToAudioFramework() { 4843 if (mActiveAudioOutDevice == null && mActiveAudioInDevice == null) { 4844 Log.e(TAG, "sendPreferredAudioProfileChangeToAudioFramework: no active device"); 4845 return 0; 4846 } 4847 4848 int audioFrameworkCalls = 0; 4849 4850 if (mActiveAudioOutDevice != null) { 4851 int volume = getAudioDeviceGroupVolume(getGroupId(mActiveAudioOutDevice)); 4852 final boolean suppressNoisyIntent = mActiveAudioOutDevice != null; 4853 Log.i( 4854 TAG, 4855 "Sending LE Audio Output active device changed for preferred profile " 4856 + "change with volume=" 4857 + volume 4858 + " and suppressNoisyIntent=" 4859 + suppressNoisyIntent); 4860 4861 final BluetoothProfileConnectionInfo connectionInfo; 4862 if (isAtLeastU()) { 4863 connectionInfo = 4864 BluetoothProfileConnectionInfo.createLeAudioOutputInfo( 4865 suppressNoisyIntent, volume); 4866 } else { 4867 connectionInfo = 4868 BluetoothProfileConnectionInfo.createLeAudioInfo(suppressNoisyIntent, true); 4869 } 4870 4871 mAudioManager.handleBluetoothActiveDeviceChanged( 4872 mActiveAudioOutDevice, mActiveAudioOutDevice, connectionInfo); 4873 audioFrameworkCalls++; 4874 } 4875 4876 if (mActiveAudioInDevice != null) { 4877 Log.i(TAG, "Sending LE Audio Input active device changed for audio profile change"); 4878 mAudioManager.handleBluetoothActiveDeviceChanged( 4879 mActiveAudioInDevice, 4880 mActiveAudioInDevice, 4881 BluetoothProfileConnectionInfo.createLeAudioInfo(false, false)); 4882 audioFrameworkCalls++; 4883 } 4884 4885 return audioFrameworkCalls; 4886 } 4887 4888 class DialingOutTimeoutEvent implements Runnable { 4889 Integer mBroadcastId; 4890 DialingOutTimeoutEvent(Integer broadcastId)4891 DialingOutTimeoutEvent(Integer broadcastId) { 4892 mBroadcastId = broadcastId; 4893 } 4894 4895 @Override run()4896 public void run() { 4897 Log.w(TAG, "Failed to start Broadcast in time: " + mBroadcastId); 4898 4899 mDialingOutTimeoutEvent = null; 4900 4901 if (getLeAudioService() == null) { 4902 Log.e(TAG, "DialingOutTimeoutEvent: No LE Audio service"); 4903 return; 4904 } 4905 4906 if (Flags.leaudioBroadcastDestroyAfterTimeout()) { 4907 transitionFromBroadcastToUnicast(); 4908 destroyBroadcast(mBroadcastId); 4909 } else { 4910 if (mActiveBroadcastAudioDevice != null) { 4911 updateBroadcastActiveDevice(null, mActiveBroadcastAudioDevice, false); 4912 } 4913 4914 mHandler.post(() -> notifyBroadcastStartFailed(BluetoothStatusCodes.ERROR_TIMEOUT)); 4915 } 4916 } 4917 } 4918 4919 class AudioModeChangeListener implements AudioManager.OnModeChangedListener { 4920 @Override onModeChanged(int mode)4921 public void onModeChanged(int mode) { 4922 handleAudioModeChange(mode); 4923 } 4924 } 4925 4926 /** Binder object: must be a static class or memory leak may occur */ 4927 @VisibleForTesting 4928 static class BluetoothLeAudioBinder extends IBluetoothLeAudio.Stub 4929 implements IProfileServiceBinder { 4930 private LeAudioService mService; 4931 4932 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)4933 private LeAudioService getService(AttributionSource source) { 4934 if (Utils.isInstrumentationTestMode()) { 4935 return mService; 4936 } 4937 if (!Utils.checkServiceAvailable(mService, TAG) 4938 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) 4939 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 4940 return null; 4941 } 4942 return mService; 4943 } 4944 BluetoothLeAudioBinder(LeAudioService svc)4945 BluetoothLeAudioBinder(LeAudioService svc) { 4946 mService = svc; 4947 } 4948 4949 @Override cleanup()4950 public void cleanup() { 4951 mService = null; 4952 } 4953 4954 @Override connect(BluetoothDevice device, AttributionSource source)4955 public boolean connect(BluetoothDevice device, AttributionSource source) { 4956 Objects.requireNonNull(device, "device cannot be null"); 4957 Objects.requireNonNull(source, "source cannot be null"); 4958 4959 LeAudioService service = getService(source); 4960 if (service == null) { 4961 return false; 4962 } 4963 4964 return service.connect(device); 4965 } 4966 4967 @Override disconnect(BluetoothDevice device, AttributionSource source)4968 public boolean disconnect(BluetoothDevice device, AttributionSource source) { 4969 Objects.requireNonNull(device, "device cannot be null"); 4970 Objects.requireNonNull(source, "source cannot be null"); 4971 4972 LeAudioService service = getService(source); 4973 if (service == null) { 4974 return false; 4975 } 4976 4977 return service.disconnect(device); 4978 } 4979 4980 @Override getConnectedDevices(AttributionSource source)4981 public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { 4982 Objects.requireNonNull(source, "source cannot be null"); 4983 4984 LeAudioService service = getService(source); 4985 if (service == null) { 4986 return Collections.emptyList(); 4987 } 4988 4989 return service.getConnectedDevices(); 4990 } 4991 4992 @Override getConnectedGroupLeadDevice(int groupId, AttributionSource source)4993 public BluetoothDevice getConnectedGroupLeadDevice(int groupId, AttributionSource source) { 4994 Objects.requireNonNull(source, "source cannot be null"); 4995 4996 LeAudioService service = getService(source); 4997 if (service == null) { 4998 return null; 4999 } 5000 5001 return service.getConnectedGroupLeadDevice(groupId); 5002 } 5003 5004 @Override getDevicesMatchingConnectionStates( int[] states, AttributionSource source)5005 public List<BluetoothDevice> getDevicesMatchingConnectionStates( 5006 int[] states, AttributionSource source) { 5007 Objects.requireNonNull(source, "source cannot be null"); 5008 5009 LeAudioService service = getService(source); 5010 if (service == null) { 5011 return Collections.emptyList(); 5012 } 5013 5014 return service.getDevicesMatchingConnectionStates(states); 5015 } 5016 5017 @Override getConnectionState(BluetoothDevice device, AttributionSource source)5018 public int getConnectionState(BluetoothDevice device, AttributionSource source) { 5019 Objects.requireNonNull(device, "device cannot be null"); 5020 Objects.requireNonNull(source, "source cannot be null"); 5021 5022 LeAudioService service = getService(source); 5023 if (service == null) { 5024 return BluetoothProfile.STATE_DISCONNECTED; 5025 } 5026 5027 return service.getConnectionState(device); 5028 } 5029 5030 @Override setActiveDevice(BluetoothDevice device, AttributionSource source)5031 public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) { 5032 Objects.requireNonNull(source, "source cannot be null"); 5033 5034 LeAudioService service = getService(source); 5035 if (service == null) { 5036 return false; 5037 } 5038 5039 if (Flags.audioRoutingCentralization()) { 5040 return ((AudioRoutingManager) service.mAdapterService.getActiveDeviceManager()) 5041 .activateDeviceProfile(device, BluetoothProfile.LE_AUDIO) 5042 .join(); 5043 } 5044 if (device == null) { 5045 return service.removeActiveDevice(true); 5046 } else { 5047 return service.setActiveDevice(device); 5048 } 5049 } 5050 5051 @Override getActiveDevices(AttributionSource source)5052 public List<BluetoothDevice> getActiveDevices(AttributionSource source) { 5053 Objects.requireNonNull(source, "source cannot be null"); 5054 5055 LeAudioService service = getService(source); 5056 if (service == null) { 5057 return Collections.emptyList(); 5058 } 5059 5060 return service.getActiveDevices(); 5061 } 5062 5063 @Override getAudioLocation(BluetoothDevice device, AttributionSource source)5064 public int getAudioLocation(BluetoothDevice device, AttributionSource source) { 5065 Objects.requireNonNull(device, "device cannot be null"); 5066 Objects.requireNonNull(source, "source cannot be null"); 5067 5068 LeAudioService service = getService(source); 5069 if (service == null) { 5070 return BluetoothLeAudio.AUDIO_LOCATION_INVALID; 5071 } 5072 5073 enforceBluetoothPrivilegedPermission(service); 5074 return service.getAudioLocation(device); 5075 } 5076 5077 @Override isInbandRingtoneEnabled(AttributionSource source, int groupId)5078 public boolean isInbandRingtoneEnabled(AttributionSource source, int groupId) { 5079 Objects.requireNonNull(source, "source cannot be null"); 5080 5081 LeAudioService service = getService(source); 5082 if (service == null) { 5083 return false; 5084 } 5085 5086 enforceBluetoothPrivilegedPermission(service); 5087 return service.isInbandRingtoneEnabled(groupId); 5088 } 5089 5090 @Override setConnectionPolicy( BluetoothDevice device, int connectionPolicy, AttributionSource source)5091 public boolean setConnectionPolicy( 5092 BluetoothDevice device, int connectionPolicy, AttributionSource source) { 5093 Objects.requireNonNull(device, "device cannot be null"); 5094 Objects.requireNonNull(source, "source cannot be null"); 5095 5096 LeAudioService service = getService(source); 5097 if (service == null) { 5098 return false; 5099 } 5100 5101 enforceBluetoothPrivilegedPermission(service); 5102 return service.setConnectionPolicy(device, connectionPolicy); 5103 } 5104 5105 @Override getConnectionPolicy(BluetoothDevice device, AttributionSource source)5106 public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { 5107 Objects.requireNonNull(device, "device cannot be null"); 5108 Objects.requireNonNull(source, "source cannot be null"); 5109 5110 LeAudioService service = getService(source); 5111 if (service == null) { 5112 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 5113 } 5114 5115 enforceBluetoothPrivilegedPermission(service); 5116 return service.getConnectionPolicy(device); 5117 } 5118 5119 @Override setCcidInformation( ParcelUuid userUuid, int ccid, int contextType, AttributionSource source)5120 public void setCcidInformation( 5121 ParcelUuid userUuid, int ccid, int contextType, AttributionSource source) { 5122 Objects.requireNonNull(userUuid, "userUuid cannot be null"); 5123 Objects.requireNonNull(source, "source cannot be null"); 5124 5125 LeAudioService service = getService(source); 5126 if (service == null) { 5127 return; 5128 } 5129 5130 enforceBluetoothPrivilegedPermission(service); 5131 service.setCcidInformation(userUuid, ccid, contextType); 5132 } 5133 5134 @Override getGroupId(BluetoothDevice device, AttributionSource source)5135 public int getGroupId(BluetoothDevice device, AttributionSource source) { 5136 Objects.requireNonNull(device, "device cannot be null"); 5137 Objects.requireNonNull(source, "source cannot be null"); 5138 5139 LeAudioService service = getService(source); 5140 if (service == null) { 5141 return LE_AUDIO_GROUP_ID_INVALID; 5142 } 5143 5144 return service.getGroupId(device); 5145 } 5146 5147 @Override groupAddNode(int groupId, BluetoothDevice device, AttributionSource source)5148 public boolean groupAddNode(int groupId, BluetoothDevice device, AttributionSource source) { 5149 Objects.requireNonNull(device, "device cannot be null"); 5150 Objects.requireNonNull(source, "source cannot be null"); 5151 5152 LeAudioService service = getService(source); 5153 if (service == null) { 5154 return false; 5155 } 5156 5157 enforceBluetoothPrivilegedPermission(service); 5158 return service.groupAddNode(groupId, device); 5159 } 5160 5161 @Override setInCall(boolean inCall, AttributionSource source)5162 public void setInCall(boolean inCall, AttributionSource source) { 5163 Objects.requireNonNull(source, "source cannot be null"); 5164 5165 LeAudioService service = getService(source); 5166 if (service == null) { 5167 return; 5168 } 5169 5170 enforceBluetoothPrivilegedPermission(service); 5171 service.setInCall(inCall); 5172 } 5173 5174 @Override setInactiveForHfpHandover( BluetoothDevice hfpHandoverDevice, AttributionSource source)5175 public void setInactiveForHfpHandover( 5176 BluetoothDevice hfpHandoverDevice, AttributionSource source) { 5177 Objects.requireNonNull(source, "source cannot be null"); 5178 5179 LeAudioService service = getService(source); 5180 if (service == null) { 5181 return; 5182 } 5183 5184 enforceBluetoothPrivilegedPermission(service); 5185 service.setInactiveForHfpHandover(hfpHandoverDevice); 5186 } 5187 5188 @Override groupRemoveNode( int groupId, BluetoothDevice device, AttributionSource source)5189 public boolean groupRemoveNode( 5190 int groupId, BluetoothDevice device, AttributionSource source) { 5191 Objects.requireNonNull(device, "device cannot be null"); 5192 Objects.requireNonNull(source, "source cannot be null"); 5193 5194 LeAudioService service = getService(source); 5195 if (service == null) { 5196 return false; 5197 } 5198 5199 enforceBluetoothPrivilegedPermission(service); 5200 return service.groupRemoveNode(groupId, device); 5201 } 5202 5203 @Override setVolume(int volume, AttributionSource source)5204 public void setVolume(int volume, AttributionSource source) { 5205 Objects.requireNonNull(source, "source cannot be null"); 5206 5207 LeAudioService service = getService(source); 5208 if (service == null) { 5209 return; 5210 } 5211 5212 enforceBluetoothPrivilegedPermission(service); 5213 service.setVolume(volume); 5214 } 5215 5216 @Override registerCallback(IBluetoothLeAudioCallback callback, AttributionSource source)5217 public void registerCallback(IBluetoothLeAudioCallback callback, AttributionSource source) { 5218 Objects.requireNonNull(callback, "callback cannot be null"); 5219 Objects.requireNonNull(source, "source cannot be null"); 5220 5221 LeAudioService service = getService(source); 5222 if ((service == null) || (service.mLeAudioCallbacks == null)) { 5223 return; 5224 } 5225 5226 enforceBluetoothPrivilegedPermission(service); 5227 service.mLeAudioCallbacks.register(callback); 5228 if (!service.mBluetoothEnabled) { 5229 service.handleBluetoothEnabled(); 5230 } 5231 } 5232 5233 @Override unregisterCallback( IBluetoothLeAudioCallback callback, AttributionSource source)5234 public void unregisterCallback( 5235 IBluetoothLeAudioCallback callback, AttributionSource source) { 5236 Objects.requireNonNull(callback, "callback cannot be null"); 5237 Objects.requireNonNull(source, "source cannot be null"); 5238 5239 LeAudioService service = getService(source); 5240 if ((service == null) || (service.mLeAudioCallbacks == null)) { 5241 return; 5242 } 5243 5244 enforceBluetoothPrivilegedPermission(service); 5245 service.mLeAudioCallbacks.unregister(callback); 5246 } 5247 5248 @Override registerLeBroadcastCallback( IBluetoothLeBroadcastCallback callback, AttributionSource source)5249 public void registerLeBroadcastCallback( 5250 IBluetoothLeBroadcastCallback callback, AttributionSource source) { 5251 Objects.requireNonNull(callback, "callback cannot be null"); 5252 Objects.requireNonNull(source, "source cannot be null"); 5253 5254 LeAudioService service = getService(source); 5255 if ((service == null) || (service.mBroadcastCallbacks == null)) { 5256 return; 5257 } 5258 5259 enforceBluetoothPrivilegedPermission(service); 5260 service.mBroadcastCallbacks.register(callback); 5261 } 5262 5263 @Override unregisterLeBroadcastCallback( IBluetoothLeBroadcastCallback callback, AttributionSource source)5264 public void unregisterLeBroadcastCallback( 5265 IBluetoothLeBroadcastCallback callback, AttributionSource source) { 5266 Objects.requireNonNull(callback, "callback cannot be null"); 5267 Objects.requireNonNull(source, "source cannot be null"); 5268 5269 LeAudioService service = getService(source); 5270 if ((service == null) || (service.mBroadcastCallbacks == null)) { 5271 return; 5272 } 5273 5274 enforceBluetoothPrivilegedPermission(service); 5275 service.mBroadcastCallbacks.unregister(callback); 5276 } 5277 5278 @Override startBroadcast( BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source)5279 public void startBroadcast( 5280 BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source) { 5281 LeAudioService service = getService(source); 5282 if (service == null) { 5283 return; 5284 } 5285 5286 enforceBluetoothPrivilegedPermission(service); 5287 service.createBroadcast(broadcastSettings); 5288 } 5289 5290 @Override stopBroadcast(int broadcastId, AttributionSource source)5291 public void stopBroadcast(int broadcastId, AttributionSource source) { 5292 LeAudioService service = getService(source); 5293 if (service == null) { 5294 return; 5295 } 5296 5297 enforceBluetoothPrivilegedPermission(service); 5298 service.stopBroadcast(broadcastId); 5299 } 5300 5301 @Override updateBroadcast( int broadcastId, BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source)5302 public void updateBroadcast( 5303 int broadcastId, 5304 BluetoothLeBroadcastSettings broadcastSettings, 5305 AttributionSource source) { 5306 LeAudioService service = getService(source); 5307 if (service == null) { 5308 return; 5309 } 5310 5311 enforceBluetoothPrivilegedPermission(service); 5312 service.updateBroadcast(broadcastId, broadcastSettings); 5313 } 5314 5315 @Override isPlaying(int broadcastId, AttributionSource source)5316 public boolean isPlaying(int broadcastId, AttributionSource source) { 5317 LeAudioService service = getService(source); 5318 if (service == null) { 5319 return false; 5320 } 5321 5322 enforceBluetoothPrivilegedPermission(service); 5323 return service.isPlaying(broadcastId); 5324 } 5325 5326 @Override getAllBroadcastMetadata( AttributionSource source)5327 public List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata( 5328 AttributionSource source) { 5329 LeAudioService service = getService(source); 5330 if (service == null) { 5331 return Collections.emptyList(); 5332 } 5333 5334 enforceBluetoothPrivilegedPermission(service); 5335 return service.getAllBroadcastMetadata(); 5336 } 5337 5338 @Override getMaximumNumberOfBroadcasts(AttributionSource source)5339 public int getMaximumNumberOfBroadcasts(AttributionSource source) { 5340 LeAudioService service = getService(source); 5341 if (service == null) { 5342 return 0; 5343 } 5344 5345 enforceBluetoothPrivilegedPermission(service); 5346 return service.getMaximumNumberOfBroadcasts(); 5347 } 5348 5349 @Override getMaximumStreamsPerBroadcast(AttributionSource source)5350 public int getMaximumStreamsPerBroadcast(AttributionSource source) { 5351 LeAudioService service = getService(source); 5352 if (service == null) { 5353 return 0; 5354 } 5355 5356 enforceBluetoothPrivilegedPermission(service); 5357 return service.getMaximumStreamsPerBroadcast(); 5358 } 5359 5360 @Override getMaximumSubgroupsPerBroadcast(AttributionSource source)5361 public int getMaximumSubgroupsPerBroadcast(AttributionSource source) { 5362 LeAudioService service = getService(source); 5363 if (service == null) { 5364 return 0; 5365 } 5366 5367 enforceBluetoothPrivilegedPermission(service); 5368 return service.getMaximumSubgroupsPerBroadcast(); 5369 } 5370 5371 @Override getCodecStatus(int groupId, AttributionSource source)5372 public BluetoothLeAudioCodecStatus getCodecStatus(int groupId, AttributionSource source) { 5373 LeAudioService service = getService(source); 5374 if (service == null) { 5375 return null; 5376 } 5377 5378 enforceBluetoothPrivilegedPermission(service); 5379 return service.getCodecStatus(groupId); 5380 } 5381 5382 @Override setCodecConfigPreference( int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig, AttributionSource source)5383 public void setCodecConfigPreference( 5384 int groupId, 5385 BluetoothLeAudioCodecConfig inputCodecConfig, 5386 BluetoothLeAudioCodecConfig outputCodecConfig, 5387 AttributionSource source) { 5388 LeAudioService service = getService(source); 5389 if (service == null) { 5390 return; 5391 } 5392 5393 enforceBluetoothPrivilegedPermission(service); 5394 service.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig); 5395 } 5396 5397 @Override isBroadcastActive(AttributionSource source)5398 public boolean isBroadcastActive(AttributionSource source) { 5399 LeAudioService service = getService(source); 5400 if (service == null) { 5401 return false; 5402 } 5403 5404 enforceBluetoothPrivilegedPermission(service); 5405 return service.isBroadcastActive(); 5406 } 5407 } 5408 5409 @Override dump(StringBuilder sb)5410 public void dump(StringBuilder sb) { 5411 super.dump(sb); 5412 ProfileService.println(sb, "isDualModeAudioEnabled: " + Utils.isDualModeAudioEnabled()); 5413 ProfileService.println(sb, "Active Groups information: "); 5414 ProfileService.println(sb, " currentlyActiveGroupId: " + getActiveGroupId()); 5415 ProfileService.println(sb, " mActiveAudioOutDevice: " + mActiveAudioOutDevice); 5416 ProfileService.println(sb, " mActiveAudioInDevice: " + mActiveAudioInDevice); 5417 ProfileService.println( 5418 sb, 5419 " mUnicastGroupIdDeactivatedForBroadcastTransition: " 5420 + mUnicastGroupIdDeactivatedForBroadcastTransition); 5421 ProfileService.println( 5422 sb, 5423 " mBroadcastIdDeactivatedForUnicastTransition: " 5424 + mBroadcastIdDeactivatedForUnicastTransition); 5425 ProfileService.println(sb, " mExposedActiveDevice: " + mExposedActiveDevice); 5426 ProfileService.println(sb, " mHfpHandoverDevice:" + mHfpHandoverDevice); 5427 ProfileService.println( 5428 sb, 5429 " mLeAudioDeviceInactivatedForHfpHandover:" 5430 + mLeAudioDeviceInactivatedForHfpHandover); 5431 ProfileService.println( 5432 sb, 5433 " mLeAudioIsInbandRingtoneSupported:" + mLeAudioInbandRingtoneSupportedByPlatform); 5434 5435 int numberOfUngroupedDevs = 0; 5436 mGroupReadLock.lock(); 5437 try { 5438 for (Map.Entry<Integer, LeAudioGroupDescriptor> groupEntry : 5439 mGroupDescriptorsView.entrySet()) { 5440 LeAudioGroupDescriptor groupDescriptor = groupEntry.getValue(); 5441 Integer groupId = groupEntry.getKey(); 5442 BluetoothDevice leadDevice = getConnectedGroupLeadDevice(groupId); 5443 5444 ProfileService.println(sb, "Group: " + groupId); 5445 ProfileService.println( 5446 sb, " activeState: " + groupDescriptor.getActiveStateString()); 5447 ProfileService.println(sb, " isConnected: " + groupDescriptor.mIsConnected); 5448 ProfileService.println(sb, " mDirection: " + groupDescriptor.mDirection); 5449 ProfileService.println(sb, " group lead: " + leadDevice); 5450 ProfileService.println( 5451 sb, " lost lead device: " + groupDescriptor.mLostLeadDeviceWhileStreaming); 5452 ProfileService.println( 5453 sb, " mInbandRingtoneEnabled: " + groupDescriptor.mInbandRingtoneEnabled); 5454 ProfileService.println( 5455 sb, 5456 "mInactivatedDueToContextType: " 5457 + groupDescriptor.mInactivatedDueToContextType); 5458 5459 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> deviceEntry : 5460 mDeviceDescriptors.entrySet()) { 5461 LeAudioDeviceDescriptor deviceDescriptor = deviceEntry.getValue(); 5462 if (!Objects.equals(deviceDescriptor.mGroupId, groupId)) { 5463 if (deviceDescriptor.mGroupId == LE_AUDIO_GROUP_ID_INVALID) { 5464 numberOfUngroupedDevs++; 5465 } 5466 continue; 5467 } 5468 5469 if (deviceDescriptor.mStateMachine != null) { 5470 deviceDescriptor.mStateMachine.dump(sb); 5471 } else { 5472 ProfileService.println(sb, "state machine is null"); 5473 } 5474 ProfileService.println( 5475 sb, " mAclConnected: " + deviceDescriptor.mAclConnected); 5476 ProfileService.println( 5477 sb, 5478 " mDevInbandRingtoneEnabled: " 5479 + deviceDescriptor.mDevInbandRingtoneEnabled); 5480 ProfileService.println( 5481 sb, " mSinkAudioLocation: " + deviceDescriptor.mSinkAudioLocation); 5482 ProfileService.println(sb, " mDirection: " + deviceDescriptor.mDirection); 5483 } 5484 } 5485 } finally { 5486 mGroupReadLock.unlock(); 5487 } 5488 5489 if (numberOfUngroupedDevs > 0) { 5490 ProfileService.println(sb, "UnGroup devices:"); 5491 for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry : 5492 mDeviceDescriptors.entrySet()) { 5493 LeAudioDeviceDescriptor deviceDescriptor = entry.getValue(); 5494 if (deviceDescriptor.mGroupId != LE_AUDIO_GROUP_ID_INVALID) { 5495 continue; 5496 } 5497 5498 deviceDescriptor.mStateMachine.dump(sb); 5499 ProfileService.println(sb, " mAclConnected: " + deviceDescriptor.mAclConnected); 5500 ProfileService.println( 5501 sb, 5502 " mDevInbandRingtoneEnabled: " 5503 + deviceDescriptor.mDevInbandRingtoneEnabled); 5504 ProfileService.println( 5505 sb, " mSinkAudioLocation: " + deviceDescriptor.mSinkAudioLocation); 5506 ProfileService.println(sb, " mDirection: " + deviceDescriptor.mDirection); 5507 } 5508 } 5509 } 5510 } 5511