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