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