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