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