1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at: 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.bluetooth; 18 19 import static java.util.Objects.requireNonNull; 20 21 import android.annotation.IntDef; 22 import android.annotation.IntRange; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.SystemApi; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.util.Arrays; 32 import java.util.List; 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: [" + paSyncState + "]"; 185 case 0x01: 186 return "SyncInfo Request: [" + paSyncState + "]"; 187 case 0x02: 188 return "Synchronized to PA: [" + paSyncState + "]"; 189 case 0x03: 190 return "Failed to synchronize to PA: [" + paSyncState + "]"; 191 case 0x04: 192 return "No PAST: [" + paSyncState + "]"; 193 default: 194 return "RFU: [" + paSyncState + "]"; 195 } 196 } 197 bigEncryptionStateToString(int bigEncryptionState)198 private static String bigEncryptionStateToString(int bigEncryptionState) { 199 switch (bigEncryptionState) { 200 case 0x00: 201 return "Not encrypted: [" + bigEncryptionState + "]"; 202 case 0x01: 203 return "Broadcast_Code required: [" + bigEncryptionState + "]"; 204 case 0x02: 205 return "Decrypting: [" + bigEncryptionState + "]"; 206 case 0x03: 207 return "Bad_Code (incorrect encryption key): [" + bigEncryptionState + "]"; 208 default: 209 return "RFU: [" + bigEncryptionState + "]"; 210 } 211 } 212 bisSyncStateToString(Long bisSyncState, int bisSyncStateIndex)213 private static String bisSyncStateToString(Long bisSyncState, int bisSyncStateIndex) { 214 if (bisSyncState == 0) { 215 return "Not synchronized to BIS_index[" 216 + bisSyncStateIndex 217 + "]: [" 218 + bisSyncState 219 + "]"; 220 } else if (bisSyncState > 0 && bisSyncState < 0xFFFFFFFF) { 221 return "Synchronized to BIS_index[" + bisSyncStateIndex + "]: [" + bisSyncState + "]"; 222 } else if (bisSyncState == 0xFFFFFFFF) { 223 return "Failed to sync to BIG: [" + bisSyncState + "]"; 224 } else { 225 return "[" + bisSyncState + "]"; 226 } 227 } 228 229 /** 230 * Constructor to create a read-only {@link BluetoothLeBroadcastReceiveState} instance. 231 * 232 * @throws NullPointerException if sourceDevice, bisSyncState, or subgroupMetadata is null 233 * @throws IllegalArgumentException if sourceID is not [0, 0xFF] or if sourceAddressType is 234 * invalid or if bisSyncState.size() != numSubgroups or if subgroupMetadata.size() != 235 * numSubgroups or if paSyncState or bigEncryptionState is not recognized bye IntDef 236 * @hide 237 */ 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)238 public BluetoothLeBroadcastReceiveState( 239 @IntRange(from = 0x00, to = 0xFF) int sourceId, 240 @BluetoothDevice.AddressType int sourceAddressType, 241 @NonNull BluetoothDevice sourceDevice, 242 int sourceAdvertisingSid, 243 int broadcastId, 244 @PaSyncState int paSyncState, 245 @BigEncryptionState int bigEncryptionState, 246 byte[] badCode, 247 @IntRange(from = 0x00) int numSubgroups, 248 @NonNull List<Long> bisSyncState, 249 @NonNull List<BluetoothLeAudioContentMetadata> subgroupMetadata) { 250 if (sourceId < 0x00 || sourceId > 0xFF) { 251 throw new IllegalArgumentException( 252 "sourceId " + sourceId + " does not fall between 0x00 and 0xFF"); 253 } 254 requireNonNull(sourceDevice); 255 if (sourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) { 256 throw new IllegalArgumentException("sourceAddressType cannot be ADDRESS_TYPE_UNKNOWN"); 257 } 258 if (sourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM 259 && sourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) { 260 throw new IllegalArgumentException( 261 "sourceAddressType " + sourceAddressType + " is invalid"); 262 } 263 requireNonNull(bisSyncState); 264 if (bisSyncState.size() != numSubgroups) { 265 throw new IllegalArgumentException( 266 "bisSyncState.size() " 267 + bisSyncState.size() 268 + " must be equal to numSubgroups " 269 + numSubgroups); 270 } 271 requireNonNull(subgroupMetadata); 272 if (subgroupMetadata.size() != numSubgroups) { 273 throw new IllegalArgumentException( 274 "subgroupMetadata.size() " 275 + subgroupMetadata.size() 276 + " must be equal to numSubgroups " 277 + numSubgroups); 278 } 279 if (paSyncState != PA_SYNC_STATE_IDLE 280 && paSyncState != PA_SYNC_STATE_SYNCINFO_REQUEST 281 && paSyncState != PA_SYNC_STATE_SYNCHRONIZED 282 && paSyncState != PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE 283 && paSyncState != PA_SYNC_STATE_NO_PAST 284 && paSyncState != PA_SYNC_STATE_INVALID) { 285 throw new IllegalArgumentException("unrecognized paSyncState " + paSyncState); 286 } 287 if (bigEncryptionState != BIG_ENCRYPTION_STATE_NOT_ENCRYPTED 288 && bigEncryptionState != BIG_ENCRYPTION_STATE_CODE_REQUIRED 289 && bigEncryptionState != BIG_ENCRYPTION_STATE_DECRYPTING 290 && bigEncryptionState != BIG_ENCRYPTION_STATE_BAD_CODE 291 && bigEncryptionState != BIG_ENCRYPTION_STATE_INVALID) { 292 throw new IllegalArgumentException( 293 "unrecognized bigEncryptionState " + bigEncryptionState); 294 } 295 if (badCode != null && badCode.length != 16) { 296 throw new IllegalArgumentException( 297 "badCode must be 16 bytes long of null, but is " 298 + badCode.length 299 + " bytes long"); 300 } 301 mSourceId = sourceId; 302 mSourceAddressType = sourceAddressType; 303 mSourceDevice = sourceDevice; 304 mSourceAdvertisingSid = sourceAdvertisingSid; 305 mBroadcastId = broadcastId; 306 mPaSyncState = paSyncState; 307 mBigEncryptionState = bigEncryptionState; 308 mBadCode = badCode; 309 mNumSubgroups = numSubgroups; 310 mBisSyncState = bisSyncState; 311 mSubgroupMetadata = subgroupMetadata; 312 } 313 314 /** 315 * Get the source ID assigned by the BASS server 316 * 317 * <p>Shall be unique for each instance of the Broadcast Receive State characteristic exposed by 318 * the server 319 * 320 * @return source ID assigned by the BASS server 321 * @hide 322 */ 323 @SystemApi getSourceId()324 public @IntRange(from = 0x00, to = 0xFF) int getSourceId() { 325 return mSourceId; 326 } 327 328 /** 329 * Get the address type of the Broadcast Source 330 * 331 * Can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} or 332 * {@link BluetoothDevice#ADDRESS_TYPE_RANDOM 333 * 334 * @return address type of the Broadcast Source 335 * @hide 336 */ 337 @SystemApi getSourceAddressType()338 public @BluetoothDevice.AddressType int getSourceAddressType() { 339 return mSourceAddressType; 340 } 341 342 /** 343 * Get the MAC address of the Broadcast Source, which can be Public Device Address, Random 344 * Device Address, Public Identity Address or Random (static) Identity Address 345 * 346 * @return MAC address of the Broadcast Source 347 * @hide 348 */ 349 @SystemApi getSourceDevice()350 public @NonNull BluetoothDevice getSourceDevice() { 351 return mSourceDevice; 352 } 353 354 /** 355 * Get Advertising_SID subfield of the ADI field of the AUX_ADV_IND PDU or the 356 * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the 357 * Broadcast Source. 358 * 359 * @return 1-byte long Advertising_SID of the Broadcast Source 360 * @hide 361 */ 362 @SystemApi getSourceAdvertisingSid()363 public int getSourceAdvertisingSid() { 364 return mSourceAdvertisingSid; 365 } 366 367 /** 368 * Broadcast_ID of the Broadcast Source 369 * 370 * @return 3-byte long Broadcast_ID of the Broadcast Source 371 * @hide 372 */ 373 @SystemApi getBroadcastId()374 public int getBroadcastId() { 375 return mBroadcastId; 376 } 377 378 /** 379 * Get the Periodic Advertisement synchronization state between the Broadcast Sink and the 380 * Broadcast source 381 * 382 * <p>Possible values are {@link #PA_SYNC_STATE_IDLE}, {@link #PA_SYNC_STATE_SYNCINFO_REQUEST}, 383 * {@link #PA_SYNC_STATE_SYNCHRONIZED}, {@link #PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE}, {@link 384 * #PA_SYNC_STATE_NO_PAST} 385 * 386 * @return Periodic Advertisement synchronization state 387 * @hide 388 */ 389 @SystemApi getPaSyncState()390 public @PaSyncState int getPaSyncState() { 391 return mPaSyncState; 392 } 393 394 /** 395 * Get the encryption state of a Broadcast Isochronous Group (BIG) 396 * 397 * <p>Possible values are {@link #BIG_ENCRYPTION_STATE_NOT_ENCRYPTED}, {@link 398 * #BIG_ENCRYPTION_STATE_CODE_REQUIRED}, {@link #BIG_ENCRYPTION_STATE_DECRYPTING}, {@link 399 * #BIG_ENCRYPTION_STATE_DECRYPTING}, and {@link #BIG_ENCRYPTION_STATE_BAD_CODE} 400 * 401 * @return encryption state of a Broadcast Isochronous Group (BIG) 402 * @hide 403 */ 404 @SystemApi getBigEncryptionState()405 public @BigEncryptionState int getBigEncryptionState() { 406 return mBigEncryptionState; 407 } 408 409 /** 410 * If {@link #getBigEncryptionState()} returns {@link #BIG_ENCRYPTION_STATE_BAD_CODE}, this 411 * method returns the value of the incorrect 16-octet Broadcast Code that fails to decrypt an 412 * audio stream from a Broadcast Source. 413 * 414 * @return 16-octet Broadcast Code, or null if {@link #getBigEncryptionState()} does not return 415 * {@link #BIG_ENCRYPTION_STATE_BAD_CODE} 416 * @hide 417 */ 418 @SystemApi getBadCode()419 public @Nullable byte[] getBadCode() { 420 return mBadCode; 421 } 422 423 /** 424 * Get number of Broadcast subgroups being added to this sink 425 * 426 * @return number of Broadcast subgroups being added to this sink 427 */ getNumSubgroups()428 public int getNumSubgroups() { 429 return mNumSubgroups; 430 } 431 432 /** 433 * Get a list of bitfield on whether a Broadcast Isochronous Stream (BIS) is synchronized 434 * between the sink and source 435 * 436 * <p>The number of items in the returned list is the same as {@link #getNumSubgroups()}. For 437 * each subgroup, at most 31 BISes are available and their synchronization state is indicated by 438 * its bit value at the particular offset (i.e. Bit 0-30 = BIS_index[1-31]) 439 * 440 * <p>For example, if (BisSyncState & 0b1 << 5) != 0, BIS 5 is synchronized between source and 441 * sync 442 * 443 * <p>There is a special case, 0xFFFFFFFF to indicate Broadcast Sink failed to synchronize to a 444 * particular subgroup 445 * 446 * @return a list of bitfield on whether a Broadcast Isochronous Stream (BIS) is synchronized 447 * between the sink and source 448 * @hide 449 */ 450 @SystemApi getBisSyncState()451 public @NonNull List<Long> getBisSyncState() { 452 return mBisSyncState; 453 } 454 455 /** 456 * Get metadata for every subgroup added to this Broadcast Sink 457 * 458 * <p>The number of items in the returned list is the same as {@link #getNumSubgroups()}. 459 * 460 * @return metadata for every subgroup added to this Broadcast Sink 461 * @hide 462 */ 463 @SystemApi getSubgroupMetadata()464 public @NonNull List<BluetoothLeAudioContentMetadata> getSubgroupMetadata() { 465 return mSubgroupMetadata; 466 } 467 468 /** 469 * {@inheritDoc} 470 * 471 * @hide 472 */ 473 @Override describeContents()474 public int describeContents() { 475 return 0; 476 } 477 478 /** 479 * {@inheritDoc} 480 * 481 * @hide 482 */ 483 @Override 484 @SuppressWarnings("AndroidFrameworkEfficientParcelable") // No Creator match List<Long> writeToParcel(Parcel out, int flags)485 public void writeToParcel(Parcel out, int flags) { 486 out.writeInt(mSourceId); 487 out.writeInt(mSourceAddressType); 488 out.writeTypedObject(mSourceDevice, 0); 489 out.writeInt(mSourceAdvertisingSid); 490 out.writeInt(mBroadcastId); 491 out.writeInt(mPaSyncState); 492 out.writeInt(mBigEncryptionState); 493 out.writeByteArray(mBadCode); 494 out.writeInt(mNumSubgroups); 495 out.writeList(mBisSyncState); 496 out.writeTypedList(mSubgroupMetadata); 497 } 498 499 /** 500 * {@inheritDoc} 501 * 502 * @hide 503 */ 504 @Override toString()505 public String toString() { 506 String receiveState = 507 ("Receiver state: " 508 + ("\n Source ID:" + mSourceId) 509 + ("\n Source Address Type:" + (int) mSourceAddressType) 510 + ("\n Source Address:" + mSourceDevice.toString()) 511 + ("\n Source Adv SID:" + mSourceAdvertisingSid) 512 + ("\n Broadcast ID:" + mBroadcastId) 513 + ("\n PA Sync State:" + paSyncStateToString(mPaSyncState)) 514 + ("\n BIG Encryption Status:" 515 + bigEncryptionStateToString(mBigEncryptionState)) 516 + ("\n Bad Broadcast Code:" + Arrays.toString(mBadCode)) 517 + ("\n Number Of Subgroups:" + mNumSubgroups)); 518 for (int i = 0; i < mNumSubgroups; i++) { 519 receiveState = 520 receiveState 521 + ("\n Subgroup index:" 522 + i 523 + "\n BIS Sync State:" 524 + bisSyncStateToString(mBisSyncState.get(i), i)) 525 + "\n Metadata:" 526 + "\n ProgramInfo:" 527 + mSubgroupMetadata.get(i).getProgramInfo() 528 + "\n Language:" 529 + mSubgroupMetadata.get(i).getLanguage() 530 + "\n RawData:" 531 + Arrays.toString(mSubgroupMetadata.get(i).getRawMetadata()); 532 } 533 return receiveState; 534 } 535 536 /** 537 * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastReceiveState} from parcel. 538 * 539 * @hide 540 */ 541 @SystemApi @NonNull 542 public static final Creator<BluetoothLeBroadcastReceiveState> CREATOR = 543 new Creator<>() { 544 public @NonNull BluetoothLeBroadcastReceiveState createFromParcel( 545 @NonNull Parcel in) { 546 final int sourceId = in.readInt(); 547 final int sourceAddressType = in.readInt(); 548 final BluetoothDevice sourceDevice = 549 in.readTypedObject(BluetoothDevice.CREATOR); 550 final int sourceAdvertisingSid = in.readInt(); 551 final int broadcastId = in.readInt(); 552 final int paSyncState = in.readInt(); 553 final int bigEncryptionState = in.readInt(); 554 final byte[] badCode = in.createByteArray(); 555 final byte numSubGroups = in.readByte(); 556 final List<Long> bisSyncState = 557 in.readArrayList(Long.class.getClassLoader(), Long.class); 558 final List<BluetoothLeAudioContentMetadata> subgroupMetadata = 559 in.createTypedArrayList(BluetoothLeAudioContentMetadata.CREATOR); 560 561 return new BluetoothLeBroadcastReceiveState( 562 sourceId, 563 sourceAddressType, 564 sourceDevice, 565 sourceAdvertisingSid, 566 broadcastId, 567 paSyncState, 568 bigEncryptionState, 569 badCode, 570 numSubGroups, 571 bisSyncState, 572 subgroupMetadata); 573 } 574 575 public @NonNull BluetoothLeBroadcastReceiveState[] newArray(int size) { 576 return new BluetoothLeBroadcastReceiveState[size]; 577 } 578 }; 579 } 580