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 android.bluetooth; 18 19 import android.annotation.IntDef; 20 import android.annotation.IntRange; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SystemApi; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 import java.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.List; 32 import java.util.Objects; 33 34 /** 35 * The {@link BluetoothLeBroadcastReceiveState} is used by the BASS server to expose information 36 * about a Broadcast Source. 37 * 38 * <p>It represents the current synchronization state of the server to a PA and/or a BIG containing 39 * one or more subgroups containing one or more BISes transmitted by that Broadcast Source. The 40 * Broadcast Receive State characteristic is also used to inform clients whether the server has 41 * detected that the BIS is encrypted, whether the server requires a Broadcast_Code, and whether the 42 * server is decrypting the BIS. 43 * 44 * @hide 45 */ 46 @SystemApi 47 public final class BluetoothLeBroadcastReceiveState implements Parcelable { 48 /** 49 * Periodic Advertising Synchronization state. 50 * 51 * <p>Periodic Advertising (PA) enables the LE Audio Broadcast Assistant to discover broadcast 52 * audio streams as well as the audio stream configuration on behalf of an LE Audio Broadcast 53 * Sink. This information can then be transferred to the LE Audio Broadcast Sink using the 54 * Periodic Advertising Synchronization Transfer (PAST) procedure. 55 * 56 * @hide 57 */ 58 @IntDef( 59 prefix = "PA_SYNC_STATE_", 60 value = { 61 PA_SYNC_STATE_IDLE, 62 PA_SYNC_STATE_SYNCINFO_REQUEST, 63 PA_SYNC_STATE_SYNCHRONIZED, 64 PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE, 65 PA_SYNC_STATE_NO_PAST 66 }) 67 @Retention(RetentionPolicy.SOURCE) 68 public @interface PaSyncState {} 69 70 /** 71 * Indicates that the Broadcast Sink is not synchronized with the Periodic Advertisements (PA) 72 * 73 * @hide 74 */ 75 @SystemApi public static final int PA_SYNC_STATE_IDLE = 0; 76 77 /** 78 * Indicates that the Broadcast Sink requested the Broadcast Assistant to synchronize with the 79 * Periodic Advertisements (PA). 80 * 81 * <p>This is also known as scan delegation or scan offloading. 82 * 83 * @hide 84 */ 85 @SystemApi public static final int PA_SYNC_STATE_SYNCINFO_REQUEST = 1; 86 87 /** 88 * Indicates that the Broadcast Sink is synchronized with the Periodic Advertisements (PA). 89 * 90 * @hide 91 */ 92 @SystemApi public static final int PA_SYNC_STATE_SYNCHRONIZED = 2; 93 94 /** 95 * Indicates that the Broadcast Sink was unable to synchronize with the Periodic Advertisements 96 * (PA). 97 * 98 * @hide 99 */ 100 @SystemApi public static final int PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE = 3; 101 102 /** 103 * Indicates that the Broadcast Sink should be synchronized with the Periodic Advertisements 104 * (PA) using the Periodic Advertisements Synchronization Transfer (PAST) procedure. 105 * 106 * @hide 107 */ 108 @SystemApi public static final int PA_SYNC_STATE_NO_PAST = 4; 109 110 /** 111 * Indicates that the Broadcast Sink synchronization state is invalid. 112 * 113 * @hide 114 */ 115 public static final int PA_SYNC_STATE_INVALID = 0xFFFF; 116 117 /** @hide */ 118 @IntDef( 119 prefix = "BIG_ENCRYPTION_STATE_", 120 value = { 121 BIG_ENCRYPTION_STATE_NOT_ENCRYPTED, 122 BIG_ENCRYPTION_STATE_CODE_REQUIRED, 123 BIG_ENCRYPTION_STATE_DECRYPTING, 124 BIG_ENCRYPTION_STATE_BAD_CODE 125 }) 126 @Retention(RetentionPolicy.SOURCE) 127 public @interface BigEncryptionState {} 128 129 /** 130 * Indicates that the Broadcast Sink is synchronized with an unencrypted audio stream from a 131 * Broadcast Source 132 * 133 * @hide 134 */ 135 @SystemApi public static final int BIG_ENCRYPTION_STATE_NOT_ENCRYPTED = 0; 136 137 /** 138 * Indicates that the Broadcast Sink needs a Broadcast Code to synchronize with an audio stream 139 * from a Broadcast Source, which was not provided when the audio stream from the Broadcast 140 * Source was added. 141 * 142 * @hide 143 */ 144 @SystemApi public static final int BIG_ENCRYPTION_STATE_CODE_REQUIRED = 1; 145 146 /** 147 * Indicates that the Broadcast Sink is synchronized with an encrypted audio stream from a 148 * Broadcast Source. 149 * 150 * @hide 151 */ 152 @SystemApi public static final int BIG_ENCRYPTION_STATE_DECRYPTING = 2; 153 154 /** 155 * Indicates that the Broadcast Sink is unable to decrypt an audio stream from a Broadcast 156 * Source due to an incorrect Broadcast Code. 157 * 158 * @hide 159 */ 160 @SystemApi public static final int BIG_ENCRYPTION_STATE_BAD_CODE = 3; 161 162 /** 163 * Indicates that the Broadcast Sink encryption state is invalid. 164 * 165 * @hide 166 */ 167 public static final int BIG_ENCRYPTION_STATE_INVALID = 0xFFFF; 168 169 private final int mSourceId; 170 private final @BluetoothDevice.AddressType int mSourceAddressType; 171 private final BluetoothDevice mSourceDevice; 172 private final int mSourceAdvertisingSid; 173 private final int mBroadcastId; 174 private final @PaSyncState int mPaSyncState; 175 private final @BigEncryptionState int mBigEncryptionState; 176 private final byte[] mBadCode; 177 private final int mNumSubgroups; 178 private final List<Long> mBisSyncState; 179 private final List<BluetoothLeAudioContentMetadata> mSubgroupMetadata; 180 paSyncStateToString(int paSyncState)181 private static String paSyncStateToString(int paSyncState) { 182 switch (paSyncState) { 183 case 0x00: 184 return "Not synchronized to PA: [" + Integer.toString(paSyncState) + "]"; 185 case 0x01: 186 return "SyncInfo Request: [" + Integer.toString(paSyncState) + "]"; 187 case 0x02: 188 return "Synchronized to PA: [" + Integer.toString(paSyncState) + "]"; 189 case 0x03: 190 return "Failed to synchronize to PA: [" + Integer.toString(paSyncState) + "]"; 191 case 0x04: 192 return "No PAST: [" + Integer.toString(paSyncState) + "]"; 193 default: 194 return "RFU: [" + Integer.toString(paSyncState) + "]"; 195 } 196 } 197 bigEncryptionStateToString(int bigEncryptionState)198 private static String bigEncryptionStateToString(int bigEncryptionState) { 199 switch (bigEncryptionState) { 200 case 0x00: 201 return "Not encrypted: [" + Integer.toString(bigEncryptionState) + "]"; 202 case 0x01: 203 return "Broadcast_Code required: [" + Integer.toString(bigEncryptionState) + "]"; 204 case 0x02: 205 return "Decrypting: [" + Integer.toString(bigEncryptionState) + "]"; 206 case 0x03: 207 return "Bad_Code (incorrect encryption key): [" 208 + Integer.toString(bigEncryptionState) 209 + "]"; 210 default: 211 return "RFU: [" + Integer.toString(bigEncryptionState) + "]"; 212 } 213 } 214 bisSyncStateToString(Long bisSyncState, int bisSyncStateIndex)215 private static String bisSyncStateToString(Long bisSyncState, int bisSyncStateIndex) { 216 if (bisSyncState == 0) { 217 return "Not synchronized to BIS_index[" 218 + Integer.toString(bisSyncStateIndex) 219 + "]: [" 220 + String.valueOf(bisSyncState) 221 + "]"; 222 } else if (bisSyncState > 0 && bisSyncState < 0xFFFFFFFF) { 223 return "Synchronized to BIS_index[" 224 + Integer.toString(bisSyncStateIndex) 225 + "]: [" 226 + String.valueOf(bisSyncState) 227 + "]"; 228 } else if (bisSyncState == 0xFFFFFFFF) { 229 return "Failed to sync to BIG: [" + String.valueOf(bisSyncState) + "]"; 230 } else { 231 return "[" + String.valueOf(bisSyncState) + "]"; 232 } 233 } 234 235 /** 236 * Constructor to create a read-only {@link BluetoothLeBroadcastReceiveState} instance. 237 * 238 * @throws NullPointerException if sourceDevice, bisSyncState, or subgroupMetadata is null 239 * @throws IllegalArgumentException if sourceID is not [0, 0xFF] or if sourceAddressType is 240 * invalid or if bisSyncState.size() != numSubgroups or if subgroupMetadata.size() != 241 * numSubgroups or if paSyncState or bigEncryptionState is not recognized bye IntDef 242 * @hide 243 */ BluetoothLeBroadcastReceiveState( @ntRangefrom = 0x00, to = 0xFF) int sourceId, @BluetoothDevice.AddressType int sourceAddressType, @NonNull BluetoothDevice sourceDevice, int sourceAdvertisingSid, int broadcastId, @PaSyncState int paSyncState, @BigEncryptionState int bigEncryptionState, byte[] badCode, @IntRange(from = 0x00) int numSubgroups, @NonNull List<Long> bisSyncState, @NonNull List<BluetoothLeAudioContentMetadata> subgroupMetadata)244 public BluetoothLeBroadcastReceiveState( 245 @IntRange(from = 0x00, to = 0xFF) int sourceId, 246 @BluetoothDevice.AddressType int sourceAddressType, 247 @NonNull BluetoothDevice sourceDevice, 248 int sourceAdvertisingSid, 249 int broadcastId, 250 @PaSyncState int paSyncState, 251 @BigEncryptionState int bigEncryptionState, 252 byte[] badCode, 253 @IntRange(from = 0x00) int numSubgroups, 254 @NonNull List<Long> bisSyncState, 255 @NonNull List<BluetoothLeAudioContentMetadata> subgroupMetadata) { 256 if (sourceId < 0x00 || sourceId > 0xFF) { 257 throw new IllegalArgumentException( 258 "sourceId " + sourceId + " does not fall between 0x00 and 0xFF"); 259 } 260 Objects.requireNonNull(sourceDevice, "sourceDevice cannot be null"); 261 if (sourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) { 262 throw new IllegalArgumentException("sourceAddressType cannot be ADDRESS_TYPE_UNKNOWN"); 263 } 264 if (sourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM 265 && sourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) { 266 throw new IllegalArgumentException( 267 "sourceAddressType " + sourceAddressType + " is invalid"); 268 } 269 Objects.requireNonNull(bisSyncState, "bisSyncState cannot be null"); 270 if (bisSyncState.size() != numSubgroups) { 271 throw new IllegalArgumentException( 272 "bisSyncState.size() " 273 + bisSyncState.size() 274 + " must be equal to numSubgroups " 275 + numSubgroups); 276 } 277 Objects.requireNonNull(subgroupMetadata, "subgroupMetadata cannot be null"); 278 if (subgroupMetadata.size() != numSubgroups) { 279 throw new IllegalArgumentException( 280 "subgroupMetadata.size() " 281 + subgroupMetadata.size() 282 + " must be equal to numSubgroups " 283 + numSubgroups); 284 } 285 if (paSyncState != PA_SYNC_STATE_IDLE 286 && paSyncState != PA_SYNC_STATE_SYNCINFO_REQUEST 287 && paSyncState != PA_SYNC_STATE_SYNCHRONIZED 288 && paSyncState != PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE 289 && paSyncState != PA_SYNC_STATE_NO_PAST 290 && paSyncState != PA_SYNC_STATE_INVALID) { 291 throw new IllegalArgumentException("unrecognized paSyncState " + paSyncState); 292 } 293 if (bigEncryptionState != BIG_ENCRYPTION_STATE_NOT_ENCRYPTED 294 && bigEncryptionState != BIG_ENCRYPTION_STATE_CODE_REQUIRED 295 && bigEncryptionState != BIG_ENCRYPTION_STATE_DECRYPTING 296 && bigEncryptionState != BIG_ENCRYPTION_STATE_BAD_CODE 297 && bigEncryptionState != BIG_ENCRYPTION_STATE_INVALID) { 298 throw new IllegalArgumentException( 299 "unrecognized bigEncryptionState " + bigEncryptionState); 300 } 301 if (badCode != null && badCode.length != 16) { 302 throw new IllegalArgumentException( 303 "badCode must be 16 bytes long of null, but is " 304 + badCode.length 305 + " + bytes long"); 306 } 307 mSourceId = sourceId; 308 mSourceAddressType = sourceAddressType; 309 mSourceDevice = sourceDevice; 310 mSourceAdvertisingSid = sourceAdvertisingSid; 311 mBroadcastId = broadcastId; 312 mPaSyncState = paSyncState; 313 mBigEncryptionState = bigEncryptionState; 314 mBadCode = badCode; 315 mNumSubgroups = numSubgroups; 316 mBisSyncState = bisSyncState; 317 mSubgroupMetadata = subgroupMetadata; 318 } 319 320 /** 321 * Get the source ID assigned by the BASS server 322 * 323 * <p>Shall be unique for each instance of the Broadcast Receive State characteristic exposed by 324 * the server 325 * 326 * @return source ID assigned by the BASS server 327 * @hide 328 */ 329 @SystemApi getSourceId()330 public @IntRange(from = 0x00, to = 0xFF) int getSourceId() { 331 return mSourceId; 332 } 333 334 /** 335 * Get the address type of the Broadcast Source 336 * 337 * Can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} or 338 * {@link BluetoothDevice#ADDRESS_TYPE_RANDOM 339 * 340 * @return address type of the Broadcast Source 341 * @hide 342 */ 343 @SystemApi getSourceAddressType()344 public @BluetoothDevice.AddressType int getSourceAddressType() { 345 return mSourceAddressType; 346 } 347 348 /** 349 * Get the MAC address of the Broadcast Source, which can be Public Device Address, Random 350 * Device Address, Public Identity Address or Random (static) Identity Address 351 * 352 * @return MAC address of the Broadcast Source 353 * @hide 354 */ 355 @SystemApi getSourceDevice()356 public @NonNull BluetoothDevice getSourceDevice() { 357 return mSourceDevice; 358 } 359 360 /** 361 * Get Advertising_SID subfield of the ADI field of the AUX_ADV_IND PDU or the 362 * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the 363 * Broadcast Source. 364 * 365 * @return 1-byte long Advertising_SID of the Broadcast Source 366 * @hide 367 */ 368 @SystemApi getSourceAdvertisingSid()369 public int getSourceAdvertisingSid() { 370 return mSourceAdvertisingSid; 371 } 372 373 /** 374 * Broadcast_ID of the Broadcast Source 375 * 376 * @return 3-byte long Broadcast_ID of the Broadcast Source 377 * @hide 378 */ 379 @SystemApi getBroadcastId()380 public int getBroadcastId() { 381 return mBroadcastId; 382 } 383 384 /** 385 * Get the Periodic Advertisement synchronization state between the Broadcast Sink and the 386 * Broadcast source 387 * 388 * <p>Possible values are {@link #PA_SYNC_STATE_IDLE}, {@link #PA_SYNC_STATE_SYNCINFO_REQUEST}, 389 * {@link #PA_SYNC_STATE_SYNCHRONIZED}, {@link #PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE}, {@link 390 * #PA_SYNC_STATE_NO_PAST} 391 * 392 * @return Periodic Advertisement synchronization state 393 * @hide 394 */ 395 @SystemApi getPaSyncState()396 public @PaSyncState int getPaSyncState() { 397 return mPaSyncState; 398 } 399 400 /** 401 * Get the encryption state of a Broadcast Isochronous Group (BIG) 402 * 403 * <p>Possible values are {@link #BIG_ENCRYPTION_STATE_NOT_ENCRYPTED}, {@link 404 * #BIG_ENCRYPTION_STATE_CODE_REQUIRED}, {@link #BIG_ENCRYPTION_STATE_DECRYPTING}, {@link 405 * #BIG_ENCRYPTION_STATE_DECRYPTING}, and {@link #BIG_ENCRYPTION_STATE_BAD_CODE} 406 * 407 * @return encryption state of a Broadcast Isochronous Group (BIG) 408 * @hide 409 */ 410 @SystemApi getBigEncryptionState()411 public @BigEncryptionState int getBigEncryptionState() { 412 return mBigEncryptionState; 413 } 414 415 /** 416 * If {@link #getBigEncryptionState()} returns {@link #BIG_ENCRYPTION_STATE_BAD_CODE}, this 417 * method returns the value of the incorrect 16-octet Broadcast Code that fails to decrypt an 418 * audio stream from a Broadcast Source. 419 * 420 * @return 16-octet Broadcast Code, or null if {@link #getBigEncryptionState()} does not return 421 * {@link #BIG_ENCRYPTION_STATE_BAD_CODE} 422 * @hide 423 */ 424 @SystemApi getBadCode()425 public @Nullable byte[] getBadCode() { 426 return mBadCode; 427 } 428 429 /** 430 * Get number of Broadcast subgroups being added to this sink 431 * 432 * @return number of Broadcast subgroups being added to this sink 433 */ getNumSubgroups()434 public int getNumSubgroups() { 435 return mNumSubgroups; 436 } 437 438 /** 439 * Get a list of bitfield on whether a Broadcast Isochronous Stream (BIS) is synchronized 440 * between the sink and source 441 * 442 * <p>The number of items in the returned list is the same as {@link #getNumSubgroups()}. For 443 * each subgroup, at most 31 BISes are available and their synchronization state is indicated by 444 * its bit value at the particular offset (i.e. Bit 0-30 = BIS_index[1-31]) 445 * 446 * <p>For example, if (BisSyncState & 0b1 << 5) != 0, BIS 5 is synchronized between source and 447 * sync 448 * 449 * <p>There is a special case, 0xFFFFFFFF to indicate Broadcast Sink failed to synchronize to a 450 * particular subgroup 451 * 452 * @return a list of bitfield on whether a Broadcast Isochronous Stream (BIS) is synchronized 453 * between the sink and source 454 * @hide 455 */ 456 @SystemApi getBisSyncState()457 public @NonNull List<Long> getBisSyncState() { 458 return mBisSyncState; 459 } 460 461 /** 462 * Get metadata for every subgroup added to this Broadcast Sink 463 * 464 * <p>The number of items in the returned list is the same as {@link #getNumSubgroups()}. 465 * 466 * @return metadata for every subgroup added to this Broadcast Sink 467 * @hide 468 */ 469 @SystemApi getSubgroupMetadata()470 public @NonNull List<BluetoothLeAudioContentMetadata> getSubgroupMetadata() { 471 return mSubgroupMetadata; 472 } 473 474 /** 475 * {@inheritDoc} 476 * 477 * @hide 478 */ 479 @Override describeContents()480 public int describeContents() { 481 return 0; 482 } 483 484 /** 485 * {@inheritDoc} 486 * 487 * @hide 488 */ 489 @Override writeToParcel(Parcel out, int flags)490 public void writeToParcel(Parcel out, int flags) { 491 out.writeInt(mSourceId); 492 out.writeInt(mSourceAddressType); 493 out.writeTypedObject(mSourceDevice, 0); 494 out.writeInt(mSourceAdvertisingSid); 495 out.writeInt(mBroadcastId); 496 out.writeInt(mPaSyncState); 497 out.writeInt(mBigEncryptionState); 498 499 if (mBadCode != null) { 500 out.writeInt(mBadCode.length); 501 out.writeByteArray(mBadCode); 502 } else { 503 // -1 indicates that there is no "bad broadcast code" 504 out.writeInt(-1); 505 } 506 out.writeInt(mNumSubgroups); 507 out.writeList(mBisSyncState); 508 out.writeTypedList(mSubgroupMetadata); 509 } 510 511 /** 512 * {@inheritDoc} 513 * 514 * @hide 515 */ 516 @Override toString()517 public String toString() { 518 String receiveState = 519 ("Receiver state: " 520 + "\n Source ID:" 521 + mSourceId 522 + "\n Source Address Type:" 523 + (int) mSourceAddressType 524 + "\n Source Address:" 525 + mSourceDevice.toString() 526 + "\n Source Adv SID:" 527 + mSourceAdvertisingSid 528 + "\n Broadcast ID:" 529 + mBroadcastId 530 + "\n PA Sync State:" 531 + paSyncStateToString(mPaSyncState) 532 + "\n BIG Encryption Status:" 533 + bigEncryptionStateToString(mBigEncryptionState) 534 + "\n Bad Broadcast Code:" 535 + Arrays.toString(mBadCode) 536 + "\n Number Of Subgroups:" 537 + mNumSubgroups); 538 for (int i = 0; i < mNumSubgroups; i++) { 539 receiveState += 540 ("\n Subgroup index:" 541 + i 542 + "\n BIS Sync State:" 543 + bisSyncStateToString(mBisSyncState.get(i), i)) 544 + "\n Metadata:" 545 + "\n ProgramInfo:" 546 + mSubgroupMetadata.get(i).getProgramInfo() 547 + "\n Language:" 548 + mSubgroupMetadata.get(i).getLanguage() 549 + "\n RawData:" 550 + Arrays.toString(mSubgroupMetadata.get(i).getRawMetadata()); 551 } 552 return receiveState; 553 } 554 555 /** 556 * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastReceiveState} from parcel. 557 * 558 * @hide 559 */ 560 @SystemApi @NonNull 561 public static final Creator<BluetoothLeBroadcastReceiveState> CREATOR = 562 new Creator<>() { 563 public @NonNull BluetoothLeBroadcastReceiveState createFromParcel( 564 @NonNull Parcel in) { 565 final int sourceId = in.readInt(); 566 final int sourceAddressType = in.readInt(); 567 final BluetoothDevice sourceDevice = 568 in.readTypedObject(BluetoothDevice.CREATOR); 569 final int sourceAdvertisingSid = in.readInt(); 570 final int broadcastId = in.readInt(); 571 final int paSyncState = in.readInt(); 572 final int bigEncryptionState = in.readInt(); 573 final int badCodeLen = in.readInt(); 574 byte[] badCode = null; 575 576 if (badCodeLen != -1) { 577 badCode = new byte[badCodeLen]; 578 if (badCodeLen > 0) { 579 in.readByteArray(badCode); 580 } 581 } 582 final byte numSubGroups = in.readByte(); 583 final List<Long> bisSyncState = 584 in.readArrayList(Long.class.getClassLoader(), Long.class); 585 final List<BluetoothLeAudioContentMetadata> subgroupMetadata = 586 new ArrayList<>(); 587 in.readTypedList(subgroupMetadata, BluetoothLeAudioContentMetadata.CREATOR); 588 589 return new BluetoothLeBroadcastReceiveState( 590 sourceId, 591 sourceAddressType, 592 sourceDevice, 593 sourceAdvertisingSid, 594 broadcastId, 595 paSyncState, 596 bigEncryptionState, 597 badCode, 598 numSubGroups, 599 bisSyncState, 600 subgroupMetadata); 601 } 602 603 public @NonNull BluetoothLeBroadcastReceiveState[] newArray(int size) { 604 return new BluetoothLeBroadcastReceiveState[size]; 605 } 606 }; 607 } 608