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