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.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
21 
22 import android.annotation.RequiresPermission;
23 import android.annotation.SuppressLint;
24 import android.bluetooth.BluetoothDevice;
25 import android.bluetooth.BluetoothLeAudio;
26 import android.bluetooth.BluetoothProfile;
27 import android.bluetooth.BluetoothUuid;
28 import android.bluetooth.IBluetoothLeAudio;
29 import android.content.Attributable;
30 import android.content.AttributionSource;
31 import android.content.BroadcastReceiver;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.media.AudioManager;
36 import android.os.HandlerThread;
37 import android.os.ParcelUuid;
38 import android.util.Log;
39 
40 import com.android.bluetooth.Utils;
41 import com.android.bluetooth.btservice.AdapterService;
42 import com.android.bluetooth.btservice.ProfileService;
43 import com.android.bluetooth.btservice.storage.DatabaseManager;
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.internal.util.ArrayUtils;
46 
47 import java.util.ArrayList;
48 import java.util.HashMap;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Objects;
52 import java.util.concurrent.ConcurrentHashMap;
53 
54 /**
55  * Provides Bluetooth LeAudio profile, as a service in the Bluetooth application.
56  * @hide
57  */
58 public class LeAudioService extends ProfileService {
59     private static final boolean DBG = true;
60     private static final String TAG = "LeAudioService";
61 
62     // Upper limit of all LeAudio devices: Bonded or Connected
63     private static final int MAX_LE_AUDIO_STATE_MACHINES = 10;
64     private static LeAudioService sLeAudioService;
65 
66     private AdapterService mAdapterService;
67     private DatabaseManager mDatabaseManager;
68     private HandlerThread mStateMachinesThread;
69     private BluetoothDevice mPreviousAudioDevice;
70 
71     LeAudioNativeInterface mLeAudioNativeInterface;
72     AudioManager mAudioManager;
73 
74     private final Map<BluetoothDevice, LeAudioStateMachine> mStateMachines = new HashMap<>();
75 
76     private final Map<BluetoothDevice, Integer> mDeviceGroupIdMap = new ConcurrentHashMap<>();
77     private final Map<Integer, Boolean> mGroupIdConnectedMap = new HashMap<>();
78     private int mActiveDeviceGroupId = LE_AUDIO_GROUP_ID_INVALID;
79 
80     private BroadcastReceiver mBondStateChangedReceiver;
81     private BroadcastReceiver mConnectionStateChangedReceiver;
82 
83     @Override
initBinder()84     protected IProfileServiceBinder initBinder() {
85         return new BluetoothLeAudioBinder(this);
86     }
87 
88     @Override
create()89     protected void create() {
90         Log.i(TAG, "create()");
91     }
92 
93     @Override
start()94     protected boolean start() {
95         Log.i(TAG, "start()");
96         if (sLeAudioService != null) {
97             throw new IllegalStateException("start() called twice");
98         }
99 
100         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
101                 "AdapterService cannot be null when LeAudioService starts");
102         mLeAudioNativeInterface = Objects.requireNonNull(LeAudioNativeInterface.getInstance(),
103                 "LeAudioNativeInterface cannot be null when LeAudioService starts");
104         mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
105                 "DatabaseManager cannot be null when A2dpService starts");
106         mAudioManager = getSystemService(AudioManager.class);
107         Objects.requireNonNull(mAudioManager,
108                 "AudioManager cannot be null when LeAudioService starts");
109 
110         // Start handler thread for state machines
111         mStateMachines.clear();
112         mStateMachinesThread = new HandlerThread("LeAudioService.StateMachines");
113         mStateMachinesThread.start();
114 
115         mDeviceGroupIdMap.clear();
116         mGroupIdConnectedMap.clear();
117 
118         // Setup broadcast receivers
119         IntentFilter filter = new IntentFilter();
120         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
121         mBondStateChangedReceiver = new BondStateChangedReceiver();
122         registerReceiver(mBondStateChangedReceiver, filter);
123         filter = new IntentFilter();
124         filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
125         mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver();
126         registerReceiver(mConnectionStateChangedReceiver, filter);
127 
128         // Mark service as started
129         setLeAudioService(this);
130 
131         mLeAudioNativeInterface.init();
132 
133         return true;
134     }
135 
136     @Override
stop()137     protected boolean stop() {
138         Log.i(TAG, "stop()");
139         if (sLeAudioService == null) {
140             Log.w(TAG, "stop() called before start()");
141             return true;
142         }
143 
144         // Cleanup native interfaces
145         mLeAudioNativeInterface.cleanup();
146         mLeAudioNativeInterface = null;
147 
148         // Set the service and BLE devices as inactive
149         setLeAudioService(null);
150 
151         // Unregister broadcast receivers
152         unregisterReceiver(mBondStateChangedReceiver);
153         mBondStateChangedReceiver = null;
154         unregisterReceiver(mConnectionStateChangedReceiver);
155         mConnectionStateChangedReceiver = null;
156 
157         // Destroy state machines and stop handler thread
158         synchronized (mStateMachines) {
159             for (LeAudioStateMachine sm : mStateMachines.values()) {
160                 sm.doQuit();
161                 sm.cleanup();
162             }
163             mStateMachines.clear();
164         }
165 
166         mDeviceGroupIdMap.clear();
167         mGroupIdConnectedMap.clear();
168 
169         if (mStateMachinesThread != null) {
170             mStateMachinesThread.quitSafely();
171             mStateMachinesThread = null;
172         }
173 
174         mAudioManager = null;
175         mAdapterService = null;
176         return true;
177     }
178 
179     @Override
cleanup()180     protected void cleanup() {
181         Log.i(TAG, "cleanup()");
182     }
183 
getLeAudioService()184     public static synchronized LeAudioService getLeAudioService() {
185         if (sLeAudioService == null) {
186             Log.w(TAG, "getLeAudioService(): service is NULL");
187             return null;
188         }
189         if (!sLeAudioService.isAvailable()) {
190             Log.w(TAG, "getLeAudioService(): service is not available");
191             return null;
192         }
193         return sLeAudioService;
194     }
195 
setLeAudioService(LeAudioService instance)196     private static synchronized void setLeAudioService(LeAudioService instance) {
197         if (DBG) {
198             Log.d(TAG, "setLeAudioService(): set to: " + instance);
199         }
200         sLeAudioService = instance;
201     }
202 
connect(BluetoothDevice device)203     public boolean connect(BluetoothDevice device) {
204         if (DBG) {
205             Log.d(TAG, "connect(): " + device);
206         }
207 
208         if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
209             Log.e(TAG, "Cannot connect to " + device + " : CONNECTION_POLICY_FORBIDDEN");
210             return false;
211         }
212         ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
213         if (!ArrayUtils.contains(featureUuids, BluetoothUuid.LE_AUDIO)) {
214             Log.e(TAG, "Cannot connect to " + device + " : Remote does not have LE_AUDIO UUID");
215             return false;
216         }
217 
218         int groupId = getGroupId(device);
219 
220         //TODO: disconnect active device if it's not in groupId
221 
222         if (DBG) {
223             Log.d(TAG, "connect(): " + device + "group id: " + groupId);
224         }
225 
226         synchronized (mStateMachines) {
227             LeAudioStateMachine sm = getOrCreateStateMachine(device);
228             if (sm == null) {
229                 Log.e(TAG, "Ignored connect request for " + device + " : no state machine");
230                 return false;
231             }
232             sm.sendMessage(LeAudioStateMachine.CONNECT, groupId);
233         }
234 
235         // Connect other devices from this group
236         if (groupId != LE_AUDIO_GROUP_ID_INVALID) {
237             for (BluetoothDevice storedDevice : mDeviceGroupIdMap.keySet()) {
238                 if (device.equals(storedDevice)) {
239                     continue;
240                 }
241                 if (getGroupId(storedDevice) != groupId) {
242                     continue;
243                 }
244                 synchronized (mStateMachines) {
245                     LeAudioStateMachine sm = getOrCreateStateMachine(storedDevice);
246                     if (sm == null) {
247                         Log.e(TAG, "Ignored connect request for " + storedDevice
248                                 + " : no state machine");
249                         continue;
250                     }
251                     sm.sendMessage(LeAudioStateMachine.CONNECT, groupId);
252                 }
253             }
254         }
255         return true;
256     }
257 
258     /**
259      * Disconnects LE Audio for the remote bluetooth device
260      *
261      * @param device is the device with which we would like to disconnect LE Audio
262      * @return true if profile disconnected, false if device not connected over LE Audio
263      */
disconnect(BluetoothDevice device)264     public boolean disconnect(BluetoothDevice device) {
265         if (DBG) {
266             Log.d(TAG, "disconnect(): " + device);
267         }
268 
269         // Disconnect this device
270         synchronized (mStateMachines) {
271             LeAudioStateMachine sm = mStateMachines.get(device);
272             if (sm == null) {
273                 Log.e(TAG, "Ignored disconnect request for " + device
274                         + " : no state machine");
275                 return false;
276             }
277             sm.sendMessage(LeAudioStateMachine.DISCONNECT);
278         }
279 
280         // Disconnect other devices from this group
281         int groupId = getGroupId(device);
282         if (groupId != LE_AUDIO_GROUP_ID_INVALID) {
283             for (BluetoothDevice storedDevice : mDeviceGroupIdMap.keySet()) {
284                 if (device.equals(storedDevice)) {
285                     continue;
286                 }
287                 if (getGroupId(storedDevice) != groupId) {
288                     continue;
289                 }
290                 synchronized (mStateMachines) {
291                     LeAudioStateMachine sm = mStateMachines.get(storedDevice);
292                     if (sm == null) {
293                         Log.e(TAG, "Ignored disconnect request for " + storedDevice
294                                 + " : no state machine");
295                         continue;
296                     }
297                     sm.sendMessage(LeAudioStateMachine.DISCONNECT);
298                 }
299             }
300         }
301         return true;
302     }
303 
getConnectedDevices()304     List<BluetoothDevice> getConnectedDevices() {
305         synchronized (mStateMachines) {
306             List<BluetoothDevice> devices = new ArrayList<>();
307             for (LeAudioStateMachine sm : mStateMachines.values()) {
308                 if (sm.isConnected()) {
309                     devices.add(sm.getDevice());
310                 }
311             }
312             return devices;
313         }
314     }
315 
getDevicesMatchingConnectionStates(int[] states)316     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
317         ArrayList<BluetoothDevice> devices = new ArrayList<>();
318         if (states == null) {
319             return devices;
320         }
321         final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
322         if (bondedDevices == null) {
323             return devices;
324         }
325         synchronized (mStateMachines) {
326             for (BluetoothDevice device : bondedDevices) {
327                 final ParcelUuid[] featureUuids = device.getUuids();
328                 if (!ArrayUtils.contains(featureUuids, BluetoothUuid.LE_AUDIO)) {
329                     continue;
330                 }
331                 int connectionState = BluetoothProfile.STATE_DISCONNECTED;
332                 LeAudioStateMachine sm = mStateMachines.get(device);
333                 if (sm != null) {
334                     connectionState = sm.getConnectionState();
335                 }
336                 for (int state : states) {
337                     if (connectionState == state) {
338                         devices.add(device);
339                         break;
340                     }
341                 }
342             }
343             return devices;
344         }
345     }
346 
347     /**
348      * Get the list of devices that have state machines.
349      *
350      * @return the list of devices that have state machines
351      */
352     @VisibleForTesting
getDevices()353     List<BluetoothDevice> getDevices() {
354         List<BluetoothDevice> devices = new ArrayList<>();
355         synchronized (mStateMachines) {
356             for (LeAudioStateMachine sm : mStateMachines.values()) {
357                 devices.add(sm.getDevice());
358             }
359             return devices;
360         }
361     }
362 
363     /**
364      * Get the current connection state of the profile
365      *
366      * @param device is the remote bluetooth device
367      * @return {@link BluetoothProfile#STATE_DISCONNECTED} if this profile is disconnected,
368      * {@link BluetoothProfile#STATE_CONNECTING} if this profile is being connected,
369      * {@link BluetoothProfile#STATE_CONNECTED} if this profile is connected, or
370      * {@link BluetoothProfile#STATE_DISCONNECTING} if this profile is being disconnected
371      */
getConnectionState(BluetoothDevice device)372     public int getConnectionState(BluetoothDevice device) {
373         synchronized (mStateMachines) {
374             LeAudioStateMachine sm = mStateMachines.get(device);
375             if (sm == null) {
376                 return BluetoothProfile.STATE_DISCONNECTED;
377             }
378             return sm.getConnectionState();
379         }
380     }
381 
382     /**
383      * Set the active device.
384      *
385      * @param device the new active device
386      * @return true on success, otherwise false
387      */
setActiveDevice(BluetoothDevice device)388     public boolean setActiveDevice(BluetoothDevice device) {
389         if (DBG) {
390             Log.d(TAG, "setActiveDevice:" + device);
391         }
392 
393         return false;
394     }
395 
396     /**
397      * Get the connected physical LeAudio devices that are active.
398      *
399      * @return the list of active devices.
400      */
getActiveDevices()401     List<BluetoothDevice> getActiveDevices() {
402         if (DBG) {
403             Log.d(TAG, "getActiveDevices");
404         }
405         ArrayList<BluetoothDevice> activeDevices = new ArrayList<>();
406         return activeDevices;
407     }
408 
409     // Suppressed since this is part of a local process
410     @SuppressLint("AndroidFrameworkRequiresPermission")
messageFromNative(LeAudioStackEvent stackEvent)411     void messageFromNative(LeAudioStackEvent stackEvent) {
412         Log.d(TAG, "Message from native: " + stackEvent);
413         BluetoothDevice device = stackEvent.device;
414 
415         if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
416         // Some events require device state machine
417             synchronized (mStateMachines) {
418                 LeAudioStateMachine sm = mStateMachines.get(device);
419                 if (sm == null) {
420                     switch (stackEvent.valueInt1) {
421                         case LeAudioStackEvent.CONNECTION_STATE_CONNECTED:
422                         case LeAudioStackEvent.CONNECTION_STATE_CONNECTING:
423                             sm = getOrCreateStateMachine(device);
424                             break;
425                         default:
426                             break;
427                     }
428                 }
429 
430                 if (sm == null) {
431                     Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent);
432                     return;
433                 }
434 
435                 sm.sendMessage(LeAudioStateMachine.STACK_EVENT, stackEvent);
436                 return;
437             }
438         }
439 
440         // Some events do not require device state machine
441         if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED) {
442             int group_id = stackEvent.valueInt1;
443             int group_status = stackEvent.valueInt2;
444             int group_flags = stackEvent.valueInt3;
445 
446             // TODO: Handle Stream events
447             switch (group_status) {
448                 case LeAudioStackEvent.GROUP_STATUS_IDLE:
449                 case LeAudioStackEvent.GROUP_STATUS_RECONFIGURED:
450                 case LeAudioStackEvent.GROUP_STATUS_DESTROYED:
451                 case LeAudioStackEvent.GROUP_STATUS_SUSPENDED:
452                     setActiveDevice(null);
453                     // TODO: Get all devices with a group and Unassign? or they are already unasigned
454                     // This event may come after removing all the nodes from a certain group but only that.
455                     break;
456                 case LeAudioStackEvent.GROUP_STATUS_STREAMING:
457                     BluetoothDevice streaming_device = getConnectedPeerDevices(group_id).get(0);
458                     setActiveDevice(streaming_device);
459                     break;
460                 default:
461                     break;
462             }
463 
464         }
465     }
466 
getOrCreateStateMachine(BluetoothDevice device)467     private LeAudioStateMachine getOrCreateStateMachine(BluetoothDevice device) {
468         if (device == null) {
469             Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null");
470             return null;
471         }
472         synchronized (mStateMachines) {
473             LeAudioStateMachine sm = mStateMachines.get(device);
474             if (sm != null) {
475                 return sm;
476             }
477             // Limit the maximum number of state machines to avoid DoS attack
478             if (mStateMachines.size() >= MAX_LE_AUDIO_STATE_MACHINES) {
479                 Log.e(TAG, "Maximum number of LeAudio state machines reached: "
480                         + MAX_LE_AUDIO_STATE_MACHINES);
481                 return null;
482             }
483             if (DBG) {
484                 Log.d(TAG, "Creating a new state machine for " + device);
485             }
486             sm = LeAudioStateMachine.make(device, this,
487                     mLeAudioNativeInterface, mStateMachinesThread.getLooper());
488             mStateMachines.put(device, sm);
489             return sm;
490         }
491     }
492 
493     // Remove state machine if the bonding for a device is removed
494     private class BondStateChangedReceiver extends BroadcastReceiver {
495         @Override
onReceive(Context context, Intent intent)496         public void onReceive(Context context, Intent intent) {
497             if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
498                 return;
499             }
500             int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
501                                            BluetoothDevice.ERROR);
502             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
503             Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
504             bondStateChanged(device, state);
505         }
506     }
507 
508     /**
509      * Process a change in the bonding state for a device.
510      *
511      * @param device the device whose bonding state has changed
512      * @param bondState the new bond state for the device. Possible values are:
513      * {@link BluetoothDevice#BOND_NONE},
514      * {@link BluetoothDevice#BOND_BONDING},
515      * {@link BluetoothDevice#BOND_BONDED}.
516      */
517     @VisibleForTesting
bondStateChanged(BluetoothDevice device, int bondState)518     void bondStateChanged(BluetoothDevice device, int bondState) {
519         if (DBG) {
520             Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState);
521         }
522         // Remove state machine if the bonding for a device is removed
523         if (bondState != BluetoothDevice.BOND_NONE) {
524             return;
525         }
526         mDeviceGroupIdMap.remove(device);
527         synchronized (mStateMachines) {
528             LeAudioStateMachine sm = mStateMachines.get(device);
529             if (sm == null) {
530                 return;
531             }
532             if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
533                 return;
534             }
535             removeStateMachine(device);
536         }
537     }
538 
removeStateMachine(BluetoothDevice device)539     private void removeStateMachine(BluetoothDevice device) {
540         synchronized (mStateMachines) {
541             LeAudioStateMachine sm = mStateMachines.get(device);
542             if (sm == null) {
543                 Log.w(TAG, "removeStateMachine: device " + device
544                         + " does not have a state machine");
545                 return;
546             }
547             Log.i(TAG, "removeStateMachine: removing state machine for device: " + device);
548             sm.doQuit();
549             sm.cleanup();
550             mStateMachines.remove(device);
551         }
552     }
553 
getConnectedPeerDevices(int groupId)554     private List<BluetoothDevice> getConnectedPeerDevices(int groupId) {
555         List<BluetoothDevice> result = new ArrayList<>();
556         for (BluetoothDevice peerDevice : getConnectedDevices()) {
557             if (getGroupId(peerDevice) == groupId) {
558                 result.add(peerDevice);
559             }
560         }
561         return result;
562     }
563 
564     @VisibleForTesting
connectionStateChanged(BluetoothDevice device, int fromState, int toState)565     synchronized void connectionStateChanged(BluetoothDevice device, int fromState,
566                                                      int toState) {
567         if ((device == null) || (fromState == toState)) {
568             Log.e(TAG, "connectionStateChanged: unexpected invocation. device=" + device
569                     + " fromState=" + fromState + " toState=" + toState);
570             return;
571         }
572         if (toState == BluetoothProfile.STATE_CONNECTED) {
573             int myGroupId = getGroupId(device);
574             if (myGroupId == LE_AUDIO_GROUP_ID_INVALID
575                     || getConnectedPeerDevices(myGroupId).size() == 1) {
576                 // Log LE Audio connection event if we are the first device in a set
577                 // Or when the GroupId has not been found
578                 // MetricsLogger.logProfileConnectionEvent(
579                 //         BluetoothMetricsProto.ProfileId.LE_AUDIO);
580             }
581             if (!mGroupIdConnectedMap.getOrDefault(myGroupId, false)) {
582                 mGroupIdConnectedMap.put(myGroupId, true);
583             }
584         }
585         if (fromState == BluetoothProfile.STATE_CONNECTED && getConnectedDevices().isEmpty()) {
586             setActiveDevice(null);
587             int myGroupId = getGroupId(device);
588             mGroupIdConnectedMap.put(myGroupId, false);
589         }
590         // Check if the device is disconnected - if unbond, remove the state machine
591         if (toState == BluetoothProfile.STATE_DISCONNECTED) {
592             int bondState = mAdapterService.getBondState(device);
593             if (bondState == BluetoothDevice.BOND_NONE) {
594                 if (DBG) {
595                     Log.d(TAG, device + " is unbond. Remove state machine");
596                 }
597                 removeStateMachine(device);
598             }
599         }
600     }
601 
602     private class ConnectionStateChangedReceiver extends BroadcastReceiver {
603         @Override
onReceive(Context context, Intent intent)604         public void onReceive(Context context, Intent intent) {
605             if (!BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
606                 return;
607             }
608             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
609             int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
610             int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
611             connectionStateChanged(device, fromState, toState);
612         }
613     }
614 
615    /**
616      * Check whether can connect to a peer device.
617      * The check considers a number of factors during the evaluation.
618      *
619      * @param device the peer device to connect to
620      * @return true if connection is allowed, otherwise false
621      */
okToConnect(BluetoothDevice device)622     public boolean okToConnect(BluetoothDevice device) {
623         // Check if this is an incoming connection in Quiet mode.
624         if (mAdapterService.isQuietModeEnabled()) {
625             Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled");
626             return false;
627         }
628         // Check connectionPolicy and accept or reject the connection.
629         int connectionPolicy = getConnectionPolicy(device);
630         int bondState = mAdapterService.getBondState(device);
631         // Allow this connection only if the device is bonded. Any attempt to connect while
632         // bonding would potentially lead to an unauthorized connection.
633         if (bondState != BluetoothDevice.BOND_BONDED) {
634             Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
635             return false;
636         } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN
637                 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
638             // Otherwise, reject the connection if connectionPolicy is not valid.
639             Log.w(TAG, "okToConnect: return false, connectionPolicy=" + connectionPolicy);
640             return false;
641         }
642         return true;
643     }
644 
645     /**
646      * Set connection policy of the profile and connects it if connectionPolicy is
647      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is
648      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
649      *
650      * <p> The device should already be paired.
651      * Connection policy can be one of:
652      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
653      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
654      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
655      *
656      * @param device the remote device
657      * @param connectionPolicy is the connection policy to set to for this profile
658      * @return true on success, otherwise false
659      */
660     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)661     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
662         enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
663                 "Need BLUETOOTH_PRIVILEGED permission");
664         if (DBG) {
665             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
666         }
667 
668         if (!mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO,
669                   connectionPolicy)) {
670             return false;
671         }
672         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
673             connect(device);
674         } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
675             disconnect(device);
676         }
677         return true;
678     }
679 
680     /**
681      * Get the connection policy of the profile.
682      *
683      * <p> The connection policy can be any of:
684      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
685      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
686      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
687      *
688      * @param device Bluetooth device
689      * @return connection policy of the device
690      * @hide
691      */
getConnectionPolicy(BluetoothDevice device)692     public int getConnectionPolicy(BluetoothDevice device) {
693         return mDatabaseManager
694                 .getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO);
695     }
696 
697     /**
698      * Get device group id. Devices with same group id belong to same group (i.e left and right
699      * earbud)
700      * @param device LE Audio capable device
701      * @return group id that this device currently belongs to
702      */
getGroupId(BluetoothDevice device)703     public int getGroupId(BluetoothDevice device) {
704         if (device == null) {
705             return LE_AUDIO_GROUP_ID_INVALID;
706         }
707         //TODO: implement
708         return LE_AUDIO_GROUP_ID_INVALID;
709     }
710 
711     /**
712      * Binder object: must be a static class or memory leak may occur
713      */
714     @VisibleForTesting
715     static class BluetoothLeAudioBinder extends IBluetoothLeAudio.Stub
716             implements IProfileServiceBinder {
717         private LeAudioService mService;
718 
719         @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
getService(AttributionSource source)720         private LeAudioService getService(AttributionSource source) {
721             if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
722                     || !Utils.checkServiceAvailable(mService, TAG)
723                     || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
724                 return null;
725             }
726             return mService;
727         }
728 
BluetoothLeAudioBinder(LeAudioService svc)729         BluetoothLeAudioBinder(LeAudioService svc) {
730             mService = svc;
731         }
732 
733         @Override
cleanup()734         public void cleanup() {
735             mService = null;
736         }
737 
738         @Override
connect(BluetoothDevice device, AttributionSource source)739         public boolean connect(BluetoothDevice device, AttributionSource source) {
740             Attributable.setAttributionSource(device, source);
741             LeAudioService service = getService(source);
742             if (service == null) {
743                 return false;
744             }
745             return service.connect(device);
746         }
747 
748         @Override
disconnect(BluetoothDevice device, AttributionSource source)749         public boolean disconnect(BluetoothDevice device, AttributionSource source) {
750             Attributable.setAttributionSource(device, source);
751             LeAudioService service = getService(source);
752             if (service == null) {
753                 return false;
754             }
755             return service.disconnect(device);
756         }
757 
758         @Override
getConnectedDevices(AttributionSource source)759         public List<BluetoothDevice> getConnectedDevices(AttributionSource source) {
760             LeAudioService service = getService(source);
761             if (service == null) {
762                 return new ArrayList<>(0);
763             }
764             return service.getConnectedDevices();
765         }
766 
767         @Override
getDevicesMatchingConnectionStates(int[] states, AttributionSource source)768         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states,
769                 AttributionSource source) {
770             LeAudioService service = getService(source);
771             if (service == null) {
772                 return new ArrayList<>(0);
773             }
774             return service.getDevicesMatchingConnectionStates(states);
775         }
776 
777         @Override
getConnectionState(BluetoothDevice device, AttributionSource source)778         public int getConnectionState(BluetoothDevice device, AttributionSource source) {
779             Attributable.setAttributionSource(device, source);
780             LeAudioService service = getService(source);
781             if (service == null) {
782                 return BluetoothProfile.STATE_DISCONNECTED;
783             }
784             return service.getConnectionState(device);
785         }
786 
787         @Override
setActiveDevice(BluetoothDevice device, AttributionSource source)788         public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) {
789             Attributable.setAttributionSource(device, source);
790             LeAudioService service = getService(source);
791             if (service == null) {
792                 return false;
793             }
794             return service.setActiveDevice(device);
795         }
796 
797         @Override
getActiveDevices(AttributionSource source)798         public List<BluetoothDevice> getActiveDevices(AttributionSource source) {
799             LeAudioService service = getService(source);
800             if (service == null) {
801                 return new ArrayList<>();
802             }
803             return service.getActiveDevices();
804         }
805 
806         @Override
setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source)807         public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy,
808                 AttributionSource source) {
809             Attributable.setAttributionSource(device, source);
810             LeAudioService service = getService(source);
811             if (service == null) {
812                 return false;
813             }
814             return service.setConnectionPolicy(device, connectionPolicy);
815         }
816 
817         @Override
getConnectionPolicy(BluetoothDevice device, AttributionSource source)818         public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) {
819             Attributable.setAttributionSource(device, source);
820             LeAudioService service = getService(source);
821             if (service == null) {
822                 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
823             }
824             return service.getConnectionPolicy(device);
825         }
826 
827         @Override
getGroupId(BluetoothDevice device, AttributionSource source)828         public int getGroupId(BluetoothDevice device, AttributionSource source) {
829             Attributable.setAttributionSource(device, source);
830             LeAudioService service = getService(source);
831             if (service == null) {
832                 return LE_AUDIO_GROUP_ID_INVALID;
833             }
834 
835             return service.getGroupId(device);
836         }
837     }
838 
839     @Override
dump(StringBuilder sb)840     public void dump(StringBuilder sb) {
841         super.dump(sb);
842         // TODO: Dump all state machines
843     }
844 }
845