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