• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.bass_client;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 
21 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
22 
23 import android.bluetooth.BluetoothAdapter;
24 import android.bluetooth.BluetoothDevice;
25 import android.bluetooth.BluetoothLeBroadcastAssistant;
26 import android.bluetooth.BluetoothLeBroadcastMetadata;
27 import android.bluetooth.BluetoothLeBroadcastReceiveState;
28 import android.bluetooth.BluetoothProfile;
29 import android.bluetooth.BluetoothStatusCodes;
30 import android.bluetooth.BluetoothUuid;
31 import android.bluetooth.IBluetoothLeBroadcastAssistant;
32 import android.bluetooth.IBluetoothLeBroadcastAssistantCallback;
33 import android.bluetooth.le.ScanCallback;
34 import android.bluetooth.le.ScanFilter;
35 import android.bluetooth.le.ScanRecord;
36 import android.bluetooth.le.ScanResult;
37 import android.bluetooth.le.ScanSettings;
38 import android.content.BroadcastReceiver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.os.Handler;
43 import android.os.HandlerThread;
44 import android.os.Looper;
45 import android.os.Message;
46 import android.os.ParcelUuid;
47 import android.os.RemoteCallbackList;
48 import android.os.RemoteException;
49 import android.sysprop.BluetoothProperties;
50 import android.util.Log;
51 import android.util.Pair;
52 
53 import com.android.bluetooth.Utils;
54 import com.android.bluetooth.btservice.AdapterService;
55 import com.android.bluetooth.btservice.ProfileService;
56 import com.android.bluetooth.btservice.ServiceFactory;
57 import com.android.bluetooth.btservice.storage.DatabaseManager;
58 import com.android.bluetooth.csip.CsipSetCoordinatorService;
59 import com.android.bluetooth.le_audio.LeAudioService;
60 import com.android.internal.annotations.VisibleForTesting;
61 
62 import java.util.ArrayList;
63 import java.util.Collections;
64 import java.util.HashMap;
65 import java.util.List;
66 import java.util.Map;
67 import java.util.Objects;
68 import java.util.Optional;
69 import java.util.concurrent.ConcurrentHashMap;
70 
71 /**
72  * Broacast Assistant Scan Service
73  */
74 public class BassClientService extends ProfileService {
75     private static final boolean DBG = true;
76     private static final String TAG = BassClientService.class.getSimpleName();
77     private static final int MAX_BASS_CLIENT_STATE_MACHINES = 10;
78 
79     private static BassClientService sService;
80 
81     private final Map<BluetoothDevice, BassClientStateMachine> mStateMachines = new HashMap<>();
82     private final Object mSearchScanCallbackLock = new Object();
83     private final Map<Integer, ScanResult> mScanBroadcasts = new HashMap<>();
84 
85     private final Map<BluetoothDevice, List<Pair<Integer, Object>>> mPendingGroupOp =
86             new ConcurrentHashMap<>();
87     private final Map<BluetoothDevice, List<Integer>> mGroupManagedSources =
88             new ConcurrentHashMap<>();
89 
90     private HandlerThread mStateMachinesThread;
91     private HandlerThread mCallbackHandlerThread;
92     private AdapterService mAdapterService;
93     private DatabaseManager mDatabaseManager;
94     private BluetoothAdapter mBluetoothAdapter = null;
95     private BassUtils mBassUtils = null;
96     private Map<BluetoothDevice, BluetoothDevice> mActiveSourceMap;
97     /* Caching the PeriodicAdvertisementResult from Broadcast source */
98     /* This is stored at service so that each device state machine can access
99     and use it as needed. Once the periodic sync in cancelled, this data will bre
100     removed to ensure stable data won't used */
101     /* broadcastSrcDevice, syncHandle */
102     private Map<BluetoothDevice, Integer> mDeviceToSyncHandleMap;
103     /*syncHandle, parsed BaseData data*/
104     private Map<Integer, BaseData> mSyncHandleToBaseDataMap;
105     /*bcastSrcDevice, corresponding PeriodicAdvertisementResult*/
106     private Map<BluetoothDevice, PeriodicAdvertisementResult> mPeriodicAdvertisementResultMap;
107     private ScanCallback mSearchScanCallback;
108     private Callbacks mCallbacks;
109     private BroadcastReceiver mIntentReceiver;
110 
111     @VisibleForTesting
112     ServiceFactory mServiceFactory = new ServiceFactory();
113 
isEnabled()114     public static boolean isEnabled() {
115         return BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false);
116     }
117 
updatePeriodicAdvertisementResultMap( BluetoothDevice device, int addressType, int syncHandle, int advSid, int advInterval, int bId)118     void updatePeriodicAdvertisementResultMap(
119             BluetoothDevice device,
120             int addressType,
121             int syncHandle,
122             int advSid,
123             int advInterval,
124             int bId) {
125         log("updatePeriodicAdvertisementResultMap: device: " + device);
126         log("updatePeriodicAdvertisementResultMap: syncHandle: " + syncHandle);
127         log("updatePeriodicAdvertisementResultMap: advSid: " + advSid);
128         log("updatePeriodicAdvertisementResultMap: addressType: " + addressType);
129         log("updatePeriodicAdvertisementResultMap: advInterval: " + advInterval);
130         log("updatePeriodicAdvertisementResultMap: broadcastId: " + bId);
131         log("mDeviceToSyncHandleMap" + mDeviceToSyncHandleMap);
132         log("mPeriodicAdvertisementResultMap" + mPeriodicAdvertisementResultMap);
133         // Cache the SyncHandle
134         if (mDeviceToSyncHandleMap != null) {
135             mDeviceToSyncHandleMap.put(device, syncHandle);
136         }
137         if (mPeriodicAdvertisementResultMap != null) {
138             PeriodicAdvertisementResult paRes = mPeriodicAdvertisementResultMap.get(device);
139             if (paRes == null) {
140                 log("PAResmap: add >>>");
141                 paRes = new PeriodicAdvertisementResult(device,
142                         addressType, syncHandle, advSid, advInterval, bId);
143                 if (paRes != null) {
144                     paRes.print();
145                     mPeriodicAdvertisementResultMap.put(device, paRes);
146                 }
147             } else {
148                 if (advSid != BassConstants.INVALID_ADV_SID) {
149                     paRes.updateAdvSid(advSid);
150                 }
151                 if (syncHandle != BassConstants.INVALID_SYNC_HANDLE) {
152                     paRes.updateSyncHandle(syncHandle);
153                 }
154                 if (addressType != BassConstants.INVALID_ADV_ADDRESS_TYPE) {
155                     paRes.updateAddressType(addressType);
156                 }
157                 if (advInterval != BassConstants.INVALID_ADV_INTERVAL) {
158                     paRes.updateAdvInterval(advInterval);
159                 }
160                 if (bId != BassConstants.INVALID_BROADCAST_ID) {
161                     paRes.updateBroadcastId(bId);
162                 }
163                 log("PAResmap: update >>>");
164                 paRes.print();
165                 mPeriodicAdvertisementResultMap.replace(device, paRes);
166             }
167         }
168         log(">>mPeriodicAdvertisementResultMap" + mPeriodicAdvertisementResultMap);
169     }
170 
getPeriodicAdvertisementResult(BluetoothDevice device)171     PeriodicAdvertisementResult getPeriodicAdvertisementResult(BluetoothDevice device) {
172         if (mPeriodicAdvertisementResultMap == null) {
173             Log.e(TAG, "getPeriodicAdvertisementResult: mPeriodicAdvertisementResultMap is null");
174             return null;
175         }
176         return mPeriodicAdvertisementResultMap.get(device);
177     }
178 
clearPeriodicAdvertisementResult(BluetoothDevice device)179     PeriodicAdvertisementResult clearPeriodicAdvertisementResult(BluetoothDevice device) {
180         if (mPeriodicAdvertisementResultMap == null) {
181             Log.e(TAG, "getPeriodicAdvertisementResult: mPeriodicAdvertisementResultMap is null");
182             return null;
183         }
184         return mPeriodicAdvertisementResultMap.remove(device);
185     }
186 
updateBase(int syncHandlemap, BaseData base)187     void updateBase(int syncHandlemap, BaseData base) {
188         if (mSyncHandleToBaseDataMap == null) {
189             Log.e(TAG, "updateBase: mSyncHandleToBaseDataMap is null");
190             return;
191         }
192         log("updateBase : mSyncHandleToBaseDataMap>>");
193         mSyncHandleToBaseDataMap.put(syncHandlemap, base);
194     }
195 
getBase(int syncHandlemap)196     BaseData getBase(int syncHandlemap) {
197         if (mSyncHandleToBaseDataMap == null) {
198             Log.e(TAG, "getBase: mSyncHandleToBaseDataMap is null");
199             return null;
200         }
201         BaseData base = mSyncHandleToBaseDataMap.get(syncHandlemap);
202         log("getBase returns" + base);
203         return base;
204     }
205 
setActiveSyncedSource(BluetoothDevice scanDelegator, BluetoothDevice sourceDevice)206     void setActiveSyncedSource(BluetoothDevice scanDelegator, BluetoothDevice sourceDevice) {
207         log("setActiveSyncedSource, scanDelegator: " + scanDelegator + ", sourceDevice: " +
208             sourceDevice);
209         if (sourceDevice == null) {
210             mActiveSourceMap.remove(scanDelegator);
211         } else {
212             mActiveSourceMap.put(scanDelegator, sourceDevice);
213         }
214     }
215 
getActiveSyncedSource(BluetoothDevice scanDelegator)216     BluetoothDevice getActiveSyncedSource(BluetoothDevice scanDelegator) {
217         BluetoothDevice currentSource = mActiveSourceMap.get(scanDelegator);
218         log("getActiveSyncedSource: scanDelegator" + scanDelegator
219                 + "returning " + currentSource);
220         return currentSource;
221     }
222 
getCallbacks()223     public Callbacks getCallbacks() {
224         return mCallbacks;
225     }
226 
227     @Override
initBinder()228     protected IProfileServiceBinder initBinder() {
229         return new BluetoothLeBroadcastAssistantBinder(this);
230     }
231 
232     @Override
start()233     protected boolean start() {
234         if (DBG) {
235             Log.d(TAG, "start()");
236         }
237         if (sService != null) {
238             throw new IllegalStateException("start() called twice");
239         }
240         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
241                 "AdapterService cannot be null when BassClientService starts");
242         mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
243                 "DatabaseManager cannot be null when BassClientService starts");
244         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
245         mStateMachines.clear();
246         mStateMachinesThread = new HandlerThread("BassClientService.StateMachines");
247         mStateMachinesThread.start();
248         mCallbackHandlerThread = new HandlerThread(TAG);
249         mCallbackHandlerThread.start();
250         mCallbacks = new Callbacks(mCallbackHandlerThread.getLooper());
251 
252         IntentFilter filter = new IntentFilter();
253         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
254         filter.addAction(BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED);
255         mIntentReceiver = new BroadcastReceiver() {
256             @Override
257             public void onReceive(Context context, Intent intent) {
258                 String action = intent.getAction();
259 
260                 if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
261                     int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
262                             BluetoothDevice.ERROR);
263                     BluetoothDevice device =
264                             intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
265                     Objects.requireNonNull(device,
266                             "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
267                     bondStateChanged(device, state);
268 
269                 } else if (action.equals(
270                             BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED)) {
271                     BluetoothDevice device =
272                             intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
273                     int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
274                     int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
275                     connectionStateChanged(device, fromState, toState);
276                 }
277             }
278         };
279         registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED);
280 
281         setBassClientService(this);
282         mBassUtils = new BassUtils(this);
283         // Saving PSync stuff for future addition
284         mDeviceToSyncHandleMap = new HashMap<BluetoothDevice, Integer>();
285         mPeriodicAdvertisementResultMap = new HashMap<BluetoothDevice,
286                 PeriodicAdvertisementResult>();
287         mSyncHandleToBaseDataMap = new HashMap<Integer, BaseData>();
288         mActiveSourceMap = new HashMap<BluetoothDevice, BluetoothDevice>();
289         mSearchScanCallback = null;
290         return true;
291     }
292 
293     @Override
stop()294     protected boolean stop() {
295         if (DBG) {
296             Log.d(TAG, "stop()");
297         }
298         synchronized (mStateMachines) {
299             for (BassClientStateMachine sm : mStateMachines.values()) {
300                 BassObjectsFactory.getInstance().destroyStateMachine(sm);
301             }
302             mStateMachines.clear();
303         }
304         if (mCallbackHandlerThread != null) {
305             mCallbackHandlerThread.quitSafely();
306             mCallbackHandlerThread = null;
307         }
308         if (mStateMachinesThread != null) {
309             mStateMachinesThread.quitSafely();
310             mStateMachinesThread = null;
311         }
312 
313         if (mIntentReceiver != null) {
314             unregisterReceiver(mIntentReceiver);
315             mIntentReceiver = null;
316         }
317 
318         setBassClientService(null);
319         if (mDeviceToSyncHandleMap != null) {
320             mDeviceToSyncHandleMap.clear();
321             mDeviceToSyncHandleMap = null;
322         }
323         if (mActiveSourceMap != null) {
324             mActiveSourceMap.clear();
325             mActiveSourceMap = null;
326         }
327         if (mBassUtils != null) {
328             mBassUtils.cleanUp();
329             mBassUtils = null;
330         }
331         if (mPendingGroupOp != null) {
332             mPendingGroupOp.clear();
333         }
334         return true;
335     }
336 
337     @Override
onUnbind(Intent intent)338     public boolean onUnbind(Intent intent) {
339         Log.d(TAG, "Need to unregister app");
340         return super.onUnbind(intent);
341     }
342 
343     /**
344      * getBassUtils
345      */
getBassUtils()346     public BassUtils getBassUtils() {
347         return mBassUtils;
348     }
349 
getDeviceForSyncHandle(int syncHandle)350     BluetoothDevice getDeviceForSyncHandle(int syncHandle) {
351         if (mDeviceToSyncHandleMap == null) {
352             return null;
353         }
354         BluetoothDevice device = null;
355         for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceToSyncHandleMap.entrySet()) {
356             Integer value = entry.getValue();
357             if (value == syncHandle) {
358                 device = entry.getKey();
359                 break;
360             }
361         }
362         return device;
363     }
364 
setBassClientService(BassClientService instance)365     private static synchronized void setBassClientService(BassClientService instance) {
366         if (DBG) {
367             Log.d(TAG, "setBassClientService(): set to: " + instance);
368         }
369         sService = instance;
370     }
371 
enqueueSourceGroupOp(BluetoothDevice sink, Integer msgId, Object obj)372     private void enqueueSourceGroupOp(BluetoothDevice sink, Integer msgId, Object obj) {
373         log("enqueueSourceGroupOp device: " + sink + ", msgId: " + msgId);
374 
375         if (!mPendingGroupOp.containsKey(sink)) {
376             mPendingGroupOp.put(sink, new ArrayList());
377         }
378         mPendingGroupOp.get(sink).add(new Pair<Integer, Object>(msgId, obj));
379     }
380 
isSuccess(int status)381     private boolean isSuccess(int status) {
382         boolean ret = false;
383         switch (status) {
384             case BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST:
385             case BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST:
386             case BluetoothStatusCodes.REASON_REMOTE_REQUEST:
387             case BluetoothStatusCodes.REASON_SYSTEM_POLICY:
388                 ret = true;
389                 break;
390             default:
391                 break;
392         }
393         return ret;
394     }
395 
checkForPendingGroupOpRequest(BluetoothDevice sink, int reason, int reqMsg, Object obj)396     private void checkForPendingGroupOpRequest(BluetoothDevice sink, int reason, int reqMsg,
397             Object obj) {
398         log("checkForPendingGroupOpRequest device: " + sink + ", reason: " + reason
399                 + ", reqMsg: " + reqMsg);
400 
401         List<Pair<Integer, Object>> operations = mPendingGroupOp.get(sink);
402         if (operations == null) {
403             return;
404         }
405 
406         switch (reqMsg) {
407             case BassClientStateMachine.ADD_BCAST_SOURCE:
408                 if (obj == null) {
409                     return;
410                 }
411                 // Identify the operation by operation type and broadcastId
412                 if (isSuccess(reason)) {
413                     BluetoothLeBroadcastReceiveState sourceState =
414                             (BluetoothLeBroadcastReceiveState) obj;
415                     boolean removed = operations.removeIf(m ->
416                             (m.first.equals(BassClientStateMachine.ADD_BCAST_SOURCE))
417                             && (sourceState.getBroadcastId()
418                                     == ((BluetoothLeBroadcastMetadata) m.second).getBroadcastId()));
419                     if (removed) {
420                         setSourceGroupManaged(sink, sourceState.getSourceId(), true);
421 
422                     }
423                 } else {
424                     BluetoothLeBroadcastMetadata metadata = (BluetoothLeBroadcastMetadata) obj;
425                     operations.removeIf(m ->
426                             (m.first.equals(BassClientStateMachine.ADD_BCAST_SOURCE))
427                             && (metadata.getBroadcastId()
428                                     == ((BluetoothLeBroadcastMetadata) m.second).getBroadcastId()));
429                 }
430                 break;
431             case BassClientStateMachine.REMOVE_BCAST_SOURCE:
432                 // Identify the operation by operation type and sourceId
433                 Integer sourceId = (Integer) obj;
434                 operations.removeIf(m ->
435                         m.first.equals(BassClientStateMachine.REMOVE_BCAST_SOURCE)
436                         && (sourceId.equals((Integer) m.second)));
437                 setSourceGroupManaged(sink, sourceId, false);
438                 break;
439             default:
440                 break;
441         }
442     }
443 
setSourceGroupManaged(BluetoothDevice sink, int sourceId, boolean isGroupOp)444     private void setSourceGroupManaged(BluetoothDevice sink, int sourceId, boolean isGroupOp) {
445         log("setSourceGroupManaged device: " + sink);
446         if (isGroupOp) {
447             if (!mGroupManagedSources.containsKey(sink)) {
448                 mGroupManagedSources.put(sink, new ArrayList<>());
449             }
450             mGroupManagedSources.get(sink).add(sourceId);
451         } else {
452             List<Integer> sources = mGroupManagedSources.get(sink);
453             if (sources != null) {
454                 sources.removeIf(e -> e.equals(sourceId));
455             }
456         }
457     }
458 
459     private Pair<BluetoothLeBroadcastMetadata, Map<BluetoothDevice, Integer>>
getGroupManagedDeviceSources(BluetoothDevice sink, Integer sourceId)460             getGroupManagedDeviceSources(BluetoothDevice sink, Integer sourceId) {
461         log("getGroupManagedDeviceSources device: " + sink + " sourceId: " + sourceId);
462         Map map = new HashMap<BluetoothDevice, Integer>();
463 
464         if (mGroupManagedSources.containsKey(sink)
465                 && mGroupManagedSources.get(sink).contains(sourceId)) {
466             BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);
467             BluetoothLeBroadcastMetadata metadata =
468                     stateMachine.getCurrentBroadcastMetadata(sourceId);
469             if (metadata != null) {
470                 int broadcastId = metadata.getBroadcastId();
471 
472                 for (BluetoothDevice device: getTargetDeviceList(sink, true)) {
473                     List<BluetoothLeBroadcastReceiveState> sources =
474                             getOrCreateStateMachine(device).getAllSources();
475 
476                     // For each device, find the source ID having this broadcast ID
477                     Optional<BluetoothLeBroadcastReceiveState> receiver = sources.stream()
478                             .filter(e -> e.getBroadcastId() == broadcastId)
479                             .findAny();
480                     if (receiver.isPresent()) {
481                         map.put(device, receiver.get().getSourceId());
482                     } else {
483                         // Put invalid source ID if the remote doesn't have it
484                         map.put(device, BassConstants.INVALID_SOURCE_ID);
485                     }
486                 }
487                 return new Pair<BluetoothLeBroadcastMetadata,
488                         Map<BluetoothDevice, Integer>>(metadata, map);
489             } else {
490                 Log.e(TAG, "Couldn't find broadcast metadata for device: "
491                         + sink.getAnonymizedAddress() + ", and sourceId:" + sourceId);
492             }
493         }
494 
495         // Just put this single device if this source is not group managed
496         map.put(sink, sourceId);
497         return new Pair<BluetoothLeBroadcastMetadata, Map<BluetoothDevice, Integer>>(null, map);
498     }
499 
getTargetDeviceList(BluetoothDevice device, boolean isGroupOp)500     private List<BluetoothDevice> getTargetDeviceList(BluetoothDevice device, boolean isGroupOp) {
501         if (isGroupOp) {
502             CsipSetCoordinatorService csipClient = mServiceFactory.getCsipSetCoordinatorService();
503             if (csipClient != null) {
504                 // Check for coordinated set of devices in the context of CAP
505                 List<BluetoothDevice> csipDevices = csipClient.getGroupDevicesOrdered(device,
506                         BluetoothUuid.CAP);
507                 if (!csipDevices.isEmpty()) {
508                     return csipDevices;
509                 } else {
510                     Log.w(TAG, "CSIP group is empty.");
511                 }
512             } else {
513                 Log.e(TAG, "CSIP service is null. No grouping information available.");
514             }
515         }
516 
517         List<BluetoothDevice> devices = new ArrayList<>();
518         devices.add(device);
519         return devices;
520     }
521 
isValidBroadcastSourceAddition( BluetoothDevice device, BluetoothLeBroadcastMetadata metaData)522     private boolean isValidBroadcastSourceAddition(
523             BluetoothDevice device, BluetoothLeBroadcastMetadata metaData) {
524         boolean retval = true;
525         List<BluetoothLeBroadcastReceiveState> currentAllSources = getAllSources(device);
526         for (int i = 0; i < currentAllSources.size(); i++) {
527             BluetoothLeBroadcastReceiveState state = currentAllSources.get(i);
528             if (metaData.getSourceDevice().equals(state.getSourceDevice())
529                     && metaData.getSourceAddressType() == state.getSourceAddressType()
530                     && metaData.getSourceAdvertisingSid() == state.getSourceAdvertisingSid()
531                     && metaData.getBroadcastId() == state.getBroadcastId()) {
532                 retval = false;
533                 Log.e(TAG, "isValidBroadcastSourceAddition: fail for " + device
534                         + " metaData: " + metaData);
535                 break;
536             }
537         }
538         return retval;
539     }
540 
hasRoomForBroadcastSourceAddition(BluetoothDevice device)541     private boolean hasRoomForBroadcastSourceAddition(BluetoothDevice device) {
542         boolean isRoomAvailable = false;
543         String emptyBluetoothDevice = "00:00:00:00:00:00";
544         for (BluetoothLeBroadcastReceiveState recvState: getAllSources(device)) {
545             if (recvState.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) {
546                 isRoomAvailable = true;
547                 break;
548             }
549         }
550         log("isRoomAvailable: " + isRoomAvailable);
551         return isRoomAvailable;
552     }
553 
getOrCreateStateMachine(BluetoothDevice device)554     private BassClientStateMachine getOrCreateStateMachine(BluetoothDevice device) {
555         if (device == null) {
556             Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null");
557             return null;
558         }
559         synchronized (mStateMachines) {
560             BassClientStateMachine stateMachine = mStateMachines.get(device);
561             if (stateMachine != null) {
562                 return stateMachine;
563             }
564             // Limit the maximum number of state machines to avoid DoS attack
565             if (mStateMachines.size() >= MAX_BASS_CLIENT_STATE_MACHINES) {
566                 Log.e(TAG, "Maximum number of Bassclient state machines reached: "
567                         + MAX_BASS_CLIENT_STATE_MACHINES);
568                 return null;
569             }
570             log("Creating a new state machine for " + device);
571             stateMachine = BassObjectsFactory.getInstance().makeStateMachine(
572                     device, this, mStateMachinesThread.getLooper());
573             mStateMachines.put(device, stateMachine);
574             return stateMachine;
575         }
576     }
577 
578     /**
579      * Get the BassClientService instance
580      *
581      * @return BassClientService instance
582      */
getBassClientService()583     public static synchronized BassClientService getBassClientService() {
584         if (sService == null) {
585             Log.w(TAG, "getBassClientService(): service is NULL");
586             return null;
587         }
588         if (!sService.isAvailable()) {
589             Log.w(TAG, "getBassClientService(): service is not available");
590             return null;
591         }
592         return sService;
593     }
594 
removeStateMachine(BluetoothDevice device)595     private void removeStateMachine(BluetoothDevice device) {
596         synchronized (mStateMachines) {
597             BassClientStateMachine sm = mStateMachines.get(device);
598             if (sm == null) {
599                 Log.w(TAG, "removeStateMachine: device " + device
600                         + " does not have a state machine");
601                 return;
602             }
603             log("removeStateMachine: removing state machine for device: " + device);
604             sm.doQuit();
605             sm.cleanup();
606             mStateMachines.remove(device);
607         }
608 
609         // Cleanup device cache
610         mPendingGroupOp.remove(device);
611         mGroupManagedSources.remove(device);
612         mActiveSourceMap.remove(device);
613         mDeviceToSyncHandleMap.remove(device);
614         mPeriodicAdvertisementResultMap.remove(device);
615     }
616 
connectionStateChanged(BluetoothDevice device, int fromState, int toState)617     synchronized void connectionStateChanged(BluetoothDevice device, int fromState,
618                                              int toState) {
619         if ((device == null) || (fromState == toState)) {
620             Log.e(TAG, "connectionStateChanged: unexpected invocation. device=" + device
621                     + " fromState=" + fromState + " toState=" + toState);
622             return;
623         }
624 
625         // Check if the device is disconnected - if unbond, remove the state machine
626         if (toState == BluetoothProfile.STATE_DISCONNECTED) {
627             mPendingGroupOp.remove(device);
628 
629             int bondState = mAdapterService.getBondState(device);
630             if (bondState == BluetoothDevice.BOND_NONE) {
631                 log("Unbonded " + device + ". Removing state machine");
632                 removeStateMachine(device);
633             }
634         }
635     }
636 
637     @VisibleForTesting
bondStateChanged(BluetoothDevice device, int bondState)638     void bondStateChanged(BluetoothDevice device, int bondState) {
639         log("Bond state changed for device: " + device + " state: " + bondState);
640 
641         // Remove state machine if the bonding for a device is removed
642         if (bondState != BluetoothDevice.BOND_NONE) {
643             return;
644         }
645 
646         synchronized (mStateMachines) {
647             BassClientStateMachine sm = mStateMachines.get(device);
648             if (sm == null) {
649                 return;
650             }
651             if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) {
652                 Log.i(TAG, "Disconnecting device because it was unbonded.");
653                 disconnect(device);
654                 return;
655             }
656             removeStateMachine(device);
657         }
658     }
659 
660     /**
661      * Connects the bass profile to the passed in device
662      *
663      * @param device is the device with which we will connect the Bass profile
664      * @return true if BAss profile successfully connected, false otherwise
665      */
connect(BluetoothDevice device)666     public boolean connect(BluetoothDevice device) {
667         if (DBG) {
668             Log.d(TAG, "connect(): " + device);
669         }
670         if (device == null) {
671             Log.e(TAG, "connect: device is null");
672             return false;
673         }
674         if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
675             Log.e(TAG, "connect: connection policy set to forbidden");
676             return false;
677         }
678         synchronized (mStateMachines) {
679             BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
680             stateMachine.sendMessage(BassClientStateMachine.CONNECT);
681         }
682         return true;
683     }
684 
685     /**
686      * Disconnects Bassclient profile for the passed in device
687      *
688      * @param device is the device with which we want to disconnected the BAss client profile
689      * @return true if Bass client profile successfully disconnected, false otherwise
690      */
disconnect(BluetoothDevice device)691     public boolean disconnect(BluetoothDevice device) {
692         if (DBG) {
693             Log.d(TAG, "disconnect(): " + device);
694         }
695         if (device == null) {
696             Log.e(TAG, "disconnect: device is null");
697             return false;
698         }
699         synchronized (mStateMachines) {
700             BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
701             stateMachine.sendMessage(BassClientStateMachine.DISCONNECT);
702         }
703         return true;
704     }
705 
706     /**
707      * Check whether can connect to a peer device. The check considers a number of factors during
708      * the evaluation.
709      *
710      * @param device the peer device to connect to
711      * @return true if connection is allowed, otherwise false
712      */
713     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
okToConnect(BluetoothDevice device)714     public boolean okToConnect(BluetoothDevice device) {
715         // Check if this is an incoming connection in Quiet mode.
716         if (mAdapterService.isQuietModeEnabled()) {
717             Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled");
718             return false;
719         }
720         // Check connection policy and accept or reject the connection.
721         int connectionPolicy = getConnectionPolicy(device);
722         int bondState = mAdapterService.getBondState(device);
723         // Allow this connection only if the device is bonded. Any attempt to connect while
724         // bonding would potentially lead to an unauthorized connection.
725         if (bondState != BluetoothDevice.BOND_BONDED) {
726             Log.w(TAG, "okToConnect: return false, bondState=" + bondState);
727             return false;
728         } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN
729                 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
730             // Otherwise, reject the connection if connectionPolicy is not valid.
731             Log.w(TAG, "okToConnect: return false, connectionPolicy=" + connectionPolicy);
732             return false;
733         }
734         return true;
735     }
736 
737     /**
738      * Get connection state of remote device
739      *
740      * @param sink the remote device
741      * @return connection state
742      */
getConnectionState(BluetoothDevice sink)743     public int getConnectionState(BluetoothDevice sink) {
744         synchronized (mStateMachines) {
745             BassClientStateMachine sm = getOrCreateStateMachine(sink);
746             if (sm == null) {
747                 log("getConnectionState returns STATE_DISC");
748                 return BluetoothProfile.STATE_DISCONNECTED;
749             }
750             return sm.getConnectionState();
751         }
752     }
753 
754     /**
755      * Get a list of all LE Audio Broadcast Sinks with the specified connection states.
756      * @param states states array representing the connection states
757      * @return a list of devices that match the provided connection states
758      */
getDevicesMatchingConnectionStates(int[] states)759     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
760         ArrayList<BluetoothDevice> devices = new ArrayList<>();
761         if (states == null) {
762             return devices;
763         }
764         final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
765         if (bondedDevices == null) {
766             return devices;
767         }
768         synchronized (mStateMachines) {
769             for (BluetoothDevice device : bondedDevices) {
770                 final ParcelUuid[] featureUuids = device.getUuids();
771                 if (!Utils.arrayContains(
772                         featureUuids, BluetoothUuid.BASS)) {
773                     continue;
774                 }
775                 int connectionState = BluetoothProfile.STATE_DISCONNECTED;
776                 BassClientStateMachine sm = getOrCreateStateMachine(device);
777                 if (sm != null) {
778                     connectionState = sm.getConnectionState();
779                 }
780                 for (int state : states) {
781                     if (connectionState == state) {
782                         devices.add(device);
783                         break;
784                     }
785                 }
786             }
787             return devices;
788         }
789     }
790 
791     /**
792      * Get a list of all LE Audio Broadcast Sinks connected with the LE Audio Broadcast Assistant.
793      * @return list of connected devices
794      */
getConnectedDevices()795     List<BluetoothDevice> getConnectedDevices() {
796         synchronized (mStateMachines) {
797             List<BluetoothDevice> devices = new ArrayList<>();
798             for (BassClientStateMachine sm : mStateMachines.values()) {
799                 if (sm.isConnected()) {
800                     devices.add(sm.getDevice());
801                 }
802             }
803             log("getConnectedDevices: " + devices);
804             return devices;
805         }
806     }
807 
808     /**
809      * Set the connectionPolicy of the Broadcast Audio Scan Service profile.
810      *
811      * <p>The connection policy can be one of:
812      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
813      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
814      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
815      *
816      * @param device paired bluetooth device
817      * @param connectionPolicy is the connection policy to set to for this profile
818      * @return true if connectionPolicy is set, false on error
819      */
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)820     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
821         if (DBG) {
822             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
823         }
824         boolean setSuccessfully =
825                 mDatabaseManager.setProfileConnectionPolicy(device,
826                         BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, connectionPolicy);
827         if (setSuccessfully && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
828             connect(device);
829         } else if (setSuccessfully
830                 && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
831             disconnect(device);
832         }
833         return setSuccessfully;
834     }
835 
836     /**
837      * Get the connection policy of the profile.
838      *
839      * <p>The connection policy can be any of:
840      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
841      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
842      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
843      *
844      * @param device paired bluetooth device
845      * @return connection policy of the device
846      */
getConnectionPolicy(BluetoothDevice device)847     public int getConnectionPolicy(BluetoothDevice device) {
848         return mDatabaseManager
849                 .getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
850     }
851 
852     /**
853      * Register callbacks that will be invoked during scan offloading.
854      *
855      * @param cb callbacks to be invoked
856      */
registerCallback(IBluetoothLeBroadcastAssistantCallback cb)857     public void registerCallback(IBluetoothLeBroadcastAssistantCallback cb) {
858         Log.i(TAG, "registerCallback");
859         mCallbacks.register(cb);
860         return;
861     }
862 
863     /**
864      * Unregister callbacks that are invoked during scan offloading.
865      *
866      * @param cb callbacks to be unregistered
867      */
unregisterCallback(IBluetoothLeBroadcastAssistantCallback cb)868     public void unregisterCallback(IBluetoothLeBroadcastAssistantCallback cb) {
869         Log.i(TAG, "unregisterCallback");
870         mCallbacks.unregister(cb);
871         return;
872     }
873 
874     /**
875      * Search for LE Audio Broadcast Sources on behalf of all devices connected via Broadcast Audio
876      * Scan Service, filtered by filters
877      *
878      * @param filters ScanFilters for finding exact Broadcast Source
879      */
startSearchingForSources(List<ScanFilter> filters)880     public void startSearchingForSources(List<ScanFilter> filters) {
881         log("startSearchingForSources");
882         if (mBluetoothAdapter == null) {
883             Log.e(TAG, "startSearchingForSources: Adapter is NULL");
884             return;
885         }
886         BluetoothLeScannerWrapper scanner = BassObjectsFactory.getInstance()
887                 .getBluetoothLeScannerWrapper(mBluetoothAdapter);
888         if (scanner == null) {
889             Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
890             return;
891         }
892         synchronized (mSearchScanCallbackLock) {
893             if (mSearchScanCallback != null) {
894                 Log.e(TAG, "LE Scan has already started");
895                 mCallbacks.notifySearchStartFailed(BluetoothStatusCodes.ERROR_UNKNOWN);
896                 return;
897             }
898             mSearchScanCallback = new ScanCallback() {
899                 @Override
900                 public void onScanResult(int callbackType, ScanResult result) {
901                     log("onScanResult:" + result);
902                     if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
903                         // Should not happen
904                         Log.e(TAG, "LE Scan has already started");
905                         return;
906                     }
907                     ScanRecord scanRecord = result.getScanRecord();
908                     if (scanRecord == null) {
909                         Log.e(TAG, "Null scan record");
910                         return;
911                     }
912                     Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData();
913                     if (listOfUuids == null) {
914                         Log.e(TAG, "Service data is null");
915                         return;
916                     }
917                     if (!listOfUuids.containsKey(
918                             BassConstants.BAAS_UUID)) {
919                         return;
920                     }
921                     log( "Broadcast Source Found:" + result.getDevice());
922                     byte[] broadcastIdArray = listOfUuids.get(BassConstants.BAAS_UUID);
923                     int broadcastId = (int)(((broadcastIdArray[2] & 0xff) << 16)
924                             | ((broadcastIdArray[1] & 0xff) << 8)
925                             | (broadcastIdArray[0] & 0xff));
926                     if (mScanBroadcasts.get(broadcastId) == null) {
927                         log("selectBroadcastSource: broadcastId " + broadcastId);
928                         mScanBroadcasts.put(broadcastId, result);
929                         synchronized (mStateMachines) {
930                             for (BassClientStateMachine sm : mStateMachines.values()) {
931                                 if (sm.isConnected()) {
932                                     selectSource(sm.getDevice(), result, false);
933                                 }
934                             }
935                         }
936                     }
937                 }
938 
939                 public void onScanFailed(int errorCode) {
940                     Log.e(TAG, "Scan Failure:" + errorCode);
941                 }
942             };
943             mScanBroadcasts.clear();
944             ScanSettings settings = new ScanSettings.Builder().setCallbackType(
945                     ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
946                     .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
947                     .setLegacy(false)
948                     .build();
949             if (filters == null) {
950                 filters = new ArrayList<ScanFilter>();
951             }
952             if (!BassUtils.containUuid(filters, BassConstants.BAAS_UUID)) {
953                 byte[] serviceData = {0x00, 0x00 ,0x00}; // Broadcast_ID
954                 byte[] serviceDataMask = {0x00, 0x00, 0x00};
955 
956                 filters.add(new ScanFilter.Builder()
957                         .setServiceData(BassConstants.BAAS_UUID,
958                                 serviceData, serviceDataMask).build());
959             }
960             scanner.startScan(filters, settings, mSearchScanCallback);
961             mCallbacks.notifySearchStarted(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
962         }
963     }
964 
965     /**
966      * Stops an ongoing search for nearby Broadcast Sources
967      */
stopSearchingForSources()968     public void stopSearchingForSources() {
969         log("stopSearchingForSources");
970         if (mBluetoothAdapter == null) {
971             Log.e(TAG, "stopSearchingForSources: Adapter is NULL");
972             return;
973         }
974         BluetoothLeScannerWrapper scanner = BassObjectsFactory.getInstance()
975                 .getBluetoothLeScannerWrapper(mBluetoothAdapter);
976         if (scanner == null) {
977             Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner");
978             return;
979         }
980         synchronized (mSearchScanCallbackLock) {
981             if (mSearchScanCallback == null) {
982                 Log.e(TAG, "Scan not started yet");
983                 mCallbacks.notifySearchStopFailed(BluetoothStatusCodes.ERROR_UNKNOWN);
984                 return;
985             }
986             scanner.stopScan(mSearchScanCallback);
987             mSearchScanCallback = null;
988             mCallbacks.notifySearchStopped(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST);
989             mScanBroadcasts.clear();
990         }
991     }
992 
993     /**
994      * Return true if a search has been started by this application
995      * @return true if a search has been started by this application
996      */
isSearchInProgress()997     public boolean isSearchInProgress() {
998         synchronized (mSearchScanCallbackLock) {
999             return mSearchScanCallback != null;
1000         }
1001     }
1002 
selectSource(BluetoothDevice sink, ScanResult result, boolean autoTrigger)1003     void selectSource(BluetoothDevice sink, ScanResult result, boolean autoTrigger) {
1004         if (!hasRoomForBroadcastSourceAddition(sink)) {
1005             log("selectSource: No more slot");
1006             return;
1007         }
1008 
1009         synchronized (mStateMachines) {
1010             BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);
1011             Message message = stateMachine.obtainMessage(
1012                     BassClientStateMachine.SELECT_BCAST_SOURCE);
1013             message.obj = result;
1014             message.arg1 = autoTrigger ? BassConstants.AUTO : BassConstants.USER;
1015             stateMachine.sendMessage(message);
1016         }
1017     }
1018 
1019     /**
1020      * Add a Broadcast Source to the Broadcast Sink
1021      *
1022      * @param sink Broadcast Sink to which the Broadcast Source should be added
1023      * @param sourceMetadata Broadcast Source metadata to be added to the Broadcast Sink
1024      * @param isGroupOp set to true If Application wants to perform this operation for all
1025      *                  coordinated set members, False otherwise
1026      */
addSource(BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata, boolean isGroupOp)1027     public void addSource(BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata,
1028             boolean isGroupOp) {
1029         log("addSource: device: " + sink + " sourceMetadata" + sourceMetadata
1030                 + " isGroupOp" + isGroupOp);
1031 
1032         List<BluetoothDevice> devices = getTargetDeviceList(sink, isGroupOp);
1033         // Don't coordinate it as a group if there's no group or there is one device only
1034         if (devices.size() < 2) {
1035             isGroupOp = false;
1036         }
1037 
1038         if (sourceMetadata == null) {
1039             log("addSource: Error bad parameter: sourceMetadata cannot be null");
1040             for (BluetoothDevice device : devices) {
1041                 mCallbacks.notifySourceAddFailed(device, sourceMetadata,
1042                         BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
1043             }
1044             return;
1045         }
1046 
1047         byte[] code = sourceMetadata.getBroadcastCode();
1048         for (BluetoothDevice device : devices) {
1049             BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
1050             if (stateMachine == null) {
1051                 log("addSource: Error bad parameter: no state machine for " + device);
1052                 mCallbacks.notifySourceAddFailed(device, sourceMetadata,
1053                         BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
1054                 continue;
1055             }
1056             if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
1057                 log("addSource: device is not connected");
1058                 mCallbacks.notifySourceAddFailed(device, sourceMetadata,
1059                         BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR);
1060                 continue;
1061             }
1062             if (stateMachine.hasPendingSourceOperation()) {
1063                 throw new IllegalStateException("addSource: source operation already pending");
1064             }
1065             if (!hasRoomForBroadcastSourceAddition(device)) {
1066                 log("addSource: device has no room");
1067                 mCallbacks.notifySourceAddFailed(device, sourceMetadata,
1068                         BluetoothStatusCodes.ERROR_REMOTE_NOT_ENOUGH_RESOURCES);
1069                 continue;
1070             }
1071             if (!isValidBroadcastSourceAddition(device, sourceMetadata)) {
1072                 log("addSource: not a valid broadcast source addition");
1073                 mCallbacks.notifySourceAddFailed(device, sourceMetadata,
1074                         BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_DUPLICATE_ADDITION);
1075                 continue;
1076             }
1077             if ((code != null) && (code.length != 0)) {
1078                 if ((code.length > 16) || (code.length < 4)) {
1079                     log("Invalid broadcast code length: " + code.length
1080                             + ", should be between 4 and 16 octets");
1081                     mCallbacks.notifySourceAddFailed(device, sourceMetadata,
1082                             BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
1083                     continue;
1084                 }
1085             }
1086 
1087             if (isGroupOp) {
1088                 enqueueSourceGroupOp(device, BassClientStateMachine.ADD_BCAST_SOURCE,
1089                         sourceMetadata);
1090             }
1091 
1092             Message message = stateMachine.obtainMessage(BassClientStateMachine.ADD_BCAST_SOURCE);
1093             message.obj = sourceMetadata;
1094             stateMachine.sendMessage(message);
1095         }
1096     }
1097 
1098     /**
1099      * Modify the Broadcast Source information on a Broadcast Sink
1100      *
1101      * @param sink representing the Broadcast Sink to which the Broadcast
1102      *               Source should be updated
1103      * @param sourceId source ID as delivered in onSourceAdded
1104      * @param updatedMetadata updated Broadcast Source metadata to be updated on the Broadcast Sink
1105      */
modifySource(BluetoothDevice sink, int sourceId, BluetoothLeBroadcastMetadata updatedMetadata)1106     public void modifySource(BluetoothDevice sink, int sourceId,
1107             BluetoothLeBroadcastMetadata updatedMetadata) {
1108         log("modifySource: device: " + sink + " sourceId " + sourceId);
1109 
1110         Map<BluetoothDevice, Integer> devices = getGroupManagedDeviceSources(sink, sourceId).second;
1111         if (updatedMetadata == null) {
1112             log("modifySource: Error bad parameters: updatedMetadata cannot be null");
1113             for (BluetoothDevice device : devices.keySet()) {
1114                 mCallbacks.notifySourceModifyFailed(device, sourceId,
1115                         BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
1116             }
1117             return;
1118         }
1119 
1120         byte[] code = updatedMetadata.getBroadcastCode();
1121         for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) {
1122             BluetoothDevice device = deviceSourceIdPair.getKey();
1123             Integer deviceSourceId = deviceSourceIdPair.getValue();
1124             BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
1125             if (updatedMetadata == null || stateMachine == null) {
1126                 log("modifySource: Error bad parameters: sourceId = " + deviceSourceId
1127                         + " updatedMetadata = " + updatedMetadata);
1128                 mCallbacks.notifySourceModifyFailed(device, sourceId,
1129                         BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
1130                 continue;
1131             }
1132             if (deviceSourceId == BassConstants.INVALID_SOURCE_ID) {
1133                 log("modifySource: no such sourceId for device: " + device);
1134                 mCallbacks.notifySourceModifyFailed(device, sourceId,
1135                         BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID);
1136                 continue;
1137             }
1138             if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
1139                 log("modifySource: device is not connected");
1140                 mCallbacks.notifySourceModifyFailed(device, sourceId,
1141                         BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR);
1142                 continue;
1143             }
1144             if ((code != null) && (code.length != 0)) {
1145                 if ((code.length > 16) || (code.length < 4)) {
1146                     log("Invalid broadcast code length: " + code.length
1147                             + ", should be between 4 and 16 octets");
1148                     mCallbacks.notifySourceModifyFailed(device, sourceId,
1149                             BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
1150                     continue;
1151                 }
1152             }
1153             if (stateMachine.hasPendingSourceOperation()) {
1154                 throw new IllegalStateException("modifySource: source operation already pending");
1155             }
1156 
1157             Message message =
1158                     stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);
1159             message.arg1 = deviceSourceId;
1160             message.arg2 = BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_INVALID;
1161             message.obj = updatedMetadata;
1162             stateMachine.sendMessage(message);
1163         }
1164     }
1165 
1166     /**
1167      * Removes the Broadcast Source from a Broadcast Sink
1168      *
1169      * @param sink representing the Broadcast Sink from which a Broadcast
1170      *               Source should be removed
1171      * @param sourceId source ID as delivered in onSourceAdded
1172      */
removeSource(BluetoothDevice sink, int sourceId)1173     public void removeSource(BluetoothDevice sink, int sourceId) {
1174         log("removeSource: device = " + sink + "sourceId " + sourceId);
1175 
1176         Map<BluetoothDevice, Integer> devices = getGroupManagedDeviceSources(sink, sourceId).second;
1177         for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) {
1178             BluetoothDevice device = deviceSourceIdPair.getKey();
1179             Integer deviceSourceId = deviceSourceIdPair.getValue();
1180             BassClientStateMachine stateMachine = getOrCreateStateMachine(device);
1181             if (stateMachine == null) {
1182                 log("removeSource: Error bad parameters: device = " + device);
1183                 mCallbacks.notifySourceRemoveFailed(device, sourceId,
1184                         BluetoothStatusCodes.ERROR_BAD_PARAMETERS);
1185                 continue;
1186             }
1187             if (deviceSourceId == BassConstants.INVALID_SOURCE_ID) {
1188                 log("removeSource: no such sourceId for device: " + device);
1189                 mCallbacks.notifySourceRemoveFailed(device, sourceId,
1190                         BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID);
1191                 continue;
1192             }
1193             if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
1194                 log("removeSource: device is not connected");
1195                 mCallbacks.notifySourceRemoveFailed(device, sourceId,
1196                         BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR);
1197                 continue;
1198             }
1199 
1200             BluetoothLeBroadcastReceiveState recvState =
1201                     stateMachine.getBroadcastReceiveStateForSourceId(sourceId);
1202             BluetoothLeBroadcastMetadata metaData =
1203                     stateMachine.getCurrentBroadcastMetadata(sourceId);
1204             if (metaData != null && recvState != null && recvState.getPaSyncState()
1205                     == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED) {
1206                 log("Force source to lost PA sync");
1207                 Message message = stateMachine.obtainMessage(
1208                         BassClientStateMachine.UPDATE_BCAST_SOURCE);
1209                 message.arg1 = sourceId;
1210                 message.arg2 = BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE;
1211                 /* Pending remove set. Remove source once not synchronized to PA */
1212                 message.obj = metaData;
1213                 stateMachine.sendMessage(message);
1214 
1215                 continue;
1216             }
1217 
1218             Message message =
1219                     stateMachine.obtainMessage(BassClientStateMachine.REMOVE_BCAST_SOURCE);
1220             message.arg1 = deviceSourceId;
1221             stateMachine.sendMessage(message);
1222         }
1223 
1224         for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) {
1225             BluetoothDevice device = deviceSourceIdPair.getKey();
1226             Integer deviceSourceId = deviceSourceIdPair.getValue();
1227             enqueueSourceGroupOp(device, BassClientStateMachine.REMOVE_BCAST_SOURCE,
1228                     Integer.valueOf(deviceSourceId));
1229         }
1230     }
1231 
1232     /**
1233      * Get information about all Broadcast Sources
1234      *
1235      * @param sink Broadcast Sink from which to get all Broadcast Sources
1236      * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState}
1237      */
getAllSources(BluetoothDevice sink)1238     public List<BluetoothLeBroadcastReceiveState> getAllSources(BluetoothDevice sink) {
1239         log("getAllSources for " + sink);
1240         synchronized (mStateMachines) {
1241             BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);
1242             if (stateMachine == null) {
1243                 log("stateMachine is null");
1244                 return Collections.emptyList();
1245             }
1246             return stateMachine.getAllSources();
1247         }
1248     }
1249 
1250     /**
1251      * Get maximum number of sources that can be added to this Broadcast Sink
1252      *
1253      * @param sink Broadcast Sink device
1254      * @return maximum number of sources that can be added to this Broadcast Sink
1255      */
getMaximumSourceCapacity(BluetoothDevice sink)1256     int getMaximumSourceCapacity(BluetoothDevice sink) {
1257         log("getMaximumSourceCapacity: device = " + sink);
1258         BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);
1259         if (stateMachine == null) {
1260             log("stateMachine is null");
1261             return 0;
1262         }
1263         return stateMachine.getMaximumSourceCapacity();
1264     }
1265 
isLocalBroadcast(BluetoothLeBroadcastMetadata metaData)1266     boolean isLocalBroadcast(BluetoothLeBroadcastMetadata metaData) {
1267         if (metaData == null) {
1268             return false;
1269         }
1270 
1271         LeAudioService leAudioService = mServiceFactory.getLeAudioService();
1272         if (leAudioService == null) {
1273             return false;
1274         }
1275 
1276         boolean wasFound = leAudioService.getAllBroadcastMetadata()
1277                 .stream()
1278                 .anyMatch(meta -> {
1279                     return meta.getSourceAdvertisingSid() == metaData.getSourceAdvertisingSid();
1280                 });
1281         log("isLocalBroadcast=" + wasFound);
1282         return wasFound;
1283     }
1284 
log(String msg)1285     static void log(String msg) {
1286         if (BassConstants.BASS_DBG) {
1287             Log.d(TAG, msg);
1288         }
1289     }
1290 
1291     /**
1292      * Callback handler
1293      */
1294     static class Callbacks extends Handler {
1295         private static final int MSG_SEARCH_STARTED = 1;
1296         private static final int MSG_SEARCH_STARTED_FAILED = 2;
1297         private static final int MSG_SEARCH_STOPPED = 3;
1298         private static final int MSG_SEARCH_STOPPED_FAILED = 4;
1299         private static final int MSG_SOURCE_FOUND = 5;
1300         private static final int MSG_SOURCE_ADDED = 6;
1301         private static final int MSG_SOURCE_ADDED_FAILED = 7;
1302         private static final int MSG_SOURCE_MODIFIED = 8;
1303         private static final int MSG_SOURCE_MODIFIED_FAILED = 9;
1304         private static final int MSG_SOURCE_REMOVED = 10;
1305         private static final int MSG_SOURCE_REMOVED_FAILED = 11;
1306         private static final int MSG_RECEIVESTATE_CHANGED = 12;
1307 
1308         private final RemoteCallbackList<IBluetoothLeBroadcastAssistantCallback>
1309                 mCallbacks = new RemoteCallbackList<>();
1310 
Callbacks(Looper looper)1311         Callbacks(Looper looper) {
1312             super(looper);
1313         }
1314 
register(IBluetoothLeBroadcastAssistantCallback callback)1315         public void register(IBluetoothLeBroadcastAssistantCallback callback) {
1316             mCallbacks.register(callback);
1317         }
1318 
unregister(IBluetoothLeBroadcastAssistantCallback callback)1319         public void unregister(IBluetoothLeBroadcastAssistantCallback callback) {
1320             mCallbacks.unregister(callback);
1321         }
1322 
checkForPendingGroupOpRequest(Message msg)1323         private void checkForPendingGroupOpRequest(Message msg) {
1324             if (sService == null) {
1325                 Log.e(TAG, "Service is null");
1326                 return;
1327             }
1328 
1329             final int reason = msg.arg1;
1330             BluetoothDevice sink;
1331 
1332             switch (msg.what) {
1333                 case MSG_SOURCE_ADDED:
1334                 case MSG_SOURCE_ADDED_FAILED:
1335                     ObjParams param = (ObjParams) msg.obj;
1336                     sink = (BluetoothDevice) param.mObj1;
1337                     sService.checkForPendingGroupOpRequest(sink, reason,
1338                             BassClientStateMachine.ADD_BCAST_SOURCE, param.mObj2);
1339                     break;
1340                 case MSG_SOURCE_REMOVED:
1341                 case MSG_SOURCE_REMOVED_FAILED:
1342                     sink = (BluetoothDevice) msg.obj;
1343                     sService.checkForPendingGroupOpRequest(sink, reason,
1344                             BassClientStateMachine.REMOVE_BCAST_SOURCE, Integer.valueOf(msg.arg2));
1345                     break;
1346                 default:
1347                     break;
1348             }
1349         }
1350 
1351         @Override
handleMessage(Message msg)1352         public void handleMessage(Message msg) {
1353             checkForPendingGroupOpRequest(msg);
1354             final int n = mCallbacks.beginBroadcast();
1355             for (int i = 0; i < n; i++) {
1356                 final IBluetoothLeBroadcastAssistantCallback callback =
1357                         mCallbacks.getBroadcastItem(i);
1358                 try {
1359                     invokeCallback(callback, msg);
1360                 } catch (RemoteException e) {
1361                     Log.e(TAG, "Stack:" + Log.getStackTraceString(e));
1362                 }
1363             }
1364             mCallbacks.finishBroadcast();
1365         }
1366 
1367         private class ObjParams {
1368             Object mObj1;
1369             Object mObj2;
ObjParams(Object o1, Object o2)1370             ObjParams(Object o1, Object o2) {
1371                 mObj1 = o1;
1372                 mObj2 = o2;
1373             }
1374         }
1375 
invokeCallback(IBluetoothLeBroadcastAssistantCallback callback, Message msg)1376         private void invokeCallback(IBluetoothLeBroadcastAssistantCallback callback,
1377                 Message msg) throws RemoteException {
1378             final int reason = msg.arg1;
1379             final int sourceId = msg.arg2;
1380             ObjParams param;
1381             BluetoothDevice sink;
1382 
1383             switch (msg.what) {
1384                 case MSG_SEARCH_STARTED:
1385                     callback.onSearchStarted(reason);
1386                     break;
1387                 case MSG_SEARCH_STARTED_FAILED:
1388                     callback.onSearchStartFailed(reason);
1389                     break;
1390                 case MSG_SEARCH_STOPPED:
1391                     callback.onSearchStopped(reason);
1392                     break;
1393                 case MSG_SEARCH_STOPPED_FAILED:
1394                     callback.onSearchStopFailed(reason);
1395                     break;
1396                 case MSG_SOURCE_FOUND:
1397                     callback.onSourceFound((BluetoothLeBroadcastMetadata) msg.obj);
1398                     break;
1399                 case MSG_SOURCE_ADDED:
1400                     param = (ObjParams) msg.obj;
1401                     sink = (BluetoothDevice) param.mObj1;
1402                     callback.onSourceAdded(sink, sourceId, reason);
1403                     break;
1404                 case MSG_SOURCE_ADDED_FAILED:
1405                     param = (ObjParams) msg.obj;
1406                     sink = (BluetoothDevice) param.mObj1;
1407                     BluetoothLeBroadcastMetadata metadata =
1408                             (BluetoothLeBroadcastMetadata) param.mObj2;
1409                     callback.onSourceAddFailed(sink, metadata, reason);
1410                     break;
1411                 case MSG_SOURCE_MODIFIED:
1412                     callback.onSourceModified((BluetoothDevice) msg.obj, sourceId, reason);
1413                     break;
1414                 case MSG_SOURCE_MODIFIED_FAILED:
1415                     callback.onSourceModifyFailed((BluetoothDevice) msg.obj, sourceId, reason);
1416                     break;
1417                 case MSG_SOURCE_REMOVED:
1418                     sink = (BluetoothDevice) msg.obj;
1419                     callback.onSourceRemoved(sink, sourceId, reason);
1420                     break;
1421                 case MSG_SOURCE_REMOVED_FAILED:
1422                     sink = (BluetoothDevice) msg.obj;
1423                     callback.onSourceRemoveFailed(sink, sourceId, reason);
1424                     break;
1425                 case MSG_RECEIVESTATE_CHANGED:
1426                     param = (ObjParams) msg.obj;
1427                     sink = (BluetoothDevice) param.mObj1;
1428                     BluetoothLeBroadcastReceiveState state =
1429                             (BluetoothLeBroadcastReceiveState) param.mObj2;
1430                     callback.onReceiveStateChanged(sink, sourceId, state);
1431                     break;
1432                 default:
1433                     Log.e(TAG, "Invalid msg: " + msg.what);
1434                     break;
1435             }
1436         }
1437 
notifySearchStarted(int reason)1438         void notifySearchStarted(int reason) {
1439             obtainMessage(MSG_SEARCH_STARTED, reason, 0).sendToTarget();
1440         }
1441 
notifySearchStartFailed(int reason)1442         void notifySearchStartFailed(int reason) {
1443             obtainMessage(MSG_SEARCH_STARTED_FAILED, reason, 0).sendToTarget();
1444         }
1445 
notifySearchStopped(int reason)1446         void notifySearchStopped(int reason) {
1447             obtainMessage(MSG_SEARCH_STOPPED, reason, 0).sendToTarget();
1448         }
1449 
notifySearchStopFailed(int reason)1450         void notifySearchStopFailed(int reason) {
1451             obtainMessage(MSG_SEARCH_STOPPED_FAILED, reason, 0).sendToTarget();
1452         }
1453 
notifySourceFound(BluetoothLeBroadcastMetadata source)1454         void notifySourceFound(BluetoothLeBroadcastMetadata source) {
1455             obtainMessage(MSG_SOURCE_FOUND, 0, 0, source).sendToTarget();
1456         }
1457 
notifySourceAdded(BluetoothDevice sink, BluetoothLeBroadcastReceiveState recvState, int reason)1458         void notifySourceAdded(BluetoothDevice sink, BluetoothLeBroadcastReceiveState recvState,
1459                 int reason) {
1460             ObjParams param = new ObjParams(sink, recvState);
1461             obtainMessage(MSG_SOURCE_ADDED, reason, 0, param).sendToTarget();
1462         }
1463 
notifySourceAddFailed(BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason)1464         void notifySourceAddFailed(BluetoothDevice sink, BluetoothLeBroadcastMetadata source,
1465                 int reason) {
1466             ObjParams param = new ObjParams(sink, source);
1467             obtainMessage(MSG_SOURCE_ADDED_FAILED, reason, 0, param).sendToTarget();
1468         }
1469 
notifySourceModified(BluetoothDevice sink, int sourceId, int reason)1470         void notifySourceModified(BluetoothDevice sink, int sourceId, int reason) {
1471             obtainMessage(MSG_SOURCE_MODIFIED, reason, sourceId, sink).sendToTarget();
1472         }
1473 
notifySourceModifyFailed(BluetoothDevice sink, int sourceId, int reason)1474         void notifySourceModifyFailed(BluetoothDevice sink, int sourceId, int reason) {
1475             obtainMessage(MSG_SOURCE_MODIFIED_FAILED, reason, sourceId, sink).sendToTarget();
1476         }
1477 
notifySourceRemoved(BluetoothDevice sink, int sourceId, int reason)1478         void notifySourceRemoved(BluetoothDevice sink, int sourceId, int reason) {
1479             obtainMessage(MSG_SOURCE_REMOVED, reason, sourceId, sink).sendToTarget();
1480         }
1481 
notifySourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason)1482         void notifySourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) {
1483             obtainMessage(MSG_SOURCE_REMOVED_FAILED, reason, sourceId, sink).sendToTarget();
1484         }
1485 
notifyReceiveStateChanged(BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state)1486         void notifyReceiveStateChanged(BluetoothDevice sink, int sourceId,
1487                 BluetoothLeBroadcastReceiveState state) {
1488             ObjParams param = new ObjParams(sink, state);
1489             obtainMessage(MSG_RECEIVESTATE_CHANGED, 0, sourceId, param).sendToTarget();
1490         }
1491     }
1492 
1493     /** Binder object: must be a static class or memory leak may occur */
1494     @VisibleForTesting
1495     static class BluetoothLeBroadcastAssistantBinder extends IBluetoothLeBroadcastAssistant.Stub
1496             implements IProfileServiceBinder {
1497         BassClientService mService;
1498 
getService()1499         private BassClientService getService() {
1500             if (Utils.isInstrumentationTestMode()) {
1501                 return mService;
1502             }
1503             if (!Utils.checkServiceAvailable(mService, TAG)
1504                     || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)) {
1505                 return null;
1506             }
1507             return mService;
1508         }
1509 
BluetoothLeBroadcastAssistantBinder(BassClientService svc)1510         BluetoothLeBroadcastAssistantBinder(BassClientService svc) {
1511             mService = svc;
1512         }
1513 
1514         @Override
cleanup()1515         public void cleanup() {
1516             mService = null;
1517         }
1518 
1519         @Override
getConnectionState(BluetoothDevice sink)1520         public int getConnectionState(BluetoothDevice sink) {
1521             try {
1522                 BassClientService service = getService();
1523                 if (service == null) {
1524                     Log.e(TAG, "Service is null");
1525                     return BluetoothProfile.STATE_DISCONNECTED;
1526                 }
1527                 return service.getConnectionState(sink);
1528             } catch (RuntimeException e) {
1529                 Log.e(TAG, "Exception happened", e);
1530                 return BluetoothProfile.STATE_DISCONNECTED;
1531             }
1532         }
1533 
1534         @Override
getDevicesMatchingConnectionStates(int[] states)1535         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1536             try {
1537                 BassClientService service = getService();
1538                 if (service == null) {
1539                     Log.e(TAG, "Service is null");
1540                     return Collections.emptyList();
1541                 }
1542                 return service.getDevicesMatchingConnectionStates(states);
1543             } catch (RuntimeException e) {
1544                 Log.e(TAG, "Exception happened", e);
1545                 return Collections.emptyList();
1546             }
1547         }
1548 
1549         @Override
getConnectedDevices()1550         public List<BluetoothDevice> getConnectedDevices() {
1551             try {
1552                 BassClientService service = getService();
1553                 if (service == null) {
1554                     Log.e(TAG, "Service is null");
1555                     return Collections.emptyList();
1556                 }
1557                 return service.getConnectedDevices();
1558             } catch (RuntimeException e) {
1559                 Log.e(TAG, "Exception happened", e);
1560                 return Collections.emptyList();
1561             }
1562         }
1563 
1564         @Override
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)1565         public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
1566             try {
1567                 BassClientService service = getService();
1568                 if (service == null) {
1569                     Log.e(TAG, "Service is null");
1570                     return false;
1571                 }
1572                 mService.enforceCallingOrSelfPermission(BLUETOOTH_CONNECT, "Need BLUETOOTH_CONNECT permission");
1573                 return service.setConnectionPolicy(device, connectionPolicy);
1574             } catch (RuntimeException e) {
1575                 Log.e(TAG, "Exception happened", e);
1576                 return false;
1577             }
1578         }
1579 
1580         @Override
getConnectionPolicy(BluetoothDevice device)1581         public int getConnectionPolicy(BluetoothDevice device) {
1582             try {
1583                 BassClientService service = getService();
1584                 if (service == null) {
1585                     Log.e(TAG, "Service is null");
1586                     return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
1587                 }
1588                 mService.enforceCallingOrSelfPermission(BLUETOOTH_CONNECT, "Need BLUETOOTH_CONNECT permission");
1589                 return service.getConnectionPolicy(device);
1590             } catch (RuntimeException e) {
1591                 Log.e(TAG, "Exception happened", e);
1592                 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
1593             }
1594         }
1595 
1596         @Override
registerCallback(IBluetoothLeBroadcastAssistantCallback cb)1597         public void registerCallback(IBluetoothLeBroadcastAssistantCallback cb) {
1598             try {
1599                 BassClientService service = getService();
1600                 if (service == null) {
1601                     Log.e(TAG, "Service is null");
1602                     return;
1603                 }
1604                 enforceBluetoothPrivilegedPermission(service);
1605                 service.registerCallback(cb);
1606             } catch (RuntimeException e) {
1607                 Log.e(TAG, "Exception happened", e);
1608             }
1609         }
1610 
1611         @Override
unregisterCallback(IBluetoothLeBroadcastAssistantCallback cb)1612         public void unregisterCallback(IBluetoothLeBroadcastAssistantCallback cb) {
1613             try {
1614                 BassClientService service = getService();
1615                 if (service == null) {
1616                     Log.e(TAG, "Service is null");
1617                     return;
1618                 }
1619                 enforceBluetoothPrivilegedPermission(service);
1620                 service.unregisterCallback(cb);
1621             } catch (RuntimeException e) {
1622                 Log.e(TAG, "Exception happened", e);
1623             }
1624         }
1625 
1626         @Override
startSearchingForSources(List<ScanFilter> filters)1627         public void startSearchingForSources(List<ScanFilter> filters) {
1628             try {
1629                 BassClientService service = getService();
1630                 if (service == null) {
1631                     Log.e(TAG, "Service is null");
1632                     return;
1633                 }
1634                 enforceBluetoothPrivilegedPermission(service);
1635                 service.startSearchingForSources(filters);
1636             } catch (RuntimeException e) {
1637                 Log.e(TAG, "Exception happened", e);
1638             }
1639         }
1640 
1641         @Override
stopSearchingForSources()1642         public void stopSearchingForSources() {
1643             try {
1644                 BassClientService service = getService();
1645                 if (service == null) {
1646                     Log.e(TAG, "Service is null");
1647                     return;
1648                 }
1649                 enforceBluetoothPrivilegedPermission(service);
1650                 service.stopSearchingForSources();
1651             } catch (RuntimeException e) {
1652                 Log.e(TAG, "Exception happened", e);
1653             }
1654         }
1655 
1656         @Override
isSearchInProgress()1657         public boolean isSearchInProgress() {
1658             try {
1659                 BassClientService service = getService();
1660                 if (service == null) {
1661                     Log.e(TAG, "Service is null");
1662                     return false;
1663                 }
1664                 enforceBluetoothPrivilegedPermission(service);
1665                 return service.isSearchInProgress();
1666             } catch (RuntimeException e) {
1667                 Log.e(TAG, "Exception happened", e);
1668                 return false;
1669             }
1670         }
1671 
1672         @Override
addSource( BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata, boolean isGroupOp)1673         public void addSource(
1674                 BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata,
1675                 boolean isGroupOp) {
1676             try {
1677                 BassClientService service = getService();
1678                 if (service == null) {
1679                     Log.e(TAG, "Service is null");
1680                     return;
1681                 }
1682                 enforceBluetoothPrivilegedPermission(service);
1683                 service.addSource(sink, sourceMetadata, isGroupOp);
1684             } catch (RuntimeException e) {
1685                 Log.e(TAG, "Exception happened", e);
1686             }
1687         }
1688 
1689         @Override
modifySource( BluetoothDevice sink, int sourceId, BluetoothLeBroadcastMetadata updatedMetadata)1690         public void modifySource(
1691                 BluetoothDevice sink, int sourceId, BluetoothLeBroadcastMetadata updatedMetadata) {
1692             try {
1693                 BassClientService service = getService();
1694                 if (service == null) {
1695                     Log.e(TAG, "Service is null");
1696                     return;
1697                 }
1698                 enforceBluetoothPrivilegedPermission(service);
1699                 service.modifySource(sink, sourceId, updatedMetadata);
1700             } catch (RuntimeException e) {
1701                 Log.e(TAG, "Exception happened", e);
1702             }
1703         }
1704 
1705         @Override
removeSource(BluetoothDevice sink, int sourceId)1706         public void removeSource(BluetoothDevice sink, int sourceId) {
1707             try {
1708                 BassClientService service = getService();
1709                 if (service == null) {
1710                     Log.e(TAG, "Service is null");
1711                     return;
1712                 }
1713                 enforceBluetoothPrivilegedPermission(service);
1714                 service.removeSource(sink, sourceId);
1715             } catch (RuntimeException e) {
1716                 Log.e(TAG, "Exception happened", e);
1717             }
1718         }
1719 
1720         @Override
getAllSources(BluetoothDevice sink)1721         public List<BluetoothLeBroadcastReceiveState> getAllSources(BluetoothDevice sink) {
1722             try {
1723                 BassClientService service = getService();
1724                 if (sink == null) {
1725                     Log.e(TAG, "Service is null");
1726                     return Collections.emptyList();
1727                 }
1728                 enforceBluetoothPrivilegedPermission(service);
1729                 return service.getAllSources(sink);
1730             } catch (RuntimeException e) {
1731                 Log.e(TAG, "Exception happened", e);
1732                 return Collections.emptyList();
1733             }
1734         }
1735 
1736         @Override
getMaximumSourceCapacity(BluetoothDevice sink)1737         public int getMaximumSourceCapacity(BluetoothDevice sink) {
1738             try {
1739                 BassClientService service = getService();
1740                 if (service == null) {
1741                     Log.e(TAG, "Service is null");
1742                     return 0;
1743                 }
1744                 enforceBluetoothPrivilegedPermission(service);
1745                 return service.getMaximumSourceCapacity(sink);
1746             } catch (RuntimeException e) {
1747                 Log.e(TAG, "Exception happened", e);
1748                 return 0;
1749             }
1750         }
1751     }
1752 }
1753