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.le.PeriodicAdvertisingCallback; 39 import android.bluetooth.le.PeriodicAdvertisingManager; 40 import android.bluetooth.le.PeriodicAdvertisingReport; 41 import android.bluetooth.le.ScanRecord; 42 import android.bluetooth.le.ScanResult; 43 import android.content.Intent; 44 import android.os.Binder; 45 import android.os.Looper; 46 import android.os.Message; 47 import android.os.ParcelUuid; 48 import android.provider.DeviceConfig; 49 import android.util.Log; 50 51 import com.android.bluetooth.BluetoothMethodProxy; 52 import com.android.bluetooth.Utils; 53 import com.android.bluetooth.btservice.ProfileService; 54 import com.android.bluetooth.btservice.ServiceFactory; 55 import com.android.internal.annotations.VisibleForTesting; 56 import com.android.internal.util.State; 57 import com.android.internal.util.StateMachine; 58 59 import java.io.ByteArrayOutputStream; 60 import java.io.FileDescriptor; 61 import java.io.PrintWriter; 62 import java.io.StringWriter; 63 import java.util.ArrayList; 64 import java.util.Arrays; 65 import java.util.Collection; 66 import java.util.HashMap; 67 import java.util.Iterator; 68 import java.util.List; 69 import java.util.Map; 70 import java.util.Scanner; 71 import java.util.UUID; 72 import java.util.stream.IntStream; 73 74 @VisibleForTesting 75 public class BassClientStateMachine extends StateMachine { 76 private static final String TAG = "BassClientStateMachine"; 77 @VisibleForTesting 78 static final byte[] REMOTE_SCAN_STOP = {00}; 79 @VisibleForTesting 80 static final byte[] REMOTE_SCAN_START = {01}; 81 private static final byte OPCODE_ADD_SOURCE = 0x02; 82 private static final byte OPCODE_UPDATE_SOURCE = 0x03; 83 private static final byte OPCODE_SET_BCAST_PIN = 0x04; 84 private static final byte OPCODE_REMOVE_SOURCE = 0x05; 85 private static final int ADD_SOURCE_FIXED_LENGTH = 16; 86 private static final int UPDATE_SOURCE_FIXED_LENGTH = 6; 87 88 static final int CONNECT = 1; 89 static final int DISCONNECT = 2; 90 static final int CONNECTION_STATE_CHANGED = 3; 91 static final int GATT_TXN_PROCESSED = 4; 92 static final int READ_BASS_CHARACTERISTICS = 5; 93 static final int START_SCAN_OFFLOAD = 6; 94 static final int STOP_SCAN_OFFLOAD = 7; 95 static final int SELECT_BCAST_SOURCE = 8; 96 static final int ADD_BCAST_SOURCE = 9; 97 static final int UPDATE_BCAST_SOURCE = 10; 98 static final int SET_BCAST_CODE = 11; 99 static final int REMOVE_BCAST_SOURCE = 12; 100 static final int GATT_TXN_TIMEOUT = 13; 101 static final int PSYNC_ACTIVE_TIMEOUT = 14; 102 static final int CONNECT_TIMEOUT = 15; 103 104 // NOTE: the value is not "final" - it is modified in the unit tests 105 @VisibleForTesting 106 private int mConnectTimeoutMs; 107 108 /*key is combination of sourceId, Address and advSid for this hashmap*/ 109 private final Map<Integer, BluetoothLeBroadcastReceiveState> 110 mBluetoothLeBroadcastReceiveStates = 111 new HashMap<Integer, BluetoothLeBroadcastReceiveState>(); 112 private final Map<Integer, BluetoothLeBroadcastMetadata> mCurrentMetadata = new HashMap(); 113 private final Disconnected mDisconnected = new Disconnected(); 114 private final Connected mConnected = new Connected(); 115 private final Connecting mConnecting = new Connecting(); 116 private final ConnectedProcessing mConnectedProcessing = new ConnectedProcessing(); 117 @VisibleForTesting 118 final List<BluetoothGattCharacteristic> mBroadcastCharacteristics = 119 new ArrayList<BluetoothGattCharacteristic>(); 120 @VisibleForTesting 121 BluetoothDevice mDevice; 122 123 private boolean mIsAllowedList = false; 124 private int mLastConnectionState = -1; 125 @VisibleForTesting 126 boolean mMTUChangeRequested = false; 127 @VisibleForTesting 128 boolean mDiscoveryInitiated = false; 129 @VisibleForTesting 130 BassClientService mService; 131 @VisibleForTesting 132 BluetoothGattCharacteristic mBroadcastScanControlPoint; 133 private boolean mFirstTimeBisDiscovery = false; 134 private int mPASyncRetryCounter = 0; 135 private ScanResult mScanRes = null; 136 @VisibleForTesting 137 int mNumOfBroadcastReceiverStates = 0; 138 private BluetoothAdapter mBluetoothAdapter = 139 BluetoothAdapter.getDefaultAdapter(); 140 private ServiceFactory mFactory = new ServiceFactory(); 141 @VisibleForTesting 142 int mPendingOperation = -1; 143 @VisibleForTesting 144 byte mPendingSourceId = -1; 145 @VisibleForTesting 146 BluetoothLeBroadcastMetadata mPendingMetadata = null; 147 private BluetoothLeBroadcastReceiveState mSetBroadcastPINRcvState = null; 148 @VisibleForTesting 149 boolean mSetBroadcastCodePending = false; 150 private final Map<Integer, Boolean> mPendingRemove = new HashMap(); 151 // Psync and PAST interfaces 152 private PeriodicAdvertisingManager mPeriodicAdvManager; 153 private boolean mAutoAssist = false; 154 @VisibleForTesting 155 boolean mAutoTriggered = false; 156 @VisibleForTesting 157 boolean mNoStopScanOffload = false; 158 private boolean mDefNoPAS = false; 159 private boolean mForceSB = false; 160 private int mBroadcastSourceIdLength = 3; 161 @VisibleForTesting 162 byte mNextSourceId = 0; 163 private boolean mAllowReconnect = false; 164 @VisibleForTesting 165 BluetoothGattTestableWrapper mBluetoothGatt = null; 166 BluetoothGattCallback mGattCallback = null; 167 BassClientStateMachine(BluetoothDevice device, BassClientService svc, Looper looper, int connectTimeoutMs)168 BassClientStateMachine(BluetoothDevice device, BassClientService svc, Looper looper, 169 int connectTimeoutMs) { 170 super(TAG + "(" + device.toString() + ")", looper); 171 mDevice = device; 172 mService = svc; 173 mConnectTimeoutMs = connectTimeoutMs; 174 addState(mDisconnected); 175 addState(mConnected); 176 addState(mConnecting); 177 addState(mConnectedProcessing); 178 setInitialState(mDisconnected); 179 // PSYNC and PAST instances 180 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 181 if (mBluetoothAdapter != null) { 182 mPeriodicAdvManager = mBluetoothAdapter.getPeriodicAdvertisingManager(); 183 } 184 long token = Binder.clearCallingIdentity(); 185 mIsAllowedList = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, 186 "persist.vendor.service.bt.wl", true); 187 mDefNoPAS = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, 188 "persist.vendor.service.bt.defNoPAS", false); 189 mForceSB = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, 190 "persist.vendor.service.bt.forceSB", false); 191 Binder.restoreCallingIdentity(token); 192 } 193 make(BluetoothDevice device, BassClientService svc, Looper looper)194 static BassClientStateMachine make(BluetoothDevice device, 195 BassClientService svc, Looper looper) { 196 Log.d(TAG, "make for device " + device); 197 BassClientStateMachine BassclientSm = new BassClientStateMachine(device, svc, looper, 198 BassConstants.CONNECT_TIMEOUT_MS); 199 BassclientSm.start(); 200 return BassclientSm; 201 } 202 destroy(BassClientStateMachine stateMachine)203 static void destroy(BassClientStateMachine stateMachine) { 204 Log.i(TAG, "destroy"); 205 if (stateMachine == null) { 206 Log.w(TAG, "destroy(), stateMachine is null"); 207 return; 208 } 209 stateMachine.doQuit(); 210 stateMachine.cleanup(); 211 } 212 doQuit()213 public void doQuit() { 214 log("doQuit for device " + mDevice); 215 quitNow(); 216 } 217 cleanup()218 public void cleanup() { 219 log("cleanup for device " + mDevice); 220 clearCharsCache(); 221 222 if (mBluetoothGatt != null) { 223 log("disconnect gatt"); 224 mBluetoothGatt.disconnect(); 225 mBluetoothGatt.close(); 226 mBluetoothGatt = null; 227 mGattCallback = null; 228 } 229 mPendingOperation = -1; 230 mPendingSourceId = -1; 231 mPendingMetadata = null; 232 mCurrentMetadata.clear(); 233 mPendingRemove.clear(); 234 } 235 hasPendingSourceOperation()236 Boolean hasPendingSourceOperation() { 237 return mPendingMetadata != null; 238 } 239 getCurrentBroadcastMetadata(Integer sourceId)240 BluetoothLeBroadcastMetadata getCurrentBroadcastMetadata(Integer sourceId) { 241 return mCurrentMetadata.getOrDefault(sourceId, null); 242 } 243 setCurrentBroadcastMetadata(Integer sourceId, BluetoothLeBroadcastMetadata metadata)244 private void setCurrentBroadcastMetadata(Integer sourceId, 245 BluetoothLeBroadcastMetadata metadata) { 246 if (metadata != null) { 247 mCurrentMetadata.put(sourceId, metadata); 248 } else { 249 mCurrentMetadata.remove(sourceId); 250 } 251 } 252 isPendingRemove(Integer sourceId)253 boolean isPendingRemove(Integer sourceId) { 254 return mPendingRemove.getOrDefault(sourceId, false); 255 } 256 setPendingRemove(Integer sourceId, boolean remove)257 private void setPendingRemove(Integer sourceId, boolean remove) { 258 if (remove) { 259 mPendingRemove.put(sourceId, remove); 260 } else { 261 mPendingRemove.remove(sourceId); 262 } 263 } 264 getBroadcastReceiveStateForSourceDevice( BluetoothDevice srcDevice)265 BluetoothLeBroadcastReceiveState getBroadcastReceiveStateForSourceDevice( 266 BluetoothDevice srcDevice) { 267 List<BluetoothLeBroadcastReceiveState> currentSources = getAllSources(); 268 BluetoothLeBroadcastReceiveState state = null; 269 for (int i = 0; i < currentSources.size(); i++) { 270 BluetoothDevice device = currentSources.get(i).getSourceDevice(); 271 if (device != null && device.equals(srcDevice)) { 272 state = currentSources.get(i); 273 Log.e(TAG, 274 "getBroadcastReceiveStateForSourceDevice: returns for: " 275 + srcDevice + "&srcInfo" + state); 276 return state; 277 } 278 } 279 return null; 280 } 281 getBroadcastReceiveStateForSourceId(int sourceId)282 BluetoothLeBroadcastReceiveState getBroadcastReceiveStateForSourceId(int sourceId) { 283 List<BluetoothLeBroadcastReceiveState> currentSources = getAllSources(); 284 for (int i = 0; i < currentSources.size(); i++) { 285 if (sourceId == currentSources.get(i).getSourceId()) { 286 return currentSources.get(i); 287 } 288 } 289 return null; 290 } 291 parseBaseData(BluetoothDevice device, int syncHandle, byte[] serviceData)292 void parseBaseData(BluetoothDevice device, int syncHandle, byte[] serviceData) { 293 log("parseBaseData" + Arrays.toString(serviceData)); 294 BaseData base = BaseData.parseBaseData(serviceData); 295 if (base != null) { 296 mService.updateBase(syncHandle, base); 297 base.print(); 298 if (mAutoTriggered) { 299 // successful auto periodic synchrnization with source 300 log("auto triggered assist"); 301 mAutoTriggered = false; 302 // perform PAST with this device 303 BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle); 304 if (srcDevice != null) { 305 BluetoothLeBroadcastReceiveState recvState = 306 getBroadcastReceiveStateForSourceDevice(srcDevice); 307 processPASyncState(recvState); 308 } else { 309 Log.w(TAG, "Autoassist: no matching device"); 310 } 311 } 312 } else { 313 Log.e(TAG, "Seems BASE is not in parsable format"); 314 if (!mAutoTriggered) { 315 BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle); 316 cancelActiveSync(srcDevice); 317 } else { 318 mAutoTriggered = false; 319 } 320 } 321 } 322 parseScanRecord(int syncHandle, ScanRecord record)323 void parseScanRecord(int syncHandle, ScanRecord record) { 324 log("parseScanRecord" + record); 325 BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle); 326 Map<ParcelUuid, byte[]> bmsAdvDataMap = record.getServiceData(); 327 if (bmsAdvDataMap != null) { 328 for (Map.Entry<ParcelUuid, byte[]> entry : bmsAdvDataMap.entrySet()) { 329 log("ParcelUUid = " + entry.getKey() + ", Value = " 330 + Arrays.toString(entry.getValue())); 331 } 332 } 333 byte[] advData = record.getServiceData(BassConstants.BASIC_AUDIO_UUID); 334 if (advData != null) { 335 parseBaseData(mDevice, syncHandle, advData); 336 } else { 337 Log.e(TAG, "No service data in Scan record"); 338 if (!mAutoTriggered) { 339 cancelActiveSync(srcDevice); 340 } else { 341 mAutoTriggered = false; 342 } 343 } 344 } 345 selectSource( ScanResult scanRes, boolean autoTriggered)346 private boolean selectSource( 347 ScanResult scanRes, boolean autoTriggered) { 348 log("selectSource: ScanResult " + scanRes); 349 mAutoTriggered = autoTriggered; 350 mFirstTimeBisDiscovery = true; 351 mPASyncRetryCounter = 1; 352 // Cache Scan res for Retrys 353 mScanRes = scanRes; 354 /*This is an override case if Previous sync is still active, cancel It, but don't stop the 355 * Scan offload as we still trying to assist remote 356 */ 357 mNoStopScanOffload = true; 358 cancelActiveSync(null); 359 try { 360 BluetoothMethodProxy.getInstance().periodicAdvertisingManagerRegisterSync( 361 mPeriodicAdvManager, scanRes, 0, BassConstants.PSYNC_TIMEOUT, 362 mPeriodicAdvCallback, null); 363 } catch (IllegalArgumentException ex) { 364 Log.w(TAG, "registerSync:IllegalArgumentException"); 365 Message message = obtainMessage(STOP_SCAN_OFFLOAD); 366 sendMessage(message); 367 return false; 368 } 369 // updating mainly for Address type and PA Interval here 370 // extract BroadcastId from ScanResult 371 ScanRecord scanRecord = scanRes.getScanRecord(); 372 if (scanRecord != null) { 373 Map<ParcelUuid, byte[]> listOfUuids = scanRecord.getServiceData(); 374 int broadcastId = BassConstants.INVALID_BROADCAST_ID; 375 if (listOfUuids != null) { 376 if (listOfUuids.containsKey(BassConstants.BAAS_UUID)) { 377 byte[] bId = listOfUuids.get(BassConstants.BAAS_UUID); 378 broadcastId = BassUtils.parseBroadcastId(bId); 379 } 380 } 381 mService.updatePeriodicAdvertisementResultMap( 382 scanRes.getDevice(), 383 scanRes.getDevice().getAddressType(), 384 BassConstants.INVALID_SYNC_HANDLE, 385 BassConstants.INVALID_ADV_SID, 386 scanRes.getPeriodicAdvertisingInterval(), 387 broadcastId); 388 } 389 return true; 390 } 391 cancelActiveSync(BluetoothDevice sourceDev)392 private void cancelActiveSync(BluetoothDevice sourceDev) { 393 log("cancelActiveSync"); 394 BluetoothDevice activeSyncedSrc = mService.getActiveSyncedSource(mDevice); 395 396 /* Stop sync if there is some running */ 397 if (activeSyncedSrc != null && (sourceDev == null || activeSyncedSrc.equals(sourceDev))) { 398 removeMessages(PSYNC_ACTIVE_TIMEOUT); 399 try { 400 log("calling unregisterSync"); 401 mPeriodicAdvManager.unregisterSync(mPeriodicAdvCallback); 402 } catch (IllegalArgumentException ex) { 403 Log.w(TAG, "unregisterSync:IllegalArgumentException"); 404 } 405 mService.clearPeriodicAdvertisementResult(activeSyncedSrc); 406 mService.setActiveSyncedSource(mDevice, null); 407 if (!mNoStopScanOffload) { 408 // trigger scan stop here 409 Message message = obtainMessage(STOP_SCAN_OFFLOAD); 410 sendMessage(message); 411 } 412 } 413 mNoStopScanOffload = false; 414 } 415 getBroadcastMetadataFromBaseData(BaseData baseData, BluetoothDevice device)416 private BluetoothLeBroadcastMetadata getBroadcastMetadataFromBaseData(BaseData baseData, 417 BluetoothDevice device) { 418 BluetoothLeBroadcastMetadata.Builder metaData = 419 new BluetoothLeBroadcastMetadata.Builder(); 420 int index = 0; 421 for (BaseData.BaseInformation baseLevel2 : baseData.getLevelTwo()) { 422 BluetoothLeBroadcastSubgroup.Builder subGroup = 423 new BluetoothLeBroadcastSubgroup.Builder(); 424 for (int j = 0; j < baseLevel2.numSubGroups; j ++) { 425 BaseData.BaseInformation baseLevel3 = 426 baseData.getLevelThree().get(index++); 427 BluetoothLeBroadcastChannel.Builder channel = 428 new BluetoothLeBroadcastChannel.Builder(); 429 channel.setChannelIndex(baseLevel3.index); 430 channel.setCodecMetadata(BluetoothLeAudioCodecConfigMetadata. 431 fromRawBytes(baseLevel3.codecConfigInfo)); 432 channel.setSelected(false); 433 subGroup.addChannel(channel.build()); 434 } 435 byte[] arrayCodecId = baseLevel2.codecId; 436 long codeId = (long) ((arrayCodecId[4] & 0xff) << 32 437 | (arrayCodecId[3] & 0xff) << 24 438 | (arrayCodecId[2] & 0xff) << 16 439 | (arrayCodecId[1] & 0xff) << 8 440 | (arrayCodecId[0] & 0xff)); 441 subGroup.setCodecId(codeId); 442 subGroup.setCodecSpecificConfig(BluetoothLeAudioCodecConfigMetadata. 443 fromRawBytes(baseLevel2.codecConfigInfo)); 444 subGroup.setContentMetadata(BluetoothLeAudioContentMetadata. 445 fromRawBytes(baseLevel2.metaData)); 446 metaData.addSubgroup(subGroup.build()); 447 } 448 metaData.setSourceDevice(device, device.getAddressType()); 449 byte[] arrayPresentationDelay = baseData.getLevelOne().presentationDelay; 450 int presentationDelay = (int) ((arrayPresentationDelay[2] & 0xff) << 16 451 | (arrayPresentationDelay[1] & 0xff) 452 | (arrayPresentationDelay[0] & 0xff)); 453 metaData.setPresentationDelayMicros(presentationDelay); 454 PeriodicAdvertisementResult result = 455 mService.getPeriodicAdvertisementResult(device); 456 if (result != null) { 457 int broadcastId = result.getBroadcastId(); 458 log("broadcast ID: " + broadcastId); 459 metaData.setBroadcastId(broadcastId); 460 metaData.setSourceAdvertisingSid(result.getAdvSid()); 461 } 462 return metaData.build(); 463 } 464 465 /** Internal periodc Advertising manager callback */ 466 private PeriodicAdvertisingCallback mPeriodicAdvCallback = 467 new PeriodicAdvertisingCallback() { 468 @Override 469 public void onSyncEstablished( 470 int syncHandle, 471 BluetoothDevice device, 472 int advertisingSid, 473 int skip, 474 int timeout, 475 int status) { 476 log("onSyncEstablished syncHandle: " + syncHandle 477 + ", device: " + device 478 + ", advertisingSid: " + advertisingSid 479 + ", skip: " + skip 480 + ", timeout: " + timeout 481 + ", status: " + status); 482 if (status == BluetoothGatt.GATT_SUCCESS) { 483 // updates syncHandle, advSid 484 mService.updatePeriodicAdvertisementResultMap( 485 device, 486 BassConstants.INVALID_ADV_ADDRESS_TYPE, 487 syncHandle, 488 advertisingSid, 489 BassConstants.INVALID_ADV_INTERVAL, 490 BassConstants.INVALID_BROADCAST_ID); 491 sendMessageDelayed(PSYNC_ACTIVE_TIMEOUT, 492 BassConstants.PSYNC_ACTIVE_TIMEOUT_MS); 493 mService.setActiveSyncedSource(mDevice, device); 494 } else { 495 log("failed to sync to PA: " + mPASyncRetryCounter); 496 mScanRes = null; 497 if (!mAutoTriggered) { 498 Message message = obtainMessage(STOP_SCAN_OFFLOAD); 499 sendMessage(message); 500 } 501 mAutoTriggered = false; 502 } 503 } 504 505 @Override 506 public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) { 507 log("onPeriodicAdvertisingReport"); 508 // Parse the BIS indices from report's service data 509 if (mFirstTimeBisDiscovery) { 510 parseScanRecord(report.getSyncHandle(), report.getData()); 511 BaseData baseData = mService.getBase(report.getSyncHandle()); 512 if (baseData != null) { 513 BluetoothLeBroadcastMetadata metaData = 514 getBroadcastMetadataFromBaseData(baseData, 515 mService.getDeviceForSyncHandle(report.getSyncHandle())); 516 mService.getCallbacks().notifySourceFound(metaData); 517 } 518 mFirstTimeBisDiscovery = false; 519 } 520 } 521 522 @Override 523 public void onSyncLost(int syncHandle) { 524 log("OnSyncLost" + syncHandle); 525 BluetoothDevice srcDevice = mService.getDeviceForSyncHandle(syncHandle); 526 cancelActiveSync(srcDevice); 527 } 528 }; 529 broadcastReceiverState( BluetoothLeBroadcastReceiveState state, int sourceId)530 private void broadcastReceiverState( 531 BluetoothLeBroadcastReceiveState state, int sourceId) { 532 log("broadcastReceiverState: " + mDevice); 533 mService.getCallbacks().notifyReceiveStateChanged(mDevice, sourceId, state); 534 } 535 536 @VisibleForTesting isEmpty(final byte[] data)537 static boolean isEmpty(final byte[] data) { 538 return IntStream.range(0, data.length).parallel().allMatch(i -> data[i] == 0); 539 } 540 processPASyncState(BluetoothLeBroadcastReceiveState recvState)541 private void processPASyncState(BluetoothLeBroadcastReceiveState recvState) { 542 log("processPASyncState " + recvState); 543 int serviceData = 0; 544 if (recvState == null) { 545 Log.e(TAG, "processPASyncState: recvState is null"); 546 return; 547 } 548 int state = recvState.getPaSyncState(); 549 if (state == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCINFO_REQUEST) { 550 log("Initiate PAST procedure"); 551 PeriodicAdvertisementResult result = 552 mService.getPeriodicAdvertisementResult( 553 recvState.getSourceDevice()); 554 if (result != null) { 555 int syncHandle = result.getSyncHandle(); 556 log("processPASyncState: syncHandle " + result.getSyncHandle()); 557 if (syncHandle != BassConstants.INVALID_SYNC_HANDLE) { 558 serviceData = 0x000000FF & recvState.getSourceId(); 559 serviceData = serviceData << 8; 560 //advA matches EXT_ADV_ADDRESS 561 //also matches source address (as we would have written) 562 serviceData = serviceData 563 & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_EXT_ADV_ADDRESS); 564 serviceData = serviceData 565 & (~BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS); 566 log("Initiate PAST for: " + mDevice + ", syncHandle: " + syncHandle 567 + "serviceData" + serviceData); 568 BluetoothMethodProxy.getInstance().periodicAdvertisingManagerTransferSync( 569 mPeriodicAdvManager, mDevice, serviceData, syncHandle); 570 } 571 } else { 572 if (mService.isLocalBroadcast(mPendingMetadata)) { 573 int advHandle = mPendingMetadata.getSourceAdvertisingSid(); 574 serviceData = 0x000000FF & recvState.getSourceId(); 575 serviceData = serviceData << 8; 576 // Address we set in the Source Address can differ from the address in the air 577 serviceData = serviceData 578 | BassConstants.ADV_ADDRESS_DONT_MATCHES_SOURCE_ADV_ADDRESS; 579 log("Initiate local broadcast PAST for: " + mDevice 580 + ", advSID/Handle: " + advHandle 581 + ", serviceData: " + serviceData); 582 BluetoothMethodProxy.getInstance().periodicAdvertisingManagerTransferSetInfo( 583 mPeriodicAdvManager, mDevice, serviceData, advHandle, 584 mPeriodicAdvCallback); 585 } else { 586 Log.e(TAG, "There is no valid sync handle for this Source"); 587 if (mAutoAssist) { 588 // Initiate Auto Assist procedure for this device 589 mService.getBassUtils().triggerAutoAssist(recvState); 590 } 591 } 592 } 593 } else if (state == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED 594 || state == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_NO_PAST) { 595 Message message = obtainMessage(STOP_SCAN_OFFLOAD); 596 sendMessage(message); 597 } 598 } 599 checkAndUpdateBroadcastCode(BluetoothLeBroadcastReceiveState recvState)600 private void checkAndUpdateBroadcastCode(BluetoothLeBroadcastReceiveState recvState) { 601 log("checkAndUpdateBroadcastCode"); 602 // non colocated case, Broadcast PIN should have been updated from lyaer 603 // If there is pending one process it Now 604 if (recvState.getBigEncryptionState() 605 == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED 606 && mSetBroadcastCodePending) { 607 log("Update the Broadcast now"); 608 Message m = obtainMessage(BassClientStateMachine.SET_BCAST_CODE); 609 610 /* Use cached receiver state if previousely didn't finished setting broadcast code or 611 * use current receiver state if this is a first check and update 612 */ 613 if (mSetBroadcastPINRcvState != null) { 614 m.obj = mSetBroadcastPINRcvState; 615 } else { 616 m.obj = recvState; 617 } 618 619 sendMessage(m); 620 } 621 } 622 parseBroadcastReceiverState( byte[] receiverState)623 private BluetoothLeBroadcastReceiveState parseBroadcastReceiverState( 624 byte[] receiverState) { 625 byte sourceId = 0; 626 if (receiverState.length > 0) { 627 sourceId = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ID_IDX]; 628 } 629 log("processBroadcastReceiverState: receiverState length: " + receiverState.length); 630 631 BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); 632 BluetoothLeBroadcastReceiveState recvState = null; 633 if (receiverState.length == 0 634 || isEmpty(Arrays.copyOfRange(receiverState, 1, receiverState.length - 1))) { 635 String emptyBluetoothDevice = "00:00:00:00:00:00"; 636 if (mPendingOperation == REMOVE_BCAST_SOURCE) { 637 recvState = new BluetoothLeBroadcastReceiveState(mPendingSourceId, 638 BluetoothDevice.ADDRESS_TYPE_PUBLIC, // sourceAddressType 639 btAdapter.getRemoteDevice(emptyBluetoothDevice), // sourceDevice 640 0, // sourceAdvertisingSid 641 0, // broadcastId 642 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, // paSyncState 643 // bigEncryptionState 644 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, 645 null, // badCode 646 0, // numSubgroups 647 Arrays.asList(new Long[0]), // bisSyncState 648 Arrays.asList(new BluetoothLeAudioContentMetadata[0]) // subgroupMetadata 649 ); 650 } else if (receiverState.length == 0) { 651 if (mBluetoothLeBroadcastReceiveStates != null) { 652 mNextSourceId = (byte) mBluetoothLeBroadcastReceiveStates.size(); 653 } 654 if (mNextSourceId >= mNumOfBroadcastReceiverStates) { 655 Log.e(TAG, "reached the remote supported max SourceInfos"); 656 return null; 657 } 658 mNextSourceId++; 659 recvState = new BluetoothLeBroadcastReceiveState(mNextSourceId, 660 BluetoothDevice.ADDRESS_TYPE_PUBLIC, // sourceAddressType 661 btAdapter.getRemoteDevice(emptyBluetoothDevice), // sourceDevice 662 0, // sourceAdvertisingSid 663 0, // broadcastId 664 BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE, // paSyncState 665 // bigEncryptionState 666 BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, 667 null, // badCode 668 0, // numSubgroups 669 Arrays.asList(new Long[0]), // bisSyncState 670 Arrays.asList(new BluetoothLeAudioContentMetadata[0]) // subgroupMetadata 671 ); 672 } 673 } else { 674 byte metaDataSyncState = receiverState[BassConstants.BCAST_RCVR_STATE_PA_SYNC_IDX]; 675 byte encryptionStatus = receiverState[BassConstants.BCAST_RCVR_STATE_ENC_STATUS_IDX]; 676 byte[] badBroadcastCode = null; 677 int badBroadcastCodeLen = 0; 678 if (encryptionStatus 679 == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_BAD_CODE) { 680 badBroadcastCode = new byte[BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE]; 681 System.arraycopy( 682 receiverState, 683 BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX, 684 badBroadcastCode, 685 0, 686 BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE); 687 badBroadcastCodeLen = BassConstants.BCAST_RCVR_STATE_BADCODE_SIZE; 688 } 689 byte numSubGroups = receiverState[BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX 690 + badBroadcastCodeLen]; 691 int offset = BassConstants.BCAST_RCVR_STATE_BADCODE_START_IDX 692 + badBroadcastCodeLen + 1; 693 ArrayList<BluetoothLeAudioContentMetadata> metadataList = 694 new ArrayList<BluetoothLeAudioContentMetadata>(); 695 ArrayList<Long> audioSyncState = new ArrayList<Long>(); 696 for (int i = 0; i < numSubGroups; i++) { 697 byte[] audioSyncIndex = new byte[BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE]; 698 System.arraycopy(receiverState, offset, audioSyncIndex, 0, 699 BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE); 700 offset += BassConstants.BCAST_RCVR_STATE_BIS_SYNC_SIZE; 701 audioSyncState.add((long) Utils.byteArrayToInt(audioSyncIndex)); 702 703 byte metaDataLength = receiverState[offset++]; 704 if (metaDataLength > 0) { 705 log("metadata of length: " + metaDataLength + "is available"); 706 byte[] metaData = new byte[metaDataLength]; 707 System.arraycopy(receiverState, offset, metaData, 0, metaDataLength); 708 offset += metaDataLength; 709 metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(metaData)); 710 } else { 711 metadataList.add(BluetoothLeAudioContentMetadata.fromRawBytes(new byte[0])); 712 } 713 } 714 byte[] broadcastIdBytes = new byte[mBroadcastSourceIdLength]; 715 System.arraycopy( 716 receiverState, 717 BassConstants.BCAST_RCVR_STATE_SRC_BCAST_ID_START_IDX, 718 broadcastIdBytes, 719 0, 720 mBroadcastSourceIdLength); 721 int broadcastId = BassUtils.parseBroadcastId(broadcastIdBytes); 722 byte[] sourceAddress = new byte[BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE]; 723 System.arraycopy( 724 receiverState, 725 BassConstants.BCAST_RCVR_STATE_SRC_ADDR_START_IDX, 726 sourceAddress, 727 0, 728 BassConstants.BCAST_RCVR_STATE_SRC_ADDR_SIZE); 729 byte sourceAddressType = receiverState[BassConstants 730 .BCAST_RCVR_STATE_SRC_ADDR_TYPE_IDX]; 731 BassUtils.reverse(sourceAddress); 732 String address = Utils.getAddressStringFromByte(sourceAddress); 733 BluetoothDevice device = btAdapter.getRemoteLeDevice( 734 address, sourceAddressType); 735 byte sourceAdvSid = receiverState[BassConstants.BCAST_RCVR_STATE_SRC_ADV_SID_IDX]; 736 recvState = new BluetoothLeBroadcastReceiveState( 737 sourceId, 738 (int) sourceAddressType, 739 device, 740 sourceAdvSid, 741 broadcastId, 742 (int) metaDataSyncState, 743 (int) encryptionStatus, 744 badBroadcastCode, 745 numSubGroups, 746 audioSyncState, 747 metadataList); 748 log("Receiver state: " 749 + "\n\tSource ID: " + sourceId 750 + "\n\tSource Address Type: " + (int) sourceAddressType 751 + "\n\tDevice: " + device 752 + "\n\tSource Adv SID: " + sourceAdvSid 753 + "\n\tBroadcast ID: " + broadcastId 754 + "\n\tMetadata Sync State: " + (int) metaDataSyncState 755 + "\n\tEncryption Status: " + (int) encryptionStatus 756 + "\n\tBad Broadcast Code: " + Arrays.toString(badBroadcastCode) 757 + "\n\tNumber Of Subgroups: " + numSubGroups 758 + "\n\tAudio Sync State: " + audioSyncState 759 + "\n\tMetadata: " + metadataList); 760 } 761 return recvState; 762 } 763 processBroadcastReceiverState( byte[] receiverState, BluetoothGattCharacteristic characteristic)764 private void processBroadcastReceiverState( 765 byte[] receiverState, BluetoothGattCharacteristic characteristic) { 766 log("processBroadcastReceiverState: characteristic:" + characteristic); 767 BluetoothLeBroadcastReceiveState recvState = parseBroadcastReceiverState( 768 receiverState); 769 if (recvState == null) { 770 log("processBroadcastReceiverState: Null recvState"); 771 return; 772 } else if (recvState.getSourceId() == -1) { 773 log("processBroadcastReceiverState: invalid index: " + recvState.getSourceId()); 774 return; 775 } 776 BluetoothLeBroadcastReceiveState oldRecvState = 777 mBluetoothLeBroadcastReceiveStates.get(characteristic.getInstanceId()); 778 if (oldRecvState == null) { 779 log("Initial Read and Populating values"); 780 if (mBluetoothLeBroadcastReceiveStates.size() == mNumOfBroadcastReceiverStates) { 781 Log.e(TAG, "reached the Max SourceInfos"); 782 return; 783 } 784 mBluetoothLeBroadcastReceiveStates.put(characteristic.getInstanceId(), recvState); 785 checkAndUpdateBroadcastCode(recvState); 786 processPASyncState(recvState); 787 } else { 788 log("old sourceInfo: " + oldRecvState); 789 log("new sourceInfo: " + recvState); 790 mBluetoothLeBroadcastReceiveStates.replace(characteristic.getInstanceId(), recvState); 791 String emptyBluetoothDevice = "00:00:00:00:00:00"; 792 if (oldRecvState.getSourceDevice() == null 793 || oldRecvState.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) { 794 log("New Source Addition"); 795 mService.getCallbacks().notifySourceAdded(mDevice, recvState, 796 BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 797 if (mPendingMetadata != null) { 798 setCurrentBroadcastMetadata(recvState.getSourceId(), mPendingMetadata); 799 } 800 checkAndUpdateBroadcastCode(recvState); 801 processPASyncState(recvState); 802 } else { 803 if (recvState.getSourceDevice() == null 804 || recvState.getSourceDevice().getAddress().equals(emptyBluetoothDevice)) { 805 BluetoothDevice removedDevice = oldRecvState.getSourceDevice(); 806 log("sourceInfo removal" + removedDevice); 807 cancelActiveSync(removedDevice); 808 setCurrentBroadcastMetadata(oldRecvState.getSourceId(), null); 809 mService.getCallbacks().notifySourceRemoved(mDevice, 810 oldRecvState.getSourceId(), 811 BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 812 } else { 813 log("update to an existing recvState"); 814 setCurrentBroadcastMetadata(recvState.getSourceId(), mPendingMetadata); 815 mService.getCallbacks().notifySourceModified(mDevice, 816 recvState.getSourceId(), BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 817 checkAndUpdateBroadcastCode(recvState); 818 processPASyncState(recvState); 819 820 if (isPendingRemove(recvState.getSourceId())) { 821 Message message = obtainMessage(REMOVE_BCAST_SOURCE); 822 message.arg1 = recvState.getSourceId(); 823 sendMessage(message); 824 } 825 } 826 } 827 } 828 broadcastReceiverState(recvState, recvState.getSourceId()); 829 } 830 831 // Implements callback methods for GATT events that the app cares about. 832 // For example, connection change and services discovered. 833 final class GattCallback extends BluetoothGattCallback { 834 @Override onConnectionStateChange(BluetoothGatt gatt, int status, int newState)835 public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { 836 boolean isStateChanged = false; 837 log("onConnectionStateChange : Status=" + status + "newState" + newState); 838 if (newState == BluetoothProfile.STATE_CONNECTED 839 && getConnectionState() != BluetoothProfile.STATE_CONNECTED) { 840 isStateChanged = true; 841 Log.w(TAG, "Bassclient Connected from Disconnected state: " + mDevice); 842 if (mService.okToConnect(mDevice)) { 843 log("Bassclient Connected to: " + mDevice); 844 if (mBluetoothGatt != null) { 845 log("Attempting to start service discovery:" 846 + mBluetoothGatt.discoverServices()); 847 mDiscoveryInitiated = true; 848 } 849 } else if (mBluetoothGatt != null) { 850 // Reject the connection 851 Log.w(TAG, "Bassclient Connect request rejected: " + mDevice); 852 mBluetoothGatt.disconnect(); 853 mBluetoothGatt.close(); 854 mBluetoothGatt = null; 855 // force move to disconnected 856 newState = BluetoothProfile.STATE_DISCONNECTED; 857 } 858 } else if (newState == BluetoothProfile.STATE_DISCONNECTED 859 && getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { 860 isStateChanged = true; 861 log("Disconnected from Bass GATT server."); 862 } 863 if (isStateChanged) { 864 Message m = obtainMessage(CONNECTION_STATE_CHANGED); 865 m.obj = newState; 866 sendMessage(m); 867 } 868 } 869 870 @Override onServicesDiscovered(BluetoothGatt gatt, int status)871 public void onServicesDiscovered(BluetoothGatt gatt, int status) { 872 log("onServicesDiscovered:" + status); 873 if (mDiscoveryInitiated) { 874 mDiscoveryInitiated = false; 875 if (status == BluetoothGatt.GATT_SUCCESS && mBluetoothGatt != null) { 876 mBluetoothGatt.requestMtu(BassConstants.BASS_MAX_BYTES); 877 mMTUChangeRequested = true; 878 } else { 879 Log.w(TAG, "onServicesDiscovered received: " 880 + status + "mBluetoothGatt" + mBluetoothGatt); 881 } 882 } else { 883 log("remote initiated callback"); 884 } 885 } 886 887 @Override onCharacteristicRead( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)888 public void onCharacteristicRead( 889 BluetoothGatt gatt, 890 BluetoothGattCharacteristic characteristic, 891 int status) { 892 log("onCharacteristicRead:: status: " + status + "char:" + characteristic); 893 if (status == BluetoothGatt.GATT_SUCCESS && characteristic.getUuid() 894 .equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) { 895 log("onCharacteristicRead: BASS_BCAST_RECEIVER_STATE: status" + status); 896 if (characteristic.getValue() == null) { 897 Log.e(TAG, "Remote receiver state is NULL"); 898 return; 899 } 900 logByteArray("Received ", characteristic.getValue(), 0, 901 characteristic.getValue().length); 902 processBroadcastReceiverState(characteristic.getValue(), characteristic); 903 } 904 // switch to receiving notifications after initial characteristic read 905 BluetoothGattDescriptor desc = characteristic 906 .getDescriptor(BassConstants.CLIENT_CHARACTERISTIC_CONFIG); 907 if (mBluetoothGatt != null && desc != null) { 908 log("Setting the value for Desc"); 909 mBluetoothGatt.setCharacteristicNotification(characteristic, true); 910 desc.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); 911 mBluetoothGatt.writeDescriptor(desc); 912 } else { 913 Log.w(TAG, "CCC for " + characteristic + "seem to be not present"); 914 // at least move the SM to stable state 915 Message m = obtainMessage(GATT_TXN_PROCESSED); 916 m.arg1 = status; 917 sendMessage(m); 918 } 919 } 920 921 @Override onDescriptorWrite( BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)922 public void onDescriptorWrite( 923 BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { 924 log("onDescriptorWrite"); 925 if (status == BluetoothGatt.GATT_SUCCESS 926 && descriptor.getUuid() 927 .equals(BassConstants.CLIENT_CHARACTERISTIC_CONFIG)) { 928 log("CCC write resp"); 929 } 930 931 // Move the SM to connected so further reads happens 932 Message m = obtainMessage(GATT_TXN_PROCESSED); 933 m.arg1 = status; 934 sendMessage(m); 935 } 936 937 @Override onMtuChanged(BluetoothGatt gatt, int mtu, int status)938 public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { 939 log("onMtuChanged: mtu:" + mtu); 940 if (mMTUChangeRequested && mBluetoothGatt != null) { 941 acquireAllBassChars(); 942 mMTUChangeRequested = false; 943 } else { 944 log("onMtuChanged is remote initiated trigger, mBluetoothGatt:" 945 + mBluetoothGatt); 946 } 947 } 948 949 @Override onCharacteristicChanged( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)950 public void onCharacteristicChanged( 951 BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { 952 log("onCharacteristicChanged :: " + characteristic.getUuid().toString()); 953 if (characteristic.getUuid().equals(BassConstants.BASS_BCAST_RECEIVER_STATE)) { 954 log("onCharacteristicChanged is rcvr State :: " 955 + characteristic.getUuid().toString()); 956 if (characteristic.getValue() == null) { 957 Log.e(TAG, "Remote receiver state is NULL"); 958 return; 959 } 960 logByteArray("onCharacteristicChanged: Received ", 961 characteristic.getValue(), 962 0, 963 characteristic.getValue().length); 964 processBroadcastReceiverState(characteristic.getValue(), characteristic); 965 } 966 } 967 968 @Override onCharacteristicWrite( BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)969 public void onCharacteristicWrite( 970 BluetoothGatt gatt, 971 BluetoothGattCharacteristic characteristic, 972 int status) { 973 log("onCharacteristicWrite: " + characteristic.getUuid().toString() 974 + "status:" + status); 975 if (status == 0 976 && characteristic.getUuid() 977 .equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) { 978 log("BASS_BCAST_AUDIO_SCAN_CTRL_POINT is written successfully"); 979 } 980 Message m = obtainMessage(GATT_TXN_PROCESSED); 981 m.arg1 = status; 982 sendMessage(m); 983 } 984 } 985 986 /** 987 * Connects to the GATT server of the device. 988 * 989 * @return {@code true} if it successfully connects to the GATT server. 990 */ 991 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) connectGatt(Boolean autoConnect)992 public boolean connectGatt(Boolean autoConnect) { 993 if (mGattCallback == null) { 994 mGattCallback = new GattCallback(); 995 } 996 997 BluetoothGatt gatt = mDevice.connectGatt(mService, autoConnect, 998 mGattCallback, BluetoothDevice.TRANSPORT_LE, 999 (BluetoothDevice.PHY_LE_1M_MASK 1000 | BluetoothDevice.PHY_LE_2M_MASK 1001 | BluetoothDevice.PHY_LE_CODED_MASK), null); 1002 1003 if (gatt != null) { 1004 mBluetoothGatt = new BluetoothGattTestableWrapper(gatt); 1005 } 1006 1007 return mBluetoothGatt != null; 1008 } 1009 1010 /** 1011 * getAllSources 1012 */ getAllSources()1013 public List<BluetoothLeBroadcastReceiveState> getAllSources() { 1014 log("getAllSources"); 1015 List list = new ArrayList(mBluetoothLeBroadcastReceiveStates.values()); 1016 return list; 1017 } 1018 acquireAllBassChars()1019 void acquireAllBassChars() { 1020 clearCharsCache(); 1021 BluetoothGattService service = null; 1022 if (mBluetoothGatt != null) { 1023 log("getting Bass Service handle"); 1024 service = mBluetoothGatt.getService(BassConstants.BASS_UUID); 1025 } 1026 if (service == null) { 1027 log("acquireAllBassChars: BASS service not found"); 1028 return; 1029 } 1030 log("found BASS_SERVICE"); 1031 List<BluetoothGattCharacteristic> allChars = service.getCharacteristics(); 1032 int numOfChars = allChars.size(); 1033 mNumOfBroadcastReceiverStates = numOfChars - 1; 1034 log("Total number of chars" + numOfChars); 1035 for (int i = 0; i < allChars.size(); i++) { 1036 if (allChars.get(i).getUuid().equals(BassConstants.BASS_BCAST_AUDIO_SCAN_CTRL_POINT)) { 1037 mBroadcastScanControlPoint = allChars.get(i); 1038 log("Index of ScanCtrlPoint:" + i); 1039 } else { 1040 log("Reading " + i + "th ReceiverState"); 1041 mBroadcastCharacteristics.add(allChars.get(i)); 1042 Message m = obtainMessage(READ_BASS_CHARACTERISTICS); 1043 m.obj = allChars.get(i); 1044 sendMessage(m); 1045 } 1046 } 1047 } 1048 clearCharsCache()1049 void clearCharsCache() { 1050 if (mBroadcastCharacteristics != null) { 1051 mBroadcastCharacteristics.clear(); 1052 } 1053 if (mBroadcastScanControlPoint != null) { 1054 mBroadcastScanControlPoint = null; 1055 } 1056 mNumOfBroadcastReceiverStates = 0; 1057 if (mBluetoothLeBroadcastReceiveStates != null) { 1058 mBluetoothLeBroadcastReceiveStates.clear(); 1059 } 1060 mPendingOperation = -1; 1061 mPendingMetadata = null; 1062 mCurrentMetadata.clear(); 1063 mPendingRemove.clear(); 1064 } 1065 1066 @VisibleForTesting 1067 class Disconnected extends State { 1068 @Override enter()1069 public void enter() { 1070 log("Enter Disconnected(" + mDevice + "): " 1071 + messageWhatToString(getCurrentMessage().what)); 1072 clearCharsCache(); 1073 mNextSourceId = 0; 1074 removeDeferredMessages(DISCONNECT); 1075 if (mLastConnectionState == -1) { 1076 log("no Broadcast of initial profile state "); 1077 } else { 1078 broadcastConnectionState( 1079 mDevice, mLastConnectionState, BluetoothProfile.STATE_DISCONNECTED); 1080 if (mLastConnectionState != BluetoothProfile.STATE_DISCONNECTED) { 1081 // Reconnect in background if not disallowed by the service 1082 if (mService.okToConnect(mDevice) && mAllowReconnect) { 1083 connectGatt(false); 1084 } 1085 } 1086 } 1087 } 1088 1089 @Override exit()1090 public void exit() { 1091 log("Exit Disconnected(" + mDevice + "): " 1092 + messageWhatToString(getCurrentMessage().what)); 1093 mLastConnectionState = BluetoothProfile.STATE_DISCONNECTED; 1094 } 1095 1096 @Override processMessage(Message message)1097 public boolean processMessage(Message message) { 1098 log("Disconnected process message(" + mDevice 1099 + "): " + messageWhatToString(message.what)); 1100 switch (message.what) { 1101 case CONNECT: 1102 log("Connecting to " + mDevice); 1103 if (mBluetoothGatt != null) { 1104 Log.d(TAG, "clear off, pending wl connection"); 1105 mBluetoothGatt.disconnect(); 1106 mBluetoothGatt.close(); 1107 mBluetoothGatt = null; 1108 } 1109 mAllowReconnect = true; 1110 if (connectGatt(mIsAllowedList)) { 1111 transitionTo(mConnecting); 1112 } else { 1113 Log.e(TAG, "Disconnected: error connecting to " + mDevice); 1114 } 1115 break; 1116 case DISCONNECT: 1117 // Disconnect if there's an ongoing background connection 1118 mAllowReconnect = false; 1119 if (mBluetoothGatt != null) { 1120 log("Cancelling the background connection to " + mDevice); 1121 mBluetoothGatt.disconnect(); 1122 mBluetoothGatt.close(); 1123 mBluetoothGatt = null; 1124 } else { 1125 Log.d(TAG, "Disconnected: DISCONNECT ignored: " + mDevice); 1126 } 1127 break; 1128 case CONNECTION_STATE_CHANGED: 1129 int state = (int) message.obj; 1130 Log.w(TAG, "connection state changed:" + state); 1131 if (state == BluetoothProfile.STATE_CONNECTED) { 1132 log("remote/wl connection"); 1133 transitionTo(mConnected); 1134 } else { 1135 Log.w(TAG, "Disconnected: Connection failed to " + mDevice); 1136 } 1137 break; 1138 case PSYNC_ACTIVE_TIMEOUT: 1139 cancelActiveSync(null); 1140 break; 1141 default: 1142 log("DISCONNECTED: not handled message:" + message.what); 1143 return NOT_HANDLED; 1144 } 1145 return HANDLED; 1146 } 1147 } 1148 1149 @VisibleForTesting 1150 class Connecting extends State { 1151 @Override enter()1152 public void enter() { 1153 log("Enter Connecting(" + mDevice + "): " 1154 + messageWhatToString(getCurrentMessage().what)); 1155 sendMessageDelayed(CONNECT_TIMEOUT, mDevice, mConnectTimeoutMs); 1156 broadcastConnectionState( 1157 mDevice, mLastConnectionState, BluetoothProfile.STATE_CONNECTING); 1158 } 1159 1160 @Override exit()1161 public void exit() { 1162 log("Exit Connecting(" + mDevice + "): " 1163 + messageWhatToString(getCurrentMessage().what)); 1164 mLastConnectionState = BluetoothProfile.STATE_CONNECTING; 1165 removeMessages(CONNECT_TIMEOUT); 1166 } 1167 1168 @Override processMessage(Message message)1169 public boolean processMessage(Message message) { 1170 log("Connecting process message(" + mDevice + "): " 1171 + messageWhatToString(message.what)); 1172 switch (message.what) { 1173 case CONNECT: 1174 log("Already Connecting to " + mDevice); 1175 log("Ignore this connection request " + mDevice); 1176 break; 1177 case DISCONNECT: 1178 Log.w(TAG, "Connecting: DISCONNECT deferred: " + mDevice); 1179 deferMessage(message); 1180 break; 1181 case READ_BASS_CHARACTERISTICS: 1182 Log.w(TAG, "defer READ_BASS_CHARACTERISTICS requested!: " + mDevice); 1183 deferMessage(message); 1184 break; 1185 case CONNECTION_STATE_CHANGED: 1186 int state = (int) message.obj; 1187 Log.w(TAG, "Connecting: connection state changed:" + state); 1188 if (state == BluetoothProfile.STATE_CONNECTED) { 1189 transitionTo(mConnected); 1190 } else { 1191 Log.w(TAG, "Connection failed to " + mDevice); 1192 transitionTo(mDisconnected); 1193 } 1194 break; 1195 case CONNECT_TIMEOUT: 1196 Log.w(TAG, "CONNECT_TIMEOUT"); 1197 BluetoothDevice device = (BluetoothDevice) message.obj; 1198 if (!mDevice.equals(device)) { 1199 Log.e(TAG, "Unknown device timeout " + device); 1200 break; 1201 } 1202 transitionTo(mDisconnected); 1203 break; 1204 case PSYNC_ACTIVE_TIMEOUT: 1205 deferMessage(message); 1206 break; 1207 default: 1208 log("CONNECTING: not handled message:" + message.what); 1209 return NOT_HANDLED; 1210 } 1211 return HANDLED; 1212 } 1213 } 1214 getBisSyncFromChannelPreference( List<BluetoothLeBroadcastChannel> channels)1215 private static int getBisSyncFromChannelPreference( 1216 List<BluetoothLeBroadcastChannel> channels) { 1217 int bisSync = 0; 1218 for (BluetoothLeBroadcastChannel channel : channels) { 1219 if (channel.isSelected()) { 1220 if (channel.getChannelIndex() == 0) { 1221 Log.e(TAG, "getBisSyncFromChannelPreference: invalid channel index=0"); 1222 continue; 1223 } 1224 bisSync |= 1 << (channel.getChannelIndex() - 1); 1225 } 1226 } 1227 1228 return bisSync; 1229 } 1230 convertMetadataToAddSourceByteArray(BluetoothLeBroadcastMetadata metaData)1231 private byte[] convertMetadataToAddSourceByteArray(BluetoothLeBroadcastMetadata metaData) { 1232 ByteArrayOutputStream stream = new ByteArrayOutputStream(); 1233 BluetoothDevice advSource = metaData.getSourceDevice(); 1234 1235 // Opcode 1236 stream.write(OPCODE_ADD_SOURCE); 1237 1238 // Advertiser_Address_Type 1239 stream.write(metaData.getSourceAddressType()); 1240 1241 // Advertiser_Address 1242 byte[] bcastSourceAddr = Utils.getBytesFromAddress(advSource.getAddress()); 1243 BassUtils.reverse(bcastSourceAddr); 1244 stream.write(bcastSourceAddr, 0, 6); 1245 log("Address bytes: " + advSource.getAddress()); 1246 1247 // Advertising_SID 1248 stream.write(metaData.getSourceAdvertisingSid()); 1249 1250 // Broadcast_ID 1251 stream.write(metaData.getBroadcastId() & 0x00000000000000FF); 1252 stream.write((metaData.getBroadcastId() & 0x000000000000FF00) >>> 8); 1253 stream.write((metaData.getBroadcastId() & 0x0000000000FF0000) >>> 16); 1254 log("mBroadcastId: " + metaData.getBroadcastId()); 1255 1256 // PA_Sync 1257 if (!mDefNoPAS) { 1258 stream.write(0x01); 1259 } else { 1260 log("setting PA sync to ZERO"); 1261 stream.write(0x00); 1262 } 1263 1264 // PA_Interval 1265 stream.write((metaData.getPaSyncInterval() & 0x00000000000000FF)); 1266 stream.write((metaData.getPaSyncInterval() & 0x000000000000FF00) >>> 8); 1267 1268 // Num_Subgroups 1269 List<BluetoothLeBroadcastSubgroup> subGroups = metaData.getSubgroups(); 1270 stream.write(metaData.getSubgroups().size()); 1271 1272 for (BluetoothLeBroadcastSubgroup subGroup : subGroups) { 1273 // BIS_Sync 1274 int bisSync = getBisSyncFromChannelPreference(subGroup.getChannels()); 1275 if (bisSync == 0) { 1276 bisSync = 0xFFFFFFFF; 1277 } 1278 stream.write(bisSync & 0x00000000000000FF); 1279 stream.write((bisSync & 0x000000000000FF00) >>> 8); 1280 stream.write((bisSync & 0x0000000000FF0000) >>> 16); 1281 stream.write((bisSync & 0x00000000FF000000) >>> 24); 1282 1283 // Metadata_Length 1284 BluetoothLeAudioContentMetadata metadata = subGroup.getContentMetadata(); 1285 stream.write(metadata.getRawMetadata().length); 1286 1287 // Metadata 1288 stream.write(metadata.getRawMetadata(), 0, metadata.getRawMetadata().length); 1289 } 1290 1291 byte[] res = stream.toByteArray(); 1292 log("ADD_BCAST_SOURCE in Bytes"); 1293 BassUtils.printByteArray(res); 1294 return res; 1295 } 1296 convertBroadcastMetadataToUpdateSourceByteArray(int sourceId, BluetoothLeBroadcastMetadata metaData, int paSync)1297 private byte[] convertBroadcastMetadataToUpdateSourceByteArray(int sourceId, 1298 BluetoothLeBroadcastMetadata metaData, int paSync) { 1299 BluetoothLeBroadcastReceiveState existingState = 1300 getBroadcastReceiveStateForSourceId(sourceId); 1301 if (existingState == null) { 1302 log("no existing SI for update source op"); 1303 return null; 1304 } 1305 BluetoothDevice broadcastSource = metaData.getSourceDevice(); 1306 PeriodicAdvertisementResult paRes = 1307 mService.getPeriodicAdvertisementResult(broadcastSource); 1308 if (paRes == null) { 1309 Log.e(TAG, "No matching psync, scan res for update"); 1310 mService.getCallbacks().notifySourceRemoveFailed( 1311 mDevice, sourceId, BluetoothStatusCodes.ERROR_UNKNOWN); 1312 return null; 1313 } 1314 // populate metadata from BASE levelOne 1315 BaseData base = mService.getBase(paRes.getSyncHandle()); 1316 if (base == null) { 1317 Log.e(TAG, "No valid base data populated for this device"); 1318 mService.getCallbacks().notifySourceRemoveFailed( 1319 mDevice, sourceId, BluetoothStatusCodes.ERROR_UNKNOWN); 1320 return null; 1321 } 1322 byte numSubGroups = base.getNumberOfSubgroupsofBIG(); 1323 byte[] res = new byte[UPDATE_SOURCE_FIXED_LENGTH + numSubGroups * 5]; 1324 int offset = 0; 1325 // Opcode 1326 res[offset++] = OPCODE_UPDATE_SOURCE; 1327 // Source_ID 1328 res[offset++] = (byte) sourceId; 1329 // PA_Sync 1330 if (paSync != BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_INVALID) { 1331 res[offset++] = (byte) paSync; 1332 } else if (existingState.getPaSyncState() 1333 == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED) { 1334 res[offset++] = (byte) (0x01); 1335 } else { 1336 res[offset++] = (byte) 0x00; 1337 } 1338 // PA_Interval 1339 res[offset++] = (byte) 0xFF; 1340 res[offset++] = (byte) 0xFF; 1341 // Num_Subgroups 1342 res[offset++] = numSubGroups; 1343 for (int i = 0; i < numSubGroups; i++) { 1344 int bisIndexValue; 1345 if (paSync != BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_INVALID) { 1346 bisIndexValue = 0; 1347 } else { 1348 bisIndexValue = existingState.getBisSyncState().get(i).intValue(); 1349 } 1350 log("UPDATE_BCAST_SOURCE: bisIndexValue : " + bisIndexValue); 1351 // BIS_Sync 1352 res[offset++] = (byte) (bisIndexValue & 0x00000000000000FF); 1353 res[offset++] = (byte) ((bisIndexValue & 0x000000000000FF00) >>> 8); 1354 res[offset++] = (byte) ((bisIndexValue & 0x0000000000FF0000) >>> 16); 1355 res[offset++] = (byte) ((bisIndexValue & 0x00000000FF000000) >>> 24); 1356 // Metadata_Length; On Modify source, don't update any Metadata 1357 res[offset++] = 0; 1358 } 1359 log("UPDATE_BCAST_SOURCE in Bytes"); 1360 BassUtils.printByteArray(res); 1361 return res; 1362 } 1363 convertRecvStateToSetBroadcastCodeByteArray( BluetoothLeBroadcastReceiveState recvState)1364 private byte[] convertRecvStateToSetBroadcastCodeByteArray( 1365 BluetoothLeBroadcastReceiveState recvState) { 1366 byte[] res = new byte[BassConstants.PIN_CODE_CMD_LEN]; 1367 // Opcode 1368 res[0] = OPCODE_SET_BCAST_PIN; 1369 // Source_ID 1370 res[1] = (byte) recvState.getSourceId(); 1371 log("convertRecvStateToSetBroadcastCodeByteArray: Source device : " 1372 + recvState.getSourceDevice()); 1373 BluetoothLeBroadcastMetadata metaData = 1374 getCurrentBroadcastMetadata(recvState.getSourceId()); 1375 if (metaData == null) { 1376 Log.e(TAG, "Fail to find broadcast source, sourceId = " 1377 + recvState.getSourceId()); 1378 return null; 1379 } 1380 // Broadcast Code 1381 byte[] actualPIN = metaData.getBroadcastCode(); 1382 if (actualPIN == null) { 1383 Log.e(TAG, "actual PIN is null"); 1384 return null; 1385 } else { 1386 log("byte array broadcast Code:" + Arrays.toString(actualPIN)); 1387 log("pinLength:" + actualPIN.length); 1388 // Broadcast_Code, Fill the PIN code in the Last Position 1389 // This effectively adds padding zeros to LSB positions when the broadcast code 1390 // is shorter than 16 octets 1391 System.arraycopy( 1392 actualPIN, 0, res, 1393 (BassConstants.PIN_CODE_CMD_LEN - actualPIN.length), actualPIN.length); 1394 log("SET_BCAST_PIN in Bytes"); 1395 BassUtils.printByteArray(res); 1396 } 1397 return res; 1398 } 1399 isItRightTimeToUpdateBroadcastPin(byte sourceId)1400 private boolean isItRightTimeToUpdateBroadcastPin(byte sourceId) { 1401 Collection<BluetoothLeBroadcastReceiveState> recvStates = 1402 mBluetoothLeBroadcastReceiveStates.values(); 1403 Iterator<BluetoothLeBroadcastReceiveState> iterator = recvStates.iterator(); 1404 boolean retval = false; 1405 if (mForceSB) { 1406 log("force SB is set"); 1407 return true; 1408 } 1409 while (iterator.hasNext()) { 1410 BluetoothLeBroadcastReceiveState state = iterator.next(); 1411 if (state == null) { 1412 log("Source state is null"); 1413 continue; 1414 } 1415 if (sourceId == state.getSourceId() && state.getBigEncryptionState() 1416 == BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_CODE_REQUIRED) { 1417 retval = true; 1418 break; 1419 } 1420 } 1421 log("IsItRightTimeToUpdateBroadcastPIN returning:" + retval); 1422 return retval; 1423 } 1424 1425 @VisibleForTesting 1426 class Connected extends State { 1427 @Override enter()1428 public void enter() { 1429 log("Enter Connected(" + mDevice + "): " 1430 + messageWhatToString(getCurrentMessage().what)); 1431 removeDeferredMessages(CONNECT); 1432 if (mLastConnectionState == BluetoothProfile.STATE_CONNECTED) { 1433 log("CONNECTED->CONNECTED: Ignore"); 1434 // Broadcast for testing purpose only 1435 if (Utils.isInstrumentationTestMode()) { 1436 Intent intent = new Intent("android.bluetooth.bass_client.NOTIFY_TEST"); 1437 mService.sendBroadcast(intent, BLUETOOTH_CONNECT, 1438 Utils.getTempAllowlistBroadcastOptions()); 1439 } 1440 } else { 1441 broadcastConnectionState(mDevice, mLastConnectionState, 1442 BluetoothProfile.STATE_CONNECTED); 1443 } 1444 } 1445 1446 @Override exit()1447 public void exit() { 1448 log("Exit Connected(" + mDevice + "): " 1449 + messageWhatToString(getCurrentMessage().what)); 1450 mLastConnectionState = BluetoothProfile.STATE_CONNECTED; 1451 } 1452 1453 @Override processMessage(Message message)1454 public boolean processMessage(Message message) { 1455 log("Connected process message(" + mDevice + "): " + messageWhatToString(message.what)); 1456 BluetoothLeBroadcastMetadata metaData; 1457 switch (message.what) { 1458 case CONNECT: 1459 Log.w(TAG, "Connected: CONNECT ignored: " + mDevice); 1460 break; 1461 case DISCONNECT: 1462 log("Disconnecting from " + mDevice); 1463 mAllowReconnect = false; 1464 if (mBluetoothGatt != null) { 1465 mBluetoothGatt.disconnect(); 1466 mBluetoothGatt.close(); 1467 mBluetoothGatt = null; 1468 cancelActiveSync(null); 1469 transitionTo(mDisconnected); 1470 } else { 1471 log("mBluetoothGatt is null"); 1472 } 1473 break; 1474 case CONNECTION_STATE_CHANGED: 1475 int state = (int) message.obj; 1476 Log.w(TAG, "Connected:connection state changed:" + state); 1477 if (state == BluetoothProfile.STATE_CONNECTED) { 1478 Log.w(TAG, "device is already connected to Bass" + mDevice); 1479 } else { 1480 Log.w(TAG, "unexpected disconnected from " + mDevice); 1481 cancelActiveSync(null); 1482 transitionTo(mDisconnected); 1483 } 1484 break; 1485 case READ_BASS_CHARACTERISTICS: 1486 BluetoothGattCharacteristic characteristic = 1487 (BluetoothGattCharacteristic) message.obj; 1488 if (mBluetoothGatt != null) { 1489 mBluetoothGatt.readCharacteristic(characteristic); 1490 transitionTo(mConnectedProcessing); 1491 } else { 1492 Log.e(TAG, "READ_BASS_CHARACTERISTICS is ignored, Gatt handle is null"); 1493 } 1494 break; 1495 case START_SCAN_OFFLOAD: 1496 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 1497 mBroadcastScanControlPoint.setValue(REMOTE_SCAN_START); 1498 mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); 1499 mPendingOperation = message.what; 1500 transitionTo(mConnectedProcessing); 1501 } else { 1502 log("no Bluetooth Gatt handle, may need to fetch write"); 1503 } 1504 break; 1505 case STOP_SCAN_OFFLOAD: 1506 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 1507 mBroadcastScanControlPoint.setValue(REMOTE_SCAN_STOP); 1508 mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); 1509 mPendingOperation = message.what; 1510 transitionTo(mConnectedProcessing); 1511 } else { 1512 log("no Bluetooth Gatt handle, may need to fetch write"); 1513 } 1514 break; 1515 case SELECT_BCAST_SOURCE: 1516 ScanResult scanRes = (ScanResult) message.obj; 1517 boolean auto = ((int) message.arg1) == BassConstants.AUTO; 1518 selectSource(scanRes, auto); 1519 break; 1520 case ADD_BCAST_SOURCE: 1521 metaData = (BluetoothLeBroadcastMetadata) message.obj; 1522 log("Adding Broadcast source" + metaData); 1523 byte[] addSourceInfo = convertMetadataToAddSourceByteArray(metaData); 1524 if (addSourceInfo == null) { 1525 Log.e(TAG, "add source: source Info is NULL"); 1526 break; 1527 } 1528 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 1529 mBroadcastScanControlPoint.setValue(addSourceInfo); 1530 mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); 1531 mPendingOperation = message.what; 1532 mPendingMetadata = metaData; 1533 if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) { 1534 mSetBroadcastCodePending = true; 1535 } 1536 transitionTo(mConnectedProcessing); 1537 sendMessageDelayed(GATT_TXN_TIMEOUT, BassConstants.GATT_TXN_TIMEOUT_MS); 1538 } else { 1539 Log.e(TAG, "ADD_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal"); 1540 mService.getCallbacks().notifySourceAddFailed(mDevice, 1541 metaData, BluetoothStatusCodes.ERROR_UNKNOWN); 1542 } 1543 break; 1544 case UPDATE_BCAST_SOURCE: 1545 metaData = (BluetoothLeBroadcastMetadata) message.obj; 1546 int sourceId = message.arg1; 1547 int paSync = message.arg2; 1548 log("Updating Broadcast source" + metaData); 1549 byte[] updateSourceInfo = convertBroadcastMetadataToUpdateSourceByteArray( 1550 sourceId, metaData, paSync); 1551 if (updateSourceInfo == null) { 1552 Log.e(TAG, "update source: source Info is NULL"); 1553 break; 1554 } 1555 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 1556 mBroadcastScanControlPoint.setValue(updateSourceInfo); 1557 mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); 1558 mPendingOperation = message.what; 1559 mPendingSourceId = (byte) sourceId; 1560 if (paSync == BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE) { 1561 setPendingRemove(sourceId, true); 1562 } 1563 if (metaData.isEncrypted() && (metaData.getBroadcastCode() != null)) { 1564 mSetBroadcastCodePending = true; 1565 } 1566 mPendingMetadata = metaData; 1567 transitionTo(mConnectedProcessing); 1568 sendMessageDelayed(GATT_TXN_TIMEOUT, BassConstants.GATT_TXN_TIMEOUT_MS); 1569 } else { 1570 Log.e(TAG, "UPDATE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal"); 1571 mService.getCallbacks().notifySourceModifyFailed( 1572 mDevice, sourceId, BluetoothStatusCodes.ERROR_UNKNOWN); 1573 } 1574 break; 1575 case SET_BCAST_CODE: 1576 BluetoothLeBroadcastReceiveState recvState = 1577 (BluetoothLeBroadcastReceiveState) message.obj; 1578 log("SET_BCAST_CODE metaData: " + recvState); 1579 if (!isItRightTimeToUpdateBroadcastPin((byte) recvState.getSourceId())) { 1580 mSetBroadcastCodePending = true; 1581 mSetBroadcastPINRcvState = recvState; 1582 log("Ignore SET_BCAST now, but store it for later"); 1583 } else { 1584 byte[] setBroadcastPINcmd = 1585 convertRecvStateToSetBroadcastCodeByteArray(recvState); 1586 if (setBroadcastPINcmd == null) { 1587 Log.e(TAG, "SET_BCAST_CODE: Broadcast code is NULL"); 1588 break; 1589 } 1590 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 1591 mBroadcastScanControlPoint.setValue(setBroadcastPINcmd); 1592 mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); 1593 mPendingOperation = message.what; 1594 mPendingSourceId = (byte) recvState.getSourceId(); 1595 transitionTo(mConnectedProcessing); 1596 sendMessageDelayed(GATT_TXN_TIMEOUT, BassConstants.GATT_TXN_TIMEOUT_MS); 1597 mSetBroadcastCodePending = false; 1598 mSetBroadcastPINRcvState = null; 1599 } 1600 } 1601 break; 1602 case REMOVE_BCAST_SOURCE: 1603 byte sid = (byte) message.arg1; 1604 log("Removing Broadcast source, sourceId: " + sid); 1605 byte[] removeSourceInfo = new byte[2]; 1606 removeSourceInfo[0] = OPCODE_REMOVE_SOURCE; 1607 removeSourceInfo[1] = sid; 1608 if (mBluetoothGatt != null && mBroadcastScanControlPoint != null) { 1609 if (isPendingRemove((int) sid)) { 1610 setPendingRemove((int) sid, false); 1611 } 1612 1613 mBroadcastScanControlPoint.setValue(removeSourceInfo); 1614 mBluetoothGatt.writeCharacteristic(mBroadcastScanControlPoint); 1615 mPendingOperation = message.what; 1616 mPendingSourceId = sid; 1617 transitionTo(mConnectedProcessing); 1618 sendMessageDelayed(GATT_TXN_TIMEOUT, BassConstants.GATT_TXN_TIMEOUT_MS); 1619 } else { 1620 Log.e(TAG, "REMOVE_BCAST_SOURCE: no Bluetooth Gatt handle, Fatal"); 1621 mService.getCallbacks().notifySourceRemoveFailed(mDevice, 1622 sid, BluetoothStatusCodes.ERROR_UNKNOWN); 1623 } 1624 break; 1625 case PSYNC_ACTIVE_TIMEOUT: 1626 cancelActiveSync(null); 1627 break; 1628 default: 1629 log("CONNECTED: not handled message:" + message.what); 1630 return NOT_HANDLED; 1631 } 1632 return HANDLED; 1633 } 1634 } 1635 isSuccess(int status)1636 private boolean isSuccess(int status) { 1637 boolean ret = false; 1638 switch (status) { 1639 case BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST: 1640 case BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST: 1641 case BluetoothStatusCodes.REASON_REMOTE_REQUEST: 1642 case BluetoothStatusCodes.REASON_SYSTEM_POLICY: 1643 ret = true; 1644 break; 1645 default: 1646 break; 1647 } 1648 return ret; 1649 } 1650 sendPendingCallbacks(int pendingOp, int status)1651 void sendPendingCallbacks(int pendingOp, int status) { 1652 switch (pendingOp) { 1653 case START_SCAN_OFFLOAD: 1654 if (!isSuccess(status)) { 1655 if (!mAutoTriggered) { 1656 cancelActiveSync(null); 1657 } else { 1658 mAutoTriggered = false; 1659 } 1660 } 1661 break; 1662 case ADD_BCAST_SOURCE: 1663 if (!isSuccess(status)) { 1664 cancelActiveSync(null); 1665 Message message = obtainMessage(STOP_SCAN_OFFLOAD); 1666 sendMessage(message); 1667 mService.getCallbacks().notifySourceAddFailed(mDevice, 1668 mPendingMetadata, status); 1669 mPendingMetadata = null; 1670 } 1671 break; 1672 case UPDATE_BCAST_SOURCE: 1673 if (!mAutoTriggered) { 1674 if (!isSuccess(status)) { 1675 mService.getCallbacks().notifySourceModifyFailed(mDevice, 1676 mPendingSourceId, status); 1677 mPendingMetadata = null; 1678 } 1679 } else { 1680 mAutoTriggered = false; 1681 } 1682 break; 1683 case REMOVE_BCAST_SOURCE: 1684 if (!isSuccess(status)) { 1685 mService.getCallbacks().notifySourceRemoveFailed(mDevice, 1686 mPendingSourceId, status); 1687 } 1688 break; 1689 case SET_BCAST_CODE: 1690 log("sendPendingCallbacks: SET_BCAST_CODE"); 1691 break; 1692 default: 1693 log("sendPendingCallbacks: unhandled case"); 1694 break; 1695 } 1696 } 1697 1698 // public for testing, but private for non-testing 1699 @VisibleForTesting 1700 class ConnectedProcessing extends State { 1701 @Override enter()1702 public void enter() { 1703 log("Enter ConnectedProcessing(" + mDevice + "): " 1704 + messageWhatToString(getCurrentMessage().what)); 1705 1706 // Broadcast for testing purpose only 1707 if (Utils.isInstrumentationTestMode()) { 1708 Intent intent = new Intent("android.bluetooth.bass_client.NOTIFY_TEST"); 1709 mService.sendBroadcast(intent, BLUETOOTH_CONNECT, 1710 Utils.getTempAllowlistBroadcastOptions()); 1711 } 1712 } 1713 @Override exit()1714 public void exit() { 1715 /* Pending Metadata will be used to bond with source ID in receiver state notify */ 1716 if (mPendingOperation == REMOVE_BCAST_SOURCE) { 1717 mPendingMetadata = null; 1718 } 1719 1720 log("Exit ConnectedProcessing(" + mDevice + "): " 1721 + messageWhatToString(getCurrentMessage().what)); 1722 } 1723 @Override processMessage(Message message)1724 public boolean processMessage(Message message) { 1725 log("ConnectedProcessing process message(" + mDevice + "): " 1726 + messageWhatToString(message.what)); 1727 switch (message.what) { 1728 case CONNECT: 1729 Log.w(TAG, "CONNECT request is ignored" + mDevice); 1730 break; 1731 case DISCONNECT: 1732 Log.w(TAG, "DISCONNECT requested!: " + mDevice); 1733 mAllowReconnect = false; 1734 if (mBluetoothGatt != null) { 1735 mBluetoothGatt.disconnect(); 1736 mBluetoothGatt.close(); 1737 mBluetoothGatt = null; 1738 cancelActiveSync(null); 1739 transitionTo(mDisconnected); 1740 } else { 1741 log("mBluetoothGatt is null"); 1742 } 1743 break; 1744 case READ_BASS_CHARACTERISTICS: 1745 Log.w(TAG, "defer READ_BASS_CHARACTERISTICS requested!: " + mDevice); 1746 deferMessage(message); 1747 break; 1748 case CONNECTION_STATE_CHANGED: 1749 int state = (int) message.obj; 1750 Log.w(TAG, "ConnectedProcessing: connection state changed:" + state); 1751 if (state == BluetoothProfile.STATE_CONNECTED) { 1752 Log.w(TAG, "should never happen from this state"); 1753 } else { 1754 Log.w(TAG, "Unexpected disconnection " + mDevice); 1755 transitionTo(mDisconnected); 1756 } 1757 break; 1758 case GATT_TXN_PROCESSED: 1759 removeMessages(GATT_TXN_TIMEOUT); 1760 int status = (int) message.arg1; 1761 log("GATT transaction processed for" + mDevice); 1762 if (status == BluetoothGatt.GATT_SUCCESS) { 1763 sendPendingCallbacks( 1764 mPendingOperation, 1765 BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST); 1766 } else { 1767 sendPendingCallbacks( 1768 mPendingOperation, 1769 BluetoothStatusCodes.ERROR_UNKNOWN); 1770 } 1771 transitionTo(mConnected); 1772 break; 1773 case GATT_TXN_TIMEOUT: 1774 log("GATT transaction timeout for" + mDevice); 1775 sendPendingCallbacks( 1776 mPendingOperation, 1777 BluetoothStatusCodes.ERROR_UNKNOWN); 1778 mPendingOperation = -1; 1779 mPendingSourceId = -1; 1780 transitionTo(mConnected); 1781 break; 1782 case START_SCAN_OFFLOAD: 1783 case STOP_SCAN_OFFLOAD: 1784 case SELECT_BCAST_SOURCE: 1785 case ADD_BCAST_SOURCE: 1786 case SET_BCAST_CODE: 1787 case REMOVE_BCAST_SOURCE: 1788 case PSYNC_ACTIVE_TIMEOUT: 1789 log("defer the message:" + message.what + "so that it will be processed later"); 1790 deferMessage(message); 1791 break; 1792 default: 1793 log("CONNECTEDPROCESSING: not handled message:" + message.what); 1794 return NOT_HANDLED; 1795 } 1796 return HANDLED; 1797 } 1798 } 1799 broadcastConnectionState(BluetoothDevice device, int fromState, int toState)1800 void broadcastConnectionState(BluetoothDevice device, int fromState, int toState) { 1801 log("broadcastConnectionState " + device + ": " + fromState + "->" + toState); 1802 if (fromState == BluetoothProfile.STATE_CONNECTED 1803 && toState == BluetoothProfile.STATE_CONNECTED) { 1804 log("CONNECTED->CONNECTED: Ignore"); 1805 return; 1806 } 1807 1808 Intent intent = new Intent(BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED); 1809 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState); 1810 intent.putExtra(BluetoothProfile.EXTRA_STATE, toState); 1811 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); 1812 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1813 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1814 mService.sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions()); 1815 } 1816 getConnectionState()1817 int getConnectionState() { 1818 String currentState = "Unknown"; 1819 if (getCurrentState() != null) { 1820 currentState = getCurrentState().getName(); 1821 } 1822 switch (currentState) { 1823 case "Disconnected": 1824 log("Disconnected"); 1825 return BluetoothProfile.STATE_DISCONNECTED; 1826 case "Connecting": 1827 log("Connecting"); 1828 return BluetoothProfile.STATE_CONNECTING; 1829 case "Connected": 1830 case "ConnectedProcessing": 1831 log("connected"); 1832 return BluetoothProfile.STATE_CONNECTED; 1833 default: 1834 Log.e(TAG, "Bad currentState: " + currentState); 1835 return BluetoothProfile.STATE_DISCONNECTED; 1836 } 1837 } 1838 getMaximumSourceCapacity()1839 int getMaximumSourceCapacity() { 1840 return mNumOfBroadcastReceiverStates; 1841 } 1842 getDevice()1843 BluetoothDevice getDevice() { 1844 return mDevice; 1845 } 1846 isConnected()1847 synchronized boolean isConnected() { 1848 return getCurrentState() == mConnected; 1849 } 1850 messageWhatToString(int what)1851 public static String messageWhatToString(int what) { 1852 switch (what) { 1853 case CONNECT: 1854 return "CONNECT"; 1855 case DISCONNECT: 1856 return "DISCONNECT"; 1857 case CONNECTION_STATE_CHANGED: 1858 return "CONNECTION_STATE_CHANGED"; 1859 case GATT_TXN_PROCESSED: 1860 return "GATT_TXN_PROCESSED"; 1861 case READ_BASS_CHARACTERISTICS: 1862 return "READ_BASS_CHARACTERISTICS"; 1863 case START_SCAN_OFFLOAD: 1864 return "START_SCAN_OFFLOAD"; 1865 case STOP_SCAN_OFFLOAD: 1866 return "STOP_SCAN_OFFLOAD"; 1867 case ADD_BCAST_SOURCE: 1868 return "ADD_BCAST_SOURCE"; 1869 case SELECT_BCAST_SOURCE: 1870 return "SELECT_BCAST_SOURCE"; 1871 case UPDATE_BCAST_SOURCE: 1872 return "UPDATE_BCAST_SOURCE"; 1873 case SET_BCAST_CODE: 1874 return "SET_BCAST_CODE"; 1875 case REMOVE_BCAST_SOURCE: 1876 return "REMOVE_BCAST_SOURCE"; 1877 case PSYNC_ACTIVE_TIMEOUT: 1878 return "PSYNC_ACTIVE_TIMEOUT"; 1879 case CONNECT_TIMEOUT: 1880 return "CONNECT_TIMEOUT"; 1881 default: 1882 break; 1883 } 1884 return Integer.toString(what); 1885 } 1886 1887 /** 1888 * Dump info 1889 */ dump(StringBuilder sb)1890 public void dump(StringBuilder sb) { 1891 ProfileService.println(sb, "mDevice: " + mDevice); 1892 ProfileService.println(sb, " StateMachine: " + this); 1893 // Dump the state machine logs 1894 StringWriter stringWriter = new StringWriter(); 1895 PrintWriter printWriter = new PrintWriter(stringWriter); 1896 super.dump(new FileDescriptor(), printWriter, new String[] {}); 1897 printWriter.flush(); 1898 stringWriter.flush(); 1899 ProfileService.println(sb, " StateMachineLog:"); 1900 Scanner scanner = new Scanner(stringWriter.toString()); 1901 while (scanner.hasNextLine()) { 1902 String line = scanner.nextLine(); 1903 ProfileService.println(sb, " " + line); 1904 } 1905 scanner.close(); 1906 } 1907 1908 @Override log(String msg)1909 protected void log(String msg) { 1910 if (BassConstants.BASS_DBG) { 1911 super.log(msg); 1912 } 1913 } 1914 logByteArray(String prefix, byte[] value, int offset, int count)1915 private static void logByteArray(String prefix, byte[] value, int offset, int count) { 1916 StringBuilder builder = new StringBuilder(prefix); 1917 for (int i = offset; i < count; i++) { 1918 builder.append(String.format("0x%02X", value[i])); 1919 if (i != value.length - 1) { 1920 builder.append(", "); 1921 } 1922 } 1923 Log.d(TAG, builder.toString()); 1924 } 1925 1926 /** Mockable wrapper of {@link BluetoothGatt}. */ 1927 @VisibleForTesting 1928 public static class BluetoothGattTestableWrapper { 1929 public final BluetoothGatt mWrappedBluetoothGatt; 1930 BluetoothGattTestableWrapper(BluetoothGatt bluetoothGatt)1931 BluetoothGattTestableWrapper(BluetoothGatt bluetoothGatt) { 1932 mWrappedBluetoothGatt = bluetoothGatt; 1933 } 1934 1935 /** See {@link BluetoothGatt#getServices()}. */ getServices()1936 public List<BluetoothGattService> getServices() { 1937 return mWrappedBluetoothGatt.getServices(); 1938 } 1939 1940 /** See {@link BluetoothGatt#getService(UUID)}. */ 1941 @Nullable getService(UUID uuid)1942 public BluetoothGattService getService(UUID uuid) { 1943 return mWrappedBluetoothGatt.getService(uuid); 1944 } 1945 1946 /** See {@link BluetoothGatt#discoverServices()}. */ discoverServices()1947 public boolean discoverServices() { 1948 return mWrappedBluetoothGatt.discoverServices(); 1949 } 1950 1951 /** 1952 * See {@link BluetoothGatt#readCharacteristic( 1953 * BluetoothGattCharacteristic)}. 1954 */ readCharacteristic(BluetoothGattCharacteristic characteristic)1955 public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { 1956 return mWrappedBluetoothGatt.readCharacteristic(characteristic); 1957 } 1958 1959 /** 1960 * See {@link BluetoothGatt#writeCharacteristic( 1961 * BluetoothGattCharacteristic, byte[], int)} . 1962 */ writeCharacteristic(BluetoothGattCharacteristic characteristic)1963 public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) { 1964 return mWrappedBluetoothGatt.writeCharacteristic(characteristic); 1965 } 1966 1967 /** See {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)}. */ readDescriptor(BluetoothGattDescriptor descriptor)1968 public boolean readDescriptor(BluetoothGattDescriptor descriptor) { 1969 return mWrappedBluetoothGatt.readDescriptor(descriptor); 1970 } 1971 1972 /** 1973 * See {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, 1974 * byte[])}. 1975 */ writeDescriptor(BluetoothGattDescriptor descriptor)1976 public boolean writeDescriptor(BluetoothGattDescriptor descriptor) { 1977 return mWrappedBluetoothGatt.writeDescriptor(descriptor); 1978 } 1979 1980 /** See {@link BluetoothGatt#requestMtu(int)}. */ requestMtu(int mtu)1981 public boolean requestMtu(int mtu) { 1982 return mWrappedBluetoothGatt.requestMtu(mtu); 1983 } 1984 1985 /** See {@link BluetoothGatt#setCharacteristicNotification}. */ setCharacteristicNotification( BluetoothGattCharacteristic characteristic, boolean enable)1986 public boolean setCharacteristicNotification( 1987 BluetoothGattCharacteristic characteristic, boolean enable) { 1988 return mWrappedBluetoothGatt.setCharacteristicNotification(characteristic, enable); 1989 } 1990 1991 /** See {@link BluetoothGatt#disconnect()}. */ disconnect()1992 public void disconnect() { 1993 mWrappedBluetoothGatt.disconnect(); 1994 } 1995 1996 /** See {@link BluetoothGatt#close()}. */ close()1997 public void close() { 1998 mWrappedBluetoothGatt.close(); 1999 } 2000 } 2001 2002 } 2003