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 import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; 21 22 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; 23 import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask; 24 import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment; 25 import static com.android.bluetooth.flags.Flags.leaudioBroadcastAudioHandoverPolicies; 26 import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine; 27 import static com.android.bluetooth.flags.Flags.leaudioBroadcastFeatureSupport; 28 import static com.android.bluetooth.flags.Flags.leaudioBroadcastMonitorSourceSyncStatus; 29 30 import android.bluetooth.BluetoothAdapter; 31 import android.bluetooth.BluetoothDevice; 32 import android.bluetooth.BluetoothGatt; 33 import android.bluetooth.BluetoothLeAudio; 34 import android.bluetooth.BluetoothLeAudioCodecConfigMetadata; 35 import android.bluetooth.BluetoothLeAudioContentMetadata; 36 import android.bluetooth.BluetoothLeBroadcastChannel; 37 import android.bluetooth.BluetoothLeBroadcastMetadata; 38 import android.bluetooth.BluetoothLeBroadcastReceiveState; 39 import android.bluetooth.BluetoothLeBroadcastSubgroup; 40 import android.bluetooth.BluetoothProfile; 41 import android.bluetooth.BluetoothStatusCodes; 42 import android.bluetooth.BluetoothUtils; 43 import android.bluetooth.BluetoothUtils.TypeValueEntry; 44 import android.bluetooth.BluetoothUuid; 45 import android.bluetooth.IBluetoothLeBroadcastAssistant; 46 import android.bluetooth.IBluetoothLeBroadcastAssistantCallback; 47 import android.bluetooth.le.PeriodicAdvertisingCallback; 48 import android.bluetooth.le.PeriodicAdvertisingReport; 49 import android.bluetooth.le.ScanCallback; 50 import android.bluetooth.le.ScanFilter; 51 import android.bluetooth.le.ScanRecord; 52 import android.bluetooth.le.ScanResult; 53 import android.bluetooth.le.ScanSettings; 54 import android.content.Context; 55 import android.os.Handler; 56 import android.os.HandlerThread; 57 import android.os.Looper; 58 import android.os.Message; 59 import android.os.ParcelUuid; 60 import android.os.RemoteCallbackList; 61 import android.os.RemoteException; 62 import android.provider.DeviceConfig; 63 import android.sysprop.BluetoothProperties; 64 import android.util.Log; 65 import android.util.Pair; 66 67 import com.android.bluetooth.BluetoothEventLogger; 68 import com.android.bluetooth.BluetoothMethodProxy; 69 import com.android.bluetooth.Utils; 70 import com.android.bluetooth.btservice.AdapterService; 71 import com.android.bluetooth.btservice.ProfileService; 72 import com.android.bluetooth.btservice.ServiceFactory; 73 import com.android.bluetooth.btservice.storage.DatabaseManager; 74 import com.android.bluetooth.csip.CsipSetCoordinatorService; 75 import com.android.bluetooth.le_audio.LeAudioService; 76 import com.android.internal.annotations.VisibleForTesting; 77 78 import java.nio.charset.StandardCharsets; 79 import java.time.Duration; 80 import java.util.ArrayDeque; 81 import java.util.ArrayList; 82 import java.util.Arrays; 83 import java.util.Collections; 84 import java.util.Comparator; 85 import java.util.Deque; 86 import java.util.HashMap; 87 import java.util.HashSet; 88 import java.util.Iterator; 89 import java.util.LinkedList; 90 import java.util.List; 91 import java.util.Map; 92 import java.util.Objects; 93 import java.util.Optional; 94 import java.util.PriorityQueue; 95 import java.util.concurrent.ConcurrentHashMap; 96 97 /** Broacast Assistant Scan Service */ 98 public class BassClientService extends ProfileService { 99 private static final String TAG = BassClientService.class.getSimpleName(); 100 private static final int MAX_BASS_CLIENT_STATE_MACHINES = 10; 101 private static final int MAX_ACTIVE_SYNCED_SOURCES_NUM = 4; 102 private static final int MAX_BIS_DISCOVERY_TRIES_NUM = 5; 103 104 private static final int STATUS_LOCAL_STREAM_REQUESTED = 0; 105 private static final int STATUS_LOCAL_STREAM_STREAMING = 1; 106 private static final int STATUS_LOCAL_STREAM_SUSPENDED = 2; 107 private static final int STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE = 3; 108 109 // Do not modify without updating the HAL bt_le_audio.h files. 110 // Match up with BroadcastState enum of bt_le_audio.h 111 private static final int BROADCAST_STATE_STOPPED = 0; 112 private static final int BROADCAST_STATE_CONFIGURING = 1; 113 private static final int BROADCAST_STATE_PAUSED = 2; 114 private static final int BROADCAST_STATE_STOPPING = 3; 115 private static final int BROADCAST_STATE_STREAMING = 4; 116 117 private static final int MESSAGE_SYNC_TIMEOUT = 1; 118 119 /* 1 minute timeout for primary device reconnection in Private Broadcast case */ 120 private static final int DIALING_OUT_TIMEOUT_MS = 60000; 121 122 // 30 secs timeout for keeping PSYNC active when searching is stopped 123 @VisibleForTesting static Duration sSyncActiveTimeout = Duration.ofSeconds(30); 124 125 private static BassClientService sService; 126 127 private final Map<BluetoothDevice, BassClientStateMachine> mStateMachines = new HashMap<>(); 128 private final Object mSearchScanCallbackLock = new Object(); 129 private final Map<Integer, ScanResult> mCachedBroadcasts = new HashMap<>(); 130 131 private final List<Integer> mActiveSyncedSources = new ArrayList<>(); 132 private final Map<Integer, PeriodicAdvertisingCallback> mPeriodicAdvCallbacksMap = 133 new HashMap<>(); 134 private final PriorityQueue<SourceSyncRequest> mSourceSyncRequestsQueue = 135 new PriorityQueue<>(sSourceSyncRequestComparator); 136 private final Map<Integer, Integer> mBisDiscoveryCounterMap = new HashMap<Integer, Integer>(); 137 private final List<AddSourceData> mPendingSourcesToAdd = new ArrayList<AddSourceData>(); 138 139 private final Map<BluetoothDevice, List<Pair<Integer, Object>>> mPendingGroupOp = 140 new ConcurrentHashMap<>(); 141 private final Map<BluetoothDevice, List<Integer>> mGroupManagedSources = 142 new ConcurrentHashMap<>(); 143 private final Map<BluetoothDevice, List<Integer>> mActiveSourceMap = new ConcurrentHashMap<>(); 144 private final Map<BluetoothDevice, BluetoothLeBroadcastMetadata> mBroadcastMetadataMap = 145 new ConcurrentHashMap<>(); 146 private final LinkedList<BluetoothDevice> mPausedBroadcastSinks = new LinkedList<>(); 147 private final Deque<AddSourceData> mPendingAddSources = new ArrayDeque<>(); 148 private final Map<Integer, HashSet<BluetoothDevice>> mLocalBroadcastReceivers = 149 new ConcurrentHashMap<>(); 150 151 private HandlerThread mStateMachinesThread; 152 private HandlerThread mCallbackHandlerThread; 153 private AdapterService mAdapterService; 154 private DatabaseManager mDatabaseManager; 155 private BluetoothAdapter mBluetoothAdapter = null; 156 private BluetoothLeScannerWrapper mBluetoothLeScannerWrapper = null; 157 private DialingOutTimeoutEvent mDialingOutTimeoutEvent = null; 158 159 /* Caching the PeriodicAdvertisementResult from Broadcast source */ 160 /* This is stored at service so that each device state machine can access 161 and use it as needed. Once the periodic sync in cancelled, this data will bre 162 removed to ensure stable data won't used */ 163 /* syncHandle, broadcastSrcDevice */ 164 private Map<Integer, BluetoothDevice> mSyncHandleToDeviceMap = 165 new HashMap<Integer, BluetoothDevice>(); 166 /*syncHandle, parsed BaseData data*/ 167 private Map<Integer, BaseData> mSyncHandleToBaseDataMap = new HashMap<Integer, BaseData>(); 168 /*syncHandle, broadcast id */ 169 private Map<Integer, Integer> mSyncHandleToBroadcastIdMap = new HashMap<Integer, Integer>(); 170 /*bcastSrcDevice, corresponding broadcast id and PeriodicAdvertisementResult*/ 171 private Map<BluetoothDevice, HashMap<Integer, PeriodicAdvertisementResult>> 172 mPeriodicAdvertisementResultMap = 173 new HashMap<BluetoothDevice, HashMap<Integer, PeriodicAdvertisementResult>>(); 174 private ScanCallback mSearchScanCallback = null; 175 private Callbacks mCallbacks; 176 private boolean mIsAssistantActive = false; 177 private boolean mIsAllowedContextOfActiveGroupModified = false; 178 Optional<Integer> mUnicastSourceStreamStatus = Optional.empty(); 179 180 private static final int LOG_NB_EVENTS = 100; 181 private static final BluetoothEventLogger sEventLogger = 182 new BluetoothEventLogger(LOG_NB_EVENTS, TAG + " event log"); 183 ; 184 185 @VisibleForTesting ServiceFactory mServiceFactory = new ServiceFactory(); 186 187 private final Handler mHandler = 188 new Handler(Looper.getMainLooper()) { 189 @Override 190 public void handleMessage(Message msg) { 191 switch (msg.what) { 192 case MESSAGE_SYNC_TIMEOUT: 193 log("MESSAGE_SYNC_TIMEOUT: clear all sync data"); 194 clearAllSyncData(); 195 break; 196 } 197 } 198 }; 199 BassClientService(Context ctx)200 public BassClientService(Context ctx) { 201 super(ctx); 202 } 203 isEnabled()204 public static boolean isEnabled() { 205 return leaudioBroadcastFeatureSupport() 206 && BluetoothProperties.isProfileBapBroadcastAssistEnabled().orElse(false); 207 } 208 209 private static class SourceSyncRequest { 210 private ScanResult mScanResult; 211 private boolean mHasPriority; 212 SourceSyncRequest(ScanResult scanResult, boolean hasPriority)213 SourceSyncRequest(ScanResult scanResult, boolean hasPriority) { 214 this.mScanResult = scanResult; 215 this.mHasPriority = hasPriority; 216 } 217 getScanResult()218 public ScanResult getScanResult() { 219 return mScanResult; 220 } 221 getRssi()222 public int getRssi() { 223 return mScanResult.getRssi(); 224 } 225 hasPriority()226 public boolean hasPriority() { 227 return mHasPriority; 228 } 229 230 @Override toString()231 public String toString() { 232 return "SourceSyncRequest{" 233 + "mScanResult=" 234 + mScanResult 235 + ", mHasPriority=" 236 + mHasPriority 237 + '}'; 238 } 239 } 240 241 private static final Comparator<SourceSyncRequest> sSourceSyncRequestComparator = 242 new Comparator<SourceSyncRequest>() { 243 @Override 244 public int compare(SourceSyncRequest ssr1, SourceSyncRequest ssr2) { 245 if (ssr1.hasPriority() && !ssr2.hasPriority()) { 246 return -1; 247 } else if (!ssr1.hasPriority() && ssr2.hasPriority()) { 248 return 1; 249 } else { 250 return Integer.compare(ssr2.getRssi(), ssr1.getRssi()); 251 } 252 } 253 }; 254 255 private static class AddSourceData { 256 BluetoothDevice mSink; 257 BluetoothLeBroadcastMetadata mSourceMetadata; 258 boolean mIsGroupOp; 259 AddSourceData( BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata, boolean isGroupOp)260 AddSourceData( 261 BluetoothDevice sink, 262 BluetoothLeBroadcastMetadata sourceMetadata, 263 boolean isGroupOp) { 264 mSink = sink; 265 mSourceMetadata = sourceMetadata; 266 mIsGroupOp = isGroupOp; 267 } 268 } 269 updatePeriodicAdvertisementResultMap( BluetoothDevice device, int addressType, int syncHandle, int advSid, int advInterval, int bId, PublicBroadcastData pbData, String broadcastName)270 void updatePeriodicAdvertisementResultMap( 271 BluetoothDevice device, 272 int addressType, 273 int syncHandle, 274 int advSid, 275 int advInterval, 276 int bId, 277 PublicBroadcastData pbData, 278 String broadcastName) { 279 log("updatePeriodicAdvertisementResultMap: device: " + device); 280 log("updatePeriodicAdvertisementResultMap: syncHandle: " + syncHandle); 281 log("updatePeriodicAdvertisementResultMap: advSid: " + advSid); 282 log("updatePeriodicAdvertisementResultMap: addressType: " + addressType); 283 log("updatePeriodicAdvertisementResultMap: advInterval: " + advInterval); 284 log("updatePeriodicAdvertisementResultMap: broadcastId: " + bId); 285 log("updatePeriodicAdvertisementResultMap: broadcastName: " + broadcastName); 286 log("mSyncHandleToDeviceMap" + mSyncHandleToDeviceMap); 287 log("mPeriodicAdvertisementResultMap" + mPeriodicAdvertisementResultMap); 288 if (mPeriodicAdvertisementResultMap != null) { 289 HashMap<Integer, PeriodicAdvertisementResult> paResMap = 290 mPeriodicAdvertisementResultMap.get(device); 291 if (paResMap == null 292 || (bId != BassConstants.INVALID_BROADCAST_ID && !paResMap.containsKey(bId))) { 293 log("PAResmap: add >>>"); 294 PeriodicAdvertisementResult paRes = 295 new PeriodicAdvertisementResult( 296 device, 297 addressType, 298 syncHandle, 299 advSid, 300 advInterval, 301 bId, 302 pbData, 303 broadcastName); 304 if (paRes != null) { 305 paRes.print(); 306 mPeriodicAdvertisementResultMap.putIfAbsent(device, new HashMap<>()); 307 mPeriodicAdvertisementResultMap.get(device).put(bId, paRes); 308 } 309 } else { 310 log("PAResmap: update >>>"); 311 if (bId == BassConstants.INVALID_BROADCAST_ID) { 312 // Update when onSyncEstablished, try to retrieve valid broadcast id 313 if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 314 bId = getBroadcastIdForSyncHandle(BassConstants.INVALID_SYNC_HANDLE); 315 316 if (bId == BassConstants.INVALID_BROADCAST_ID 317 || !paResMap.containsKey(bId)) { 318 Log.e(TAG, "PAResmap: error! no valid broadcast id found>>>"); 319 return; 320 } 321 322 int oldBroadcastId = getBroadcastIdForSyncHandle(syncHandle); 323 if (oldBroadcastId != BassConstants.INVALID_BROADCAST_ID 324 && oldBroadcastId != bId) { 325 log( 326 "updatePeriodicAdvertisementResultMap: SyncEstablished on the" 327 + " same syncHandle=" 328 + syncHandle 329 + ", before syncLost"); 330 if (leaudioBroadcastMonitorSourceSyncStatus()) { 331 log( 332 "Notify broadcast source lost, broadcast id: " 333 + oldBroadcastId); 334 mCallbacks.notifySourceLost(oldBroadcastId); 335 } 336 clearAllDataForSyncHandle(syncHandle); 337 mCachedBroadcasts.remove(oldBroadcastId); 338 } 339 } else { 340 for (Map.Entry<Integer, PeriodicAdvertisementResult> entry : 341 paResMap.entrySet()) { 342 PeriodicAdvertisementResult value = entry.getValue(); 343 if (value.getBroadcastId() != BassConstants.INVALID_BROADCAST_ID) { 344 bId = value.getBroadcastId(); 345 break; 346 } 347 } 348 if (bId == BassConstants.INVALID_BROADCAST_ID) { 349 log("PAResmap: error! no valid broadcast id found>>>"); 350 return; 351 } 352 } 353 } 354 PeriodicAdvertisementResult paRes = paResMap.get(bId); 355 if (advSid != BassConstants.INVALID_ADV_SID) { 356 paRes.updateAdvSid(advSid); 357 } 358 if (syncHandle != BassConstants.INVALID_SYNC_HANDLE) { 359 if (mSyncHandleToDeviceMap != null) { 360 mSyncHandleToDeviceMap.put(syncHandle, device); 361 } 362 paRes.updateSyncHandle(syncHandle); 363 if (paRes.getBroadcastId() != BassConstants.INVALID_BROADCAST_ID) { 364 // broadcast successfully synced 365 // update the sync handle for the broadcast source 366 updateSyncHandleForBroadcastId(syncHandle, paRes.getBroadcastId()); 367 } 368 } 369 if (addressType != BassConstants.INVALID_ADV_ADDRESS_TYPE) { 370 paRes.updateAddressType(addressType); 371 } 372 if (advInterval != BassConstants.INVALID_ADV_INTERVAL) { 373 paRes.updateAdvInterval(advInterval); 374 } 375 if (bId != BassConstants.INVALID_BROADCAST_ID) { 376 paRes.updateBroadcastId(bId); 377 } 378 if (pbData != null) { 379 paRes.updatePublicBroadcastData(pbData); 380 } 381 if (broadcastName != null) { 382 paRes.updateBroadcastName(broadcastName); 383 } 384 paRes.print(); 385 paResMap.replace(bId, paRes); 386 } 387 } 388 log(">>mPeriodicAdvertisementResultMap" + mPeriodicAdvertisementResultMap); 389 } 390 getPeriodicAdvertisementResult( BluetoothDevice device, int broadcastId)391 PeriodicAdvertisementResult getPeriodicAdvertisementResult( 392 BluetoothDevice device, int broadcastId) { 393 if (mPeriodicAdvertisementResultMap == null) { 394 Log.e(TAG, "getPeriodicAdvertisementResult: mPeriodicAdvertisementResultMap is null"); 395 return null; 396 } 397 398 if (broadcastId == BassConstants.INVALID_BROADCAST_ID) { 399 Log.e(TAG, "getPeriodicAdvertisementResult: invalid broadcast id"); 400 return null; 401 } 402 403 if (mPeriodicAdvertisementResultMap.containsKey(device)) { 404 return mPeriodicAdvertisementResultMap.get(device).get(broadcastId); 405 } 406 return null; 407 } 408 clearNotifiedFlags()409 void clearNotifiedFlags() { 410 log("clearNotifiedFlags"); 411 for (Map.Entry<BluetoothDevice, HashMap<Integer, PeriodicAdvertisementResult>> entry : 412 mPeriodicAdvertisementResultMap.entrySet()) { 413 HashMap<Integer, PeriodicAdvertisementResult> value = entry.getValue(); 414 for (PeriodicAdvertisementResult result : value.values()) { 415 result.setNotified(false); 416 result.print(); 417 } 418 } 419 } 420 updateBase(int syncHandlemap, BaseData base)421 void updateBase(int syncHandlemap, BaseData base) { 422 if (mSyncHandleToBaseDataMap == null) { 423 Log.e(TAG, "updateBase: mSyncHandleToBaseDataMap is null"); 424 return; 425 } 426 log("updateBase : mSyncHandleToBaseDataMap>>"); 427 mSyncHandleToBaseDataMap.put(syncHandlemap, base); 428 } 429 getBase(int syncHandlemap)430 BaseData getBase(int syncHandlemap) { 431 if (mSyncHandleToBaseDataMap == null) { 432 Log.e(TAG, "getBase: mSyncHandleToBaseDataMap is null"); 433 return null; 434 } 435 BaseData base = mSyncHandleToBaseDataMap.get(syncHandlemap); 436 log("getBase returns " + base); 437 return base; 438 } 439 removeActiveSyncedSource(BluetoothDevice scanDelegator, Integer syncHandle)440 void removeActiveSyncedSource(BluetoothDevice scanDelegator, Integer syncHandle) { 441 if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 442 throw new RuntimeException( 443 "Should never be executed with" 444 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 445 } 446 if (mActiveSourceMap == null) { 447 Log.e(TAG, "removeActiveSyncedSource: mActiveSourceMap is null"); 448 return; 449 } 450 451 log( 452 "removeActiveSyncedSource, scanDelegator: " 453 + scanDelegator 454 + ", syncHandle: " 455 + syncHandle); 456 if (syncHandle == null) { 457 // remove all sources for this scanDelegator 458 mActiveSourceMap.remove(scanDelegator); 459 } else { 460 List<Integer> sources = mActiveSourceMap.get(scanDelegator); 461 if (sources != null) { 462 sources.removeIf(e -> e.equals(syncHandle)); 463 if (sources.isEmpty()) { 464 mActiveSourceMap.remove(scanDelegator); 465 } 466 } 467 } 468 sEventLogger.logd( 469 TAG, 470 "Broadcast Source Unsynced: scanDelegator= " 471 + scanDelegator 472 + ", syncHandle= " 473 + syncHandle); 474 } 475 addActiveSyncedSource(BluetoothDevice scanDelegator, Integer syncHandle)476 void addActiveSyncedSource(BluetoothDevice scanDelegator, Integer syncHandle) { 477 if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 478 throw new RuntimeException( 479 "Should never be executed with" 480 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 481 } 482 if (mActiveSourceMap == null) { 483 Log.e(TAG, "addActiveSyncedSource: mActiveSourceMap is null"); 484 return; 485 } 486 487 log( 488 "addActiveSyncedSource, scanDelegator: " 489 + scanDelegator 490 + ", syncHandle: " 491 + syncHandle); 492 if (syncHandle != BassConstants.INVALID_SYNC_HANDLE) { 493 mActiveSourceMap.putIfAbsent(scanDelegator, new ArrayList<>()); 494 if (!mActiveSourceMap.get(scanDelegator).contains(syncHandle)) { 495 mActiveSourceMap.get(scanDelegator).add(syncHandle); 496 } 497 } 498 sEventLogger.logd( 499 TAG, 500 "Broadcast Source Synced: scanDelegator= " 501 + scanDelegator 502 + ", syncHandle= " 503 + syncHandle); 504 } 505 getActiveSyncedSources(BluetoothDevice scanDelegator)506 List<Integer> getActiveSyncedSources(BluetoothDevice scanDelegator) { 507 if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 508 throw new RuntimeException( 509 "Should never be executed with" 510 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 511 } 512 if (mActiveSourceMap == null) { 513 Log.e(TAG, "getActiveSyncedSources: mActiveSourceMap is null"); 514 return null; 515 } 516 517 List<Integer> currentSources = mActiveSourceMap.get(scanDelegator); 518 if (currentSources != null) { 519 log( 520 "getActiveSyncedSources: scanDelegator: " 521 + scanDelegator 522 + ", sources num: " 523 + currentSources.size()); 524 } else { 525 log( 526 "getActiveSyncedSources: scanDelegator: " 527 + scanDelegator 528 + ", currentSources is null"); 529 } 530 return currentSources; 531 } 532 removeActiveSyncedSource(Integer syncHandle)533 void removeActiveSyncedSource(Integer syncHandle) { 534 log("removeActiveSyncedSource, syncHandle: " + syncHandle); 535 if (syncHandle == null) { 536 // remove all sources 537 mActiveSyncedSources.clear(); 538 } else { 539 mActiveSyncedSources.removeIf(e -> e.equals(syncHandle)); 540 } 541 sEventLogger.logd(TAG, "Broadcast Source Unsynced: syncHandle= " + syncHandle); 542 } 543 addActiveSyncedSource(Integer syncHandle)544 void addActiveSyncedSource(Integer syncHandle) { 545 log("addActiveSyncedSource, syncHandle: " + syncHandle); 546 if (syncHandle != BassConstants.INVALID_SYNC_HANDLE) { 547 if (!mActiveSyncedSources.contains(syncHandle)) { 548 mActiveSyncedSources.add(syncHandle); 549 } 550 } 551 sEventLogger.logd(TAG, "Broadcast Source Synced: syncHandle= " + syncHandle); 552 } 553 getActiveSyncedSources()554 List<Integer> getActiveSyncedSources() { 555 log("getActiveSyncedSources: sources num: " + mActiveSyncedSources.size()); 556 return mActiveSyncedSources; 557 } 558 getCachedBroadcast(int broadcastId)559 ScanResult getCachedBroadcast(int broadcastId) { 560 return mCachedBroadcasts.get(broadcastId); 561 } 562 getCallbacks()563 public Callbacks getCallbacks() { 564 return mCallbacks; 565 } 566 567 @Override initBinder()568 protected IProfileServiceBinder initBinder() { 569 return new BluetoothLeBroadcastAssistantBinder(this); 570 } 571 572 @Override start()573 public void start() { 574 Log.d(TAG, "start()"); 575 if (sService != null) { 576 throw new IllegalStateException("start() called twice"); 577 } 578 mAdapterService = 579 Objects.requireNonNull( 580 AdapterService.getAdapterService(), 581 "AdapterService cannot be null when BassClientService starts"); 582 mDatabaseManager = 583 Objects.requireNonNull( 584 mAdapterService.getDatabase(), 585 "DatabaseManager cannot be null when BassClientService starts"); 586 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 587 588 mStateMachines.clear(); 589 mStateMachinesThread = new HandlerThread("BassClientService.StateMachines"); 590 mStateMachinesThread.start(); 591 mCallbackHandlerThread = new HandlerThread(TAG); 592 mCallbackHandlerThread.start(); 593 mCallbacks = new Callbacks(mCallbackHandlerThread.getLooper()); 594 595 setBassClientService(this); 596 // While removing leaudioBroadcastExtractPeriodicScannerFromStateMachine remove all checks 597 // against null for: mSyncHandleToDeviceMap, mPeriodicAdvertisementResultMap, 598 // mSyncHandleToBaseDataMap, mSyncHandleToBroadcastIdMap as they never be null 599 if (!leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 600 // Saving PSync stuff for future addition 601 mSyncHandleToDeviceMap = new HashMap<Integer, BluetoothDevice>(); 602 mPeriodicAdvertisementResultMap = 603 new HashMap<BluetoothDevice, HashMap<Integer, PeriodicAdvertisementResult>>(); 604 mSyncHandleToBaseDataMap = new HashMap<Integer, BaseData>(); 605 mSyncHandleToBroadcastIdMap = new HashMap<Integer, Integer>(); 606 mSearchScanCallback = null; 607 } 608 } 609 610 @Override stop()611 public void stop() { 612 Log.d(TAG, "stop()"); 613 614 mUnicastSourceStreamStatus = Optional.empty(); 615 616 if (mDialingOutTimeoutEvent != null) { 617 mHandler.removeCallbacks(mDialingOutTimeoutEvent); 618 mDialingOutTimeoutEvent = null; 619 } 620 621 if (mIsAssistantActive) { 622 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 623 if (leAudioService != null) { 624 leAudioService.activeBroadcastAssistantNotification(false); 625 } 626 mIsAssistantActive = false; 627 } 628 629 if (mIsAllowedContextOfActiveGroupModified) { 630 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 631 if (leAudioService != null) { 632 leAudioService.setActiveGroupAllowedContextMask( 633 BluetoothLeAudio.CONTEXTS_ALL, BluetoothLeAudio.CONTEXTS_ALL); 634 } 635 mIsAllowedContextOfActiveGroupModified = false; 636 } 637 638 synchronized (mStateMachines) { 639 for (BassClientStateMachine sm : mStateMachines.values()) { 640 BassObjectsFactory.getInstance().destroyStateMachine(sm); 641 } 642 mStateMachines.clear(); 643 } 644 if (mCallbackHandlerThread != null) { 645 mCallbackHandlerThread.quitSafely(); 646 mCallbackHandlerThread = null; 647 } 648 if (mStateMachinesThread != null) { 649 mStateMachinesThread.quitSafely(); 650 mStateMachinesThread = null; 651 } 652 653 mHandler.removeCallbacksAndMessages(null); 654 655 setBassClientService(null); 656 if (!leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 657 if (mSyncHandleToDeviceMap != null) { 658 mSyncHandleToDeviceMap.clear(); 659 mSyncHandleToDeviceMap = null; 660 } 661 if (mPeriodicAdvertisementResultMap != null) { 662 mPeriodicAdvertisementResultMap.clear(); 663 mPeriodicAdvertisementResultMap = null; 664 } 665 if (mActiveSourceMap != null) { 666 mActiveSourceMap.clear(); 667 } 668 if (mLocalBroadcastReceivers != null) { 669 mLocalBroadcastReceivers.clear(); 670 } 671 if (mPendingGroupOp != null) { 672 mPendingGroupOp.clear(); 673 } 674 if (mCachedBroadcasts != null) { 675 mCachedBroadcasts.clear(); 676 } 677 if (mBroadcastMetadataMap != null) { 678 mBroadcastMetadataMap.clear(); 679 } 680 if (mSyncHandleToBroadcastIdMap != null) { 681 mSyncHandleToBroadcastIdMap.clear(); 682 mSyncHandleToBroadcastIdMap = null; 683 } 684 if (mSyncHandleToBaseDataMap != null) { 685 mSyncHandleToBaseDataMap.clear(); 686 mSyncHandleToBaseDataMap = null; 687 } 688 } else { 689 synchronized (mSearchScanCallbackLock) { 690 if (mBluetoothLeScannerWrapper != null && mSearchScanCallback != null) { 691 mBluetoothLeScannerWrapper.stopScan(mSearchScanCallback); 692 } 693 mBluetoothLeScannerWrapper = null; 694 mSearchScanCallback = null; 695 clearAllSyncData(); 696 } 697 698 mLocalBroadcastReceivers.clear(); 699 mPendingGroupOp.clear(); 700 mBroadcastMetadataMap.clear(); 701 } 702 } 703 getDeviceForSyncHandle(int syncHandle)704 BluetoothDevice getDeviceForSyncHandle(int syncHandle) { 705 if (mSyncHandleToDeviceMap == null) { 706 return null; 707 } 708 return mSyncHandleToDeviceMap.get(syncHandle); 709 } 710 getSyncHandleForBroadcastId(int broadcastId)711 int getSyncHandleForBroadcastId(int broadcastId) { 712 if (mSyncHandleToBroadcastIdMap == null) { 713 return BassConstants.INVALID_SYNC_HANDLE; 714 } 715 716 int syncHandle = BassConstants.INVALID_SYNC_HANDLE; 717 for (Map.Entry<Integer, Integer> entry : mSyncHandleToBroadcastIdMap.entrySet()) { 718 Integer value = entry.getValue(); 719 if (value == broadcastId) { 720 syncHandle = entry.getKey(); 721 break; 722 } 723 } 724 return syncHandle; 725 } 726 getBroadcastIdForSyncHandle(int syncHandle)727 int getBroadcastIdForSyncHandle(int syncHandle) { 728 if (mSyncHandleToBroadcastIdMap == null) { 729 return BassConstants.INVALID_BROADCAST_ID; 730 } 731 732 if (mSyncHandleToBroadcastIdMap.containsKey(syncHandle)) { 733 return mSyncHandleToBroadcastIdMap.get(syncHandle); 734 } 735 return BassConstants.INVALID_BROADCAST_ID; 736 } 737 updateSyncHandleForBroadcastId(int syncHandle, int broadcastId)738 void updateSyncHandleForBroadcastId(int syncHandle, int broadcastId) { 739 if (mSyncHandleToBroadcastIdMap == null) { 740 Log.e(TAG, "mSyncHandleToBroadcastIdMap is null"); 741 return; 742 } 743 744 mSyncHandleToBroadcastIdMap.entrySet().removeIf(entry -> entry.getValue() == broadcastId); 745 mSyncHandleToBroadcastIdMap.put(syncHandle, broadcastId); 746 log("Updated mSyncHandleToBroadcastIdMap: " + mSyncHandleToBroadcastIdMap); 747 } 748 setBassClientService(BassClientService instance)749 private static synchronized void setBassClientService(BassClientService instance) { 750 Log.d(TAG, "setBassClientService(): set to: " + instance); 751 sService = instance; 752 } 753 enqueueSourceGroupOp(BluetoothDevice sink, Integer msgId, Object obj)754 private void enqueueSourceGroupOp(BluetoothDevice sink, Integer msgId, Object obj) { 755 log("enqueueSourceGroupOp device: " + sink + ", msgId: " + msgId); 756 757 if (!mPendingGroupOp.containsKey(sink)) { 758 mPendingGroupOp.put(sink, new ArrayList()); 759 } 760 mPendingGroupOp.get(sink).add(new Pair<Integer, Object>(msgId, obj)); 761 } 762 isSuccess(int status)763 private boolean isSuccess(int status) { 764 boolean ret = false; 765 switch (status) { 766 case BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST: 767 case BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST: 768 case BluetoothStatusCodes.REASON_REMOTE_REQUEST: 769 case BluetoothStatusCodes.REASON_SYSTEM_POLICY: 770 ret = true; 771 break; 772 default: 773 break; 774 } 775 return ret; 776 } 777 isAnyPendingAddSourceOperation()778 private boolean isAnyPendingAddSourceOperation() { 779 for (BluetoothDevice device : getConnectedDevices()) { 780 List<Pair<Integer, Object>> operations = mPendingGroupOp.get(device); 781 if (operations == null) { 782 continue; 783 } 784 785 boolean isAnyPendingAddSourceOperationForDevice = 786 operations.stream() 787 .anyMatch(e -> e.first.equals(BassClientStateMachine.ADD_BCAST_SOURCE)); 788 789 if (isAnyPendingAddSourceOperationForDevice) { 790 return true; 791 } 792 } 793 794 return false; 795 } 796 checkForPendingGroupOpRequest( BluetoothDevice sink, int reason, int reqMsg, Object obj)797 private void checkForPendingGroupOpRequest( 798 BluetoothDevice sink, int reason, int reqMsg, Object obj) { 799 log( 800 "checkForPendingGroupOpRequest device: " 801 + sink 802 + ", reason: " 803 + reason 804 + ", reqMsg: " 805 + reqMsg); 806 807 List<Pair<Integer, Object>> operations = mPendingGroupOp.get(sink); 808 if (operations == null) { 809 return; 810 } 811 812 switch (reqMsg) { 813 case BassClientStateMachine.ADD_BCAST_SOURCE: 814 if (obj == null) { 815 return; 816 } 817 // Identify the operation by operation type and broadcastId 818 if (isSuccess(reason)) { 819 BluetoothLeBroadcastReceiveState sourceState = 820 (BluetoothLeBroadcastReceiveState) obj; 821 boolean removed = 822 operations.removeIf( 823 m -> 824 (m.first.equals( 825 BassClientStateMachine 826 .ADD_BCAST_SOURCE)) 827 && (sourceState.getBroadcastId() 828 == ((BluetoothLeBroadcastMetadata) 829 m.second) 830 .getBroadcastId())); 831 if (removed) { 832 setSourceGroupManaged(sink, sourceState.getSourceId(), true); 833 } 834 } else { 835 BluetoothLeBroadcastMetadata metadata = (BluetoothLeBroadcastMetadata) obj; 836 operations.removeIf( 837 m -> 838 (m.first.equals(BassClientStateMachine.ADD_BCAST_SOURCE)) 839 && (metadata.getBroadcastId() 840 == ((BluetoothLeBroadcastMetadata) m.second) 841 .getBroadcastId())); 842 843 if (!isAnyPendingAddSourceOperation() 844 && mIsAssistantActive 845 && mPausedBroadcastSinks.isEmpty()) { 846 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 847 mIsAssistantActive = false; 848 mUnicastSourceStreamStatus = Optional.empty(); 849 850 if (leAudioService != null) { 851 leAudioService.activeBroadcastAssistantNotification(false); 852 } 853 } 854 } 855 break; 856 case BassClientStateMachine.REMOVE_BCAST_SOURCE: 857 // Identify the operation by operation type and sourceId 858 Integer sourceId = (Integer) obj; 859 operations.removeIf( 860 m -> 861 m.first.equals(BassClientStateMachine.REMOVE_BCAST_SOURCE) 862 && (sourceId.equals((Integer) m.second))); 863 setSourceGroupManaged(sink, sourceId, false); 864 break; 865 default: 866 break; 867 } 868 } 869 isDevicePartOfActiveUnicastGroup(BluetoothDevice device)870 private boolean isDevicePartOfActiveUnicastGroup(BluetoothDevice device) { 871 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 872 if (leAudioService == null) { 873 return false; 874 } 875 876 return (leAudioService.getActiveGroupId() != LE_AUDIO_GROUP_ID_INVALID) 877 && (leAudioService.getActiveDevices().contains(device)); 878 } 879 isAnyDeviceFromActiveUnicastGroupReceivingBroadcast()880 private boolean isAnyDeviceFromActiveUnicastGroupReceivingBroadcast() { 881 return getActiveBroadcastSinks().stream() 882 .anyMatch(d -> isDevicePartOfActiveUnicastGroup(d)); 883 } 884 localNotifyReceiveStateChanged(BluetoothDevice sink)885 private void localNotifyReceiveStateChanged(BluetoothDevice sink) { 886 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 887 if (leAudioService == null) { 888 return; 889 } 890 891 boolean isAssistantActive = 892 areReceiversReceivingOnlyExternalBroadcast(getConnectedDevices()); 893 894 if (isAssistantActive) { 895 /* Assistant become active */ 896 if (!mIsAssistantActive) { 897 mIsAssistantActive = true; 898 leAudioService.activeBroadcastAssistantNotification(true); 899 } 900 901 if (leaudioAllowedContextMask()) { 902 /* Don't bother active group (external broadcaster scenario) with SOUND EFFECTS */ 903 if (!mIsAllowedContextOfActiveGroupModified 904 && isDevicePartOfActiveUnicastGroup(sink)) { 905 leAudioService.setActiveGroupAllowedContextMask( 906 BluetoothLeAudio.CONTEXTS_ALL 907 & ~BluetoothLeAudio.CONTEXT_TYPE_SOUND_EFFECTS, 908 BluetoothLeAudio.CONTEXTS_ALL); 909 mIsAllowedContextOfActiveGroupModified = true; 910 } 911 } 912 } else { 913 /* Assistant become inactive */ 914 if (mIsAssistantActive && mPausedBroadcastSinks.isEmpty()) { 915 mIsAssistantActive = false; 916 mUnicastSourceStreamStatus = Optional.empty(); 917 leAudioService.activeBroadcastAssistantNotification(false); 918 } 919 920 if (leaudioAllowedContextMask()) { 921 /* Restore allowed context mask for active device */ 922 if (mIsAllowedContextOfActiveGroupModified) { 923 if (!isAnyDeviceFromActiveUnicastGroupReceivingBroadcast()) { 924 leAudioService.setActiveGroupAllowedContextMask( 925 BluetoothLeAudio.CONTEXTS_ALL, BluetoothLeAudio.CONTEXTS_ALL); 926 } 927 mIsAllowedContextOfActiveGroupModified = false; 928 } 929 } 930 } 931 } 932 localNotifySourceAdded( BluetoothDevice sink, BluetoothLeBroadcastReceiveState receiveState)933 private void localNotifySourceAdded( 934 BluetoothDevice sink, BluetoothLeBroadcastReceiveState receiveState) { 935 if (!isLocalBroadcast(receiveState)) { 936 return; 937 } 938 939 int broadcastId = receiveState.getBroadcastId(); 940 941 /* Track devices bonded to local broadcast for further broadcast status handling when sink 942 * device is: 943 * - disconnecting (if no more receivers, broadcast can be stopped) 944 * - connecting (resynchronize if connection lost) 945 */ 946 if (mLocalBroadcastReceivers.containsKey(broadcastId)) { 947 mLocalBroadcastReceivers.get(broadcastId).add(sink); 948 } else { 949 mLocalBroadcastReceivers.put( 950 broadcastId, new HashSet<BluetoothDevice>(Arrays.asList(sink))); 951 } 952 } 953 setSourceGroupManaged(BluetoothDevice sink, int sourceId, boolean isGroupOp)954 private void setSourceGroupManaged(BluetoothDevice sink, int sourceId, boolean isGroupOp) { 955 log("setSourceGroupManaged device: " + sink); 956 if (isGroupOp) { 957 if (!mGroupManagedSources.containsKey(sink)) { 958 mGroupManagedSources.put(sink, new ArrayList<>()); 959 } 960 mGroupManagedSources.get(sink).add(sourceId); 961 } else { 962 List<Integer> sources = mGroupManagedSources.get(sink); 963 if (sources != null) { 964 sources.removeIf(e -> e.equals(sourceId)); 965 } 966 } 967 } 968 969 private Pair<BluetoothLeBroadcastMetadata, Map<BluetoothDevice, Integer>> getGroupManagedDeviceSources(BluetoothDevice sink, Integer sourceId)970 getGroupManagedDeviceSources(BluetoothDevice sink, Integer sourceId) { 971 log("getGroupManagedDeviceSources device: " + sink + " sourceId: " + sourceId); 972 Map map = new HashMap<BluetoothDevice, Integer>(); 973 974 if (mGroupManagedSources.containsKey(sink) 975 && mGroupManagedSources.get(sink).contains(sourceId)) { 976 BassClientStateMachine stateMachine = getOrCreateStateMachine(sink); 977 if (stateMachine == null) { 978 Log.e(TAG, "Can't get state machine for device: " + sink); 979 return new Pair<BluetoothLeBroadcastMetadata, Map<BluetoothDevice, Integer>>( 980 null, null); 981 } 982 983 BluetoothLeBroadcastMetadata metadata = 984 stateMachine.getCurrentBroadcastMetadata(sourceId); 985 if (metadata != null) { 986 int broadcastId = metadata.getBroadcastId(); 987 988 for (BluetoothDevice device : getTargetDeviceList(sink, true)) { 989 List<BluetoothLeBroadcastReceiveState> sources = 990 getOrCreateStateMachine(device).getAllSources(); 991 992 // For each device, find the source ID having this broadcast ID 993 Optional<BluetoothLeBroadcastReceiveState> receiver = 994 sources.stream() 995 .filter(e -> e.getBroadcastId() == broadcastId) 996 .findAny(); 997 if (receiver.isPresent()) { 998 map.put(device, receiver.get().getSourceId()); 999 } else { 1000 // Put invalid source ID if the remote doesn't have it 1001 map.put(device, BassConstants.INVALID_SOURCE_ID); 1002 } 1003 } 1004 return new Pair<BluetoothLeBroadcastMetadata, Map<BluetoothDevice, Integer>>( 1005 metadata, map); 1006 } else { 1007 Log.e( 1008 TAG, 1009 "Couldn't find broadcast metadata for device: " 1010 + sink.getAnonymizedAddress() 1011 + ", and sourceId:" 1012 + sourceId); 1013 } 1014 } 1015 1016 // Just put this single device if this source is not group managed 1017 map.put(sink, sourceId); 1018 return new Pair<BluetoothLeBroadcastMetadata, Map<BluetoothDevice, Integer>>(null, map); 1019 } 1020 getTargetDeviceList(BluetoothDevice device, boolean isGroupOp)1021 private List<BluetoothDevice> getTargetDeviceList(BluetoothDevice device, boolean isGroupOp) { 1022 if (isGroupOp) { 1023 CsipSetCoordinatorService csipClient = mServiceFactory.getCsipSetCoordinatorService(); 1024 if (csipClient != null) { 1025 // Check for coordinated set of devices in the context of CAP 1026 List<BluetoothDevice> csipDevices = 1027 csipClient.getGroupDevicesOrdered(device, BluetoothUuid.CAP); 1028 if (!csipDevices.isEmpty()) { 1029 return csipDevices; 1030 } else { 1031 Log.w(TAG, "CSIP group is empty."); 1032 } 1033 } else { 1034 Log.e(TAG, "CSIP service is null. No grouping information available."); 1035 } 1036 } 1037 1038 List<BluetoothDevice> devices = new ArrayList<>(); 1039 devices.add(device); 1040 return devices; 1041 } 1042 isValidBroadcastSourceAddition( BluetoothDevice device, BluetoothLeBroadcastMetadata metaData)1043 private boolean isValidBroadcastSourceAddition( 1044 BluetoothDevice device, BluetoothLeBroadcastMetadata metaData) { 1045 boolean retval = true; 1046 List<BluetoothLeBroadcastReceiveState> currentAllSources = getAllSources(device); 1047 for (int i = 0; i < currentAllSources.size(); i++) { 1048 BluetoothLeBroadcastReceiveState state = currentAllSources.get(i); 1049 if (metaData.getSourceDevice().equals(state.getSourceDevice()) 1050 && metaData.getSourceAddressType() == state.getSourceAddressType() 1051 && metaData.getSourceAdvertisingSid() == state.getSourceAdvertisingSid() 1052 && metaData.getBroadcastId() == state.getBroadcastId()) { 1053 retval = false; 1054 Log.e( 1055 TAG, 1056 "isValidBroadcastSourceAddition: fail for " 1057 + device 1058 + " metaData: " 1059 + metaData); 1060 break; 1061 } 1062 } 1063 return retval; 1064 } 1065 hasRoomForBroadcastSourceAddition(BluetoothDevice device)1066 private boolean hasRoomForBroadcastSourceAddition(BluetoothDevice device) { 1067 BassClientStateMachine stateMachine = null; 1068 synchronized (mStateMachines) { 1069 stateMachine = getOrCreateStateMachine(device); 1070 } 1071 if (stateMachine == null) { 1072 log("stateMachine is null"); 1073 return false; 1074 } 1075 boolean isRoomAvailable = false; 1076 String emptyBluetoothDevice = "00:00:00:00:00:00"; 1077 for (BluetoothLeBroadcastReceiveState recvState : stateMachine.getAllSources()) { 1078 if (recvState.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) { 1079 isRoomAvailable = true; 1080 break; 1081 } 1082 } 1083 log("isRoomAvailable: " + isRoomAvailable); 1084 return isRoomAvailable; 1085 } 1086 getSourceIdToRemove(BluetoothDevice device)1087 private Integer getSourceIdToRemove(BluetoothDevice device) { 1088 BassClientStateMachine stateMachine = null; 1089 1090 synchronized (mStateMachines) { 1091 stateMachine = getOrCreateStateMachine(device); 1092 } 1093 if (stateMachine == null) { 1094 log("stateMachine is null"); 1095 return BassConstants.INVALID_SOURCE_ID; 1096 } 1097 List<BluetoothLeBroadcastReceiveState> sources = stateMachine.getAllSources(); 1098 if (sources.isEmpty()) { 1099 log("sources is empty"); 1100 return BassConstants.INVALID_SOURCE_ID; 1101 } 1102 1103 Integer sourceId = BassConstants.INVALID_SOURCE_ID; 1104 // Select the source by checking if there is one with PA not synced 1105 Optional<BluetoothLeBroadcastReceiveState> receiver = 1106 sources.stream() 1107 .filter( 1108 e -> 1109 (e.getPaSyncState() 1110 != BluetoothLeBroadcastReceiveState 1111 .PA_SYNC_STATE_SYNCHRONIZED)) 1112 .findAny(); 1113 if (receiver.isPresent()) { 1114 sourceId = receiver.get().getSourceId(); 1115 } else { 1116 // If all sources are synced, continue to pick the 1st source 1117 sourceId = sources.get(0).getSourceId(); 1118 } 1119 return sourceId; 1120 } 1121 getOrCreateStateMachine(BluetoothDevice device)1122 private BassClientStateMachine getOrCreateStateMachine(BluetoothDevice device) { 1123 if (device == null) { 1124 Log.e(TAG, "getOrCreateStateMachine failed: device cannot be null"); 1125 return null; 1126 } 1127 synchronized (mStateMachines) { 1128 BassClientStateMachine stateMachine = mStateMachines.get(device); 1129 if (stateMachine != null) { 1130 return stateMachine; 1131 } 1132 // Limit the maximum number of state machines to avoid DoS attack 1133 if (mStateMachines.size() >= MAX_BASS_CLIENT_STATE_MACHINES) { 1134 Log.e( 1135 TAG, 1136 "Maximum number of Bassclient state machines reached: " 1137 + MAX_BASS_CLIENT_STATE_MACHINES); 1138 return null; 1139 } 1140 log("Creating a new state machine for " + device); 1141 stateMachine = 1142 BassObjectsFactory.getInstance() 1143 .makeStateMachine( 1144 device, 1145 this, 1146 mAdapterService, 1147 mStateMachinesThread.getLooper()); 1148 if (stateMachine != null) { 1149 mStateMachines.put(device, stateMachine); 1150 } 1151 1152 return stateMachine; 1153 } 1154 } 1155 1156 class DialingOutTimeoutEvent implements Runnable { 1157 Integer mBroadcastId; 1158 DialingOutTimeoutEvent(Integer broadcastId)1159 DialingOutTimeoutEvent(Integer broadcastId) { 1160 mBroadcastId = broadcastId; 1161 } 1162 1163 @Override run()1164 public void run() { 1165 mDialingOutTimeoutEvent = null; 1166 1167 if (getBassClientService() == null) { 1168 Log.e(TAG, "DialingOutTimeoutEvent: No Bass service"); 1169 return; 1170 } 1171 1172 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 1173 if (leAudioService == null) { 1174 Log.d(TAG, "DialingOutTimeoutEvent: No available LeAudioService"); 1175 return; 1176 } 1177 1178 sEventLogger.logd(TAG, "Broadcast timeout: " + mBroadcastId); 1179 mLocalBroadcastReceivers.remove(mBroadcastId); 1180 leAudioService.stopBroadcast(mBroadcastId); 1181 } 1182 isScheduledForBroadcast(Integer broadcastId)1183 public boolean isScheduledForBroadcast(Integer broadcastId) { 1184 return mBroadcastId.equals(broadcastId); 1185 } 1186 } 1187 1188 /** 1189 * Get the BassClientService instance 1190 * 1191 * @return BassClientService instance 1192 */ getBassClientService()1193 public static synchronized BassClientService getBassClientService() { 1194 if (sService == null) { 1195 Log.w(TAG, "getBassClientService(): service is NULL"); 1196 return null; 1197 } 1198 if (!sService.isAvailable()) { 1199 Log.w(TAG, "getBassClientService(): service is not available"); 1200 return null; 1201 } 1202 return sService; 1203 } 1204 removeStateMachine(BluetoothDevice device)1205 private void removeStateMachine(BluetoothDevice device) { 1206 synchronized (mStateMachines) { 1207 BassClientStateMachine sm = mStateMachines.get(device); 1208 if (sm == null) { 1209 Log.w( 1210 TAG, 1211 "removeStateMachine: device " + device + " does not have a state machine"); 1212 return; 1213 } 1214 log("removeStateMachine: removing state machine for device: " + device); 1215 sm.doQuit(); 1216 sm.cleanup(); 1217 mStateMachines.remove(device); 1218 } 1219 1220 // Cleanup device cache 1221 mPendingGroupOp.remove(device); 1222 mGroupManagedSources.remove(device); 1223 mActiveSourceMap.remove(device); 1224 } 1225 handleReconnectingAudioSharingModeDevice(BluetoothDevice device)1226 private void handleReconnectingAudioSharingModeDevice(BluetoothDevice device) { 1227 /* In case of reconnecting Audio Sharing mode device */ 1228 if (mDialingOutTimeoutEvent != null) { 1229 for (Map.Entry<Integer, HashSet<BluetoothDevice>> entry : 1230 mLocalBroadcastReceivers.entrySet()) { 1231 Integer broadcastId = entry.getKey(); 1232 HashSet<BluetoothDevice> devices = entry.getValue(); 1233 1234 /* If associated with any broadcast, try to remove pending timeout callback */ 1235 if ((mDialingOutTimeoutEvent.isScheduledForBroadcast(broadcastId)) 1236 && (devices.contains(device))) { 1237 Log.i( 1238 TAG, 1239 "connectionStateChanged: reconnected previousely synced device: " 1240 + device); 1241 mHandler.removeCallbacks(mDialingOutTimeoutEvent); 1242 mDialingOutTimeoutEvent = null; 1243 break; 1244 } 1245 } 1246 } 1247 } 1248 informConnectedDeviceAboutScanOffloadStop()1249 private void informConnectedDeviceAboutScanOffloadStop() { 1250 for (BluetoothDevice device : getConnectedDevices()) { 1251 synchronized (mStateMachines) { 1252 BassClientStateMachine stateMachine = getOrCreateStateMachine(device); 1253 if (stateMachine == null) { 1254 Log.w( 1255 TAG, 1256 "informConnectedDeviceAboutScanOffloadStop: Can't get state " 1257 + "machine for device: " 1258 + device); 1259 continue; 1260 } 1261 stateMachine.sendMessage(BassClientStateMachine.STOP_SCAN_OFFLOAD); 1262 } 1263 } 1264 } 1265 areValidParametersToModifySource( BluetoothLeBroadcastMetadata updatedMetadata, BassClientStateMachine stateMachine, Integer deviceSourceId, BluetoothDevice device)1266 private int areValidParametersToModifySource( 1267 BluetoothLeBroadcastMetadata updatedMetadata, 1268 BassClientStateMachine stateMachine, 1269 Integer deviceSourceId, 1270 BluetoothDevice device) { 1271 if (updatedMetadata == null || stateMachine == null) { 1272 log( 1273 "areValidParametersToModifySource: Error bad parameters: sourceId = " 1274 + deviceSourceId 1275 + " updatedMetadata = " 1276 + updatedMetadata); 1277 return BluetoothStatusCodes.ERROR_BAD_PARAMETERS; 1278 } 1279 if (deviceSourceId == BassConstants.INVALID_SOURCE_ID) { 1280 log("areValidParametersToModifySource: no such sourceId for device: " + device); 1281 return BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID; 1282 } 1283 if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { 1284 log("areValidParametersToModifySource: device is not connected"); 1285 return BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR; 1286 } 1287 byte[] code = updatedMetadata.getBroadcastCode(); 1288 if ((code != null) && (code.length != 0)) { 1289 if ((code.length > 16) || (code.length < 4)) { 1290 log( 1291 "areValidParametersToModifySource: Invalid broadcast code length: " 1292 + code.length 1293 + ", should be between 4 and 16 octets"); 1294 return BluetoothStatusCodes.ERROR_BAD_PARAMETERS; 1295 } 1296 } 1297 if (stateMachine.hasPendingSourceOperation()) { 1298 Log.w( 1299 TAG, 1300 "modifySource: source operation already pending, device: " 1301 + device 1302 + ", broadcastId: " 1303 + updatedMetadata.getBroadcastId()); 1304 return BluetoothStatusCodes.ERROR_ALREADY_IN_TARGET_STATE; 1305 } 1306 1307 return BluetoothStatusCodes.SUCCESS; 1308 } 1309 handleConnectionStateChanged(BluetoothDevice device, int fromState, int toState)1310 void handleConnectionStateChanged(BluetoothDevice device, int fromState, int toState) { 1311 mHandler.post(() -> connectionStateChanged(device, fromState, toState)); 1312 } 1313 connectionStateChanged(BluetoothDevice device, int fromState, int toState)1314 synchronized void connectionStateChanged(BluetoothDevice device, int fromState, int toState) { 1315 if (!isAvailable()) { 1316 Log.w(TAG, "connectionStateChanged: service is not available"); 1317 return; 1318 } 1319 1320 if ((device == null) || (fromState == toState)) { 1321 Log.e( 1322 TAG, 1323 "connectionStateChanged: unexpected invocation. device=" 1324 + device 1325 + " fromState=" 1326 + fromState 1327 + " toState=" 1328 + toState); 1329 return; 1330 } 1331 1332 sEventLogger.logd( 1333 TAG, 1334 "connectionStateChanged: device: " 1335 + device 1336 + ", fromState= " 1337 + BluetoothProfile.getConnectionStateName(fromState) 1338 + ", toState= " 1339 + BluetoothProfile.getConnectionStateName(toState)); 1340 1341 // Check if the device is disconnected - if unbond, remove the state machine 1342 if (toState == BluetoothProfile.STATE_DISCONNECTED) { 1343 mPendingGroupOp.remove(device); 1344 1345 int bondState = mAdapterService.getBondState(device); 1346 if (bondState == BluetoothDevice.BOND_NONE) { 1347 log("Unbonded " + device + ". Removing state machine"); 1348 removeStateMachine(device); 1349 } 1350 } else if (toState == BluetoothProfile.STATE_CONNECTED) { 1351 handleReconnectingAudioSharingModeDevice(device); 1352 } 1353 } 1354 handleBondStateChanged(BluetoothDevice device, int fromState, int toState)1355 public void handleBondStateChanged(BluetoothDevice device, int fromState, int toState) { 1356 mHandler.post(() -> bondStateChanged(device, toState)); 1357 } 1358 1359 @VisibleForTesting bondStateChanged(BluetoothDevice device, int bondState)1360 void bondStateChanged(BluetoothDevice device, int bondState) { 1361 log("Bond state changed for device: " + device + " state: " + bondState); 1362 1363 // Remove state machine if the bonding for a device is removed 1364 if (bondState != BluetoothDevice.BOND_NONE) { 1365 return; 1366 } 1367 1368 synchronized (mStateMachines) { 1369 BassClientStateMachine sm = mStateMachines.get(device); 1370 if (sm == null) { 1371 return; 1372 } 1373 if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { 1374 Log.i(TAG, "Disconnecting device because it was unbonded."); 1375 disconnect(device); 1376 return; 1377 } 1378 removeStateMachine(device); 1379 } 1380 } 1381 1382 /** 1383 * Connects the bass profile to the passed in device 1384 * 1385 * @param device is the device with which we will connect the Bass profile 1386 * @return true if BAss profile successfully connected, false otherwise 1387 */ connect(BluetoothDevice device)1388 public boolean connect(BluetoothDevice device) { 1389 Log.d(TAG, "connect(): " + device); 1390 if (device == null) { 1391 Log.e(TAG, "connect: device is null"); 1392 return false; 1393 } 1394 if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 1395 Log.e(TAG, "connect: connection policy set to forbidden"); 1396 return false; 1397 } 1398 synchronized (mStateMachines) { 1399 BassClientStateMachine stateMachine = getOrCreateStateMachine(device); 1400 if (stateMachine == null) { 1401 Log.e(TAG, "Can't get state machine for device: " + device); 1402 return false; 1403 } 1404 1405 stateMachine.sendMessage(BassClientStateMachine.CONNECT); 1406 } 1407 return true; 1408 } 1409 1410 /** 1411 * Disconnects Bassclient profile for the passed in device 1412 * 1413 * @param device is the device with which we want to disconnected the BAss client profile 1414 * @return true if Bass client profile successfully disconnected, false otherwise 1415 */ disconnect(BluetoothDevice device)1416 public boolean disconnect(BluetoothDevice device) { 1417 Log.d(TAG, "disconnect(): " + device); 1418 if (device == null) { 1419 Log.e(TAG, "disconnect: device is null"); 1420 return false; 1421 } 1422 synchronized (mStateMachines) { 1423 BassClientStateMachine stateMachine = getOrCreateStateMachine(device); 1424 if (stateMachine == null) { 1425 Log.e(TAG, "Can't get state machine for device: " + device); 1426 return false; 1427 } 1428 1429 stateMachine.sendMessage(BassClientStateMachine.DISCONNECT); 1430 } 1431 return true; 1432 } 1433 1434 /** 1435 * Check whether can connect to a peer device. The check considers a number of factors during 1436 * the evaluation. 1437 * 1438 * @param device the peer device to connect to 1439 * @return true if connection is allowed, otherwise false 1440 */ 1441 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) okToConnect(BluetoothDevice device)1442 public boolean okToConnect(BluetoothDevice device) { 1443 // Check if this is an incoming connection in Quiet mode. 1444 if (mAdapterService.isQuietModeEnabled()) { 1445 Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled"); 1446 return false; 1447 } 1448 // Check connection policy and accept or reject the connection. 1449 int connectionPolicy = getConnectionPolicy(device); 1450 int bondState = mAdapterService.getBondState(device); 1451 // Allow this connection only if the device is bonded. Any attempt to connect while 1452 // bonding would potentially lead to an unauthorized connection. 1453 if (bondState != BluetoothDevice.BOND_BONDED) { 1454 Log.w(TAG, "okToConnect: return false, bondState=" + bondState); 1455 return false; 1456 } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN 1457 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 1458 // Otherwise, reject the connection if connectionPolicy is not valid. 1459 Log.w(TAG, "okToConnect: return false, connectionPolicy=" + connectionPolicy); 1460 return false; 1461 } 1462 return true; 1463 } 1464 1465 /** 1466 * Get connection state of remote device 1467 * 1468 * @param sink the remote device 1469 * @return connection state 1470 */ getConnectionState(BluetoothDevice sink)1471 public int getConnectionState(BluetoothDevice sink) { 1472 synchronized (mStateMachines) { 1473 BassClientStateMachine sm = getOrCreateStateMachine(sink); 1474 if (sm == null) { 1475 log("getConnectionState returns STATE_DISC"); 1476 return BluetoothProfile.STATE_DISCONNECTED; 1477 } 1478 return sm.getConnectionState(); 1479 } 1480 } 1481 1482 /** 1483 * Get a list of all LE Audio Broadcast Sinks with the specified connection states. 1484 * 1485 * @param states states array representing the connection states 1486 * @return a list of devices that match the provided connection states 1487 */ getDevicesMatchingConnectionStates(int[] states)1488 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 1489 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 1490 if (states == null) { 1491 return devices; 1492 } 1493 final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 1494 if (bondedDevices == null) { 1495 return devices; 1496 } 1497 synchronized (mStateMachines) { 1498 for (BluetoothDevice device : bondedDevices) { 1499 final ParcelUuid[] featureUuids = device.getUuids(); 1500 if (!Utils.arrayContains(featureUuids, BluetoothUuid.BASS)) { 1501 continue; 1502 } 1503 int connectionState = BluetoothProfile.STATE_DISCONNECTED; 1504 BassClientStateMachine sm = getOrCreateStateMachine(device); 1505 if (sm != null) { 1506 connectionState = sm.getConnectionState(); 1507 } 1508 for (int state : states) { 1509 if (connectionState == state) { 1510 devices.add(device); 1511 break; 1512 } 1513 } 1514 } 1515 return devices; 1516 } 1517 } 1518 1519 /** 1520 * Get a list of all LE Audio Broadcast Sinks connected with the LE Audio Broadcast Assistant. 1521 * 1522 * @return list of connected devices 1523 */ getConnectedDevices()1524 public List<BluetoothDevice> getConnectedDevices() { 1525 synchronized (mStateMachines) { 1526 List<BluetoothDevice> devices = new ArrayList<>(); 1527 for (BassClientStateMachine sm : mStateMachines.values()) { 1528 if (sm.isConnected()) { 1529 devices.add(sm.getDevice()); 1530 } 1531 } 1532 log("getConnectedDevices: " + devices); 1533 return devices; 1534 } 1535 } 1536 1537 /** 1538 * Set the connectionPolicy of the Broadcast Audio Scan Service profile. 1539 * 1540 * <p>The connection policy can be one of: {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 1541 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link 1542 * BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 1543 * 1544 * @param device paired bluetooth device 1545 * @param connectionPolicy is the connection policy to set to for this profile 1546 * @return true if connectionPolicy is set, false on error 1547 */ setConnectionPolicy(BluetoothDevice device, int connectionPolicy)1548 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 1549 Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 1550 boolean setSuccessfully = 1551 mDatabaseManager.setProfileConnectionPolicy( 1552 device, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, connectionPolicy); 1553 if (setSuccessfully && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 1554 connect(device); 1555 } else if (setSuccessfully 1556 && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 1557 disconnect(device); 1558 } 1559 return setSuccessfully; 1560 } 1561 1562 /** 1563 * Get the connection policy of the profile. 1564 * 1565 * <p>The connection policy can be any of: {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 1566 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link 1567 * BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 1568 * 1569 * @param device paired bluetooth device 1570 * @return connection policy of the device 1571 */ getConnectionPolicy(BluetoothDevice device)1572 public int getConnectionPolicy(BluetoothDevice device) { 1573 return mDatabaseManager.getProfileConnectionPolicy( 1574 device, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT); 1575 } 1576 1577 /** 1578 * Register callbacks that will be invoked during scan offloading. 1579 * 1580 * @param cb callbacks to be invoked 1581 */ registerCallback(IBluetoothLeBroadcastAssistantCallback cb)1582 public void registerCallback(IBluetoothLeBroadcastAssistantCallback cb) { 1583 Log.i(TAG, "registerCallback"); 1584 mCallbacks.register(cb); 1585 } 1586 1587 /** 1588 * Unregister callbacks that are invoked during scan offloading. 1589 * 1590 * @param cb callbacks to be unregistered 1591 */ unregisterCallback(IBluetoothLeBroadcastAssistantCallback cb)1592 public void unregisterCallback(IBluetoothLeBroadcastAssistantCallback cb) { 1593 Log.i(TAG, "unregisterCallback"); 1594 mCallbacks.unregister(cb); 1595 } 1596 1597 /** 1598 * Search for LE Audio Broadcast Sources on behalf of all devices connected via Broadcast Audio 1599 * Scan Service, filtered by filters 1600 * 1601 * @param filters ScanFilters for finding exact Broadcast Source 1602 */ startSearchingForSources(List<ScanFilter> filters)1603 public void startSearchingForSources(List<ScanFilter> filters) { 1604 log("startSearchingForSources"); 1605 if (mBluetoothAdapter == null) { 1606 Log.e(TAG, "startSearchingForSources: Adapter is NULL"); 1607 mCallbacks.notifySearchStartFailed(BluetoothStatusCodes.ERROR_UNKNOWN); 1608 return; 1609 } 1610 1611 if (!BluetoothMethodProxy.getInstance() 1612 .initializePeriodicAdvertisingManagerOnDefaultAdapter()) { 1613 Log.e(TAG, "Failed to initialize Periodic Advertising Manager on Default Adapter"); 1614 mCallbacks.notifySearchStartFailed(BluetoothStatusCodes.ERROR_UNKNOWN); 1615 return; 1616 } 1617 1618 synchronized (mSearchScanCallbackLock) { 1619 if (!leaudioBroadcastExtractPeriodicScannerFromStateMachine() 1620 || mBluetoothLeScannerWrapper == null) { 1621 mBluetoothLeScannerWrapper = 1622 BassObjectsFactory.getInstance() 1623 .getBluetoothLeScannerWrapper(mBluetoothAdapter); 1624 } 1625 if (mBluetoothLeScannerWrapper == null) { 1626 Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner"); 1627 mCallbacks.notifySearchStartFailed(BluetoothStatusCodes.ERROR_UNKNOWN); 1628 return; 1629 } 1630 if (mSearchScanCallback != null) { 1631 Log.e(TAG, "LE Scan has already started"); 1632 mCallbacks.notifySearchStartFailed(BluetoothStatusCodes.ERROR_UNKNOWN); 1633 return; 1634 } 1635 mSearchScanCallback = 1636 new ScanCallback() { 1637 @Override 1638 public void onScanResult(int callbackType, ScanResult result) { 1639 log("onScanResult:" + result); 1640 synchronized (mSearchScanCallbackLock) { 1641 // check mSearchScanCallback because even after 1642 // mBluetoothLeScannerWrapper.stopScan(mSearchScanCallback) that 1643 // callback could be called 1644 if (mSearchScanCallback == null) { 1645 log("onScanResult: scanner already stopped"); 1646 return; 1647 } 1648 if (callbackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) { 1649 // Should not happen 1650 Log.e(TAG, "LE Scan has already started"); 1651 return; 1652 } 1653 ScanRecord scanRecord = result.getScanRecord(); 1654 if (scanRecord == null) { 1655 Log.e(TAG, "Null scan record"); 1656 return; 1657 } 1658 Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData(); 1659 if (listOfUuids == null) { 1660 Log.e(TAG, "Service data is null"); 1661 return; 1662 } 1663 if (!listOfUuids.containsKey(BassConstants.BAAS_UUID)) { 1664 return; 1665 } 1666 log("Broadcast Source Found:" + result.getDevice()); 1667 byte[] broadcastIdArray = listOfUuids.get(BassConstants.BAAS_UUID); 1668 int broadcastId = 1669 (int) 1670 (((broadcastIdArray[2] & 0xff) << 16) 1671 | ((broadcastIdArray[1] & 0xff) << 8) 1672 | (broadcastIdArray[0] & 0xff)); 1673 1674 sEventLogger.logd( 1675 TAG, 1676 "Broadcast Source Found: Broadcast ID: " + broadcastId); 1677 1678 if (broadcastId != BassConstants.INVALID_BROADCAST_ID 1679 && mCachedBroadcasts.get(broadcastId) == null) { 1680 log("selectBroadcastSource: broadcastId " + broadcastId); 1681 mCachedBroadcasts.put(broadcastId, result); 1682 if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1683 addSelectSourceRequest(result, false); 1684 } else { 1685 synchronized (mStateMachines) { 1686 for (BassClientStateMachine sm : 1687 mStateMachines.values()) { 1688 if (sm.isConnected()) { 1689 selectSource(sm.getDevice(), result, false); 1690 } 1691 } 1692 } 1693 } 1694 } 1695 } 1696 } 1697 1698 public void onScanFailed(int errorCode) { 1699 Log.e(TAG, "Scan Failure:" + errorCode); 1700 informConnectedDeviceAboutScanOffloadStop(); 1701 } 1702 }; 1703 mHandler.removeMessages(MESSAGE_SYNC_TIMEOUT); 1704 // when starting scan, clear the previously cached broadcast scan results 1705 mCachedBroadcasts.clear(); 1706 // clear previous sources notify flag before scanning new result 1707 // this is to make sure the active sources are notified even if already synced 1708 if (mPeriodicAdvertisementResultMap != null) { 1709 clearNotifiedFlags(); 1710 } 1711 ScanSettings settings = 1712 new ScanSettings.Builder() 1713 .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) 1714 .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) 1715 .setLegacy(false) 1716 .build(); 1717 if (filters == null) { 1718 filters = new ArrayList<ScanFilter>(); 1719 } 1720 if (!BassUtils.containUuid(filters, BassConstants.BAAS_UUID)) { 1721 byte[] serviceData = {0x00, 0x00, 0x00}; // Broadcast_ID 1722 byte[] serviceDataMask = {0x00, 0x00, 0x00}; 1723 1724 filters.add( 1725 new ScanFilter.Builder() 1726 .setServiceData( 1727 BassConstants.BAAS_UUID, serviceData, serviceDataMask) 1728 .build()); 1729 } 1730 1731 for (BluetoothDevice device : getConnectedDevices()) { 1732 synchronized (mStateMachines) { 1733 BassClientStateMachine stateMachine = getOrCreateStateMachine(device); 1734 if (stateMachine == null) { 1735 Log.w( 1736 TAG, 1737 "startSearchingForSources: Can't get state machine for " 1738 + "device: " 1739 + device); 1740 continue; 1741 } 1742 stateMachine.sendMessage(BassClientStateMachine.START_SCAN_OFFLOAD); 1743 } 1744 } 1745 1746 mBluetoothLeScannerWrapper.startScan(filters, settings, mSearchScanCallback); 1747 sEventLogger.logd(TAG, "startSearchingForSources"); 1748 mCallbacks.notifySearchStarted(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 1749 } 1750 } 1751 1752 /** Stops an ongoing search for nearby Broadcast Sources */ stopSearchingForSources()1753 public void stopSearchingForSources() { 1754 log("stopSearchingForSources"); 1755 if (mBluetoothAdapter == null) { 1756 Log.e(TAG, "stopSearchingForSources: Adapter is NULL"); 1757 return; 1758 } 1759 if (!leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1760 BluetoothLeScannerWrapper scanner = 1761 BassObjectsFactory.getInstance() 1762 .getBluetoothLeScannerWrapper(mBluetoothAdapter); 1763 if (scanner == null) { 1764 Log.e(TAG, "startLeScan: cannot get BluetoothLeScanner"); 1765 return; 1766 } 1767 synchronized (mSearchScanCallbackLock) { 1768 if (mSearchScanCallback == null) { 1769 Log.e(TAG, "Scan not started yet"); 1770 mCallbacks.notifySearchStopFailed(BluetoothStatusCodes.ERROR_UNKNOWN); 1771 return; 1772 } 1773 informConnectedDeviceAboutScanOffloadStop(); 1774 scanner.stopScan(mSearchScanCallback); 1775 mSearchScanCallback = null; 1776 sEventLogger.logd(TAG, "stopSearchingForSources"); 1777 mCallbacks.notifySearchStopped(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 1778 } 1779 } else { 1780 synchronized (mSearchScanCallbackLock) { 1781 if (mBluetoothLeScannerWrapper == null || mSearchScanCallback == null) { 1782 Log.e(TAG, "Scan not started yet"); 1783 mCallbacks.notifySearchStopFailed(BluetoothStatusCodes.ERROR_UNKNOWN); 1784 return; 1785 } 1786 mBluetoothLeScannerWrapper.stopScan(mSearchScanCallback); 1787 mBluetoothLeScannerWrapper = null; 1788 mSearchScanCallback = null; 1789 clearAllSyncData(); 1790 informConnectedDeviceAboutScanOffloadStop(); 1791 sEventLogger.logd(TAG, "stopSearchingForSources"); 1792 mCallbacks.notifySearchStopped(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 1793 } 1794 } 1795 } 1796 clearAllSyncData()1797 private void clearAllSyncData() { 1798 mSourceSyncRequestsQueue.clear(); 1799 mPendingSourcesToAdd.clear(); 1800 1801 cancelActiveSync(null); 1802 mActiveSyncedSources.clear(); 1803 mPeriodicAdvCallbacksMap.clear(); 1804 mBisDiscoveryCounterMap.clear(); 1805 1806 mSyncHandleToDeviceMap.clear(); 1807 mSyncHandleToBaseDataMap.clear(); 1808 mSyncHandleToBroadcastIdMap.clear(); 1809 mPeriodicAdvertisementResultMap.clear(); 1810 } 1811 1812 /** 1813 * Return true if a search has been started by this application 1814 * 1815 * @return true if a search has been started by this application 1816 */ isSearchInProgress()1817 public boolean isSearchInProgress() { 1818 synchronized (mSearchScanCallbackLock) { 1819 return mSearchScanCallback != null; 1820 } 1821 } 1822 1823 /** Internal periodc Advertising manager callback */ 1824 final class PACallback extends PeriodicAdvertisingCallback { 1825 @Override onSyncEstablished( int syncHandle, BluetoothDevice device, int advertisingSid, int skip, int timeout, int status)1826 public void onSyncEstablished( 1827 int syncHandle, 1828 BluetoothDevice device, 1829 int advertisingSid, 1830 int skip, 1831 int timeout, 1832 int status) { 1833 log( 1834 "onSyncEstablished syncHandle: " 1835 + syncHandle 1836 + ", device: " 1837 + device 1838 + ", advertisingSid: " 1839 + advertisingSid 1840 + ", skip: " 1841 + skip 1842 + ", timeout: " 1843 + timeout 1844 + ", status: " 1845 + status); 1846 if (status == BluetoothGatt.GATT_SUCCESS) { 1847 // updates syncHandle, advSid 1848 // set other fields as invalid or null 1849 updatePeriodicAdvertisementResultMap( 1850 device, 1851 BassConstants.INVALID_ADV_ADDRESS_TYPE, 1852 syncHandle, 1853 advertisingSid, 1854 BassConstants.INVALID_ADV_INTERVAL, 1855 BassConstants.INVALID_BROADCAST_ID, 1856 null, 1857 null); 1858 addActiveSyncedSource(syncHandle); 1859 1860 synchronized (mSearchScanCallbackLock) { 1861 // when searching is stopped then start timer to stop active syncs 1862 if (mSearchScanCallback == null) { 1863 mHandler.removeMessages(MESSAGE_SYNC_TIMEOUT); 1864 log("onSyncEstablished started timeout for canceling syncs"); 1865 mHandler.sendEmptyMessageDelayed( 1866 MESSAGE_SYNC_TIMEOUT, sSyncActiveTimeout.toMillis()); 1867 } 1868 } 1869 1870 // update valid sync handle in mPeriodicAdvCallbacksMap 1871 synchronized (mPeriodicAdvCallbacksMap) { 1872 if (mPeriodicAdvCallbacksMap.containsKey(BassConstants.INVALID_SYNC_HANDLE)) { 1873 PeriodicAdvertisingCallback paCb = 1874 mPeriodicAdvCallbacksMap.get(BassConstants.INVALID_SYNC_HANDLE); 1875 mPeriodicAdvCallbacksMap.put(syncHandle, paCb); 1876 mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE); 1877 } 1878 } 1879 mBisDiscoveryCounterMap.put(syncHandle, MAX_BIS_DISCOVERY_TRIES_NUM); 1880 1881 synchronized (mPendingSourcesToAdd) { 1882 Iterator<AddSourceData> iterator = mPendingSourcesToAdd.iterator(); 1883 while (iterator.hasNext()) { 1884 AddSourceData pendingSourcesToAdd = iterator.next(); 1885 BluetoothDevice sourceDevice = 1886 pendingSourcesToAdd.mSourceMetadata.getSourceDevice(); 1887 if (sourceDevice.equals(device)) { 1888 addSource( 1889 pendingSourcesToAdd.mSink, 1890 pendingSourcesToAdd.mSourceMetadata, 1891 pendingSourcesToAdd.mIsGroupOp); 1892 iterator.remove(); 1893 } 1894 } 1895 } 1896 } else { 1897 // remove failed sync handle 1898 int broadcastId = getBroadcastIdForSyncHandle(BassConstants.INVALID_SYNC_HANDLE); 1899 log("onSyncEstablished failed for broadcast id: " + broadcastId); 1900 mCachedBroadcasts.remove(broadcastId); 1901 mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE); 1902 } 1903 handleSelectSourceRequest(); 1904 } 1905 1906 @Override onPeriodicAdvertisingReport(PeriodicAdvertisingReport report)1907 public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { 1908 int syncHandle = report.getSyncHandle(); 1909 log("onPeriodicAdvertisingReport " + syncHandle); 1910 Integer bisCounter = mBisDiscoveryCounterMap.get(syncHandle); 1911 1912 // Parse the BIS indices from report's service data 1913 if (bisCounter != null && bisCounter != 0) { 1914 if (parseScanRecord(syncHandle, report.getData())) { 1915 mBisDiscoveryCounterMap.put(syncHandle, 0); 1916 } else { 1917 bisCounter--; 1918 mBisDiscoveryCounterMap.put(syncHandle, bisCounter); 1919 if (bisCounter == 0) { 1920 cancelActiveSync(syncHandle); 1921 } 1922 } 1923 } 1924 } 1925 1926 @Override onSyncLost(int syncHandle)1927 public void onSyncLost(int syncHandle) { 1928 int broadcastId = getBroadcastIdForSyncHandle(syncHandle); 1929 log("OnSyncLost: syncHandle=" + syncHandle + ", broadcastID=" + broadcastId); 1930 if (leaudioBroadcastMonitorSourceSyncStatus()) { 1931 if (broadcastId != BassConstants.INVALID_BROADCAST_ID) { 1932 log("Notify broadcast source lost, broadcast id: " + broadcastId); 1933 mCallbacks.notifySourceLost(broadcastId); 1934 } 1935 } 1936 clearAllDataForSyncHandle(syncHandle); 1937 // Clear from cache to make possible sync again 1938 mCachedBroadcasts.remove(broadcastId); 1939 } 1940 1941 @Override onBigInfoAdvertisingReport(int syncHandle, boolean encrypted)1942 public void onBigInfoAdvertisingReport(int syncHandle, boolean encrypted) { 1943 log( 1944 "onBIGInfoAdvertisingReport: syncHandle=" 1945 + syncHandle 1946 + ", encrypted =" 1947 + encrypted); 1948 BluetoothDevice srcDevice = getDeviceForSyncHandle(syncHandle); 1949 if (srcDevice == null) { 1950 log("No device found."); 1951 return; 1952 } 1953 PeriodicAdvertisementResult result = 1954 getPeriodicAdvertisementResult( 1955 srcDevice, getBroadcastIdForSyncHandle(syncHandle)); 1956 if (result == null) { 1957 log("No PA record found"); 1958 return; 1959 } 1960 if (!result.isNotified()) { 1961 result.setNotified(true); 1962 BaseData baseData = getBase(syncHandle); 1963 if (baseData == null) { 1964 log("No BaseData found"); 1965 return; 1966 } 1967 BluetoothLeBroadcastMetadata metaData = 1968 getBroadcastMetadataFromBaseData( 1969 baseData, srcDevice, syncHandle, encrypted); 1970 log("Notify broadcast source found"); 1971 mCallbacks.notifySourceFound(metaData); 1972 } 1973 } 1974 1975 @Override onSyncTransferred(BluetoothDevice device, int status)1976 public void onSyncTransferred(BluetoothDevice device, int status) { 1977 log("onSyncTransferred: device=" + device + ", status =" + status); 1978 } 1979 } 1980 clearAllDataForSyncHandle(Integer syncHandle)1981 private void clearAllDataForSyncHandle(Integer syncHandle) { 1982 removeActiveSyncedSource(syncHandle); 1983 mPeriodicAdvCallbacksMap.remove(syncHandle); 1984 mSyncHandleToBaseDataMap.remove(syncHandle); 1985 mBisDiscoveryCounterMap.remove(syncHandle); 1986 BluetoothDevice srcDevice = getDeviceForSyncHandle(syncHandle); 1987 mSyncHandleToDeviceMap.remove(syncHandle); 1988 int broadcastId = getBroadcastIdForSyncHandle(syncHandle); 1989 mSyncHandleToBroadcastIdMap.remove(syncHandle); 1990 if (srcDevice != null) { 1991 mPeriodicAdvertisementResultMap.get(srcDevice).remove(broadcastId); 1992 if (mPeriodicAdvertisementResultMap.get(srcDevice).isEmpty()) { 1993 mPeriodicAdvertisementResultMap.remove(srcDevice); 1994 } 1995 } 1996 } 1997 getBroadcastMetadataFromBaseData( BaseData baseData, BluetoothDevice device, int syncHandle, boolean encrypted)1998 private BluetoothLeBroadcastMetadata getBroadcastMetadataFromBaseData( 1999 BaseData baseData, BluetoothDevice device, int syncHandle, boolean encrypted) { 2000 BluetoothLeBroadcastMetadata.Builder metaData = new BluetoothLeBroadcastMetadata.Builder(); 2001 int index = 0; 2002 for (BaseData.BaseInformation baseLevel2 : baseData.getLevelTwo()) { 2003 BluetoothLeBroadcastSubgroup.Builder subGroup = 2004 new BluetoothLeBroadcastSubgroup.Builder(); 2005 for (int j = 0; j < baseLevel2.numSubGroups; j++) { 2006 BaseData.BaseInformation baseLevel3 = baseData.getLevelThree().get(index++); 2007 BluetoothLeBroadcastChannel.Builder channel = 2008 new BluetoothLeBroadcastChannel.Builder(); 2009 channel.setChannelIndex(baseLevel3.index); 2010 channel.setSelected(false); 2011 try { 2012 channel.setCodecMetadata( 2013 BluetoothLeAudioCodecConfigMetadata.fromRawBytes( 2014 baseLevel3.codecConfigInfo)); 2015 } catch (IllegalArgumentException e) { 2016 Log.w(TAG, "Invalid metadata, adding empty data. Error: " + e); 2017 channel.setCodecMetadata( 2018 BluetoothLeAudioCodecConfigMetadata.fromRawBytes(new byte[0])); 2019 } 2020 subGroup.addChannel(channel.build()); 2021 } 2022 byte[] arrayCodecId = baseLevel2.codecId; 2023 long codeId = 2024 ((long) (arrayCodecId[4] & 0xff)) << 32 2025 | (arrayCodecId[3] & 0xff) << 24 2026 | (arrayCodecId[2] & 0xff) << 16 2027 | (arrayCodecId[1] & 0xff) << 8 2028 | (arrayCodecId[0] & 0xff); 2029 subGroup.setCodecId(codeId); 2030 try { 2031 subGroup.setCodecSpecificConfig( 2032 BluetoothLeAudioCodecConfigMetadata.fromRawBytes( 2033 baseLevel2.codecConfigInfo)); 2034 } catch (IllegalArgumentException e) { 2035 Log.w(TAG, "Invalid config, adding empty one. Error: " + e); 2036 subGroup.setCodecSpecificConfig( 2037 BluetoothLeAudioCodecConfigMetadata.fromRawBytes(new byte[0])); 2038 } 2039 2040 try { 2041 subGroup.setContentMetadata( 2042 BluetoothLeAudioContentMetadata.fromRawBytes(baseLevel2.metaData)); 2043 } catch (IllegalArgumentException e) { 2044 Log.w(TAG, "Invalid metadata, adding empty one. Error: " + e); 2045 subGroup.setContentMetadata( 2046 BluetoothLeAudioContentMetadata.fromRawBytes(new byte[0])); 2047 } 2048 2049 metaData.addSubgroup(subGroup.build()); 2050 } 2051 metaData.setSourceDevice(device, device.getAddressType()); 2052 byte[] arrayPresentationDelay = baseData.getLevelOne().presentationDelay; 2053 int presentationDelay = 2054 (int) 2055 ((arrayPresentationDelay[2] & 0xff) << 16 2056 | (arrayPresentationDelay[1] & 0xff) << 8 2057 | (arrayPresentationDelay[0] & 0xff)); 2058 metaData.setPresentationDelayMicros(presentationDelay); 2059 PeriodicAdvertisementResult result = 2060 getPeriodicAdvertisementResult(device, getBroadcastIdForSyncHandle(syncHandle)); 2061 if (result != null) { 2062 int broadcastId = result.getBroadcastId(); 2063 log("broadcast ID: " + broadcastId); 2064 metaData.setBroadcastId(broadcastId); 2065 metaData.setSourceAdvertisingSid(result.getAdvSid()); 2066 2067 PublicBroadcastData pbData = result.getPublicBroadcastData(); 2068 if (pbData != null) { 2069 metaData.setPublicBroadcast(true); 2070 metaData.setAudioConfigQuality(pbData.getAudioConfigQuality()); 2071 try { 2072 metaData.setPublicBroadcastMetadata( 2073 BluetoothLeAudioContentMetadata.fromRawBytes(pbData.getMetadata())); 2074 } catch (IllegalArgumentException e) { 2075 Log.w(TAG, "Invalid public metadata, adding empty one. Error " + e); 2076 metaData.setPublicBroadcastMetadata(null); 2077 } 2078 } 2079 2080 String broadcastName = result.getBroadcastName(); 2081 if (broadcastName != null) { 2082 metaData.setBroadcastName(broadcastName); 2083 } 2084 } 2085 metaData.setEncrypted(encrypted); 2086 if (leaudioBroadcastMonitorSourceSyncStatus()) { 2087 // update the rssi value 2088 ScanResult scanRes = getCachedBroadcast(result.getBroadcastId()); 2089 if (scanRes != null) { 2090 metaData.setRssi(scanRes.getRssi()); 2091 } 2092 } 2093 return metaData.build(); 2094 } 2095 cancelActiveSync(Integer syncHandle)2096 private void cancelActiveSync(Integer syncHandle) { 2097 log("cancelActiveSync: syncHandle = " + syncHandle); 2098 if (syncHandle == null) { 2099 // clean up the pending sync request if syncHandle is null 2100 unsyncSource(BassConstants.INVALID_SYNC_HANDLE); 2101 } 2102 List<Integer> activeSyncedSrc = new ArrayList<>(getActiveSyncedSources()); 2103 2104 /* Stop sync if there is some running */ 2105 if (!activeSyncedSrc.isEmpty() 2106 && (syncHandle == null || activeSyncedSrc.contains(syncHandle))) { 2107 if (syncHandle != null) { 2108 // only one source needs to be unsynced 2109 unsyncSource(syncHandle); 2110 } else { 2111 // remove all the sources 2112 for (int handle : activeSyncedSrc) { 2113 unsyncSource(handle); 2114 } 2115 } 2116 } 2117 } 2118 unsyncSource(int syncHandle)2119 private boolean unsyncSource(int syncHandle) { 2120 log("unsyncSource: syncHandle: " + syncHandle); 2121 if (mPeriodicAdvCallbacksMap.containsKey(syncHandle)) { 2122 try { 2123 BluetoothMethodProxy.getInstance() 2124 .periodicAdvertisingManagerUnregisterSync( 2125 BassClientPeriodicAdvertisingManager 2126 .getPeriodicAdvertisingManager(), 2127 mPeriodicAdvCallbacksMap.get(syncHandle)); 2128 } catch (IllegalArgumentException ex) { 2129 Log.w(TAG, "unregisterSync:IllegalArgumentException"); 2130 return false; 2131 } 2132 } else { 2133 log("calling unregisterSync, not found syncHandle: " + syncHandle); 2134 } 2135 clearAllDataForSyncHandle(syncHandle); 2136 return true; 2137 } 2138 parseBaseData(int syncHandle, byte[] serviceData)2139 boolean parseBaseData(int syncHandle, byte[] serviceData) { 2140 log("parseBaseData" + Arrays.toString(serviceData)); 2141 BaseData base = BaseData.parseBaseData(serviceData); 2142 if (base != null) { 2143 updateBase(syncHandle, base); 2144 base.print(); 2145 return true; 2146 } else { 2147 Log.e(TAG, "Seems BASE is not in parsable format"); 2148 } 2149 return false; 2150 } 2151 parseScanRecord(int syncHandle, ScanRecord record)2152 boolean parseScanRecord(int syncHandle, ScanRecord record) { 2153 int broadcastId = getBroadcastIdForSyncHandle(syncHandle); 2154 log( 2155 "parseScanRecord: syncHandle=" 2156 + syncHandle 2157 + ", broadcastID=" 2158 + broadcastId 2159 + ", record=" 2160 + record); 2161 Map<ParcelUuid, byte[]> bmsAdvDataMap = record.getServiceData(); 2162 if (bmsAdvDataMap != null) { 2163 for (Map.Entry<ParcelUuid, byte[]> entry : bmsAdvDataMap.entrySet()) { 2164 log( 2165 "ParcelUUid = " 2166 + entry.getKey() 2167 + ", Value = " 2168 + Arrays.toString(entry.getValue())); 2169 } 2170 } 2171 byte[] advData = record.getServiceData(BassConstants.BASIC_AUDIO_UUID); 2172 if (advData != null) { 2173 return parseBaseData(syncHandle, advData); 2174 } else { 2175 Log.e(TAG, "No service data in Scan record"); 2176 } 2177 return false; 2178 } 2179 checkAndParseBroadcastName(ScanRecord record)2180 private String checkAndParseBroadcastName(ScanRecord record) { 2181 log("checkAndParseBroadcastName"); 2182 byte[] rawBytes = record.getBytes(); 2183 List<TypeValueEntry> entries = BluetoothUtils.parseLengthTypeValueBytes(rawBytes); 2184 if (rawBytes.length > 0 && rawBytes[0] > 0 && entries.isEmpty()) { 2185 Log.e(TAG, "Invalid LTV entries in Scan record"); 2186 return null; 2187 } 2188 2189 String broadcastName = null; 2190 for (TypeValueEntry entry : entries) { 2191 // Only use the first value of each type 2192 if (broadcastName == null && entry.getType() == BassConstants.BCAST_NAME_AD_TYPE) { 2193 byte[] bytes = entry.getValue(); 2194 int len = bytes.length; 2195 if (len < BassConstants.BCAST_NAME_LEN_MIN 2196 || len > BassConstants.BCAST_NAME_LEN_MAX) { 2197 Log.e(TAG, "Invalid broadcast name length in Scan record" + len); 2198 return null; 2199 } 2200 broadcastName = new String(bytes, StandardCharsets.UTF_8); 2201 } 2202 } 2203 return broadcastName; 2204 } 2205 addSelectSourceRequest(ScanResult scanRes, boolean hasPriority)2206 void addSelectSourceRequest(ScanResult scanRes, boolean hasPriority) { 2207 sEventLogger.logd( 2208 TAG, 2209 "Add Select Broadcast Source, result: " 2210 + scanRes 2211 + ", hasPriority: " 2212 + hasPriority); 2213 2214 ScanRecord scanRecord = scanRes.getScanRecord(); 2215 if (scanRecord == null) { 2216 log("addSelectSourceRequest: ScanRecord empty"); 2217 return; 2218 } 2219 2220 synchronized (mSourceSyncRequestsQueue) { 2221 mSourceSyncRequestsQueue.add(new SourceSyncRequest(scanRes, hasPriority)); 2222 } 2223 2224 handleSelectSourceRequest(); 2225 } 2226 handleSelectSourceRequest()2227 private void handleSelectSourceRequest() { 2228 PeriodicAdvertisingCallback paCb; 2229 synchronized (mPeriodicAdvCallbacksMap) { 2230 if (mSourceSyncRequestsQueue.isEmpty()) { 2231 return; 2232 } else if (mPeriodicAdvCallbacksMap.containsKey(BassConstants.INVALID_SYNC_HANDLE)) { 2233 log("handleSelectSourceRequest: already pending sync"); 2234 return; 2235 } else { 2236 paCb = new PACallback(); 2237 // put INVALID_SYNC_HANDLE and update in onSyncEstablished 2238 mPeriodicAdvCallbacksMap.put(BassConstants.INVALID_SYNC_HANDLE, paCb); 2239 } 2240 } 2241 ScanResult scanRes; 2242 synchronized (mSourceSyncRequestsQueue) { 2243 scanRes = mSourceSyncRequestsQueue.poll().getScanResult(); 2244 } 2245 ScanRecord scanRecord = scanRes.getScanRecord(); 2246 2247 sEventLogger.logd(TAG, "Select Broadcast Source, result: " + scanRes); 2248 2249 // updating mainly for Address type and PA Interval here 2250 // extract BroadcastId from ScanResult 2251 Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData(); 2252 int broadcastId = BassConstants.INVALID_BROADCAST_ID; 2253 PublicBroadcastData pbData = null; 2254 if (listOfUuids != null) { 2255 if (listOfUuids.containsKey(BassConstants.BAAS_UUID)) { 2256 byte[] bId = listOfUuids.get(BassConstants.BAAS_UUID); 2257 broadcastId = BassUtils.parseBroadcastId(bId); 2258 } 2259 if (listOfUuids.containsKey(BassConstants.PUBLIC_BROADCAST_UUID)) { 2260 byte[] pbAnnouncement = listOfUuids.get(BassConstants.PUBLIC_BROADCAST_UUID); 2261 pbData = PublicBroadcastData.parsePublicBroadcastData(pbAnnouncement); 2262 } 2263 } 2264 2265 if (broadcastId == BassConstants.INVALID_BROADCAST_ID || pbData == null) { 2266 Log.w(TAG, "Invalid broadcast ID or public broadcast data"); 2267 mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE); 2268 handleSelectSourceRequest(); 2269 return; 2270 } 2271 2272 // Check if broadcast name present in scan record and parse 2273 // null if no name present 2274 String broadcastName = checkAndParseBroadcastName(scanRecord); 2275 2276 // Avoid duplicated sync request if the same broadcast BIG is synced 2277 List<Integer> activeSyncedSrc = getActiveSyncedSources(); 2278 if (activeSyncedSrc.contains(getSyncHandleForBroadcastId(broadcastId))) { 2279 log("Skip duplicated sync request to broadcast id: " + broadcastId); 2280 mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE); 2281 handleSelectSourceRequest(); 2282 return; 2283 } 2284 2285 // Check if there are resources for sync 2286 if (activeSyncedSrc.size() >= MAX_ACTIVE_SYNCED_SOURCES_NUM) { 2287 log("handleSelectSourceRequest: reached max allowed active source"); 2288 int syncHandle = activeSyncedSrc.get(0); 2289 // removing the 1st synced source before proceeding to add new 2290 cancelActiveSync(syncHandle); 2291 } 2292 2293 try { 2294 BluetoothMethodProxy.getInstance() 2295 .periodicAdvertisingManagerRegisterSync( 2296 BassClientPeriodicAdvertisingManager.getPeriodicAdvertisingManager(), 2297 scanRes, 2298 0, 2299 BassConstants.PSYNC_TIMEOUT, 2300 paCb, 2301 null); 2302 } catch (IllegalArgumentException ex) { 2303 Log.w(TAG, "registerSync:IllegalArgumentException"); 2304 mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE); 2305 handleSelectSourceRequest(); 2306 return; 2307 } 2308 2309 updateSyncHandleForBroadcastId(BassConstants.INVALID_SYNC_HANDLE, broadcastId); 2310 updatePeriodicAdvertisementResultMap( 2311 scanRes.getDevice(), 2312 scanRes.getDevice().getAddressType(), 2313 BassConstants.INVALID_SYNC_HANDLE, 2314 BassConstants.INVALID_ADV_SID, 2315 scanRes.getPeriodicAdvertisingInterval(), 2316 broadcastId, 2317 pbData, 2318 broadcastName); 2319 } 2320 selectSource(BluetoothDevice sink, ScanResult result, boolean autoTrigger)2321 void selectSource(BluetoothDevice sink, ScanResult result, boolean autoTrigger) { 2322 if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 2323 throw new RuntimeException( 2324 "Should never be executed with" 2325 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 2326 } 2327 List<Integer> activeSyncedSrc = getActiveSyncedSources(sink); 2328 if (activeSyncedSrc != null && activeSyncedSrc.size() >= MAX_ACTIVE_SYNCED_SOURCES_NUM) { 2329 log("selectSource : reached max allowed active source"); 2330 int syncHandle = activeSyncedSrc.get(0); 2331 // removing the 1st synced source before proceeding to add new 2332 synchronized (mStateMachines) { 2333 BassClientStateMachine stateMachine = getOrCreateStateMachine(sink); 2334 if (stateMachine == null) { 2335 Log.e(TAG, "Can't get state machine for device: " + sink); 2336 return; 2337 } 2338 Message message = 2339 stateMachine.obtainMessage(BassClientStateMachine.REACHED_MAX_SOURCE_LIMIT); 2340 message.arg1 = syncHandle; 2341 stateMachine.sendMessage(message); 2342 } 2343 } 2344 2345 synchronized (mStateMachines) { 2346 sEventLogger.logd( 2347 TAG, "Select Broadcast Source: sink: " + sink + ", result: " + result); 2348 2349 BassClientStateMachine stateMachine = getOrCreateStateMachine(sink); 2350 if (stateMachine == null) { 2351 Log.e(TAG, "Can't get state machine for device: " + sink); 2352 return; 2353 } 2354 Message message = 2355 stateMachine.obtainMessage(BassClientStateMachine.SELECT_BCAST_SOURCE); 2356 message.obj = result; 2357 message.arg1 = autoTrigger ? BassConstants.AUTO : BassConstants.USER; 2358 stateMachine.sendMessage(message); 2359 } 2360 } 2361 2362 /** 2363 * Add a Broadcast Source to the Broadcast Sink 2364 * 2365 * @param sink Broadcast Sink to which the Broadcast Source should be added 2366 * @param sourceMetadata Broadcast Source metadata to be added to the Broadcast Sink 2367 * @param isGroupOp set to true If Application wants to perform this operation for all 2368 * coordinated set members, False otherwise 2369 */ addSource( BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata, boolean isGroupOp)2370 public void addSource( 2371 BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata, boolean isGroupOp) { 2372 log( 2373 "addSource: " 2374 + ("device: " + sink) 2375 + (", sourceMetadata: " + sourceMetadata) 2376 + (", isGroupOp: " + isGroupOp)); 2377 2378 List<BluetoothDevice> devices = getTargetDeviceList(sink, isGroupOp); 2379 // Don't coordinate it as a group if there's no group or there is one device only 2380 if (devices.size() < 2) { 2381 isGroupOp = false; 2382 } 2383 2384 if (sourceMetadata == null) { 2385 log("addSource: Error bad parameter: sourceMetadata cannot be null"); 2386 return; 2387 } 2388 2389 if (leaudioBroadcastAssistantPeripheralEntrustment()) { 2390 if (isLocalBroadcast(sourceMetadata)) { 2391 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 2392 if (leAudioService == null 2393 || !leAudioService.isPlaying(sourceMetadata.getBroadcastId())) { 2394 Log.w(TAG, "addSource: Local source can't be add"); 2395 2396 mCallbacks.notifySourceAddFailed( 2397 sink, 2398 sourceMetadata, 2399 BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES); 2400 2401 return; 2402 } 2403 } 2404 } else { 2405 if (!isAllowedToAddSource()) { 2406 Log.d(TAG, "Add source to pending list"); 2407 mPendingAddSources.push(new AddSourceData(sink, sourceMetadata, isGroupOp)); 2408 2409 return; 2410 } 2411 } 2412 2413 if (leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 2414 List<Integer> activeSyncedSrc = getActiveSyncedSources(); 2415 BluetoothDevice sourceDevice = sourceMetadata.getSourceDevice(); 2416 if (!isLocalBroadcast(sourceMetadata) 2417 && (!activeSyncedSrc.contains( 2418 getSyncHandleForBroadcastId(sourceMetadata.getBroadcastId())))) { 2419 log("Adding inactive source: " + sourceDevice); 2420 int broadcastId = sourceMetadata.getBroadcastId(); 2421 if (broadcastId != BassConstants.INVALID_BROADCAST_ID 2422 && getCachedBroadcast(broadcastId) != null) { 2423 // If the source has been synced before, try to re-sync 2424 // with the source by previously cached scan result 2425 addSelectSourceRequest(getCachedBroadcast(broadcastId), true); 2426 synchronized (mPendingSourcesToAdd) { 2427 mPendingSourcesToAdd.add( 2428 new AddSourceData(sink, sourceMetadata, isGroupOp)); 2429 } 2430 } else { 2431 log("AddSource: broadcast not cached or invalid, broadcastId: " + broadcastId); 2432 mCallbacks.notifySourceAddFailed( 2433 sink, sourceMetadata, BluetoothStatusCodes.ERROR_UNKNOWN); 2434 } 2435 return; 2436 } 2437 } 2438 2439 byte[] code = sourceMetadata.getBroadcastCode(); 2440 for (BluetoothDevice device : devices) { 2441 BassClientStateMachine stateMachine = getOrCreateStateMachine(device); 2442 if (stateMachine == null) { 2443 log("addSource: Error bad parameter: no state machine for " + device); 2444 mCallbacks.notifySourceAddFailed( 2445 device, sourceMetadata, BluetoothStatusCodes.ERROR_BAD_PARAMETERS); 2446 continue; 2447 } 2448 if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { 2449 log("addSource: device is not connected"); 2450 mCallbacks.notifySourceAddFailed( 2451 device, sourceMetadata, BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR); 2452 continue; 2453 } 2454 if (stateMachine.hasPendingSourceOperation()) { 2455 Log.w( 2456 TAG, 2457 "addSource: source operation already pending, device: " 2458 + device 2459 + ", broadcastId: " 2460 + sourceMetadata.getBroadcastId()); 2461 mCallbacks.notifySourceAddFailed( 2462 device, sourceMetadata, BluetoothStatusCodes.ERROR_ALREADY_IN_TARGET_STATE); 2463 continue; 2464 } 2465 if (!hasRoomForBroadcastSourceAddition(device)) { 2466 log("addSource: device has no room"); 2467 Integer sourceId = getSourceIdToRemove(device); 2468 if (sourceId != BassConstants.INVALID_SOURCE_ID) { 2469 sEventLogger.logd( 2470 TAG, 2471 "Switch Broadcast Source: " 2472 + ("device: " + device) 2473 + (", old SourceId: " + sourceId) 2474 + (", new broadcastId: " + sourceMetadata.getBroadcastId()) 2475 + (", new broadcastName: " 2476 + sourceMetadata.getBroadcastName())); 2477 2478 // new source will be added once the existing source got removed 2479 if (isGroupOp) { 2480 // mark group op for both remove and add source 2481 // so setSourceGroupManaged will be updated accordingly in callbacks 2482 enqueueSourceGroupOp( 2483 device, BassClientStateMachine.REMOVE_BCAST_SOURCE, sourceId); 2484 enqueueSourceGroupOp( 2485 device, BassClientStateMachine.ADD_BCAST_SOURCE, sourceMetadata); 2486 } 2487 2488 /* Store metadata for sink device */ 2489 mBroadcastMetadataMap.put(device, sourceMetadata); 2490 2491 Message message = 2492 stateMachine.obtainMessage(BassClientStateMachine.SWITCH_BCAST_SOURCE); 2493 message.obj = sourceMetadata; 2494 message.arg1 = sourceId; 2495 stateMachine.sendMessage(message); 2496 } else { 2497 mCallbacks.notifySourceAddFailed( 2498 device, 2499 sourceMetadata, 2500 BluetoothStatusCodes.ERROR_REMOTE_NOT_ENOUGH_RESOURCES); 2501 } 2502 continue; 2503 } 2504 if (!isValidBroadcastSourceAddition(device, sourceMetadata)) { 2505 log("addSource: not a valid broadcast source addition"); 2506 mCallbacks.notifySourceAddFailed( 2507 device, 2508 sourceMetadata, 2509 BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_DUPLICATE_ADDITION); 2510 continue; 2511 } 2512 if ((code != null) && (code.length != 0)) { 2513 if ((code.length > 16) || (code.length < 4)) { 2514 log( 2515 "Invalid broadcast code length: " 2516 + code.length 2517 + ", should be between 4 and 16 octets"); 2518 mCallbacks.notifySourceAddFailed( 2519 device, sourceMetadata, BluetoothStatusCodes.ERROR_BAD_PARAMETERS); 2520 continue; 2521 } 2522 } 2523 2524 /* Store metadata for sink device */ 2525 mBroadcastMetadataMap.put(device, sourceMetadata); 2526 2527 if (isGroupOp) { 2528 enqueueSourceGroupOp( 2529 device, BassClientStateMachine.ADD_BCAST_SOURCE, sourceMetadata); 2530 } 2531 2532 sEventLogger.logd( 2533 TAG, 2534 "Add Broadcast Source: " 2535 + ("device: " + device) 2536 + (", broadcastId: " + sourceMetadata.getBroadcastId()) 2537 + (", broadcastName: " + sourceMetadata.getBroadcastName()) 2538 + (", isGroupOp: " + isGroupOp)); 2539 2540 Message message = stateMachine.obtainMessage(BassClientStateMachine.ADD_BCAST_SOURCE); 2541 message.obj = sourceMetadata; 2542 stateMachine.sendMessage(message); 2543 if (code != null && code.length != 0) { 2544 sEventLogger.logd( 2545 TAG, 2546 "Set Broadcast Code (Add Source context): " 2547 + ("device: " + device) 2548 + (", broadcastId: " + sourceMetadata.getBroadcastId()) 2549 + (", broadcastName: " + sourceMetadata.getBroadcastName())); 2550 2551 message = stateMachine.obtainMessage(BassClientStateMachine.SET_BCAST_CODE); 2552 message.obj = sourceMetadata; 2553 message.arg1 = BassClientStateMachine.ARGTYPE_METADATA; 2554 stateMachine.sendMessage(message); 2555 } 2556 } 2557 } 2558 2559 /** 2560 * Modify the Broadcast Source information on a Broadcast Sink 2561 * 2562 * @param sink representing the Broadcast Sink to which the Broadcast Source should be updated 2563 * @param sourceId source ID as delivered in onSourceAdded 2564 * @param updatedMetadata updated Broadcast Source metadata to be updated on the Broadcast Sink 2565 */ modifySource( BluetoothDevice sink, int sourceId, BluetoothLeBroadcastMetadata updatedMetadata)2566 public void modifySource( 2567 BluetoothDevice sink, int sourceId, BluetoothLeBroadcastMetadata updatedMetadata) { 2568 log( 2569 "modifySource: " 2570 + ("device: " + sink) 2571 + ("sourceId: " + sourceId) 2572 + (", updatedMetadata: " + updatedMetadata)); 2573 2574 Map<BluetoothDevice, Integer> devices = getGroupManagedDeviceSources(sink, sourceId).second; 2575 if (updatedMetadata == null) { 2576 log("modifySource: Error bad parameters: updatedMetadata cannot be null"); 2577 for (BluetoothDevice device : devices.keySet()) { 2578 mCallbacks.notifySourceModifyFailed( 2579 device, sourceId, BluetoothStatusCodes.ERROR_BAD_PARAMETERS); 2580 } 2581 return; 2582 } 2583 2584 /* Update metadata for sink device */ 2585 mBroadcastMetadataMap.put(sink, updatedMetadata); 2586 2587 byte[] code = updatedMetadata.getBroadcastCode(); 2588 for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) { 2589 BluetoothDevice device = deviceSourceIdPair.getKey(); 2590 Integer deviceSourceId = deviceSourceIdPair.getValue(); 2591 BassClientStateMachine stateMachine = getOrCreateStateMachine(device); 2592 2593 int statusCode = 2594 areValidParametersToModifySource( 2595 updatedMetadata, stateMachine, deviceSourceId, device); 2596 if (statusCode != BluetoothStatusCodes.SUCCESS) { 2597 mCallbacks.notifySourceModifyFailed(device, sourceId, statusCode); 2598 continue; 2599 } 2600 2601 sEventLogger.logd( 2602 TAG, 2603 "Modify Broadcast Source: " 2604 + ("device: " + device) 2605 + ("sourceId: " + sourceId) 2606 + (", updatedBroadcastId: " + updatedMetadata.getBroadcastId()) 2607 + (", updatedBroadcastName: " + updatedMetadata.getBroadcastName())); 2608 2609 Message message = 2610 stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE); 2611 message.arg1 = deviceSourceId; 2612 message.arg2 = BassConstants.INVALID_PA_SYNC_VALUE; 2613 message.obj = updatedMetadata; 2614 stateMachine.sendMessage(message); 2615 if (code != null && code.length != 0) { 2616 sEventLogger.logd( 2617 TAG, 2618 "Set Broadcast Code (Modify Source context): " 2619 + ("device: " + device) 2620 + ("sourceId: " + sourceId) 2621 + (", updatedBroadcastId: " + updatedMetadata.getBroadcastId()) 2622 + (", updatedBroadcastName: " 2623 + updatedMetadata.getBroadcastName())); 2624 message = stateMachine.obtainMessage(BassClientStateMachine.SET_BCAST_CODE); 2625 message.obj = updatedMetadata; 2626 message.arg1 = BassClientStateMachine.ARGTYPE_METADATA; 2627 stateMachine.sendMessage(message); 2628 } 2629 } 2630 } 2631 2632 /** 2633 * Removes the Broadcast Source from a Broadcast Sink 2634 * 2635 * @param sink representing the Broadcast Sink from which a Broadcast Source should be removed 2636 * @param sourceId source ID as delivered in onSourceAdded 2637 */ removeSource(BluetoothDevice sink, int sourceId)2638 public void removeSource(BluetoothDevice sink, int sourceId) { 2639 log("removeSource: device: " + sink + ", sourceId: " + sourceId); 2640 2641 Map<BluetoothDevice, Integer> devices = getGroupManagedDeviceSources(sink, sourceId).second; 2642 for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) { 2643 BluetoothDevice device = deviceSourceIdPair.getKey(); 2644 Integer deviceSourceId = deviceSourceIdPair.getValue(); 2645 BassClientStateMachine stateMachine = getOrCreateStateMachine(device); 2646 2647 /* Removes metadata for sink device if not paused */ 2648 if (!mPausedBroadcastSinks.contains(device)) { 2649 mBroadcastMetadataMap.remove(device); 2650 } 2651 2652 if (stateMachine == null) { 2653 log("removeSource: Error bad parameters: device = " + device); 2654 mCallbacks.notifySourceRemoveFailed( 2655 device, sourceId, BluetoothStatusCodes.ERROR_BAD_PARAMETERS); 2656 continue; 2657 } 2658 if (deviceSourceId == BassConstants.INVALID_SOURCE_ID) { 2659 log("removeSource: no such sourceId for device: " + device); 2660 mCallbacks.notifySourceRemoveFailed( 2661 device, 2662 sourceId, 2663 BluetoothStatusCodes.ERROR_LE_BROADCAST_ASSISTANT_INVALID_SOURCE_ID); 2664 continue; 2665 } 2666 if (getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) { 2667 log("removeSource: device is not connected"); 2668 mCallbacks.notifySourceRemoveFailed( 2669 device, sourceId, BluetoothStatusCodes.ERROR_REMOTE_LINK_ERROR); 2670 continue; 2671 } 2672 2673 BluetoothLeBroadcastMetadata metaData = 2674 stateMachine.getCurrentBroadcastMetadata(sourceId); 2675 if (metaData != null && stateMachine.isSyncedToTheSource(sourceId)) { 2676 sEventLogger.logd( 2677 TAG, 2678 "Remove Broadcast Source(Force lost PA sync): " 2679 + ("device: " + device) 2680 + (", sourceId: " + sourceId) 2681 + (", broadcastId: " + metaData.getBroadcastId()) 2682 + (", broadcastName: " + metaData.getBroadcastName())); 2683 2684 log("Force source to lost PA sync"); 2685 Message message = 2686 stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE); 2687 message.arg1 = sourceId; 2688 message.arg2 = BassConstants.PA_SYNC_DO_NOT_SYNC; 2689 /* Pending remove set. Remove source once not synchronized to PA */ 2690 message.obj = metaData; 2691 stateMachine.sendMessage(message); 2692 2693 continue; 2694 } 2695 2696 sEventLogger.logd( 2697 TAG, "Remove Broadcast Source: device: " + device + ", sourceId: " + sourceId); 2698 2699 Message message = 2700 stateMachine.obtainMessage(BassClientStateMachine.REMOVE_BCAST_SOURCE); 2701 message.arg1 = deviceSourceId; 2702 stateMachine.sendMessage(message); 2703 } 2704 2705 for (Map.Entry<BluetoothDevice, Integer> deviceSourceIdPair : devices.entrySet()) { 2706 BluetoothDevice device = deviceSourceIdPair.getKey(); 2707 Integer deviceSourceId = deviceSourceIdPair.getValue(); 2708 enqueueSourceGroupOp( 2709 device, 2710 BassClientStateMachine.REMOVE_BCAST_SOURCE, 2711 Integer.valueOf(deviceSourceId)); 2712 } 2713 } 2714 2715 /** 2716 * Get information about all Broadcast Sources 2717 * 2718 * @param sink Broadcast Sink from which to get all Broadcast Sources 2719 * @return the list of Broadcast Receive State {@link BluetoothLeBroadcastReceiveState} 2720 */ getAllSources(BluetoothDevice sink)2721 public List<BluetoothLeBroadcastReceiveState> getAllSources(BluetoothDevice sink) { 2722 log("getAllSources for " + sink); 2723 synchronized (mStateMachines) { 2724 BassClientStateMachine stateMachine = getOrCreateStateMachine(sink); 2725 if (stateMachine == null) { 2726 log("stateMachine is null"); 2727 return Collections.emptyList(); 2728 } 2729 List<BluetoothLeBroadcastReceiveState> recvStates = 2730 new ArrayList<BluetoothLeBroadcastReceiveState>(); 2731 for (BluetoothLeBroadcastReceiveState rs : stateMachine.getAllSources()) { 2732 String emptyBluetoothDevice = "00:00:00:00:00:00"; 2733 if (!rs.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) { 2734 recvStates.add(rs); 2735 } 2736 } 2737 return recvStates; 2738 } 2739 } 2740 2741 /** 2742 * Get maximum number of sources that can be added to this Broadcast Sink 2743 * 2744 * @param sink Broadcast Sink device 2745 * @return maximum number of sources that can be added to this Broadcast Sink 2746 */ getMaximumSourceCapacity(BluetoothDevice sink)2747 int getMaximumSourceCapacity(BluetoothDevice sink) { 2748 log("getMaximumSourceCapacity: device = " + sink); 2749 BassClientStateMachine stateMachine = getOrCreateStateMachine(sink); 2750 if (stateMachine == null) { 2751 log("stateMachine is null"); 2752 return 0; 2753 } 2754 return stateMachine.getMaximumSourceCapacity(); 2755 } 2756 isLocalBroadcast(int sourceAdvertisingSid)2757 private boolean isLocalBroadcast(int sourceAdvertisingSid) { 2758 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 2759 if (leAudioService == null) { 2760 return false; 2761 } 2762 2763 boolean wasFound = 2764 leAudioService.getAllBroadcastMetadata().stream() 2765 .anyMatch( 2766 meta -> { 2767 return meta.getSourceAdvertisingSid() == sourceAdvertisingSid; 2768 }); 2769 log("isLocalBroadcast=" + wasFound); 2770 return wasFound; 2771 } 2772 isLocalBroadcast(BluetoothLeBroadcastMetadata metaData)2773 boolean isLocalBroadcast(BluetoothLeBroadcastMetadata metaData) { 2774 if (metaData == null) { 2775 return false; 2776 } 2777 2778 return isLocalBroadcast(metaData.getSourceAdvertisingSid()); 2779 } 2780 isLocalBroadcast(BluetoothLeBroadcastReceiveState receiveState)2781 boolean isLocalBroadcast(BluetoothLeBroadcastReceiveState receiveState) { 2782 if (receiveState == null) { 2783 return false; 2784 } 2785 2786 return isLocalBroadcast(receiveState.getSourceAdvertisingSid()); 2787 } 2788 log(String msg)2789 static void log(String msg) { 2790 Log.d(TAG, msg); 2791 } 2792 2793 private List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>> getReceiveStateDevicePairs(int broadcastId)2794 getReceiveStateDevicePairs(int broadcastId) { 2795 List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>> list = new ArrayList<>(); 2796 2797 for (BluetoothDevice device : getConnectedDevices()) { 2798 for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) { 2799 /* Check if local/last broadcast is the synced one. Invalid broadcast ID means 2800 * that all receivers should be considered. 2801 */ 2802 if ((broadcastId != BassConstants.INVALID_BROADCAST_ID) 2803 && (receiveState.getBroadcastId() != broadcastId)) { 2804 continue; 2805 } 2806 2807 list.add( 2808 new Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>( 2809 receiveState, device)); 2810 } 2811 } 2812 2813 return list; 2814 } 2815 cancelPendingSourceOperations(int broadcastId)2816 private void cancelPendingSourceOperations(int broadcastId) { 2817 for (BluetoothDevice device : getConnectedDevices()) { 2818 synchronized (mStateMachines) { 2819 BassClientStateMachine sm = getOrCreateStateMachine(device); 2820 if (sm != null && sm.hasPendingSourceOperation(broadcastId)) { 2821 Message message = 2822 sm.obtainMessage( 2823 BassClientStateMachine.CANCEL_PENDING_SOURCE_OPERATION); 2824 message.arg1 = broadcastId; 2825 sm.sendMessage(message); 2826 } 2827 } 2828 } 2829 } 2830 stopSourceReceivers(int broadcastId)2831 private void stopSourceReceivers(int broadcastId) { 2832 List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>> sourcesToRemove = 2833 getReceiveStateDevicePairs(broadcastId); 2834 2835 for (Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice> pair : sourcesToRemove) { 2836 removeSource(pair.second, pair.first.getSourceId()); 2837 } 2838 2839 /* There may be some pending add/modify source operations */ 2840 cancelPendingSourceOperations(broadcastId); 2841 } 2842 stopSourceReceivers(int broadcastId, boolean store)2843 private void stopSourceReceivers(int broadcastId, boolean store) { 2844 Log.d(TAG, "stopSourceReceivers(), broadcastId: " + broadcastId + ", store: " + store); 2845 2846 if (store && !mPausedBroadcastSinks.isEmpty()) { 2847 Log.w(TAG, "stopSourceReceivers(), paused broadcast sinks are replaced"); 2848 sEventLogger.logd(TAG, "Clear broadcast sinks paused cache"); 2849 mPausedBroadcastSinks.clear(); 2850 } 2851 2852 Map<BluetoothDevice, Integer> sourcesToRemove = new HashMap<>(); 2853 2854 for (BluetoothDevice device : getConnectedDevices()) { 2855 for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) { 2856 /* Check if local/last broadcast is the synced one. Invalid broadcast ID means 2857 * that all receivers should be considered. 2858 */ 2859 if ((broadcastId != BassConstants.INVALID_BROADCAST_ID) 2860 && (receiveState.getBroadcastId() != broadcastId)) { 2861 continue; 2862 } 2863 2864 if (store && !mPausedBroadcastSinks.contains(device)) { 2865 sEventLogger.logd(TAG, "Add broadcast sink to paused cache: " + device); 2866 mPausedBroadcastSinks.add(device); 2867 } 2868 2869 sourcesToRemove.put(device, receiveState.getSourceId()); 2870 } 2871 } 2872 2873 for (Map.Entry<BluetoothDevice, Integer> entry : sourcesToRemove.entrySet()) { 2874 removeSource(entry.getKey(), entry.getValue()); 2875 } 2876 } 2877 isAllowedToAddSource()2878 private boolean isAllowedToAddSource() { 2879 if (leaudioBroadcastAudioHandoverPolicies()) { 2880 /* Check if should wait for status update */ 2881 if (mUnicastSourceStreamStatus.isEmpty()) { 2882 /* Assistant was not active, inform about activation */ 2883 if (!mIsAssistantActive) { 2884 mIsAssistantActive = true; 2885 2886 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 2887 if (leAudioService != null) { 2888 leAudioService.activeBroadcastAssistantNotification(true); 2889 } 2890 } 2891 2892 return false; 2893 } 2894 2895 return mUnicastSourceStreamStatus.get() == STATUS_LOCAL_STREAM_SUSPENDED; 2896 } 2897 2898 /* Don't block if this is not a handover case */ 2899 return true; 2900 } 2901 2902 /** Return true if there is any non primary device receiving broadcast */ isAudioSharingModeOn(Integer broadcastId)2903 private boolean isAudioSharingModeOn(Integer broadcastId) { 2904 if (mLocalBroadcastReceivers == null) { 2905 Log.w(TAG, "isAudioSharingModeOn: Local Broadcaster Receivers is not initialized"); 2906 return false; 2907 } 2908 2909 HashSet<BluetoothDevice> devices = mLocalBroadcastReceivers.get(broadcastId); 2910 if (devices == null) { 2911 Log.w(TAG, "isAudioSharingModeOn: No receivers receiving broadcast: " + broadcastId); 2912 return false; 2913 } 2914 2915 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 2916 if (leAudioService == null) { 2917 Log.d(TAG, "isAudioSharingModeOn: No available LeAudioService"); 2918 return false; 2919 } 2920 2921 return devices.stream().anyMatch(d -> !leAudioService.isPrimaryDevice(d)); 2922 } 2923 2924 /** Handle disconnection of potential broadcast sinks */ handleDeviceDisconnection(BluetoothDevice sink, boolean isIntentional)2925 public void handleDeviceDisconnection(BluetoothDevice sink, boolean isIntentional) { 2926 LeAudioService leAudioService = mServiceFactory.getLeAudioService(); 2927 if (leAudioService == null) { 2928 Log.d(TAG, "BluetoothLeBroadcastReceiveState: No available LeAudioService"); 2929 return; 2930 } 2931 2932 for (Map.Entry<Integer, HashSet<BluetoothDevice>> entry : 2933 mLocalBroadcastReceivers.entrySet()) { 2934 Integer broadcastId = entry.getKey(); 2935 HashSet<BluetoothDevice> devices = entry.getValue(); 2936 2937 /* If somehow there is a non playing broadcast, let's remove it */ 2938 if (!leAudioService.isPlaying(broadcastId)) { 2939 Log.w(TAG, "Non playing broadcast remove from receivers list"); 2940 mLocalBroadcastReceivers.remove(broadcastId); 2941 continue; 2942 } 2943 2944 if (isIntentional) { 2945 /* Check if disconnecting device participated in this broadcast reception */ 2946 if (!devices.remove(sink)) { 2947 continue; 2948 } 2949 2950 mBroadcastMetadataMap.remove(sink); 2951 2952 /* Check if there is any other primary device receiving this broadcast */ 2953 if (devices.stream() 2954 .anyMatch( 2955 d -> 2956 ((getConnectionState(d) == BluetoothProfile.STATE_CONNECTED) 2957 && leAudioService.isPrimaryDevice(d)))) { 2958 continue; 2959 } 2960 2961 Log.d( 2962 TAG, 2963 "handleIntendedDeviceDisconnection: No more potential broadcast " 2964 + "(broadcast ID: " 2965 + broadcastId 2966 + ") receivers - stopping broadcast"); 2967 mLocalBroadcastReceivers.remove(broadcastId); 2968 leAudioService.stopBroadcast(broadcastId); 2969 } else { 2970 /* Unintentional disconnection of primary device in private broadcast mode */ 2971 if (!isAudioSharingModeOn(broadcastId) 2972 && !devices.stream() 2973 .anyMatch( 2974 d -> 2975 !d.equals(sink) 2976 && (getConnectionState(d) 2977 == BluetoothProfile 2978 .STATE_CONNECTED))) { 2979 mLocalBroadcastReceivers.remove(broadcastId); 2980 leAudioService.stopBroadcast(broadcastId); 2981 continue; 2982 } 2983 2984 /* Unintentional disconnection of primary/secondary in broadcast sharing mode */ 2985 if (devices.stream() 2986 .anyMatch( 2987 d -> 2988 !d.equals(sink) 2989 && (getConnectionState(d) 2990 == BluetoothProfile.STATE_CONNECTED))) { 2991 continue; 2992 } else { 2993 Log.d( 2994 TAG, 2995 "handleUnintendedDeviceDisconnection: No more potential broadcast " 2996 + "(broadcast ID: " 2997 + broadcastId 2998 + ") receivers - stopping broadcast"); 2999 mDialingOutTimeoutEvent = new DialingOutTimeoutEvent(broadcastId); 3000 mHandler.postDelayed(mDialingOutTimeoutEvent, DIALING_OUT_TIMEOUT_MS); 3001 } 3002 } 3003 } 3004 } 3005 3006 /** Cache suspending sources */ cacheSuspendingSources(int broadcastId)3007 public void cacheSuspendingSources(int broadcastId) { 3008 sEventLogger.logd(TAG, "Cache suspending sources: " + broadcastId); 3009 List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>> sourcesToCache = 3010 getReceiveStateDevicePairs(broadcastId); 3011 3012 if (!mPausedBroadcastSinks.isEmpty()) { 3013 Log.w(TAG, "cacheSuspendingSources(), paused broadcast sinks are replaced"); 3014 sEventLogger.logd(TAG, "Clear broadcast sinks paused cache"); 3015 mPausedBroadcastSinks.clear(); 3016 } 3017 3018 for (Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice> pair : sourcesToCache) { 3019 mPausedBroadcastSinks.add(pair.second); 3020 } 3021 } 3022 3023 /** Request receivers to suspend broadcast sources synchronization */ suspendReceiversSourceSynchronization(int broadcastId)3024 public void suspendReceiversSourceSynchronization(int broadcastId) { 3025 sEventLogger.logd(TAG, "Suspend receivers source synchronization: " + broadcastId); 3026 stopSourceReceivers(broadcastId, true); 3027 } 3028 3029 /** Request all receivers to suspend broadcast sources synchronization */ suspendAllReceiversSourceSynchronization()3030 public void suspendAllReceiversSourceSynchronization() { 3031 sEventLogger.logd(TAG, "Suspend all receivers source synchronization"); 3032 stopSourceReceivers(BassConstants.INVALID_BROADCAST_ID, true); 3033 } 3034 3035 /** Request receivers to stop broadcast sources synchronization and remove them */ stopReceiversSourceSynchronization(int broadcastId)3036 public void stopReceiversSourceSynchronization(int broadcastId) { 3037 sEventLogger.logd(TAG, "Stop receivers source synchronization: " + broadcastId); 3038 if (leaudioBroadcastAssistantPeripheralEntrustment()) { 3039 stopSourceReceivers(broadcastId); 3040 } else { 3041 stopSourceReceivers(broadcastId, false); 3042 } 3043 } 3044 3045 /** Request receivers to resume broadcast source synchronization */ resumeReceiversSourceSynchronization()3046 public void resumeReceiversSourceSynchronization() { 3047 sEventLogger.logd(TAG, "Resume receivers source synchronization"); 3048 3049 while (!mPausedBroadcastSinks.isEmpty()) { 3050 BluetoothDevice sink = mPausedBroadcastSinks.remove(); 3051 sEventLogger.logd(TAG, "Remove broadcast sink from paused cache: " + sink); 3052 BluetoothLeBroadcastMetadata metadata = mBroadcastMetadataMap.get(sink); 3053 3054 if (leaudioBroadcastAssistantPeripheralEntrustment()) { 3055 if (metadata == null) { 3056 Log.w( 3057 TAG, 3058 "resumeReceiversSourceSynchronization: failed to get metadata to resume" 3059 + " sink: " 3060 + sink); 3061 continue; 3062 } 3063 3064 // For each device, find the source ID having this broadcast ID 3065 BassClientStateMachine stateMachine = getOrCreateStateMachine(sink); 3066 List<BluetoothLeBroadcastReceiveState> sources = stateMachine.getAllSources(); 3067 Optional<BluetoothLeBroadcastReceiveState> receiveState = 3068 sources.stream() 3069 .filter(e -> e.getBroadcastId() == metadata.getBroadcastId()) 3070 .findAny(); 3071 3072 if (receiveState.isPresent()) { 3073 /* Update metadata for sink device */ 3074 mBroadcastMetadataMap.put(sink, metadata); 3075 3076 int sourceId = receiveState.get().getSourceId(); 3077 int statusCode = 3078 areValidParametersToModifySource( 3079 metadata, stateMachine, sourceId, sink); 3080 3081 if (statusCode != BluetoothStatusCodes.SUCCESS) { 3082 mCallbacks.notifySourceModifyFailed(sink, sourceId, statusCode); 3083 continue; 3084 } 3085 3086 sEventLogger.logd( 3087 TAG, 3088 "Modify Broadcast Source (resume): " 3089 + ("device: " + sink) 3090 + ("sourceId: " + sourceId) 3091 + (", updatedBroadcastId: " + metadata.getBroadcastId()) 3092 + (", updatedBroadcastName: " + metadata.getBroadcastName())); 3093 Message message = 3094 stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE); 3095 message.arg1 = sourceId; 3096 message.arg2 = 3097 DeviceConfig.getBoolean( 3098 DeviceConfig.NAMESPACE_BLUETOOTH, 3099 "persist.vendor.service.bt.defNoPAS", 3100 true) 3101 ? BassConstants.PA_SYNC_PAST_AVAILABLE 3102 : BassConstants.PA_SYNC_PAST_NOT_AVAILABLE; 3103 message.obj = metadata; 3104 stateMachine.sendMessage(message); 3105 } else { 3106 addSource(sink, metadata, false); 3107 } 3108 } else { 3109 if (metadata != null) { 3110 addSource(sink, metadata, false); 3111 } else { 3112 Log.w( 3113 TAG, 3114 "resumeReceiversSourceSynchronization: failed to get metadata to resume" 3115 + " sink: " 3116 + sink); 3117 } 3118 } 3119 } 3120 } 3121 3122 /** Handle Unicast source stream status change */ handleUnicastSourceStreamStatusChange(int status)3123 public void handleUnicastSourceStreamStatusChange(int status) { 3124 mUnicastSourceStreamStatus = Optional.of(status); 3125 3126 if (status == STATUS_LOCAL_STREAM_REQUESTED) { 3127 if (areReceiversReceivingOnlyExternalBroadcast(getConnectedDevices())) { 3128 if (leaudioBroadcastAssistantPeripheralEntrustment()) { 3129 cacheSuspendingSources(BassConstants.INVALID_BROADCAST_ID); 3130 } else { 3131 suspendAllReceiversSourceSynchronization(); 3132 } 3133 } 3134 } else if (status == STATUS_LOCAL_STREAM_SUSPENDED) { 3135 /* Resume paused receivers if there are some */ 3136 if (!mPausedBroadcastSinks.isEmpty()) { 3137 resumeReceiversSourceSynchronization(); 3138 } 3139 3140 if (!leaudioBroadcastAssistantPeripheralEntrustment()) { 3141 /* Add pending sources if there are some */ 3142 while (!mPendingAddSources.isEmpty()) { 3143 AddSourceData addSourceData = mPendingAddSources.pop(); 3144 3145 addSource( 3146 addSourceData.mSink, 3147 addSourceData.mSourceMetadata, 3148 addSourceData.mIsGroupOp); 3149 } 3150 } 3151 } else if (status == STATUS_LOCAL_STREAM_STREAMING) { 3152 Log.d(TAG, "Ignore STREAMING source status"); 3153 } else if (status == STATUS_LOCAL_STREAM_REQUESTED_NO_CONTEXT_VALIDATE) { 3154 suspendAllReceiversSourceSynchronization(); 3155 } 3156 } 3157 3158 /** Check if any sink receivers are receiving broadcast stream */ isAnyReceiverReceivingBroadcast(List<BluetoothDevice> devices)3159 public boolean isAnyReceiverReceivingBroadcast(List<BluetoothDevice> devices) { 3160 for (BluetoothDevice device : devices) { 3161 for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) { 3162 for (int i = 0; i < receiveState.getNumSubgroups(); i++) { 3163 Long syncState = receiveState.getBisSyncState().get(i); 3164 /* Synced to BIS */ 3165 if (syncState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS 3166 && syncState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG) { 3167 return true; 3168 } 3169 } 3170 } 3171 } 3172 3173 return false; 3174 } 3175 3176 /** Check if any sink receivers are receiving broadcast stream */ areReceiversReceivingOnlyExternalBroadcast(List<BluetoothDevice> devices)3177 public boolean areReceiversReceivingOnlyExternalBroadcast(List<BluetoothDevice> devices) { 3178 boolean isReceivingExternalBroadcast = false; 3179 3180 for (BluetoothDevice device : devices) { 3181 for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) { 3182 for (int i = 0; i < receiveState.getNumSubgroups(); i++) { 3183 Long syncState = receiveState.getBisSyncState().get(i); 3184 /* Synced to BIS */ 3185 if (syncState != BassConstants.BIS_SYNC_NOT_SYNC_TO_BIS 3186 && syncState != BassConstants.BIS_SYNC_FAILED_SYNC_TO_BIG) { 3187 if (isLocalBroadcast(receiveState)) { 3188 return false; 3189 } else { 3190 isReceivingExternalBroadcast = true; 3191 } 3192 } 3193 3194 } 3195 } 3196 } 3197 3198 return isReceivingExternalBroadcast; 3199 } 3200 3201 /** Get the active broadcast sink devices receiving broadcast stream */ getActiveBroadcastSinks()3202 public List<BluetoothDevice> getActiveBroadcastSinks() { 3203 List<BluetoothDevice> activeSinks = new ArrayList<>(); 3204 3205 for (BluetoothDevice device : getConnectedDevices()) { 3206 // Check if any device's source in active sync state 3207 if (getAllSources(device).stream() 3208 .anyMatch( 3209 receiveState -> 3210 (receiveState.getBisSyncState().stream() 3211 .anyMatch( 3212 syncState -> 3213 syncState 3214 != BassConstants 3215 .BIS_SYNC_NOT_SYNC_TO_BIS 3216 && syncState 3217 != BassConstants 3218 .BIS_SYNC_FAILED_SYNC_TO_BIG)))) { 3219 activeSinks.add(device); 3220 } 3221 } 3222 return activeSinks; 3223 } 3224 3225 /** Handle broadcast state changed */ notifyBroadcastStateChanged(int state, int broadcastId)3226 public void notifyBroadcastStateChanged(int state, int broadcastId) { 3227 switch (state) { 3228 case BROADCAST_STATE_STOPPED: 3229 if (mLocalBroadcastReceivers == null) { 3230 Log.e(TAG, "notifyBroadcastStateChanged: mLocalBroadcastReceivers is invalid"); 3231 break; 3232 } 3233 3234 if (mLocalBroadcastReceivers.remove(broadcastId) != null) { 3235 sEventLogger.logd(TAG, "Broadcast ID: " + broadcastId + ", stopped"); 3236 } 3237 break; 3238 case BROADCAST_STATE_CONFIGURING: 3239 case BROADCAST_STATE_PAUSED: 3240 case BROADCAST_STATE_STOPPING: 3241 case BROADCAST_STATE_STREAMING: 3242 default: 3243 break; 3244 } 3245 } 3246 3247 /** Callback handler */ 3248 static class Callbacks extends Handler { 3249 private static final int MSG_SEARCH_STARTED = 1; 3250 private static final int MSG_SEARCH_STARTED_FAILED = 2; 3251 private static final int MSG_SEARCH_STOPPED = 3; 3252 private static final int MSG_SEARCH_STOPPED_FAILED = 4; 3253 private static final int MSG_SOURCE_FOUND = 5; 3254 private static final int MSG_SOURCE_ADDED = 6; 3255 private static final int MSG_SOURCE_ADDED_FAILED = 7; 3256 private static final int MSG_SOURCE_MODIFIED = 8; 3257 private static final int MSG_SOURCE_MODIFIED_FAILED = 9; 3258 private static final int MSG_SOURCE_REMOVED = 10; 3259 private static final int MSG_SOURCE_REMOVED_FAILED = 11; 3260 private static final int MSG_RECEIVESTATE_CHANGED = 12; 3261 private static final int MSG_SOURCE_LOST = 13; 3262 3263 private final RemoteCallbackList<IBluetoothLeBroadcastAssistantCallback> mCallbacks = 3264 new RemoteCallbackList<>(); 3265 Callbacks(Looper looper)3266 Callbacks(Looper looper) { 3267 super(looper); 3268 } 3269 register(IBluetoothLeBroadcastAssistantCallback callback)3270 public void register(IBluetoothLeBroadcastAssistantCallback callback) { 3271 mCallbacks.register(callback); 3272 } 3273 unregister(IBluetoothLeBroadcastAssistantCallback callback)3274 public void unregister(IBluetoothLeBroadcastAssistantCallback callback) { 3275 mCallbacks.unregister(callback); 3276 } 3277 checkForPendingGroupOpRequest(Message msg)3278 private void checkForPendingGroupOpRequest(Message msg) { 3279 if (sService == null) { 3280 Log.e(TAG, "Service is null"); 3281 return; 3282 } 3283 3284 final int reason = msg.arg1; 3285 BluetoothDevice sink; 3286 3287 switch (msg.what) { 3288 case MSG_SOURCE_ADDED: 3289 case MSG_SOURCE_ADDED_FAILED: 3290 ObjParams param = (ObjParams) msg.obj; 3291 sink = (BluetoothDevice) param.mObj1; 3292 sService.checkForPendingGroupOpRequest( 3293 sink, reason, BassClientStateMachine.ADD_BCAST_SOURCE, param.mObj2); 3294 break; 3295 case MSG_SOURCE_REMOVED: 3296 case MSG_SOURCE_REMOVED_FAILED: 3297 sink = (BluetoothDevice) msg.obj; 3298 sService.checkForPendingGroupOpRequest( 3299 sink, 3300 reason, 3301 BassClientStateMachine.REMOVE_BCAST_SOURCE, 3302 Integer.valueOf(msg.arg2)); 3303 break; 3304 default: 3305 break; 3306 } 3307 } 3308 3309 @Override handleMessage(Message msg)3310 public void handleMessage(Message msg) { 3311 checkForPendingGroupOpRequest(msg); 3312 final int n = mCallbacks.beginBroadcast(); 3313 for (int i = 0; i < n; i++) { 3314 final IBluetoothLeBroadcastAssistantCallback callback = 3315 mCallbacks.getBroadcastItem(i); 3316 try { 3317 invokeCallback(callback, msg); 3318 } catch (RemoteException e) { 3319 continue; 3320 } 3321 } 3322 mCallbacks.finishBroadcast(); 3323 } 3324 3325 private static class ObjParams { 3326 Object mObj1; 3327 Object mObj2; 3328 ObjParams(Object o1, Object o2)3329 ObjParams(Object o1, Object o2) { 3330 mObj1 = o1; 3331 mObj2 = o2; 3332 } 3333 } 3334 invokeCallback(IBluetoothLeBroadcastAssistantCallback callback, Message msg)3335 private void invokeCallback(IBluetoothLeBroadcastAssistantCallback callback, Message msg) 3336 throws RemoteException { 3337 final int reason = msg.arg1; 3338 final int sourceId = msg.arg2; 3339 ObjParams param; 3340 BluetoothDevice sink; 3341 3342 switch (msg.what) { 3343 case MSG_SEARCH_STARTED: 3344 callback.onSearchStarted(reason); 3345 break; 3346 case MSG_SEARCH_STARTED_FAILED: 3347 callback.onSearchStartFailed(reason); 3348 break; 3349 case MSG_SEARCH_STOPPED: 3350 callback.onSearchStopped(reason); 3351 break; 3352 case MSG_SEARCH_STOPPED_FAILED: 3353 callback.onSearchStopFailed(reason); 3354 break; 3355 case MSG_SOURCE_FOUND: 3356 callback.onSourceFound((BluetoothLeBroadcastMetadata) msg.obj); 3357 break; 3358 case MSG_SOURCE_ADDED: 3359 param = (ObjParams) msg.obj; 3360 sink = (BluetoothDevice) param.mObj1; 3361 callback.onSourceAdded(sink, sourceId, reason); 3362 break; 3363 case MSG_SOURCE_ADDED_FAILED: 3364 param = (ObjParams) msg.obj; 3365 sink = (BluetoothDevice) param.mObj1; 3366 BluetoothLeBroadcastMetadata metadata = 3367 (BluetoothLeBroadcastMetadata) param.mObj2; 3368 callback.onSourceAddFailed(sink, metadata, reason); 3369 break; 3370 case MSG_SOURCE_MODIFIED: 3371 callback.onSourceModified((BluetoothDevice) msg.obj, sourceId, reason); 3372 break; 3373 case MSG_SOURCE_MODIFIED_FAILED: 3374 callback.onSourceModifyFailed((BluetoothDevice) msg.obj, sourceId, reason); 3375 break; 3376 case MSG_SOURCE_REMOVED: 3377 sink = (BluetoothDevice) msg.obj; 3378 callback.onSourceRemoved(sink, sourceId, reason); 3379 break; 3380 case MSG_SOURCE_REMOVED_FAILED: 3381 sink = (BluetoothDevice) msg.obj; 3382 callback.onSourceRemoveFailed(sink, sourceId, reason); 3383 break; 3384 case MSG_RECEIVESTATE_CHANGED: 3385 param = (ObjParams) msg.obj; 3386 sink = (BluetoothDevice) param.mObj1; 3387 BluetoothLeBroadcastReceiveState state = 3388 (BluetoothLeBroadcastReceiveState) param.mObj2; 3389 callback.onReceiveStateChanged(sink, sourceId, state); 3390 break; 3391 case MSG_SOURCE_LOST: 3392 callback.onSourceLost(sourceId); 3393 break; 3394 default: 3395 Log.e(TAG, "Invalid msg: " + msg.what); 3396 break; 3397 } 3398 } 3399 notifySearchStarted(int reason)3400 void notifySearchStarted(int reason) { 3401 sEventLogger.logd(TAG, "notifySearchStarted: reason: " + reason); 3402 obtainMessage(MSG_SEARCH_STARTED, reason, 0).sendToTarget(); 3403 } 3404 notifySearchStartFailed(int reason)3405 void notifySearchStartFailed(int reason) { 3406 sEventLogger.loge(TAG, "notifySearchStartFailed: reason: " + reason); 3407 obtainMessage(MSG_SEARCH_STARTED_FAILED, reason, 0).sendToTarget(); 3408 } 3409 notifySearchStopped(int reason)3410 void notifySearchStopped(int reason) { 3411 sEventLogger.logd(TAG, "notifySearchStopped: reason: " + reason); 3412 obtainMessage(MSG_SEARCH_STOPPED, reason, 0).sendToTarget(); 3413 } 3414 notifySearchStopFailed(int reason)3415 void notifySearchStopFailed(int reason) { 3416 sEventLogger.loge(TAG, "notifySearchStopFailed: reason: " + reason); 3417 obtainMessage(MSG_SEARCH_STOPPED_FAILED, reason, 0).sendToTarget(); 3418 } 3419 notifySourceFound(BluetoothLeBroadcastMetadata source)3420 void notifySourceFound(BluetoothLeBroadcastMetadata source) { 3421 sEventLogger.logd( 3422 TAG, 3423 "invokeCallback: MSG_SOURCE_FOUND" 3424 + ", source: " 3425 + source.getSourceDevice() 3426 + ", broadcastId: " 3427 + source.getBroadcastId() 3428 + ", broadcastName: " 3429 + source.getBroadcastName() 3430 + ", isPublic: " 3431 + source.isPublicBroadcast() 3432 + ", isEncrypted: " 3433 + source.isEncrypted()); 3434 obtainMessage(MSG_SOURCE_FOUND, 0, 0, source).sendToTarget(); 3435 } 3436 notifySourceAdded( BluetoothDevice sink, BluetoothLeBroadcastReceiveState recvState, int reason)3437 void notifySourceAdded( 3438 BluetoothDevice sink, BluetoothLeBroadcastReceiveState recvState, int reason) { 3439 sService.localNotifySourceAdded(sink, recvState); 3440 3441 sEventLogger.logd( 3442 TAG, 3443 "notifySourceAdded: " 3444 + "sink: " 3445 + sink 3446 + ", sourceId: " 3447 + recvState.getSourceId() 3448 + ", reason: " 3449 + reason); 3450 3451 ObjParams param = new ObjParams(sink, recvState); 3452 obtainMessage(MSG_SOURCE_ADDED, reason, recvState.getSourceId(), param).sendToTarget(); 3453 } 3454 notifySourceAddFailed( BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason)3455 void notifySourceAddFailed( 3456 BluetoothDevice sink, BluetoothLeBroadcastMetadata source, int reason) { 3457 sEventLogger.loge( 3458 TAG, 3459 "notifySourceAddFailed: sink: " 3460 + sink 3461 + ", source: " 3462 + source 3463 + ", reason: " 3464 + reason); 3465 ObjParams param = new ObjParams(sink, source); 3466 obtainMessage(MSG_SOURCE_ADDED_FAILED, reason, 0, param).sendToTarget(); 3467 } 3468 notifySourceModified(BluetoothDevice sink, int sourceId, int reason)3469 void notifySourceModified(BluetoothDevice sink, int sourceId, int reason) { 3470 sEventLogger.logd( 3471 TAG, 3472 "notifySourceModified: " 3473 + "sink: " 3474 + sink 3475 + ", sourceId: " 3476 + sourceId 3477 + ", reason: " 3478 + reason); 3479 obtainMessage(MSG_SOURCE_MODIFIED, reason, sourceId, sink).sendToTarget(); 3480 } 3481 notifySourceModifyFailed(BluetoothDevice sink, int sourceId, int reason)3482 void notifySourceModifyFailed(BluetoothDevice sink, int sourceId, int reason) { 3483 sEventLogger.loge( 3484 TAG, 3485 "notifySourceModifyFailed: sink: " 3486 + sink 3487 + ", sourceId: " 3488 + sourceId 3489 + ", reason: " 3490 + reason); 3491 obtainMessage(MSG_SOURCE_MODIFIED_FAILED, reason, sourceId, sink).sendToTarget(); 3492 } 3493 notifySourceRemoved(BluetoothDevice sink, int sourceId, int reason)3494 void notifySourceRemoved(BluetoothDevice sink, int sourceId, int reason) { 3495 sEventLogger.logd( 3496 TAG, 3497 "notifySourceRemoved: " 3498 + "sink: " 3499 + sink 3500 + ", sourceId: " 3501 + sourceId 3502 + ", reason: " 3503 + reason); 3504 obtainMessage(MSG_SOURCE_REMOVED, reason, sourceId, sink).sendToTarget(); 3505 } 3506 notifySourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason)3507 void notifySourceRemoveFailed(BluetoothDevice sink, int sourceId, int reason) { 3508 sEventLogger.loge( 3509 TAG, 3510 "notifySourceRemoveFailed: " 3511 + "sink: " 3512 + sink 3513 + ", sourceId: " 3514 + sourceId 3515 + ", reason: " 3516 + reason); 3517 obtainMessage(MSG_SOURCE_REMOVED_FAILED, reason, sourceId, sink).sendToTarget(); 3518 } 3519 notifyReceiveStateChanged( BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state)3520 void notifyReceiveStateChanged( 3521 BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) { 3522 ObjParams param = new ObjParams(sink, state); 3523 3524 sService.localNotifyReceiveStateChanged(sink); 3525 3526 String subgroupState = " / SUB GROUPS: "; 3527 for (int i = 0; i < state.getNumSubgroups(); i++) { 3528 subgroupState += "IDX: " + i + ", SYNC: " + state.getBisSyncState().get(i); 3529 } 3530 3531 sEventLogger.logd( 3532 TAG, 3533 "notifyReceiveStateChanged: " 3534 + "sink: " 3535 + sink 3536 + ", state: SRC ID: " 3537 + state.getSourceId() 3538 + " / ADDR TYPE: " 3539 + state.getSourceAddressType() 3540 + " / SRC DEV: " 3541 + state.getSourceDevice() 3542 + " / ADV SID: " 3543 + state.getSourceAdvertisingSid() 3544 + " / BID: " 3545 + state.getBroadcastId() 3546 + " / PA STATE: " 3547 + state.getPaSyncState() 3548 + " / BENC STATE: " 3549 + state.getBigEncryptionState() 3550 + " / BAD CODE: " 3551 + Arrays.toString(state.getBadCode()) 3552 + subgroupState); 3553 obtainMessage(MSG_RECEIVESTATE_CHANGED, 0, sourceId, param).sendToTarget(); 3554 } 3555 notifySourceLost(int broadcastId)3556 void notifySourceLost(int broadcastId) { 3557 sEventLogger.logd(TAG, "notifySourceLost: broadcastId: " + broadcastId); 3558 obtainMessage(MSG_SOURCE_LOST, 0, broadcastId).sendToTarget(); 3559 } 3560 } 3561 3562 @Override dump(StringBuilder sb)3563 public void dump(StringBuilder sb) { 3564 super.dump(sb); 3565 3566 sb.append("Broadcast Assistant Service instance:\n"); 3567 3568 /* Dump first connected state machines */ 3569 for (Map.Entry<BluetoothDevice, BassClientStateMachine> entry : mStateMachines.entrySet()) { 3570 BassClientStateMachine sm = entry.getValue(); 3571 if (sm.getConnectionState() == BluetoothProfile.STATE_CONNECTED) { 3572 sm.dump(sb); 3573 sb.append("\n\n"); 3574 } 3575 } 3576 3577 /* Dump at least all other than connected state machines */ 3578 for (Map.Entry<BluetoothDevice, BassClientStateMachine> entry : mStateMachines.entrySet()) { 3579 BassClientStateMachine sm = entry.getValue(); 3580 if (sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) { 3581 sm.dump(sb); 3582 } 3583 } 3584 3585 sb.append("\n\n"); 3586 sEventLogger.dump(sb); 3587 sb.append("\n"); 3588 } 3589 3590 /** Binder object: must be a static class or memory leak may occur */ 3591 @VisibleForTesting 3592 static class BluetoothLeBroadcastAssistantBinder extends IBluetoothLeBroadcastAssistant.Stub 3593 implements IProfileServiceBinder { 3594 BassClientService mService; 3595 getService()3596 private BassClientService getService() { 3597 if (Utils.isInstrumentationTestMode()) { 3598 return mService; 3599 } 3600 if (!Utils.checkServiceAvailable(mService, TAG) 3601 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)) { 3602 return null; 3603 } 3604 return mService; 3605 } 3606 BluetoothLeBroadcastAssistantBinder(BassClientService svc)3607 BluetoothLeBroadcastAssistantBinder(BassClientService svc) { 3608 mService = svc; 3609 } 3610 3611 @Override cleanup()3612 public void cleanup() { 3613 mService = null; 3614 } 3615 3616 @Override getConnectionState(BluetoothDevice sink)3617 public int getConnectionState(BluetoothDevice sink) { 3618 try { 3619 BassClientService service = getService(); 3620 if (service == null) { 3621 Log.e(TAG, "Service is null"); 3622 return BluetoothProfile.STATE_DISCONNECTED; 3623 } 3624 return service.getConnectionState(sink); 3625 } catch (RuntimeException e) { 3626 Log.e(TAG, "Exception happened", e); 3627 return BluetoothProfile.STATE_DISCONNECTED; 3628 } 3629 } 3630 3631 @Override getDevicesMatchingConnectionStates(int[] states)3632 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 3633 try { 3634 BassClientService service = getService(); 3635 if (service == null) { 3636 Log.e(TAG, "Service is null"); 3637 return Collections.emptyList(); 3638 } 3639 return service.getDevicesMatchingConnectionStates(states); 3640 } catch (RuntimeException e) { 3641 Log.e(TAG, "Exception happened", e); 3642 return Collections.emptyList(); 3643 } 3644 } 3645 3646 @Override getConnectedDevices()3647 public List<BluetoothDevice> getConnectedDevices() { 3648 try { 3649 BassClientService service = getService(); 3650 if (service == null) { 3651 Log.e(TAG, "Service is null"); 3652 return Collections.emptyList(); 3653 } 3654 return service.getConnectedDevices(); 3655 } catch (RuntimeException e) { 3656 Log.e(TAG, "Exception happened", e); 3657 return Collections.emptyList(); 3658 } 3659 } 3660 3661 @Override setConnectionPolicy(BluetoothDevice device, int connectionPolicy)3662 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 3663 try { 3664 BassClientService service = getService(); 3665 if (service == null) { 3666 Log.e(TAG, "Service is null"); 3667 return false; 3668 } 3669 mService.enforceCallingOrSelfPermission( 3670 BLUETOOTH_CONNECT, "Need BLUETOOTH_CONNECT permission"); 3671 return service.setConnectionPolicy(device, connectionPolicy); 3672 } catch (RuntimeException e) { 3673 Log.e(TAG, "Exception happened", e); 3674 return false; 3675 } 3676 } 3677 3678 @Override getConnectionPolicy(BluetoothDevice device)3679 public int getConnectionPolicy(BluetoothDevice device) { 3680 try { 3681 BassClientService service = getService(); 3682 if (service == null) { 3683 Log.e(TAG, "Service is null"); 3684 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 3685 } 3686 mService.enforceCallingOrSelfPermission( 3687 BLUETOOTH_CONNECT, "Need BLUETOOTH_CONNECT permission"); 3688 return service.getConnectionPolicy(device); 3689 } catch (RuntimeException e) { 3690 Log.e(TAG, "Exception happened", e); 3691 return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; 3692 } 3693 } 3694 3695 @Override registerCallback(IBluetoothLeBroadcastAssistantCallback cb)3696 public void registerCallback(IBluetoothLeBroadcastAssistantCallback cb) { 3697 try { 3698 BassClientService service = getService(); 3699 if (service == null) { 3700 Log.e(TAG, "Service is null"); 3701 return; 3702 } 3703 enforceBluetoothPrivilegedPermission(service); 3704 service.registerCallback(cb); 3705 } catch (RuntimeException e) { 3706 Log.e(TAG, "Exception happened", e); 3707 } 3708 } 3709 3710 @Override unregisterCallback(IBluetoothLeBroadcastAssistantCallback cb)3711 public void unregisterCallback(IBluetoothLeBroadcastAssistantCallback cb) { 3712 try { 3713 BassClientService service = getService(); 3714 if (service == null) { 3715 Log.e(TAG, "Service is null"); 3716 return; 3717 } 3718 enforceBluetoothPrivilegedPermission(service); 3719 service.unregisterCallback(cb); 3720 } catch (RuntimeException e) { 3721 Log.e(TAG, "Exception happened", e); 3722 } 3723 } 3724 3725 @Override startSearchingForSources(List<ScanFilter> filters)3726 public void startSearchingForSources(List<ScanFilter> filters) { 3727 try { 3728 BassClientService service = getService(); 3729 if (service == null) { 3730 Log.e(TAG, "Service is null"); 3731 return; 3732 } 3733 enforceBluetoothPrivilegedPermission(service); 3734 service.startSearchingForSources(filters); 3735 } catch (RuntimeException e) { 3736 Log.e(TAG, "Exception happened", e); 3737 } 3738 } 3739 3740 @Override stopSearchingForSources()3741 public void stopSearchingForSources() { 3742 try { 3743 BassClientService service = getService(); 3744 if (service == null) { 3745 Log.e(TAG, "Service is null"); 3746 return; 3747 } 3748 enforceBluetoothPrivilegedPermission(service); 3749 service.stopSearchingForSources(); 3750 } catch (RuntimeException e) { 3751 Log.e(TAG, "Exception happened", e); 3752 } 3753 } 3754 3755 @Override isSearchInProgress()3756 public boolean isSearchInProgress() { 3757 try { 3758 BassClientService service = getService(); 3759 if (service == null) { 3760 Log.e(TAG, "Service is null"); 3761 return false; 3762 } 3763 enforceBluetoothPrivilegedPermission(service); 3764 return service.isSearchInProgress(); 3765 } catch (RuntimeException e) { 3766 Log.e(TAG, "Exception happened", e); 3767 return false; 3768 } 3769 } 3770 3771 @Override addSource( BluetoothDevice sink, BluetoothLeBroadcastMetadata sourceMetadata, boolean isGroupOp)3772 public void addSource( 3773 BluetoothDevice sink, 3774 BluetoothLeBroadcastMetadata sourceMetadata, 3775 boolean isGroupOp) { 3776 try { 3777 BassClientService service = getService(); 3778 if (service == null) { 3779 Log.e(TAG, "Service is null"); 3780 return; 3781 } 3782 enforceBluetoothPrivilegedPermission(service); 3783 service.addSource(sink, sourceMetadata, isGroupOp); 3784 } catch (RuntimeException e) { 3785 Log.e(TAG, "Exception happened", e); 3786 } 3787 } 3788 3789 @Override modifySource( BluetoothDevice sink, int sourceId, BluetoothLeBroadcastMetadata updatedMetadata)3790 public void modifySource( 3791 BluetoothDevice sink, int sourceId, BluetoothLeBroadcastMetadata updatedMetadata) { 3792 try { 3793 BassClientService service = getService(); 3794 if (service == null) { 3795 Log.e(TAG, "Service is null"); 3796 return; 3797 } 3798 enforceBluetoothPrivilegedPermission(service); 3799 service.modifySource(sink, sourceId, updatedMetadata); 3800 } catch (RuntimeException e) { 3801 Log.e(TAG, "Exception happened", e); 3802 } 3803 } 3804 3805 @Override removeSource(BluetoothDevice sink, int sourceId)3806 public void removeSource(BluetoothDevice sink, int sourceId) { 3807 try { 3808 BassClientService service = getService(); 3809 if (service == null) { 3810 Log.e(TAG, "Service is null"); 3811 return; 3812 } 3813 enforceBluetoothPrivilegedPermission(service); 3814 service.removeSource(sink, sourceId); 3815 } catch (RuntimeException e) { 3816 Log.e(TAG, "Exception happened", e); 3817 } 3818 } 3819 3820 @Override getAllSources(BluetoothDevice sink)3821 public List<BluetoothLeBroadcastReceiveState> getAllSources(BluetoothDevice sink) { 3822 try { 3823 BassClientService service = getService(); 3824 if (sink == null) { 3825 Log.e(TAG, "Service is null"); 3826 return Collections.emptyList(); 3827 } 3828 enforceBluetoothPrivilegedPermission(service); 3829 return service.getAllSources(sink); 3830 } catch (RuntimeException e) { 3831 Log.e(TAG, "Exception happened", e); 3832 return Collections.emptyList(); 3833 } 3834 } 3835 3836 @Override getMaximumSourceCapacity(BluetoothDevice sink)3837 public int getMaximumSourceCapacity(BluetoothDevice sink) { 3838 try { 3839 BassClientService service = getService(); 3840 if (service == null) { 3841 Log.e(TAG, "Service is null"); 3842 return 0; 3843 } 3844 enforceBluetoothPrivilegedPermission(service); 3845 return service.getMaximumSourceCapacity(sink); 3846 } catch (RuntimeException e) { 3847 Log.e(TAG, "Exception happened", e); 3848 return 0; 3849 } 3850 } 3851 } 3852 } 3853