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