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