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