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