1 /* 2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.bass_client; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 21 import android.annotation.Nullable; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothGatt; 25 import android.bluetooth.BluetoothGattCallback; 26 import android.bluetooth.BluetoothGattCharacteristic; 27 import android.bluetooth.BluetoothGattDescriptor; 28 import android.bluetooth.BluetoothGattService; 29 import android.bluetooth.BluetoothLeAudioCodecConfigMetadata; 30 import android.bluetooth.BluetoothLeAudioContentMetadata; 31 import android.bluetooth.BluetoothLeBroadcastAssistant; 32 import android.bluetooth.BluetoothLeBroadcastChannel; 33 import android.bluetooth.BluetoothLeBroadcastMetadata; 34 import android.bluetooth.BluetoothLeBroadcastReceiveState; 35 import android.bluetooth.BluetoothLeBroadcastSubgroup; 36 import android.bluetooth.BluetoothProfile; 37 import android.bluetooth.BluetoothStatusCodes; 38 import android.bluetooth.BluetoothUtils; 39 import android.bluetooth.BluetoothUtils.TypeValueEntry; 40 import android.bluetooth.le.PeriodicAdvertisingCallback; 41 import android.bluetooth.le.PeriodicAdvertisingReport; 42 import android.bluetooth.le.ScanRecord; 43 import android.bluetooth.le.ScanResult; 44 import android.content.Intent; 45 import android.os.Binder; 46 import android.os.Looper; 47 import android.os.Message; 48 import android.os.ParcelUuid; 49 import android.provider.DeviceConfig; 50 import android.util.Log; 51 import android.util.Pair; 52 53 import com.android.bluetooth.BluetoothMethodProxy; 54 import com.android.bluetooth.Utils; 55 import com.android.bluetooth.btservice.AdapterService; 56 import com.android.bluetooth.btservice.ProfileService; 57 import com.android.bluetooth.flags.Flags; 58 import com.android.internal.annotations.VisibleForTesting; 59 import com.android.internal.util.State; 60 import com.android.internal.util.StateMachine; 61 62 import java.io.ByteArrayOutputStream; 63 import java.io.FileDescriptor; 64 import java.io.PrintWriter; 65 import java.io.StringWriter; 66 import java.nio.charset.StandardCharsets; 67 import java.util.ArrayList; 68 import java.util.Arrays; 69 import java.util.Collection; 70 import java.util.HashMap; 71 import java.util.Iterator; 72 import java.util.List; 73 import java.util.Map; 74 import java.util.Scanner; 75 import java.util.UUID; 76 import java.util.stream.IntStream; 77 78 @VisibleForTesting 79 public class BassClientStateMachine extends StateMachine { 80 private static final String TAG = "BassClientStateMachine"; 81 @VisibleForTesting static final byte[] REMOTE_SCAN_STOP = {00}; 82 @VisibleForTesting static final byte[] REMOTE_SCAN_START = {01}; 83 private static final byte OPCODE_ADD_SOURCE = 0x02; 84 private static final byte OPCODE_UPDATE_SOURCE = 0x03; 85 private static final byte OPCODE_SET_BCAST_PIN = 0x04; 86 private static final byte OPCODE_REMOVE_SOURCE = 0x05; 87 private static final int UPDATE_SOURCE_FIXED_LENGTH = 6; 88 89 static final int CONNECT = 1; 90 static final int DISCONNECT = 2; 91 static final int CONNECTION_STATE_CHANGED = 3; 92 static final int GATT_TXN_PROCESSED = 4; 93 static final int READ_BASS_CHARACTERISTICS = 5; 94 static final int START_SCAN_OFFLOAD = 6; 95 static final int STOP_SCAN_OFFLOAD = 7; 96 static final int SELECT_BCAST_SOURCE = 8; 97 static final int ADD_BCAST_SOURCE = 9; 98 static final int UPDATE_BCAST_SOURCE = 10; 99 static final int SET_BCAST_CODE = 11; 100 static final int REMOVE_BCAST_SOURCE = 12; 101 static final int GATT_TXN_TIMEOUT = 13; 102 static final int PSYNC_ACTIVE_TIMEOUT = 14; 103 static final int CONNECT_TIMEOUT = 15; 104 static final int REACHED_MAX_SOURCE_LIMIT = 16; 105 static final int SWITCH_BCAST_SOURCE = 17; 106 static final int CANCEL_PENDING_SOURCE_OPERATION = 18; 107 108 // NOTE: the value is not "final" - it is modified in the unit tests 109 @VisibleForTesting private int mConnectTimeoutMs; 110 111 // Type of argument for set broadcast code operation 112 static final int ARGTYPE_METADATA = 1; 113 static final int ARGTYPE_RCVSTATE = 2; 114 115 static final int ATT_WRITE_CMD_HDR_LEN = 3; 116 117 private final Map<Integer, PeriodicAdvertisingCallback> mPeriodicAdvCallbacksMap = 118 new HashMap<>(); 119 /*key is combination of sourceId, Address and advSid for this hashmap*/ 120 private final Map<Integer, BluetoothLeBroadcastReceiveState> 121 mBluetoothLeBroadcastReceiveStates = 122 new HashMap<Integer, BluetoothLeBroadcastReceiveState>(); 123 private final Map<Integer, BluetoothLeBroadcastMetadata> mCurrentMetadata = new HashMap(); 124 private final Disconnected mDisconnected = new Disconnected(); 125 private final Connected mConnected = new Connected(); 126 private final Connecting mConnecting = new Connecting(); 127 private final ConnectedProcessing mConnectedProcessing = new ConnectedProcessing(); 128 private final List<Pair<ScanResult, Integer>> mSourceSyncRequestsQueue = 129 new ArrayList<Pair<ScanResult, Integer>>(); 130 131 @VisibleForTesting 132 final List<BluetoothGattCharacteristic> mBroadcastCharacteristics = 133 new ArrayList<BluetoothGattCharacteristic>(); 134 135 @VisibleForTesting BluetoothDevice mDevice; 136 137 private boolean mIsAllowedList = false; 138 private int mLastConnectionState = -1; 139 @VisibleForTesting boolean mMTUChangeRequested = false; 140 @VisibleForTesting boolean mDiscoveryInitiated = false; 141 @VisibleForTesting BassClientService mService; 142 AdapterService mAdapterService; 143 @VisibleForTesting BluetoothGattCharacteristic mBroadcastScanControlPoint; 144 private final Map<Integer, Boolean> mFirstTimeBisDiscoveryMap; 145 private int mPASyncRetryCounter = 0; 146 @VisibleForTesting int mNumOfBroadcastReceiverStates = 0; 147 @VisibleForTesting int mPendingOperation = -1; 148 @VisibleForTesting byte mPendingSourceId = -1; 149 @VisibleForTesting BluetoothLeBroadcastMetadata mPendingMetadata = null; 150 private BluetoothLeBroadcastMetadata mSetBroadcastPINMetadata = null; 151 @VisibleForTesting boolean mSetBroadcastCodePending = false; 152 private final Map<Integer, Boolean> mPendingRemove = new HashMap(); 153 @VisibleForTesting boolean mAutoTriggered = false; 154 private boolean mDefNoPAS = false; 155 private boolean mForceSB = false; 156 @VisibleForTesting BluetoothLeBroadcastMetadata mPendingSourceToAdd = null; 157 private int mBroadcastSourceIdLength = 3; 158 @VisibleForTesting byte mNextSourceId = 0; 159 private boolean mAllowReconnect = false; 160 @VisibleForTesting BluetoothGattTestableWrapper mBluetoothGatt = null; 161 BluetoothGattCallback mGattCallback = null; 162 @VisibleForTesting PeriodicAdvertisingCallback mLocalPeriodicAdvCallback = new PACallback(); 163 int mMaxSingleAttributeWriteValueLen = 0; 164 @VisibleForTesting BluetoothLeBroadcastMetadata mPendingSourceToSwitch = null; 165 BassClientStateMachine( BluetoothDevice device, BassClientService svc, AdapterService adapterService, Looper looper, int connectTimeoutMs)166 BassClientStateMachine( 167 BluetoothDevice device, 168 BassClientService svc, 169 AdapterService adapterService, 170 Looper looper, 171 int connectTimeoutMs) { 172 super(TAG + "(" + device.toString() + ")", looper); 173 mDevice = device; 174 mService = svc; 175 mAdapterService = adapterService; 176 mConnectTimeoutMs = connectTimeoutMs; 177 addState(mDisconnected); 178 addState(mConnected); 179 addState(mConnecting); 180 addState(mConnectedProcessing); 181 setInitialState(mDisconnected); 182 mFirstTimeBisDiscoveryMap = new HashMap<Integer, Boolean>(); 183 long token = Binder.clearCallingIdentity(); 184 mIsAllowedList = 185 DeviceConfig.getBoolean( 186 DeviceConfig.NAMESPACE_BLUETOOTH, "persist.vendor.service.bt.wl", true); 187 mDefNoPAS = 188 DeviceConfig.getBoolean( 189 DeviceConfig.NAMESPACE_BLUETOOTH, 190 "persist.vendor.service.bt.defNoPAS", 191 false); 192 mForceSB = 193 DeviceConfig.getBoolean( 194 DeviceConfig.NAMESPACE_BLUETOOTH, 195 "persist.vendor.service.bt.forceSB", 196 false); 197 Binder.restoreCallingIdentity(token); 198 } 199 make( BluetoothDevice device, BassClientService svc, AdapterService adapterService, Looper looper)200 static BassClientStateMachine make( 201 BluetoothDevice device, 202 BassClientService svc, 203 AdapterService adapterService, 204 Looper looper) { 205 Log.d(TAG, "make for device " + device); 206 207 if (!BassClientPeriodicAdvertisingManager 208 .initializePeriodicAdvertisingManagerOnDefaultAdapter()) { 209 Log.e(TAG, "Failed to initialize Periodic Advertising Manager on Default Adapter"); 210 return null; 211 } 212 213 BassClientStateMachine BassclientSm = 214 new BassClientStateMachine( 215 device, svc, adapterService, looper, BassConstants.CONNECT_TIMEOUT_MS); 216 BassclientSm.start(); 217 return BassclientSm; 218 } 219 destroy(BassClientStateMachine stateMachine)220 static void destroy(BassClientStateMachine stateMachine) { 221 Log.i(TAG, "destroy"); 222 if (stateMachine == null) { 223 Log.w(TAG, "destroy(), stateMachine is null"); 224 return; 225 } 226 stateMachine.doQuit(); 227 stateMachine.cleanup(); 228 } 229 doQuit()230 public void doQuit() { 231 log("doQuit for device " + mDevice); 232 quitNow(); 233 } 234 cleanup()235 public void cleanup() { 236 log("cleanup for device " + mDevice); 237 clearCharsCache(); 238 239 if (mBluetoothGatt != null) { 240 log("disconnect gatt"); 241 mBluetoothGatt.disconnect(); 242 mBluetoothGatt.close(); 243 mBluetoothGatt = null; 244 mGattCallback = null; 245 } 246 mPendingOperation = -1; 247 mPendingSourceId = -1; 248 mPendingMetadata = null; 249 mPendingSourceToAdd = null; 250 mPendingSourceToSwitch = null; 251 mCurrentMetadata.clear(); 252 mPendingRemove.clear(); 253 mPeriodicAdvCallbacksMap.clear(); 254 mSourceSyncRequestsQueue.clear(); 255 } 256 hasPendingSourceOperation()257 Boolean hasPendingSourceOperation() { 258 return mPendingMetadata != null; 259 } 260 hasPendingSourceOperation(int broadcastId)261 Boolean hasPendingSourceOperation(int broadcastId) { 262 return mPendingMetadata != null && mPendingMetadata.getBroadcastId() == broadcastId; 263 } 264 cancelPendingSourceOperation(int broadcastId)265 private void cancelPendingSourceOperation(int broadcastId) { 266 if ((mPendingMetadata != null) && (mPendingMetadata.getBroadcastId() == broadcastId)) { 267 Log.d(TAG, "clearPendingSourceOperation: broadcast ID: " + broadcastId); 268 mPendingMetadata = null; 269 } 270 } 271 getCurrentBroadcastMetadata(Integer sourceId)272 BluetoothLeBroadcastMetadata getCurrentBroadcastMetadata(Integer sourceId) { 273 return mCurrentMetadata.getOrDefault(sourceId, null); 274 } 275 setCurrentBroadcastMetadata( Integer sourceId, BluetoothLeBroadcastMetadata metadata)276 private void setCurrentBroadcastMetadata( 277 Integer sourceId, BluetoothLeBroadcastMetadata metadata) { 278 if (metadata != null) { 279 mCurrentMetadata.put(sourceId, metadata); 280 } else { 281 mCurrentMetadata.remove(sourceId); 282 } 283 } 284 isPendingRemove(Integer sourceId)285 boolean isPendingRemove(Integer sourceId) { 286 return mPendingRemove.getOrDefault(sourceId, false); 287 } 288 setPendingRemove(Integer sourceId, boolean remove)289 private void setPendingRemove(Integer sourceId, boolean remove) { 290 if (remove) { 291 mPendingRemove.put(sourceId, remove); 292 } else { 293 mPendingRemove.remove(sourceId); 294 } 295 } 296 getBroadcastReceiveStateForSourceDevice( BluetoothDevice srcDevice)297 BluetoothLeBroadcastReceiveState getBroadcastReceiveStateForSourceDevice( 298 BluetoothDevice srcDevice) { 299 List<BluetoothLeBroadcastReceiveState> currentSources = getAllSources(); 300 BluetoothLeBroadcastReceiveState state = null; 301 for (int i = 0; i < currentSources.size(); i++) { 302 BluetoothDevice device = currentSources.get(i).getSourceDevice(); 303 if (device != null && device.equals(srcDevice)) { 304 state = currentSources.get(i); 305 Log.e( 306 TAG, 307 "getBroadcastReceiveStateForSourceDevice: returns for: " 308 + srcDevice 309 + "&srcInfo" 310 + state); 311 return state; 312 } 313 } 314 return null; 315 } 316 getBroadcastReceiveStateForSourceId(int sourceId)317 BluetoothLeBroadcastReceiveState getBroadcastReceiveStateForSourceId(int sourceId) { 318 List<BluetoothLeBroadcastReceiveState> currentSources = getAllSources(); 319 for (int i = 0; i < currentSources.size(); i++) { 320 if (sourceId == currentSources.get(i).getSourceId()) { 321 return currentSources.get(i); 322 } 323 } 324 return null; 325 } 326 isSyncedToTheSource(int sourceId)327 boolean isSyncedToTheSource(int sourceId) { 328 BluetoothLeBroadcastReceiveState recvState = getBroadcastReceiveStateForSourceId(sourceId); 329 330 return recvState != null 331 && (recvState.getPaSyncState() 332 == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED 333 || recvState.getBisSyncState().stream() 334 .anyMatch( 335 bitmap -> { 336 return bitmap != 0; 337 })); 338 } 339 parseBaseData(BluetoothDevice device, int syncHandle, byte[] serviceData)340 void parseBaseData(BluetoothDevice device, int syncHandle, byte[] serviceData) { 341 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 342 throw new RuntimeException( 343 "Should never be executed with" 344 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 345 } 346 log("parseBaseData" + Arrays.toString(serviceData)); 347 BaseData base = BaseData.parseBaseData(serviceData); 348 if (base != null) { 349 mService.updateBase(syncHandle, base); 350 base.print(); 351 if (mAutoTriggered) { 352 // successful auto periodic synchrnization with source 353 log("auto triggered assist"); 354 mAutoTriggered = false; 355 // perform PAST with this device 356 BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle); 357 if (srcDevice != null) { 358 BluetoothLeBroadcastReceiveState recvState = 359 getBroadcastReceiveStateForSourceDevice(srcDevice); 360 processPASyncState(recvState); 361 } else { 362 Log.w(TAG, "Autoassist: no matching device"); 363 } 364 } 365 } else { 366 Log.e(TAG, "Seems BASE is not in parsable format"); 367 if (!mAutoTriggered) { 368 cancelActiveSync(syncHandle); 369 } else { 370 mAutoTriggered = false; 371 } 372 } 373 } 374 parseScanRecord(int syncHandle, ScanRecord record)375 void parseScanRecord(int syncHandle, ScanRecord record) { 376 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 377 throw new RuntimeException( 378 "Should never be executed with" 379 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 380 } 381 log("parseScanRecord: " + record); 382 Map<ParcelUuid, byte[]> bmsAdvDataMap = record.getServiceData(); 383 if (bmsAdvDataMap != null) { 384 for (Map.Entry<ParcelUuid, byte[]> entry : bmsAdvDataMap.entrySet()) { 385 log( 386 "ParcelUUid = " 387 + entry.getKey() 388 + ", Value = " 389 + Arrays.toString(entry.getValue())); 390 } 391 } 392 byte[] advData = record.getServiceData(BassConstants.BASIC_AUDIO_UUID); 393 if (advData != null) { 394 parseBaseData(mDevice, syncHandle, advData); 395 } else { 396 Log.e(TAG, "No service data in Scan record"); 397 if (!mAutoTriggered) { 398 cancelActiveSync(syncHandle); 399 } else { 400 mAutoTriggered = false; 401 } 402 } 403 } 404 checkAndParseBroadcastName(ScanRecord record)405 private String checkAndParseBroadcastName(ScanRecord record) { 406 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 407 throw new RuntimeException( 408 "Should never be executed with" 409 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 410 } 411 log("checkAndParseBroadcastName"); 412 byte[] rawBytes = record.getBytes(); 413 List<TypeValueEntry> entries = BluetoothUtils.parseLengthTypeValueBytes(rawBytes); 414 if (rawBytes.length > 0 && rawBytes[0] > 0 && entries.isEmpty()) { 415 Log.e(TAG, "Invalid LTV entries in Scan record"); 416 return null; 417 } 418 419 String broadcastName = null; 420 for (TypeValueEntry entry : entries) { 421 // Only use the first value of each type 422 if (broadcastName == null && entry.getType() == BassConstants.BCAST_NAME_AD_TYPE) { 423 byte[] bytes = entry.getValue(); 424 int len = bytes.length; 425 if (len < BassConstants.BCAST_NAME_LEN_MIN 426 || len > BassConstants.BCAST_NAME_LEN_MAX) { 427 Log.e(TAG, "Invalid broadcast name length in Scan record" + len); 428 return null; 429 } 430 broadcastName = new String(bytes, StandardCharsets.UTF_8); 431 } 432 } 433 return broadcastName; 434 } 435 selectSource(ScanResult scanRes, boolean autoTriggered)436 private boolean selectSource(ScanResult scanRes, boolean autoTriggered) { 437 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 438 throw new RuntimeException( 439 "Should never be executed with" 440 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 441 } 442 log("selectSource: ScanResult " + scanRes); 443 mAutoTriggered = autoTriggered; 444 mPASyncRetryCounter = 1; 445 446 // updating mainly for Address type and PA Interval here 447 // extract BroadcastId from ScanResult 448 ScanRecord scanRecord = scanRes.getScanRecord(); 449 if (scanRecord != null) { 450 Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData(); 451 int broadcastId = BassConstants.INVALID_BROADCAST_ID; 452 PublicBroadcastData pbData = null; 453 if (listOfUuids != null) { 454 if (listOfUuids.containsKey(BassConstants.BAAS_UUID)) { 455 byte[] bId = listOfUuids.get(BassConstants.BAAS_UUID); 456 broadcastId = BassUtils.parseBroadcastId(bId); 457 } 458 if (listOfUuids.containsKey(BassConstants.PUBLIC_BROADCAST_UUID)) { 459 byte[] pbAnnouncement = listOfUuids.get(BassConstants.PUBLIC_BROADCAST_UUID); 460 pbData = PublicBroadcastData.parsePublicBroadcastData(pbAnnouncement); 461 } 462 } 463 464 if (broadcastId == BassConstants.INVALID_BROADCAST_ID || pbData == null) { 465 Log.w(TAG, "Invalid broadcast ID or public broadcast data"); 466 return false; 467 } 468 469 // Check if broadcast name present in scan record and parse 470 // null if no name present 471 String broadcastName = checkAndParseBroadcastName(scanRecord); 472 473 // Avoid duplicated sync request if the same broadcast BIG is synced 474 if (isSourceSynced(broadcastId)) { 475 log("Skip duplicated sync request to broadcast id: " + broadcastId); 476 return false; 477 } 478 479 PeriodicAdvertisingCallback paCb = new PACallback(); 480 // put temp sync handle and update in onSyncEstablished 481 int tempHandle = BassConstants.INVALID_SYNC_HANDLE; 482 mPeriodicAdvCallbacksMap.put(tempHandle, paCb); 483 try { 484 BluetoothMethodProxy.getInstance() 485 .periodicAdvertisingManagerRegisterSync( 486 BassClientPeriodicAdvertisingManager 487 .getPeriodicAdvertisingManager(), 488 scanRes, 489 0, 490 BassConstants.PSYNC_TIMEOUT, 491 paCb, 492 null); 493 } catch (IllegalArgumentException ex) { 494 Log.w(TAG, "registerSync:IllegalArgumentException"); 495 mPeriodicAdvCallbacksMap.remove(tempHandle); 496 return false; 497 } 498 499 mService.updatePeriodicAdvertisementResultMap( 500 scanRes.getDevice(), 501 scanRes.getDevice().getAddressType(), 502 BassConstants.INVALID_SYNC_HANDLE, 503 BassConstants.INVALID_ADV_SID, 504 scanRes.getPeriodicAdvertisingInterval(), 505 broadcastId, 506 pbData, 507 broadcastName); 508 } 509 return true; 510 } 511 isSourceSynced(int broadcastId)512 private boolean isSourceSynced(int broadcastId) { 513 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 514 throw new RuntimeException( 515 "Should never be executed with" 516 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 517 } 518 List<Integer> activeSyncedSrc = mService.getActiveSyncedSources(mDevice); 519 return (activeSyncedSrc != null 520 && activeSyncedSrc.contains(mService.getSyncHandleForBroadcastId(broadcastId))); 521 } 522 cancelActiveSync(Integer syncHandle)523 private void cancelActiveSync(Integer syncHandle) { 524 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 525 throw new RuntimeException( 526 "Should never be executed with" 527 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 528 } 529 log("cancelActiveSync: syncHandle = " + syncHandle); 530 if (syncHandle == null) { 531 // clean up the pending sync request if syncHandle is null 532 mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE); 533 } 534 List<Integer> activeSyncedSrc = mService.getActiveSyncedSources(mDevice); 535 536 /* Stop sync if there is some running */ 537 if (activeSyncedSrc != null 538 && (syncHandle == null || activeSyncedSrc.contains(syncHandle))) { 539 if (syncHandle != null) { 540 // only one source needs to be unsynced 541 unsyncSource(syncHandle); 542 mService.removeActiveSyncedSource(mDevice, syncHandle); 543 } else { 544 // remove all the sources 545 for (int handle : activeSyncedSrc) { 546 unsyncSource(handle); 547 } 548 mService.removeActiveSyncedSource(mDevice, null); 549 } 550 if (mService.getActiveSyncedSources(mDevice) == null) { 551 // all sources are removed, clean up 552 removeMessages(PSYNC_ACTIVE_TIMEOUT); 553 mService.clearNotifiedFlags(); 554 } 555 } 556 } 557 unsyncSource(int syncHandle)558 private boolean unsyncSource(int syncHandle) { 559 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 560 throw new RuntimeException( 561 "Should never be executed with" 562 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 563 } 564 if (syncHandle != BassConstants.INVALID_SYNC_HANDLE 565 && mPeriodicAdvCallbacksMap.containsKey(syncHandle)) { 566 try { 567 BluetoothMethodProxy.getInstance() 568 .periodicAdvertisingManagerUnregisterSync( 569 BassClientPeriodicAdvertisingManager 570 .getPeriodicAdvertisingManager(), 571 mPeriodicAdvCallbacksMap.get(syncHandle)); 572 } catch (IllegalArgumentException ex) { 573 Log.w(TAG, "unregisterSync:IllegalArgumentException"); 574 return false; 575 } 576 mPeriodicAdvCallbacksMap.remove(syncHandle); 577 } else { 578 log("calling unregisterSync, not found syncHandle: " + syncHandle); 579 } 580 return true; 581 } 582 resetBluetoothGatt()583 private void resetBluetoothGatt() { 584 // cleanup mBluetoothGatt 585 if (mBluetoothGatt != null) { 586 mBluetoothGatt.close(); 587 mBluetoothGatt = null; 588 } 589 } 590 getBroadcastMetadataFromBaseData( BaseData baseData, BluetoothDevice device, int syncHandle, boolean encrypted)591 private BluetoothLeBroadcastMetadata getBroadcastMetadataFromBaseData( 592 BaseData baseData, BluetoothDevice device, int syncHandle, boolean encrypted) { 593 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 594 throw new RuntimeException( 595 "Should never be executed with" 596 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 597 } 598 BluetoothLeBroadcastMetadata.Builder metaData = new BluetoothLeBroadcastMetadata.Builder(); 599 int index = 0; 600 for (BaseData.BaseInformation baseLevel2 : baseData.getLevelTwo()) { 601 BluetoothLeBroadcastSubgroup.Builder subGroup = 602 new BluetoothLeBroadcastSubgroup.Builder(); 603 for (int j = 0; j < baseLevel2.numSubGroups; j++) { 604 BaseData.BaseInformation baseLevel3 = baseData.getLevelThree().get(index++); 605 BluetoothLeBroadcastChannel.Builder channel = 606 new BluetoothLeBroadcastChannel.Builder(); 607 channel.setChannelIndex(baseLevel3.index); 608 channel.setSelected(false); 609 try { 610 channel.setCodecMetadata( 611 BluetoothLeAudioCodecConfigMetadata.fromRawBytes( 612 baseLevel3.codecConfigInfo)); 613 } catch (IllegalArgumentException e) { 614 Log.w(TAG, "Invalid metadata, adding empty data. Error: " + e); 615 channel.setCodecMetadata( 616 BluetoothLeAudioCodecConfigMetadata.fromRawBytes(new byte[0])); 617 } 618 subGroup.addChannel(channel.build()); 619 } 620 byte[] arrayCodecId = baseLevel2.codecId; 621 long codeId = 622 ((long) (arrayCodecId[4] & 0xff)) << 32 623 | (arrayCodecId[3] & 0xff) << 24 624 | (arrayCodecId[2] & 0xff) << 16 625 | (arrayCodecId[1] & 0xff) << 8 626 | (arrayCodecId[0] & 0xff); 627 subGroup.setCodecId(codeId); 628 try { 629 subGroup.setCodecSpecificConfig( 630 BluetoothLeAudioCodecConfigMetadata.fromRawBytes( 631 baseLevel2.codecConfigInfo)); 632 } catch (IllegalArgumentException e) { 633 Log.w(TAG, "Invalid config, adding empty one. Error: " + e); 634 subGroup.setCodecSpecificConfig( 635 BluetoothLeAudioCodecConfigMetadata.fromRawBytes(new byte[0])); 636 } 637 638 try { 639 subGroup.setContentMetadata( 640 BluetoothLeAudioContentMetadata.fromRawBytes(baseLevel2.metaData)); 641 } catch (IllegalArgumentException e) { 642 Log.w(TAG, "Invalid metadata, adding empty one. Error: " + e); 643 subGroup.setContentMetadata( 644 BluetoothLeAudioContentMetadata.fromRawBytes(new byte[0])); 645 } 646 647 metaData.addSubgroup(subGroup.build()); 648 } 649 metaData.setSourceDevice(device, device.getAddressType()); 650 byte[] arrayPresentationDelay = baseData.getLevelOne().presentationDelay; 651 int presentationDelay = 652 (int) 653 ((arrayPresentationDelay[2] & 0xff) << 16 654 | (arrayPresentationDelay[1] & 0xff) << 8 655 | (arrayPresentationDelay[0] & 0xff)); 656 metaData.setPresentationDelayMicros(presentationDelay); 657 PeriodicAdvertisementResult result = 658 mService.getPeriodicAdvertisementResult( 659 device, mService.getBroadcastIdForSyncHandle(syncHandle)); 660 if (result != null) { 661 int broadcastId = result.getBroadcastId(); 662 log("broadcast ID: " + broadcastId); 663 metaData.setBroadcastId(broadcastId); 664 metaData.setSourceAdvertisingSid(result.getAdvSid()); 665 666 PublicBroadcastData pbData = result.getPublicBroadcastData(); 667 if (pbData != null) { 668 metaData.setPublicBroadcast(true); 669 metaData.setAudioConfigQuality(pbData.getAudioConfigQuality()); 670 try { 671 metaData.setPublicBroadcastMetadata( 672 BluetoothLeAudioContentMetadata.fromRawBytes(pbData.getMetadata())); 673 } catch (IllegalArgumentException e) { 674 Log.w(TAG, "Invalid public metadata, adding empty one. Error " + e); 675 metaData.setPublicBroadcastMetadata(null); 676 } 677 } 678 679 String broadcastName = result.getBroadcastName(); 680 if (broadcastName != null) { 681 metaData.setBroadcastName(broadcastName); 682 } 683 } 684 metaData.setEncrypted(encrypted); 685 if (Flags.leaudioBroadcastMonitorSourceSyncStatus()) { 686 // update the rssi value 687 ScanResult scanRes = mService.getCachedBroadcast(result.getBroadcastId()); 688 if (scanRes != null) { 689 metaData.setRssi(scanRes.getRssi()); 690 } 691 } 692 return metaData.build(); 693 } 694 broadcastReceiverState(BluetoothLeBroadcastReceiveState state, int sourceId)695 private void broadcastReceiverState(BluetoothLeBroadcastReceiveState state, int sourceId) { 696 log("broadcastReceiverState: " + mDevice); 697 mService.getCallbacks().notifyReceiveStateChanged(mDevice, sourceId, state); 698 } 699 700 @VisibleForTesting isEmpty(final byte[] data)701 static boolean isEmpty(final byte[] data) { 702 return IntStream.range(0, data.length).parallel().allMatch(i -> data[i] == 0); 703 } 704 processPASyncState(BluetoothLeBroadcastReceiveState recvState)705 private void processPASyncState(BluetoothLeBroadcastReceiveState recvState) { 706 int serviceData = 0; 707 if (recvState == null) { 708 Log.e(TAG, "processPASyncState: recvState is null"); 709 return; 710 } 711 int state = recvState.getPaSyncState(); 712 if (state == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST) { 713 log("Initiate PAST procedure"); 714 PeriodicAdvertisementResult result = 715 mService.getPeriodicAdvertisementResult( 716 recvState.getSourceDevice(), recvState.getBroadcastId()); 717 if (result != null) { 718 int syncHandle = result.getSyncHandle(); 719 log("processPASyncState: syncHandle " + result.getSyncHandle()); 720 if (syncHandle != BassConstants.INVALID_SYNC_HANDLE) { 721 serviceData = 0x000000FF & recvState.getSourceId(); 722 serviceData = serviceData << 8; 723 // advA matches EXT_ADV_ADDRESS 724 // also matches source address (as we would have written) 725 serviceData = 726 serviceData & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS); 727 serviceData = 728 serviceData 729 & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS); 730 log( 731 "Initiate PAST for: " 732 + mDevice 733 + ", syncHandle: " 734 + syncHandle 735 + "serviceData" 736 + serviceData); 737 BluetoothMethodProxy.getInstance() 738 .periodicAdvertisingManagerTransferSync( 739 BassClientPeriodicAdvertisingManager 740 .getPeriodicAdvertisingManager(), 741 mDevice, 742 serviceData, 743 syncHandle); 744 } 745 } else { 746 BluetoothLeBroadcastMetadata currentMetadata = 747 getCurrentBroadcastMetadata(recvState.getSourceId()); 748 if (mService.isLocalBroadcast(currentMetadata)) { 749 int advHandle = currentMetadata.getSourceAdvertisingSid(); 750 serviceData = 0x000000FF & recvState.getSourceId(); 751 serviceData = serviceData << 8; 752 // Address we set in the Source Address can differ from the address in the air 753 serviceData = 754 serviceData | BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS; 755 log( 756 "Initiate local broadcast PAST for: " 757 + mDevice 758 + ", advSID/Handle: " 759 + advHandle 760 + ", serviceData: " 761 + serviceData); 762 BluetoothMethodProxy.getInstance() 763 .periodicAdvertisingManagerTransferSetInfo( 764 BassClientPeriodicAdvertisingManager 765 .getPeriodicAdvertisingManager(), 766 mDevice, 767 serviceData, 768 advHandle, 769 mLocalPeriodicAdvCallback); 770 } else { 771 Log.e(TAG, "There is no valid sync handle for this Source"); 772 } 773 } 774 } 775 } 776 checkAndUpdateBroadcastCode(BluetoothLeBroadcastReceiveState recvState)777 private void checkAndUpdateBroadcastCode(BluetoothLeBroadcastReceiveState recvState) { 778 log("checkAndUpdateBroadcastCode"); 779 // non colocated case, Broadcast PIN should have been updated from lyaer 780 // If there is pending one process it Now 781 if (recvState.getBigEncryptionState() 782 == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED 783 && mSetBroadcastCodePending) { 784 log("Update the Broadcast now"); 785 if (mSetBroadcastPINMetadata != null) { 786 setCurrentBroadcastMetadata(recvState.getSourceId(), mSetBroadcastPINMetadata); 787 } 788 Message m = obtainMessage(BassClientStateMachine.SET_BCAST_CODE); 789 m.obj = recvState; 790 m.arg1 = ARGTYPE_RCVSTATE; 791 sendMessage(m); 792 mSetBroadcastCodePending = false; 793 mSetBroadcastPINMetadata = null; 794 } 795 } 796 parseBroadcastReceiverState(byte[] receiverState)797 private BluetoothLeBroadcastReceiveState parseBroadcastReceiverState(byte[] receiverState) { 798 byte sourceId = 0; 799 if (receiverState.length > 0) { 800 sourceId = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ID_IDX]; 801 } 802 log("processBroadcastReceiverState: receiverState length: " + receiverState.length); 803 804 BluetoothLeBroadcastReceiveState recvState = null; 805 if (receiverState.length == 0 806 || isEmpty(Arrays.copyOfRange(receiverState, 1, receiverState.length - 1))) { 807 byte[] emptyBluetoothDeviceAddress = Utils.getBytesFromAddress("00:00:00:00:00:00"); 808 if (mPendingOperation == REMOVE_BCAST_SOURCE) { 809 recvState = 810 new BluetoothLeBroadcastReceiveState( 811 mPendingSourceId, 812 BluetoothDevice.ADDRESS_TYPE_PUBLIC, // sourceAddressType 813 mAdapterService.getDeviceFromByte( 814 emptyBluetoothDeviceAddress), // sourceDev 815 0, // sourceAdvertisingSid 816 0, // broadcastId 817 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, // paSyncState 818 // bigEncryptionState 819 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, 820 null, // badCode 821 0, // numSubgroups 822 Arrays.asList(new Long[0]), // bisSyncState 823 Arrays.asList( 824 new BluetoothLeAudioContentMetadata[0]) // subgroupMetadata 825 ); 826 } else if (receiverState.length == 0) { 827 if (mBluetoothLeBroadcastReceiveStates != null) { 828 mNextSourceId = (byte) mBluetoothLeBroadcastReceiveStates.size(); 829 } 830 if (mNextSourceId >= mNumOfBroadcastReceiverStates) { 831 Log.e(TAG, "reached the remote supported max SourceInfos"); 832 return null; 833 } 834 mNextSourceId++; 835 recvState = 836 new BluetoothLeBroadcastReceiveState( 837 mNextSourceId, 838 BluetoothDevice.ADDRESS_TYPE_PUBLIC, // sourceAddressType 839 mAdapterService.getDeviceFromByte( 840 emptyBluetoothDeviceAddress), // sourceDev 841 0, // sourceAdvertisingSid 842 0, // broadcastId 843 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, // paSyncState 844 // bigEncryptionState 845 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, 846 null, // badCode 847 0, // numSubgroups 848 Arrays.asList(new Long[0]), // bisSyncState 849 Arrays.asList( 850 new BluetoothLeAudioContentMetadata[0]) // subgroupMetadata 851 ); 852 } 853 } else { 854 byte paSyncState = receiverState[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX]; 855 byte bigEncryptionStatus = receiverState[BassConstants.BCAST_RCVR_STATE_ENC_STATUS_IDX]; 856 byte[] badBroadcastCode = null; 857 int badBroadcastCodeLen = 0; 858 if (bigEncryptionStatus 859 == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE) { 860 badBroadcastCode = new byte[BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE]; 861 System.arraycopy( 862 receiverState, 863 BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX, 864 badBroadcastCode, 865 0, 866 BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE); 867 badBroadcastCodeLen = BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE; 868 } 869 byte numSubGroups = 870 receiverState[ 871 BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX + badBroadcastCodeLen]; 872 int offset = BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX + badBroadcastCodeLen + 1; 873 ArrayList<BluetoothLeAudioContentMetadata> metadataList = 874 new ArrayList<BluetoothLeAudioContentMetadata>(); 875 ArrayList<Long> bisSyncState = new ArrayList<Long>(); 876 for (int i = 0; i < numSubGroups; i++) { 877 byte[] bisSyncIndex = new byte[BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE]; 878 System.arraycopy( 879 receiverState, 880 offset, 881 bisSyncIndex, 882 0, 883 BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE); 884 offset += BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE; 885 bisSyncState.add((long) Utils.byteArrayToInt(bisSyncIndex)); 886 887 int metaDataLength = receiverState[offset++] & 0xFF; 888 if (metaDataLength > 0) { 889 log("metadata of length: " + metaDataLength + "is available"); 890 byte[] metaData = new byte[metaDataLength]; 891 System.arraycopy(receiverState, offset, metaData, 0, metaDataLength); 892 offset += metaDataLength; 893 metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(metaData)); 894 } else { 895 metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(new byte[0])); 896 } 897 } 898 byte[] broadcastIdBytes = new byte[mBroadcastSourceIdLength]; 899 System.arraycopy( 900 receiverState, 901 BassConstants.BCAST_RCVR_STATE_SRC_BCAST_ID_START_IDX, 902 broadcastIdBytes, 903 0, 904 mBroadcastSourceIdLength); 905 int broadcastId = BassUtils.parseBroadcastId(broadcastIdBytes); 906 byte[] sourceAddress = new byte[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE]; 907 System.arraycopy( 908 receiverState, 909 BassConstants.BCAST_RCVR_STATE_SRC_ADDR_START_IDX, 910 sourceAddress, 911 0, 912 BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE); 913 byte sourceAddressType = 914 receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_TYPE_IDX]; 915 BassUtils.reverse(sourceAddress); 916 String address = Utils.getAddressStringFromByte(sourceAddress); 917 BluetoothDevice device = 918 BluetoothAdapter.getDefaultAdapter() 919 .getRemoteLeDevice(address, sourceAddressType); 920 byte sourceAdvSid = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ADV_SID_IDX]; 921 recvState = 922 new BluetoothLeBroadcastReceiveState( 923 sourceId, 924 (int) sourceAddressType, 925 device, 926 sourceAdvSid, 927 broadcastId, 928 (int) paSyncState, 929 (int) bigEncryptionStatus, 930 badBroadcastCode, 931 numSubGroups, 932 bisSyncState, 933 metadataList); 934 } 935 return recvState; 936 } 937 processBroadcastReceiverState( byte[] receiverState, BluetoothGattCharacteristic characteristic)938 private void processBroadcastReceiverState( 939 byte[] receiverState, BluetoothGattCharacteristic characteristic) { 940 log("processBroadcastReceiverState: characteristic:" + characteristic); 941 BluetoothLeBroadcastReceiveState recvState = parseBroadcastReceiverState(receiverState); 942 if (recvState == null) { 943 log("processBroadcastReceiverState: Null recvState"); 944 return; 945 } else if (recvState.getSourceId() == -1) { 946 log("processBroadcastReceiverState: invalid index: " + recvState.getSourceId()); 947 return; 948 } 949 BluetoothLeBroadcastReceiveState oldRecvState = 950 mBluetoothLeBroadcastReceiveStates.get(characteristic.getInstanceId()); 951 if (oldRecvState == null) { 952 log("Initial Read and Populating values"); 953 if (mBluetoothLeBroadcastReceiveStates.size() == mNumOfBroadcastReceiverStates) { 954 Log.e(TAG, "reached the Max SourceInfos"); 955 return; 956 } 957 mBluetoothLeBroadcastReceiveStates.put(characteristic.getInstanceId(), recvState); 958 checkAndUpdateBroadcastCode(recvState); 959 processPASyncState(recvState); 960 } else { 961 log("Updated receiver state: " + recvState); 962 mBluetoothLeBroadcastReceiveStates.replace(characteristic.getInstanceId(), recvState); 963 String emptyBluetoothDevice = "00:00:00:00:00:00"; 964 if (oldRecvState.getSourceDevice() == null 965 || oldRecvState.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) { 966 log("New Source Addition"); 967 removeMessages(CANCEL_PENDING_SOURCE_OPERATION); 968 mService.getCallbacks() 969 .notifySourceAdded( 970 mDevice, recvState, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 971 if (mPendingMetadata != null) { 972 setCurrentBroadcastMetadata(recvState.getSourceId(), mPendingMetadata); 973 mPendingMetadata = null; 974 } 975 checkAndUpdateBroadcastCode(recvState); 976 processPASyncState(recvState); 977 } else { 978 if (recvState.getSourceDevice() == null 979 || recvState.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) { 980 BluetoothDevice removedDevice = oldRecvState.getSourceDevice(); 981 log("sourceInfo removal " + removedDevice); 982 if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 983 cancelActiveSync( 984 mService.getSyncHandleForBroadcastId(recvState.getBroadcastId())); 985 } 986 setCurrentBroadcastMetadata(oldRecvState.getSourceId(), null); 987 if (mPendingSourceToSwitch != null) { 988 // Source remove is triggered by switch source request 989 mService.getCallbacks() 990 .notifySourceRemoved( 991 mDevice, 992 oldRecvState.getSourceId(), 993 BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST); 994 log("Switching to new source"); 995 Message message = obtainMessage(ADD_BCAST_SOURCE); 996 message.obj = mPendingSourceToSwitch; 997 sendMessage(message); 998 mPendingSourceToSwitch = null; 999 } else { 1000 mService.getCallbacks() 1001 .notifySourceRemoved( 1002 mDevice, 1003 oldRecvState.getSourceId(), 1004 BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 1005 } 1006 } else { 1007 log("update to an existing recvState"); 1008 if (mPendingMetadata != null) { 1009 setCurrentBroadcastMetadata(recvState.getSourceId(), mPendingMetadata); 1010 mPendingMetadata = null; 1011 } 1012 removeMessages(CANCEL_PENDING_SOURCE_OPERATION); 1013 mService.getCallbacks() 1014 .notifySourceModified( 1015 mDevice, 1016 recvState.getSourceId(), 1017 BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 1018 checkAndUpdateBroadcastCode(recvState); 1019 processPASyncState(recvState); 1020 1021 if (isPendingRemove(recvState.getSourceId())) { 1022 Message message = obtainMessage(REMOVE_BCAST_SOURCE); 1023 message.arg1 = recvState.getSourceId(); 1024 sendMessage(message); 1025 } 1026 } 1027 } 1028 } 1029 broadcastReceiverState(recvState, recvState.getSourceId()); 1030 } 1031 1032 // Implements callback methods for GATT events that the app cares about. 1033 // For example, connection change and services discovered. 1034 final class GattCallback extends BluetoothGattCallback { 1035 @Override onConnectionStateChange(BluetoothGatt gatt, int status, int newState)1036 public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 1037 boolean isStateChanged = false; 1038 log("onConnectionStateChange : Status=" + status + "newState" + newState); 1039 if (newState == BluetoothProfile.STATE_CONNECTED 1040 && getConnectionState() != BluetoothProfile.STATE_CONNECTED) { 1041 isStateChanged = true; 1042 Log.w(TAG, "Bassclient Connected from Disconnected state: " + mDevice); 1043 if (mService.okToConnect(mDevice)) { 1044 log("Bassclient Connected to: " + mDevice); 1045 if (mBluetoothGatt != null) { 1046 log( 1047 "Attempting to start service discovery:" 1048 + mBluetoothGatt.discoverServices()); 1049 mDiscoveryInitiated = true; 1050 } 1051 } else if (mBluetoothGatt != null) { 1052 // Reject the connection 1053 Log.w(TAG, "Bassclient Connect request rejected: " + mDevice); 1054 mBluetoothGatt.disconnect(); 1055 mBluetoothGatt.close(); 1056 mBluetoothGatt = null; 1057 // force move to disconnected 1058 newState = BluetoothProfile.STATE_DISCONNECTED; 1059 } 1060 } else if (newState == BluetoothProfile.STATE_DISCONNECTED 1061 && getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { 1062 isStateChanged = true; 1063 log("Disconnected from Bass GATT server."); 1064 } 1065 if (isStateChanged) { 1066 Message m = obtainMessage(CONNECTION_STATE_CHANGED); 1067 m.obj = newState; 1068 sendMessage(m); 1069 } 1070 } 1071 1072 @Override onServicesDiscovered(BluetoothGatt gatt, int status)1073 public void onServicesDiscovered(BluetoothGatt gatt, int status) { 1074 log("onServicesDiscovered:" + status); 1075 if (mDiscoveryInitiated) { 1076 mDiscoveryInitiated = false; 1077 if (status == BluetoothGatt.GATT_SUCCESS && mBluetoothGatt != null) { 1078 mBluetoothGatt.requestMtu(BassConstants.BASS_MAX_BYTES); 1079 mMTUChangeRequested = true; 1080 } else { 1081 Log.w( 1082 TAG, 1083 "onServicesDiscovered received: " 1084 + status 1085 + "mBluetoothGatt" 1086 + mBluetoothGatt); 1087 } 1088 } else { 1089 log("remote initiated callback"); 1090 } 1091 } 1092 1093 @Override onCharacteristicRead( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)1094 public void onCharacteristicRead( 1095 BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { 1096 if (status == BluetoothGatt.GATT_SUCCESS 1097 && characteristic.getUuid().equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) { 1098 log("onCharacteristicRead: BASS_BCAST_RECEIVER_STATE: status" + status); 1099 if (characteristic.getValue() == null) { 1100 Log.e(TAG, "Remote receiver state is NULL"); 1101 return; 1102 } 1103 logByteArray( 1104 "Received ", 1105 characteristic.getValue(), 1106 0, 1107 characteristic.getValue().length); 1108 processBroadcastReceiverState(characteristic.getValue(), characteristic); 1109 } 1110 // switch to receiving notifications after initial characteristic read 1111 BluetoothGattDescriptor desc = 1112 characteristic.getDescriptor(BassConstants.CLIENT_CHARACTERISTIC_CONFIG); 1113 if (mBluetoothGatt != null && desc != null) { 1114 log("Setting the value for Desc"); 1115 mBluetoothGatt.setCharacteristicNotification(characteristic, true); 1116 desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 1117 mBluetoothGatt.writeDescriptor(desc); 1118 } else { 1119 Log.w(TAG, "CCC for " + characteristic + "seem to be not present"); 1120 // at least move the SM to stable state 1121 Message m = obtainMessage(GATT_TXN_PROCESSED); 1122 m.arg1 = status; 1123 sendMessage(m); 1124 } 1125 } 1126 1127 @Override onDescriptorWrite( BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)1128 public void onDescriptorWrite( 1129 BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { 1130 // Move the SM to connected so further reads happens 1131 Message m = obtainMessage(GATT_TXN_PROCESSED); 1132 m.arg1 = status; 1133 sendMessage(m); 1134 } 1135 1136 @Override onMtuChanged(BluetoothGatt gatt, int mtu, int status)1137 public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { 1138 if (mMTUChangeRequested && mBluetoothGatt != null) { 1139 acquireAllBassChars(); 1140 mMTUChangeRequested = false; 1141 } else { 1142 log("onMtuChanged is remote initiated trigger, mBluetoothGatt:" + mBluetoothGatt); 1143 } 1144 1145 if (status == BluetoothGatt.GATT_SUCCESS) { 1146 Log.d(TAG, "mtu: " + mtu); 1147 mMaxSingleAttributeWriteValueLen = mtu - ATT_WRITE_CMD_HDR_LEN; 1148 } 1149 } 1150 1151 @Override onCharacteristicChanged( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)1152 public void onCharacteristicChanged( 1153 BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { 1154 if (characteristic.getUuid().equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) { 1155 if (characteristic.getValue() == null) { 1156 Log.e(TAG, "Remote receiver state is NULL"); 1157 return; 1158 } 1159 processBroadcastReceiverState(characteristic.getValue(), characteristic); 1160 } 1161 } 1162 1163 @Override onCharacteristicWrite( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)1164 public void onCharacteristicWrite( 1165 BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { 1166 Message m = obtainMessage(GATT_TXN_PROCESSED); 1167 m.arg1 = status; 1168 sendMessage(m); 1169 } 1170 } 1171 1172 /** Internal periodc Advertising manager callback */ 1173 private final class PACallback extends PeriodicAdvertisingCallback { 1174 @Override onSyncEstablished( int syncHandle, BluetoothDevice device, int advertisingSid, int skip, int timeout, int status)1175 public void onSyncEstablished( 1176 int syncHandle, 1177 BluetoothDevice device, 1178 int advertisingSid, 1179 int skip, 1180 int timeout, 1181 int status) { 1182 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1183 throw new RuntimeException( 1184 "Should never be executed with" 1185 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 1186 } 1187 log( 1188 "onSyncEstablished syncHandle: " 1189 + syncHandle 1190 + ", device: " 1191 + device 1192 + ", advertisingSid: " 1193 + advertisingSid 1194 + ", skip: " 1195 + skip 1196 + ", timeout: " 1197 + timeout 1198 + ", status: " 1199 + status); 1200 if (status == BluetoothGatt.GATT_SUCCESS) { 1201 // updates syncHandle, advSid 1202 // set other fields as invalid or null 1203 mService.updatePeriodicAdvertisementResultMap( 1204 device, 1205 BassConstants.INVALID_ADV_ADDRESS_TYPE, 1206 syncHandle, 1207 advertisingSid, 1208 BassConstants.INVALID_ADV_INTERVAL, 1209 BassConstants.INVALID_BROADCAST_ID, 1210 null, 1211 null); 1212 removeMessages(PSYNC_ACTIVE_TIMEOUT); 1213 // Refresh sync timeout if another source synced 1214 sendMessageDelayed(PSYNC_ACTIVE_TIMEOUT, BassConstants.PSYNC_ACTIVE_TIMEOUT_MS); 1215 mService.addActiveSyncedSource(mDevice, syncHandle); 1216 1217 // update valid sync handle in mPeriodicAdvCallbacksMap 1218 if (mPeriodicAdvCallbacksMap.containsKey(BassConstants.INVALID_SYNC_HANDLE)) { 1219 PeriodicAdvertisingCallback paCb = 1220 mPeriodicAdvCallbacksMap.get(BassConstants.INVALID_SYNC_HANDLE); 1221 mPeriodicAdvCallbacksMap.put(syncHandle, paCb); 1222 mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE); 1223 } 1224 mFirstTimeBisDiscoveryMap.put(syncHandle, true); 1225 if (mPendingSourceToAdd != null) { 1226 Message message = obtainMessage(ADD_BCAST_SOURCE); 1227 message.obj = mPendingSourceToAdd; 1228 sendMessage(message); 1229 } 1230 } else { 1231 log("failed to sync to PA: " + mPASyncRetryCounter); 1232 mAutoTriggered = false; 1233 // remove failed sync handle 1234 mPeriodicAdvCallbacksMap.remove(BassConstants.INVALID_SYNC_HANDLE); 1235 } 1236 mPendingSourceToAdd = null; 1237 if (!mSourceSyncRequestsQueue.isEmpty()) { 1238 log("Processing the next source to sync"); 1239 Pair<ScanResult, Integer> queuedSourceToSync = mSourceSyncRequestsQueue.remove(0); 1240 Message msg = obtainMessage(SELECT_BCAST_SOURCE); 1241 msg.obj = queuedSourceToSync.first; 1242 msg.arg1 = queuedSourceToSync.second; 1243 sendMessage(msg); 1244 } 1245 } 1246 1247 @Override onPeriodicAdvertisingReport(PeriodicAdvertisingReport report)1248 public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { 1249 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1250 throw new RuntimeException( 1251 "Should never be executed with" 1252 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 1253 } 1254 log("onPeriodicAdvertisingReport"); 1255 Boolean first = mFirstTimeBisDiscoveryMap.get(report.getSyncHandle()); 1256 // Parse the BIS indices from report's service data 1257 if (first != null && first.booleanValue() == true) { 1258 parseScanRecord(report.getSyncHandle(), report.getData()); 1259 mFirstTimeBisDiscoveryMap.put(report.getSyncHandle(), false); 1260 } 1261 } 1262 1263 @Override onSyncLost(int syncHandle)1264 public void onSyncLost(int syncHandle) { 1265 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1266 throw new RuntimeException( 1267 "Should never be executed with" 1268 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 1269 } 1270 log("OnSyncLost" + syncHandle); 1271 if (Flags.leaudioBroadcastMonitorSourceSyncStatus()) { 1272 int broadcastId = mService.getBroadcastIdForSyncHandle(syncHandle); 1273 if (broadcastId != BassConstants.INVALID_BROADCAST_ID) { 1274 log("Notify broadcast source lost, broadcast id: " + broadcastId); 1275 mService.getCallbacks().notifySourceLost(broadcastId); 1276 } 1277 } 1278 cancelActiveSync(syncHandle); 1279 } 1280 1281 @Override onBigInfoAdvertisingReport(int syncHandle, boolean encrypted)1282 public void onBigInfoAdvertisingReport(int syncHandle, boolean encrypted) { 1283 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1284 throw new RuntimeException( 1285 "Should never be executed with" 1286 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine flag"); 1287 } 1288 log( 1289 "onBIGInfoAdvertisingReport: syncHandle=" 1290 + syncHandle 1291 + ", encrypted =" 1292 + encrypted); 1293 BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle); 1294 if (srcDevice == null) { 1295 log("No device found."); 1296 return; 1297 } 1298 PeriodicAdvertisementResult result = 1299 mService.getPeriodicAdvertisementResult( 1300 srcDevice, mService.getBroadcastIdForSyncHandle(syncHandle)); 1301 if (result == null) { 1302 log("No PA record found"); 1303 return; 1304 } 1305 if (!result.isNotified()) { 1306 result.setNotified(true); 1307 BaseData baseData = mService.getBase(syncHandle); 1308 if (baseData == null) { 1309 log("No BaseData found"); 1310 return; 1311 } 1312 BluetoothLeBroadcastMetadata metaData = 1313 getBroadcastMetadataFromBaseData( 1314 baseData, srcDevice, syncHandle, encrypted); 1315 log("Notify broadcast source found"); 1316 mService.getCallbacks().notifySourceFound(metaData); 1317 } 1318 } 1319 1320 @Override onSyncTransferred(BluetoothDevice device, int status)1321 public void onSyncTransferred(BluetoothDevice device, int status) { 1322 log("onSyncTransferred: device=" + device + ", status =" + status); 1323 } 1324 } 1325 1326 /** 1327 * Connects to the GATT server of the device. 1328 * 1329 * @return {@code true} if it successfully connects to the GATT server. 1330 */ 1331 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) connectGatt(Boolean autoConnect)1332 public boolean connectGatt(Boolean autoConnect) { 1333 if (mGattCallback == null) { 1334 mGattCallback = new GattCallback(); 1335 } 1336 1337 BluetoothGatt gatt = 1338 mDevice.connectGatt( 1339 mService, 1340 autoConnect, 1341 mGattCallback, 1342 BluetoothDevice.TRANSPORT_LE, 1343 (BluetoothDevice.PHY_LE_1M_MASK 1344 | BluetoothDevice.PHY_LE_2M_MASK 1345 | BluetoothDevice.PHY_LE_CODED_MASK), 1346 null); 1347 1348 if (gatt != null) { 1349 mBluetoothGatt = new BluetoothGattTestableWrapper(gatt); 1350 } 1351 1352 return mBluetoothGatt != null; 1353 } 1354 1355 /** getAllSources */ getAllSources()1356 public List<BluetoothLeBroadcastReceiveState> getAllSources() { 1357 List list = new ArrayList(mBluetoothLeBroadcastReceiveStates.values()); 1358 return list; 1359 } 1360 acquireAllBassChars()1361 void acquireAllBassChars() { 1362 clearCharsCache(); 1363 BluetoothGattService service = null; 1364 if (mBluetoothGatt != null) { 1365 log("getting Bass Service handle"); 1366 service = mBluetoothGatt.getService(BassConstants.BASS_UUID); 1367 } 1368 if (service == null) { 1369 log("acquireAllBassChars: BASS service not found"); 1370 return; 1371 } 1372 log("found BASS_SERVICE"); 1373 List<BluetoothGattCharacteristic> allChars = service.getCharacteristics(); 1374 int numOfChars = allChars.size(); 1375 mNumOfBroadcastReceiverStates = numOfChars - 1; 1376 log("Total number of chars" + numOfChars); 1377 for (int i = 0; i < allChars.size(); i++) { 1378 if (allChars.get(i).getUuid().equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) { 1379 int properties = allChars.get(i).getProperties(); 1380 1381 if (((properties & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) 1382 || ((properties & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0)) { 1383 Log.w( 1384 TAG, 1385 "Broadcast Audio Scan Control Point characteristic has invalid " 1386 + "properties!"); 1387 } else { 1388 mBroadcastScanControlPoint = allChars.get(i); 1389 log("Index of ScanCtrlPoint:" + i); 1390 } 1391 } else { 1392 log("Reading " + i + "th ReceiverState"); 1393 mBroadcastCharacteristics.add(allChars.get(i)); 1394 Message m = obtainMessage(READ_BASS_CHARACTERISTICS); 1395 m.obj = allChars.get(i); 1396 sendMessage(m); 1397 } 1398 } 1399 } 1400 clearCharsCache()1401 void clearCharsCache() { 1402 if (mBroadcastCharacteristics != null) { 1403 mBroadcastCharacteristics.clear(); 1404 } 1405 if (mBroadcastScanControlPoint != null) { 1406 mBroadcastScanControlPoint = null; 1407 } 1408 mNumOfBroadcastReceiverStates = 0; 1409 if (mBluetoothLeBroadcastReceiveStates != null) { 1410 mBluetoothLeBroadcastReceiveStates.clear(); 1411 } 1412 mPendingOperation = -1; 1413 mPendingMetadata = null; 1414 mCurrentMetadata.clear(); 1415 mPendingRemove.clear(); 1416 } 1417 1418 @VisibleForTesting 1419 class Disconnected extends State { 1420 @Override enter()1421 public void enter() { 1422 log( 1423 "Enter Disconnected(" 1424 + mDevice 1425 + "): " 1426 + messageWhatToString(getCurrentMessage().what)); 1427 clearCharsCache(); 1428 mNextSourceId = 0; 1429 removeDeferredMessages(DISCONNECT); 1430 if (mLastConnectionState == -1) { 1431 log("no Broadcast of initial profile state "); 1432 } else { 1433 broadcastConnectionState( 1434 mDevice, mLastConnectionState, BluetoothProfile.STATE_DISCONNECTED); 1435 if (mLastConnectionState != BluetoothProfile.STATE_DISCONNECTED) { 1436 // Reconnect in background if not disallowed by the service 1437 if (mService.okToConnect(mDevice) && mAllowReconnect) { 1438 connectGatt(true); 1439 } 1440 } 1441 } 1442 } 1443 1444 @Override exit()1445 public void exit() { 1446 log( 1447 "Exit Disconnected(" 1448 + mDevice 1449 + "): " 1450 + messageWhatToString(getCurrentMessage().what)); 1451 mLastConnectionState = BluetoothProfile.STATE_DISCONNECTED; 1452 } 1453 1454 @Override processMessage(Message message)1455 public boolean processMessage(Message message) { 1456 log( 1457 "Disconnected process message(" 1458 + mDevice 1459 + "): " 1460 + messageWhatToString(message.what)); 1461 switch (message.what) { 1462 case CONNECT: 1463 log("Connecting to " + mDevice); 1464 if (mBluetoothGatt != null) { 1465 Log.d(TAG, "clear off, pending wl connection"); 1466 mBluetoothGatt.disconnect(); 1467 mBluetoothGatt.close(); 1468 mBluetoothGatt = null; 1469 } 1470 mAllowReconnect = true; 1471 if (connectGatt(mIsAllowedList)) { 1472 transitionTo(mConnecting); 1473 } else { 1474 Log.e(TAG, "Disconnected: error connecting to " + mDevice); 1475 } 1476 break; 1477 case DISCONNECT: 1478 // Disconnect if there's an ongoing background connection 1479 mAllowReconnect = false; 1480 if (mBluetoothGatt != null) { 1481 log("Cancelling the background connection to " + mDevice); 1482 mBluetoothGatt.disconnect(); 1483 mBluetoothGatt.close(); 1484 mBluetoothGatt = null; 1485 } else { 1486 Log.d(TAG, "Disconnected: DISCONNECT ignored: " + mDevice); 1487 } 1488 break; 1489 case CONNECTION_STATE_CHANGED: 1490 int state = (int) message.obj; 1491 Log.w(TAG, "connection state changed:" + state); 1492 if (state == BluetoothProfile.STATE_CONNECTED) { 1493 log("remote/wl connection"); 1494 transitionTo(mConnected); 1495 } else { 1496 Log.w(TAG, "Disconnected: Connection failed to " + mDevice); 1497 } 1498 break; 1499 case PSYNC_ACTIVE_TIMEOUT: 1500 cancelActiveSync(null); 1501 break; 1502 default: 1503 log("DISCONNECTED: not handled message:" + message.what); 1504 return NOT_HANDLED; 1505 } 1506 return HANDLED; 1507 } 1508 } 1509 1510 @VisibleForTesting 1511 class Connecting extends State { 1512 @Override enter()1513 public void enter() { 1514 log( 1515 "Enter Connecting(" 1516 + mDevice 1517 + "): " 1518 + messageWhatToString(getCurrentMessage().what)); 1519 sendMessageDelayed(CONNECT_TIMEOUT, mDevice, mConnectTimeoutMs); 1520 broadcastConnectionState( 1521 mDevice, mLastConnectionState, BluetoothProfile.STATE_CONNECTING); 1522 } 1523 1524 @Override exit()1525 public void exit() { 1526 log( 1527 "Exit Connecting(" 1528 + mDevice 1529 + "): " 1530 + messageWhatToString(getCurrentMessage().what)); 1531 mLastConnectionState = BluetoothProfile.STATE_CONNECTING; 1532 removeMessages(CONNECT_TIMEOUT); 1533 } 1534 1535 @Override processMessage(Message message)1536 public boolean processMessage(Message message) { 1537 log( 1538 "Connecting process message(" 1539 + mDevice 1540 + "): " 1541 + messageWhatToString(message.what)); 1542 switch (message.what) { 1543 case CONNECT: 1544 log("Already Connecting to " + mDevice); 1545 log("Ignore this connection request " + mDevice); 1546 break; 1547 case DISCONNECT: 1548 Log.w(TAG, "Connecting: DISCONNECT deferred: " + mDevice); 1549 deferMessage(message); 1550 break; 1551 case READ_BASS_CHARACTERISTICS: 1552 Log.w(TAG, "defer READ_BASS_CHARACTERISTICS requested!: " + mDevice); 1553 deferMessage(message); 1554 break; 1555 case CONNECTION_STATE_CHANGED: 1556 int state = (int) message.obj; 1557 Log.w(TAG, "Connecting: connection state changed:" + state); 1558 if (state == BluetoothProfile.STATE_CONNECTED) { 1559 transitionTo(mConnected); 1560 } else { 1561 Log.w(TAG, "Connection failed to " + mDevice); 1562 resetBluetoothGatt(); 1563 transitionTo(mDisconnected); 1564 } 1565 break; 1566 case CONNECT_TIMEOUT: 1567 Log.w(TAG, "CONNECT_TIMEOUT"); 1568 BluetoothDevice device = (BluetoothDevice) message.obj; 1569 if (!mDevice.equals(device)) { 1570 Log.e(TAG, "Unknown device timeout " + device); 1571 break; 1572 } 1573 resetBluetoothGatt(); 1574 transitionTo(mDisconnected); 1575 break; 1576 case PSYNC_ACTIVE_TIMEOUT: 1577 deferMessage(message); 1578 break; 1579 default: 1580 log("CONNECTING: not handled message:" + message.what); 1581 return NOT_HANDLED; 1582 } 1583 return HANDLED; 1584 } 1585 } 1586 getBisSyncFromChannelPreference(List<BluetoothLeBroadcastChannel> channels)1587 private static int getBisSyncFromChannelPreference(List<BluetoothLeBroadcastChannel> channels) { 1588 int bisSync = 0; 1589 for (BluetoothLeBroadcastChannel channel : channels) { 1590 if (channel.isSelected()) { 1591 if (channel.getChannelIndex() == 0) { 1592 Log.e(TAG, "getBisSyncFromChannelPreference: invalid channel index=0"); 1593 continue; 1594 } 1595 bisSync |= 1 << (channel.getChannelIndex() - 1); 1596 } 1597 } 1598 1599 return bisSync; 1600 } 1601 convertMetadataToAddSourceByteArray(BluetoothLeBroadcastMetadata metaData)1602 private byte[] convertMetadataToAddSourceByteArray(BluetoothLeBroadcastMetadata metaData) { 1603 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 1604 BluetoothDevice advSource = metaData.getSourceDevice(); 1605 1606 // Opcode 1607 stream.write(OPCODE_ADD_SOURCE); 1608 1609 // Advertiser_Address_Type 1610 stream.write(metaData.getSourceAddressType()); 1611 1612 // Advertiser_Address 1613 byte[] bcastSourceAddr = Utils.getBytesFromAddress(advSource.getAddress()); 1614 BassUtils.reverse(bcastSourceAddr); 1615 stream.write(bcastSourceAddr, 0, 6); 1616 1617 // Advertising_SID 1618 stream.write(metaData.getSourceAdvertisingSid()); 1619 1620 // Broadcast_ID 1621 stream.write(metaData.getBroadcastId() & 0x00000000000000FF); 1622 stream.write((metaData.getBroadcastId() & 0x000000000000FF00) >>> 8); 1623 stream.write((metaData.getBroadcastId() & 0x0000000000FF0000) >>> 16); 1624 1625 // PA_Sync 1626 if (mDefNoPAS) { 1627 // Synchronize to PA – PAST not available 1628 stream.write(0x02); 1629 } else { 1630 // Synchronize to PA – PAST available 1631 stream.write(0x01); 1632 } 1633 1634 // PA_Interval 1635 stream.write((metaData.getPaSyncInterval() & 0x00000000000000FF)); 1636 stream.write((metaData.getPaSyncInterval() & 0x000000000000FF00) >>> 8); 1637 1638 // Num_Subgroups 1639 List<BluetoothLeBroadcastSubgroup> subGroups = metaData.getSubgroups(); 1640 stream.write(metaData.getSubgroups().size()); 1641 1642 for (BluetoothLeBroadcastSubgroup subGroup : subGroups) { 1643 // BIS_Sync 1644 int bisSync = getBisSyncFromChannelPreference(subGroup.getChannels()); 1645 if (bisSync == 0) { 1646 bisSync = 0xFFFFFFFF; 1647 } 1648 stream.write(bisSync & 0x00000000000000FF); 1649 stream.write((bisSync & 0x000000000000FF00) >>> 8); 1650 stream.write((bisSync & 0x0000000000FF0000) >>> 16); 1651 stream.write((bisSync & 0x00000000FF000000) >>> 24); 1652 1653 // Metadata_Length 1654 BluetoothLeAudioContentMetadata metadata = subGroup.getContentMetadata(); 1655 stream.write(metadata.getRawMetadata().length); 1656 1657 // Metadata 1658 stream.write(metadata.getRawMetadata(), 0, metadata.getRawMetadata().length); 1659 } 1660 1661 byte[] res = stream.toByteArray(); 1662 BassUtils.printByteArray(res); 1663 return res; 1664 } 1665 convertBroadcastMetadataToUpdateSourceByteArray( int sourceId, BluetoothLeBroadcastMetadata metaData, int paSync)1666 private byte[] convertBroadcastMetadataToUpdateSourceByteArray( 1667 int sourceId, BluetoothLeBroadcastMetadata metaData, int paSync) { 1668 BluetoothLeBroadcastReceiveState existingState = 1669 getBroadcastReceiveStateForSourceId(sourceId); 1670 if (existingState == null) { 1671 log("no existing SI for update source op"); 1672 return null; 1673 } 1674 List<BluetoothLeBroadcastSubgroup> subGroups = metaData.getSubgroups(); 1675 byte numSubGroups = (byte) subGroups.size(); 1676 byte[] res = new byte[UPDATE_SOURCE_FIXED_LENGTH + numSubGroups * 5]; 1677 int offset = 0; 1678 // Opcode 1679 res[offset++] = OPCODE_UPDATE_SOURCE; 1680 // Source_ID 1681 res[offset++] = (byte) sourceId; 1682 // PA_Sync 1683 if (paSync != BassConstants.INVALID_PA_SYNC_VALUE) { 1684 res[offset++] = (byte) paSync; 1685 } else if (existingState.getPaSyncState() 1686 == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED) { 1687 res[offset++] = (byte) (0x01); 1688 } else { 1689 res[offset++] = (byte) 0x00; 1690 } 1691 // PA_Interval 1692 res[offset++] = (byte) 0xFF; 1693 res[offset++] = (byte) 0xFF; 1694 // Num_Subgroups 1695 res[offset++] = numSubGroups; 1696 1697 for (BluetoothLeBroadcastSubgroup subGroup : subGroups) { 1698 int bisIndexValue; 1699 if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) { 1700 bisIndexValue = 0; 1701 } else if (paSync == BassConstants.PA_SYNC_PAST_AVAILABLE 1702 || paSync == BassConstants.PA_SYNC_PAST_NOT_AVAILABLE) { 1703 bisIndexValue = getBisSyncFromChannelPreference(subGroup.getChannels()); 1704 1705 // Let sink decide to which BIS sync if there is no channel preference 1706 if (bisIndexValue == 0) { 1707 bisIndexValue = 0xFFFFFFFF; 1708 } 1709 } else { 1710 bisIndexValue = 1711 existingState.getBisSyncState().get(subGroups.indexOf(subGroup)).intValue(); 1712 } 1713 log("UPDATE_BCAST_SOURCE: bisIndexValue : " + bisIndexValue); 1714 // BIS_Sync 1715 res[offset++] = (byte) (bisIndexValue & 0x00000000000000FF); 1716 res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00) >>> 8); 1717 res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000) >>> 16); 1718 res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000) >>> 24); 1719 // Metadata_Length; On Modify source, don't update any Metadata 1720 res[offset++] = 0; 1721 } 1722 log("UPDATE_BCAST_SOURCE in Bytes"); 1723 BassUtils.printByteArray(res); 1724 return res; 1725 } 1726 convertRecvStateToSetBroadcastCodeByteArray( BluetoothLeBroadcastReceiveState recvState)1727 private byte[] convertRecvStateToSetBroadcastCodeByteArray( 1728 BluetoothLeBroadcastReceiveState recvState) { 1729 byte[] res = new byte[BassConstants.PIN_CODE_CMD_LEN]; 1730 // Opcode 1731 res[0] = OPCODE_SET_BCAST_PIN; 1732 // Source_ID 1733 res[1] = (byte) recvState.getSourceId(); 1734 log( 1735 "convertRecvStateToSetBroadcastCodeByteArray: Source device : " 1736 + recvState.getSourceDevice()); 1737 BluetoothLeBroadcastMetadata metaData = 1738 getCurrentBroadcastMetadata(recvState.getSourceId()); 1739 if (metaData == null) { 1740 Log.e(TAG, "Fail to find broadcast source, sourceId = " + recvState.getSourceId()); 1741 return null; 1742 } 1743 // Broadcast Code 1744 byte[] actualPIN = metaData.getBroadcastCode(); 1745 if (actualPIN == null) { 1746 Log.e(TAG, "actual PIN is null"); 1747 return null; 1748 } else { 1749 log("byte array broadcast Code:" + Arrays.toString(actualPIN)); 1750 log("pinLength:" + actualPIN.length); 1751 // Broadcast_Code, Fill the PIN code in the Last Position 1752 // This effectively adds padding zeros to MSB positions when the broadcast code 1753 // is shorter than 16 octets, skip the first 2 bytes for opcode and source_id. 1754 System.arraycopy(actualPIN, 0, res, 2, actualPIN.length); 1755 log("SET_BCAST_PIN in Bytes"); 1756 BassUtils.printByteArray(res); 1757 } 1758 return res; 1759 } 1760 isItRightTimeToUpdateBroadcastPin(byte sourceId)1761 private boolean isItRightTimeToUpdateBroadcastPin(byte sourceId) { 1762 Collection<BluetoothLeBroadcastReceiveState> recvStates = 1763 mBluetoothLeBroadcastReceiveStates.values(); 1764 Iterator<BluetoothLeBroadcastReceiveState> iterator = recvStates.iterator(); 1765 boolean retval = false; 1766 if (mForceSB) { 1767 log("force SB is set"); 1768 return true; 1769 } 1770 while (iterator.hasNext()) { 1771 BluetoothLeBroadcastReceiveState state = iterator.next(); 1772 if (state == null) { 1773 log("Source state is null"); 1774 continue; 1775 } 1776 if (sourceId == state.getSourceId() 1777 && state.getBigEncryptionState() 1778 == BluetoothLeBroadcastReceiveState 1779 .BIG_ENCRYPTION_STATE_CODE_REQUIRED) { 1780 retval = true; 1781 break; 1782 } 1783 } 1784 log("IsItRightTimeToUpdateBroadcastPIN returning:" + retval); 1785 return retval; 1786 } 1787 1788 @VisibleForTesting 1789 class Connected extends State { 1790 @Override enter()1791 public void enter() { 1792 log( 1793 "Enter Connected(" 1794 + mDevice 1795 + "): " 1796 + messageWhatToString(getCurrentMessage().what)); 1797 removeDeferredMessages(CONNECT); 1798 if (mLastConnectionState != BluetoothProfile.STATE_CONNECTED) { 1799 broadcastConnectionState( 1800 mDevice, mLastConnectionState, BluetoothProfile.STATE_CONNECTED); 1801 } 1802 } 1803 1804 @Override exit()1805 public void exit() { 1806 log( 1807 "Exit Connected(" 1808 + mDevice 1809 + "): " 1810 + messageWhatToString(getCurrentMessage().what)); 1811 mLastConnectionState = BluetoothProfile.STATE_CONNECTED; 1812 } 1813 writeBassControlPoint(byte[] value)1814 private void writeBassControlPoint(byte[] value) { 1815 if (value.length > mMaxSingleAttributeWriteValueLen) { 1816 mBroadcastScanControlPoint.setWriteType( 1817 BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); 1818 } else { 1819 mBroadcastScanControlPoint.setWriteType( 1820 BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE); 1821 } 1822 1823 mBroadcastScanControlPoint.setValue(value); 1824 mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); 1825 } 1826 1827 @Override processMessage(Message message)1828 public boolean processMessage(Message message) { 1829 log("Connected process message(" + mDevice + "): " + messageWhatToString(message.what)); 1830 BluetoothLeBroadcastMetadata metaData; 1831 switch (message.what) { 1832 case CONNECT: 1833 Log.w(TAG, "Connected: CONNECT ignored: " + mDevice); 1834 break; 1835 case DISCONNECT: 1836 log("Disconnecting from " + mDevice); 1837 mAllowReconnect = false; 1838 if (mBluetoothGatt != null) { 1839 mService.handleDeviceDisconnection(mDevice, true); 1840 mBluetoothGatt.disconnect(); 1841 mBluetoothGatt.close(); 1842 mBluetoothGatt = null; 1843 if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1844 cancelActiveSync(null); 1845 } 1846 transitionTo(mDisconnected); 1847 } else { 1848 log("mBluetoothGatt is null"); 1849 } 1850 break; 1851 case CONNECTION_STATE_CHANGED: 1852 int state = (int) message.obj; 1853 Log.w(TAG, "Connected:connection state changed:" + state); 1854 if (state == BluetoothProfile.STATE_CONNECTED) { 1855 Log.w(TAG, "device is already connected to Bass" + mDevice); 1856 } else { 1857 Log.w(TAG, "unexpected disconnected from " + mDevice); 1858 mService.handleDeviceDisconnection(mDevice, false); 1859 resetBluetoothGatt(); 1860 if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1861 cancelActiveSync(null); 1862 } 1863 transitionTo(mDisconnected); 1864 } 1865 break; 1866 case READ_BASS_CHARACTERISTICS: 1867 BluetoothGattCharacteristic characteristic = 1868 (BluetoothGattCharacteristic) message.obj; 1869 if (mBluetoothGatt != null) { 1870 mBluetoothGatt.readCharacteristic(characteristic); 1871 transitionTo(mConnectedProcessing); 1872 } else { 1873 Log.e(TAG, "READ_BASS_CHARACTERISTICS is ignored, Gatt handle is null"); 1874 } 1875 break; 1876 case START_SCAN_OFFLOAD: 1877 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 1878 writeBassControlPoint(REMOTE_SCAN_START); 1879 mPendingOperation = message.what; 1880 transitionTo(mConnectedProcessing); 1881 } else { 1882 log("no Bluetooth Gatt handle, may need to fetch write"); 1883 } 1884 break; 1885 case STOP_SCAN_OFFLOAD: 1886 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 1887 writeBassControlPoint(REMOTE_SCAN_STOP); 1888 mPendingOperation = message.what; 1889 transitionTo(mConnectedProcessing); 1890 } else { 1891 log("no Bluetooth Gatt handle, may need to fetch write"); 1892 } 1893 break; 1894 case SELECT_BCAST_SOURCE: 1895 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1896 throw new RuntimeException( 1897 "Should never be executed with" 1898 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine" 1899 + " flag"); 1900 } 1901 ScanResult scanRes = (ScanResult) message.obj; 1902 boolean auto = ((int) message.arg1) == BassConstants.AUTO; 1903 // check if invalid sync handle exists indicating a pending sync request 1904 if (mPeriodicAdvCallbacksMap.containsKey(BassConstants.INVALID_SYNC_HANDLE)) { 1905 log( 1906 "SELECT_BCAST_SOURCE queued due to waiting for a previous sync" 1907 + " response"); 1908 mSourceSyncRequestsQueue.add( 1909 new Pair<ScanResult, Integer>(scanRes, message.arg1)); 1910 } else { 1911 selectSource(scanRes, auto); 1912 } 1913 break; 1914 case REACHED_MAX_SOURCE_LIMIT: 1915 if (Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1916 throw new RuntimeException( 1917 "Should never be executed with" 1918 + " leaudioBroadcastExtractPeriodicScannerFromStateMachine" 1919 + " flag"); 1920 } 1921 int handle = message.arg1; 1922 cancelActiveSync(handle); 1923 break; 1924 case SWITCH_BCAST_SOURCE: 1925 metaData = (BluetoothLeBroadcastMetadata) message.obj; 1926 int sourceIdToRemove = message.arg1; 1927 // Save pending source to be added once existing source got removed 1928 mPendingSourceToSwitch = metaData; 1929 // Remove the source first 1930 BluetoothLeBroadcastMetadata metaDataToUpdate = 1931 getCurrentBroadcastMetadata(sourceIdToRemove); 1932 if (metaDataToUpdate != null && isSyncedToTheSource(sourceIdToRemove)) { 1933 log("SWITCH_BCAST_SOURCE force source to lost PA sync"); 1934 Message msg = obtainMessage(UPDATE_BCAST_SOURCE); 1935 msg.arg1 = sourceIdToRemove; 1936 msg.arg2 = BassConstants.PA_SYNC_DO_NOT_SYNC; 1937 msg.obj = metaDataToUpdate; 1938 /* Pending remove set. Remove source once not synchronized to PA */ 1939 sendMessage(msg); 1940 } else { 1941 Message msg = obtainMessage(REMOVE_BCAST_SOURCE); 1942 msg.arg1 = sourceIdToRemove; 1943 sendMessage(msg); 1944 } 1945 break; 1946 case ADD_BCAST_SOURCE: 1947 metaData = (BluetoothLeBroadcastMetadata) message.obj; 1948 1949 if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 1950 List<Integer> activeSyncedSrc = mService.getActiveSyncedSources(mDevice); 1951 BluetoothDevice sourceDevice = metaData.getSourceDevice(); 1952 if (!mService.isLocalBroadcast(metaData) 1953 && (activeSyncedSrc == null 1954 || !activeSyncedSrc.contains( 1955 mService.getSyncHandleForBroadcastId( 1956 metaData.getBroadcastId())))) { 1957 log("Adding inactive source: " + sourceDevice); 1958 int broadcastId = metaData.getBroadcastId(); 1959 if (broadcastId != BassConstants.INVALID_BROADCAST_ID 1960 && mService.getCachedBroadcast(broadcastId) != null) { 1961 // If the source has been synced before, try to re-sync(auto/true) 1962 // with the source by previously cached scan result 1963 Message msg = obtainMessage(SELECT_BCAST_SOURCE); 1964 msg.obj = mService.getCachedBroadcast(broadcastId); 1965 msg.arg1 = BassConstants.AUTO; 1966 sendMessage(msg); 1967 mPendingSourceToAdd = metaData; 1968 } else { 1969 mService.getCallbacks() 1970 .notifySourceAddFailed( 1971 mDevice, 1972 metaData, 1973 BluetoothStatusCodes.ERROR_UNKNOWN); 1974 } 1975 break; 1976 } 1977 } 1978 1979 byte[] addSourceInfo = convertMetadataToAddSourceByteArray(metaData); 1980 if (addSourceInfo == null) { 1981 Log.e(TAG, "add source: source Info is NULL"); 1982 break; 1983 } 1984 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 1985 writeBassControlPoint(addSourceInfo); 1986 mPendingOperation = message.what; 1987 mPendingMetadata = metaData; 1988 if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) { 1989 mSetBroadcastCodePending = true; 1990 } 1991 transitionTo(mConnectedProcessing); 1992 sendMessageDelayed( 1993 GATT_TXN_TIMEOUT, 1994 ADD_BCAST_SOURCE, 1995 BassConstants.GATT_TXN_TIMEOUT_MS); 1996 sendMessageDelayed( 1997 CANCEL_PENDING_SOURCE_OPERATION, 1998 metaData.getBroadcastCode(), 1999 BassConstants.SOURCE_OPERATION_TIMEOUT_MS); 2000 } else { 2001 Log.e(TAG, "ADD_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal"); 2002 mService.getCallbacks() 2003 .notifySourceAddFailed( 2004 mDevice, metaData, BluetoothStatusCodes.ERROR_UNKNOWN); 2005 } 2006 break; 2007 case UPDATE_BCAST_SOURCE: 2008 metaData = (BluetoothLeBroadcastMetadata) message.obj; 2009 int sourceId = message.arg1; 2010 int paSync = message.arg2; 2011 log("Updating Broadcast source: " + metaData); 2012 byte[] updateSourceInfo = 2013 convertBroadcastMetadataToUpdateSourceByteArray( 2014 sourceId, metaData, paSync); 2015 if (updateSourceInfo == null) { 2016 Log.e(TAG, "update source: source Info is NULL"); 2017 break; 2018 } 2019 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 2020 writeBassControlPoint(updateSourceInfo); 2021 mPendingOperation = message.what; 2022 mPendingSourceId = (byte) sourceId; 2023 if (paSync == BassConstants.PA_SYNC_DO_NOT_SYNC) { 2024 setPendingRemove(sourceId, true); 2025 } 2026 if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) { 2027 mSetBroadcastCodePending = true; 2028 } 2029 mPendingMetadata = metaData; 2030 transitionTo(mConnectedProcessing); 2031 sendMessageDelayed( 2032 GATT_TXN_TIMEOUT, 2033 UPDATE_BCAST_SOURCE, 2034 BassConstants.GATT_TXN_TIMEOUT_MS); 2035 sendMessageDelayed( 2036 CANCEL_PENDING_SOURCE_OPERATION, 2037 metaData.getBroadcastCode(), 2038 BassConstants.SOURCE_OPERATION_TIMEOUT_MS); 2039 } else { 2040 Log.e(TAG, "UPDATE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal"); 2041 mService.getCallbacks() 2042 .notifySourceModifyFailed( 2043 mDevice, sourceId, BluetoothStatusCodes.ERROR_UNKNOWN); 2044 } 2045 break; 2046 case SET_BCAST_CODE: 2047 int argType = message.arg1; 2048 mSetBroadcastCodePending = false; 2049 BluetoothLeBroadcastReceiveState recvState = null; 2050 if (argType == ARGTYPE_METADATA) { 2051 mSetBroadcastPINMetadata = (BluetoothLeBroadcastMetadata) message.obj; 2052 mSetBroadcastCodePending = true; 2053 } else { 2054 recvState = (BluetoothLeBroadcastReceiveState) message.obj; 2055 if (!isItRightTimeToUpdateBroadcastPin((byte) recvState.getSourceId())) { 2056 mSetBroadcastCodePending = true; 2057 } 2058 } 2059 if (mSetBroadcastCodePending == true) { 2060 log("Ignore SET_BCAST now, but restore it for later"); 2061 break; 2062 } 2063 byte[] setBroadcastPINcmd = 2064 convertRecvStateToSetBroadcastCodeByteArray(recvState); 2065 if (setBroadcastPINcmd == null) { 2066 Log.e(TAG, "SET_BCAST_CODE: Broadcast code is NULL"); 2067 break; 2068 } 2069 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 2070 writeBassControlPoint(setBroadcastPINcmd); 2071 mPendingOperation = message.what; 2072 mPendingSourceId = (byte) recvState.getSourceId(); 2073 transitionTo(mConnectedProcessing); 2074 sendMessageDelayed( 2075 GATT_TXN_TIMEOUT, 2076 SET_BCAST_CODE, 2077 BassConstants.GATT_TXN_TIMEOUT_MS); 2078 } 2079 break; 2080 case REMOVE_BCAST_SOURCE: 2081 byte sid = (byte) message.arg1; 2082 log("Removing Broadcast source, sourceId: " + sid); 2083 byte[] removeSourceInfo = new byte[2]; 2084 removeSourceInfo[0] = OPCODE_REMOVE_SOURCE; 2085 removeSourceInfo[1] = sid; 2086 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 2087 if (isPendingRemove((int) sid)) { 2088 setPendingRemove((int) sid, false); 2089 } 2090 2091 writeBassControlPoint(removeSourceInfo); 2092 mPendingOperation = message.what; 2093 mPendingSourceId = sid; 2094 transitionTo(mConnectedProcessing); 2095 sendMessageDelayed( 2096 GATT_TXN_TIMEOUT, 2097 REMOVE_BCAST_SOURCE, 2098 BassConstants.GATT_TXN_TIMEOUT_MS); 2099 } else { 2100 Log.e(TAG, "REMOVE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal"); 2101 mService.getCallbacks() 2102 .notifySourceRemoveFailed( 2103 mDevice, sid, BluetoothStatusCodes.ERROR_UNKNOWN); 2104 if (mPendingSourceToSwitch != null) { 2105 // Switching source failed 2106 // Need to notify add source failure for service to cleanup 2107 mService.getCallbacks() 2108 .notifySourceAddFailed( 2109 mDevice, 2110 mPendingSourceToSwitch, 2111 BluetoothStatusCodes.ERROR_UNKNOWN); 2112 mPendingSourceToSwitch = null; 2113 } 2114 } 2115 break; 2116 case PSYNC_ACTIVE_TIMEOUT: 2117 cancelActiveSync(null); 2118 break; 2119 case CANCEL_PENDING_SOURCE_OPERATION: 2120 int broadcastId = message.arg1; 2121 cancelPendingSourceOperation(broadcastId); 2122 break; 2123 default: 2124 log("CONNECTED: not handled message:" + message.what); 2125 return NOT_HANDLED; 2126 } 2127 return HANDLED; 2128 } 2129 } 2130 isSuccess(int status)2131 private boolean isSuccess(int status) { 2132 boolean ret = false; 2133 switch (status) { 2134 case BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST: 2135 case BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST: 2136 case BluetoothStatusCodes.REASON_REMOTE_REQUEST: 2137 case BluetoothStatusCodes.REASON_SYSTEM_POLICY: 2138 ret = true; 2139 break; 2140 default: 2141 break; 2142 } 2143 return ret; 2144 } 2145 sendPendingCallbacks(int pendingOp, int status)2146 void sendPendingCallbacks(int pendingOp, int status) { 2147 switch (pendingOp) { 2148 case START_SCAN_OFFLOAD: 2149 // Do not want to cancel sync because one remote does not receive START_SCAN_OFFLOAD 2150 if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 2151 if (!isSuccess(status)) { 2152 if (!mAutoTriggered) { 2153 cancelActiveSync(null); 2154 } else { 2155 mAutoTriggered = false; 2156 } 2157 } 2158 } 2159 break; 2160 case ADD_BCAST_SOURCE: 2161 if (!isSuccess(status)) { 2162 if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 2163 cancelActiveSync(null); 2164 } 2165 if (mPendingMetadata != null) { 2166 mService.getCallbacks() 2167 .notifySourceAddFailed(mDevice, mPendingMetadata, status); 2168 mPendingMetadata = null; 2169 } 2170 removeMessages(CANCEL_PENDING_SOURCE_OPERATION); 2171 } 2172 break; 2173 case UPDATE_BCAST_SOURCE: 2174 if (!mAutoTriggered 2175 || Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 2176 if (!isSuccess(status)) { 2177 mService.getCallbacks() 2178 .notifySourceModifyFailed(mDevice, mPendingSourceId, status); 2179 mPendingMetadata = null; 2180 removeMessages(CANCEL_PENDING_SOURCE_OPERATION); 2181 } 2182 } else { 2183 mAutoTriggered = false; 2184 } 2185 break; 2186 case REMOVE_BCAST_SOURCE: 2187 if (!isSuccess(status)) { 2188 mService.getCallbacks() 2189 .notifySourceRemoveFailed(mDevice, mPendingSourceId, status); 2190 if (mPendingSourceToSwitch != null) { 2191 // Switching source failed 2192 // Need to notify add source failure for service to cleanup 2193 mService.getCallbacks() 2194 .notifySourceAddFailed(mDevice, mPendingSourceToSwitch, status); 2195 mPendingSourceToSwitch = null; 2196 } 2197 } 2198 break; 2199 case SET_BCAST_CODE: 2200 log("sendPendingCallbacks: SET_BCAST_CODE"); 2201 break; 2202 default: 2203 log("sendPendingCallbacks: unhandled case"); 2204 break; 2205 } 2206 } 2207 2208 // public for testing, but private for non-testing 2209 @VisibleForTesting 2210 class ConnectedProcessing extends State { 2211 @Override enter()2212 public void enter() { 2213 log( 2214 "Enter ConnectedProcessing(" 2215 + mDevice 2216 + "): " 2217 + messageWhatToString(getCurrentMessage().what)); 2218 } 2219 2220 @Override exit()2221 public void exit() { 2222 /* Pending Metadata will be used to bond with source ID in receiver state notify */ 2223 if (mPendingOperation == REMOVE_BCAST_SOURCE) { 2224 mPendingMetadata = null; 2225 } 2226 2227 log( 2228 "Exit ConnectedProcessing(" 2229 + mDevice 2230 + "): " 2231 + messageWhatToString(getCurrentMessage().what)); 2232 } 2233 2234 @Override processMessage(Message message)2235 public boolean processMessage(Message message) { 2236 log( 2237 "ConnectedProcessing process message(" 2238 + mDevice 2239 + "): " 2240 + messageWhatToString(message.what)); 2241 switch (message.what) { 2242 case CONNECT: 2243 Log.w(TAG, "CONNECT request is ignored" + mDevice); 2244 break; 2245 case DISCONNECT: 2246 Log.w(TAG, "DISCONNECT requested!: " + mDevice); 2247 mAllowReconnect = false; 2248 if (mBluetoothGatt != null) { 2249 mService.handleDeviceDisconnection(mDevice, true); 2250 mBluetoothGatt.disconnect(); 2251 mBluetoothGatt.close(); 2252 mBluetoothGatt = null; 2253 if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 2254 cancelActiveSync(null); 2255 } 2256 transitionTo(mDisconnected); 2257 } else { 2258 log("mBluetoothGatt is null"); 2259 } 2260 break; 2261 case READ_BASS_CHARACTERISTICS: 2262 Log.w(TAG, "defer READ_BASS_CHARACTERISTICS requested!: " + mDevice); 2263 deferMessage(message); 2264 break; 2265 case CONNECTION_STATE_CHANGED: 2266 int state = (int) message.obj; 2267 Log.w(TAG, "ConnectedProcessing: connection state changed:" + state); 2268 if (state == BluetoothProfile.STATE_CONNECTED) { 2269 Log.w(TAG, "should never happen from this state"); 2270 } else { 2271 Log.w(TAG, "Unexpected disconnection " + mDevice); 2272 mService.handleDeviceDisconnection(mDevice, false); 2273 resetBluetoothGatt(); 2274 if (!Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine()) { 2275 cancelActiveSync(null); 2276 } 2277 transitionTo(mDisconnected); 2278 } 2279 break; 2280 case GATT_TXN_PROCESSED: 2281 removeMessages(GATT_TXN_TIMEOUT); 2282 int status = (int) message.arg1; 2283 log("GATT transaction processed for" + mDevice); 2284 if (status == BluetoothGatt.GATT_SUCCESS) { 2285 sendPendingCallbacks( 2286 mPendingOperation, BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 2287 } else { 2288 sendPendingCallbacks(mPendingOperation, BluetoothStatusCodes.ERROR_UNKNOWN); 2289 } 2290 transitionTo(mConnected); 2291 break; 2292 case GATT_TXN_TIMEOUT: 2293 log("GATT transaction timeout for" + mDevice); 2294 sendPendingCallbacks(mPendingOperation, BluetoothStatusCodes.ERROR_UNKNOWN); 2295 mPendingOperation = -1; 2296 mPendingSourceId = -1; 2297 if ((message.arg1 == UPDATE_BCAST_SOURCE) 2298 || (message.arg1 == ADD_BCAST_SOURCE)) { 2299 mPendingMetadata = null; 2300 } 2301 transitionTo(mConnected); 2302 break; 2303 case START_SCAN_OFFLOAD: 2304 case STOP_SCAN_OFFLOAD: 2305 case SELECT_BCAST_SOURCE: 2306 case ADD_BCAST_SOURCE: 2307 case SET_BCAST_CODE: 2308 case REMOVE_BCAST_SOURCE: 2309 case REACHED_MAX_SOURCE_LIMIT: 2310 case SWITCH_BCAST_SOURCE: 2311 case PSYNC_ACTIVE_TIMEOUT: 2312 log( 2313 "defer the message: " 2314 + messageWhatToString(message.what) 2315 + ", so that it will be processed later"); 2316 deferMessage(message); 2317 break; 2318 case CANCEL_PENDING_SOURCE_OPERATION: 2319 int broadcastId = message.arg1; 2320 cancelPendingSourceOperation(broadcastId); 2321 break; 2322 default: 2323 log("CONNECTEDPROCESSING: not handled message:" + message.what); 2324 return NOT_HANDLED; 2325 } 2326 return HANDLED; 2327 } 2328 } 2329 broadcastConnectionState(BluetoothDevice device, int fromState, int toState)2330 void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) { 2331 log("broadcastConnectionState " + device + ": " + fromState + "->" + toState); 2332 if (fromState == BluetoothProfile.STATE_CONNECTED 2333 && toState == BluetoothProfile.STATE_CONNECTED) { 2334 log("CONNECTED->CONNECTED: Ignore"); 2335 return; 2336 } 2337 2338 mService.handleConnectionStateChanged(device, fromState, toState); 2339 Intent intent = new Intent(BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED); 2340 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); 2341 intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); 2342 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 2343 intent.addFlags( 2344 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 2345 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 2346 mService.sendBroadcast( 2347 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 2348 } 2349 getConnectionState()2350 int getConnectionState() { 2351 String currentState = "Unknown"; 2352 if (getCurrentState() != null) { 2353 currentState = getCurrentState().getName(); 2354 } 2355 switch (currentState) { 2356 case "Disconnected": 2357 return BluetoothProfile.STATE_DISCONNECTED; 2358 case "Connecting": 2359 return BluetoothProfile.STATE_CONNECTING; 2360 case "Connected": 2361 case "ConnectedProcessing": 2362 return BluetoothProfile.STATE_CONNECTED; 2363 default: 2364 Log.e(TAG, "Bad currentState: " + currentState); 2365 return BluetoothProfile.STATE_DISCONNECTED; 2366 } 2367 } 2368 getMaximumSourceCapacity()2369 int getMaximumSourceCapacity() { 2370 return mNumOfBroadcastReceiverStates; 2371 } 2372 getDevice()2373 BluetoothDevice getDevice() { 2374 return mDevice; 2375 } 2376 isConnected()2377 synchronized boolean isConnected() { 2378 return (getCurrentState() == mConnected) || (getCurrentState() == mConnectedProcessing); 2379 } 2380 messageWhatToString(int what)2381 public static String messageWhatToString(int what) { 2382 switch (what) { 2383 case CONNECT: 2384 return "CONNECT"; 2385 case DISCONNECT: 2386 return "DISCONNECT"; 2387 case CONNECTION_STATE_CHANGED: 2388 return "CONNECTION_STATE_CHANGED"; 2389 case GATT_TXN_PROCESSED: 2390 return "GATT_TXN_PROCESSED"; 2391 case READ_BASS_CHARACTERISTICS: 2392 return "READ_BASS_CHARACTERISTICS"; 2393 case START_SCAN_OFFLOAD: 2394 return "START_SCAN_OFFLOAD"; 2395 case STOP_SCAN_OFFLOAD: 2396 return "STOP_SCAN_OFFLOAD"; 2397 case ADD_BCAST_SOURCE: 2398 return "ADD_BCAST_SOURCE"; 2399 case SELECT_BCAST_SOURCE: 2400 return "SELECT_BCAST_SOURCE"; 2401 case UPDATE_BCAST_SOURCE: 2402 return "UPDATE_BCAST_SOURCE"; 2403 case SET_BCAST_CODE: 2404 return "SET_BCAST_CODE"; 2405 case REMOVE_BCAST_SOURCE: 2406 return "REMOVE_BCAST_SOURCE"; 2407 case REACHED_MAX_SOURCE_LIMIT: 2408 return "REACHED_MAX_SOURCE_LIMIT"; 2409 case SWITCH_BCAST_SOURCE: 2410 return "SWITCH_BCAST_SOURCE"; 2411 case PSYNC_ACTIVE_TIMEOUT: 2412 return "PSYNC_ACTIVE_TIMEOUT"; 2413 case CONNECT_TIMEOUT: 2414 return "CONNECT_TIMEOUT"; 2415 case CANCEL_PENDING_SOURCE_OPERATION: 2416 return "CANCEL_PENDING_SOURCE_OPERATION"; 2417 default: 2418 break; 2419 } 2420 return Integer.toString(what); 2421 } 2422 2423 /** Dump info */ dump(StringBuilder sb)2424 public void dump(StringBuilder sb) { 2425 ProfileService.println(sb, "mDevice: " + mDevice); 2426 ProfileService.println(sb, " StateMachine: " + this); 2427 // Dump the state machine logs 2428 StringWriter stringWriter = new StringWriter(); 2429 PrintWriter printWriter = new PrintWriter(stringWriter); 2430 super.dump(new FileDescriptor(), printWriter, new String[] {}); 2431 printWriter.flush(); 2432 stringWriter.flush(); 2433 ProfileService.println(sb, " StateMachineLog:"); 2434 Scanner scanner = new Scanner(stringWriter.toString()); 2435 while (scanner.hasNextLine()) { 2436 String line = scanner.nextLine(); 2437 ProfileService.println(sb, " " + line); 2438 } 2439 scanner.close(); 2440 for (Map.Entry<Integer, BluetoothLeBroadcastReceiveState> entry : 2441 mBluetoothLeBroadcastReceiveStates.entrySet()) { 2442 BluetoothLeBroadcastReceiveState state = entry.getValue(); 2443 sb.append(state); 2444 } 2445 } 2446 2447 @Override log(String msg)2448 protected void log(String msg) { 2449 super.log(msg); 2450 } 2451 logByteArray(String prefix, byte[] value, int offset, int count)2452 private static void logByteArray(String prefix, byte[] value, int offset, int count) { 2453 StringBuilder builder = new StringBuilder(prefix); 2454 for (int i = offset; i < count; i++) { 2455 builder.append(String.format("0x%02X", value[i])); 2456 if (i != value.length - 1) { 2457 builder.append(", "); 2458 } 2459 } 2460 Log.d(TAG, builder.toString()); 2461 } 2462 2463 /** Mockable wrapper of {@link BluetoothGatt}. */ 2464 @VisibleForTesting 2465 public static class BluetoothGattTestableWrapper { 2466 public final BluetoothGatt mWrappedBluetoothGatt; 2467 BluetoothGattTestableWrapper(BluetoothGatt bluetoothGatt)2468 BluetoothGattTestableWrapper(BluetoothGatt bluetoothGatt) { 2469 mWrappedBluetoothGatt = bluetoothGatt; 2470 } 2471 2472 /** See {@link BluetoothGatt#getServices()}. */ getServices()2473 public List<BluetoothGattService> getServices() { 2474 return mWrappedBluetoothGatt.getServices(); 2475 } 2476 2477 /** See {@link BluetoothGatt#getService(UUID)}. */ 2478 @Nullable getService(UUID uuid)2479 public BluetoothGattService getService(UUID uuid) { 2480 return mWrappedBluetoothGatt.getService(uuid); 2481 } 2482 2483 /** See {@link BluetoothGatt#discoverServices()}. */ discoverServices()2484 public boolean discoverServices() { 2485 return mWrappedBluetoothGatt.discoverServices(); 2486 } 2487 2488 /** See {@link BluetoothGatt#readCharacteristic( BluetoothGattCharacteristic)}. */ readCharacteristic(BluetoothGattCharacteristic characteristic)2489 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { 2490 return mWrappedBluetoothGatt.readCharacteristic(characteristic); 2491 } 2492 2493 /** 2494 * See {@link BluetoothGatt#writeCharacteristic( BluetoothGattCharacteristic, byte[], int)} 2495 * . 2496 */ writeCharacteristic(BluetoothGattCharacteristic characteristic)2497 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { 2498 return mWrappedBluetoothGatt.writeCharacteristic(characteristic); 2499 } 2500 2501 /** See {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)}. */ readDescriptor(BluetoothGattDescriptor descriptor)2502 public boolean readDescriptor(BluetoothGattDescriptor descriptor) { 2503 return mWrappedBluetoothGatt.readDescriptor(descriptor); 2504 } 2505 2506 /** See {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])}. */ writeDescriptor(BluetoothGattDescriptor descriptor)2507 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { 2508 return mWrappedBluetoothGatt.writeDescriptor(descriptor); 2509 } 2510 2511 /** See {@link BluetoothGatt#requestMtu(int)}. */ requestMtu(int mtu)2512 public boolean requestMtu(int mtu) { 2513 return mWrappedBluetoothGatt.requestMtu(mtu); 2514 } 2515 2516 /** See {@link BluetoothGatt#setCharacteristicNotification}. */ setCharacteristicNotification( BluetoothGattCharacteristic characteristic, boolean enable)2517 public boolean setCharacteristicNotification( 2518 BluetoothGattCharacteristic characteristic, boolean enable) { 2519 return mWrappedBluetoothGatt.setCharacteristicNotification(characteristic, enable); 2520 } 2521 2522 /** See {@link BluetoothGatt#disconnect()}. */ disconnect()2523 public void disconnect() { 2524 mWrappedBluetoothGatt.disconnect(); 2525 } 2526 2527 /** See {@link BluetoothGatt#close()}. */ close()2528 public void close() { 2529 mWrappedBluetoothGatt.close(); 2530 } 2531 } 2532 } 2533