• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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