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.IntRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SystemApi; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.List; 29 import java.util.Objects; 30 31 /** 32 * This class represents a Broadcast Source group and the associated information that is needed 33 * by Broadcast Audio Scan Service (BASS) to set up a Broadcast Sink. 34 * 35 * <p>For example, an LE Audio Broadcast Sink can use the information contained within an instance 36 * of this class to synchronize with an LE Audio Broadcast group in order to listen to audio from 37 * Broadcast subgroup using one or more Broadcast Channels. 38 * 39 * @hide 40 */ 41 @SystemApi 42 public final class BluetoothLeBroadcastMetadata implements Parcelable { 43 // Information needed for adding broadcast Source 44 45 // Optional: Identity address type 46 private final @BluetoothDevice.AddressType int mSourceAddressType; 47 // Optional: Must use identity address 48 private final BluetoothDevice mSourceDevice; 49 private final int mSourceAdvertisingSid; 50 private final int mBroadcastId; 51 private final int mPaSyncInterval; 52 private final boolean mIsEncrypted; 53 private final byte[] mBroadcastCode; 54 55 // BASE structure 56 57 // See Section 7 for description. Range: 0x000000 – 0xFFFFFF Units: μs 58 //All other values: RFU 59 private final int mPresentationDelayMicros; 60 // Number of subgroups used to group BISes present in the BIG 61 //Shall be at least 1, as defined by Rule 1 62 // Sub group info numSubGroup = mSubGroups.length 63 private final List<BluetoothLeBroadcastSubgroup> mSubgroups; 64 BluetoothLeBroadcastMetadata(int sourceAddressType, BluetoothDevice sourceDevice, int sourceAdvertisingSid, int broadcastId, int paSyncInterval, boolean isEncrypted, byte[] broadcastCode, int presentationDelay, List<BluetoothLeBroadcastSubgroup> subgroups)65 private BluetoothLeBroadcastMetadata(int sourceAddressType, 66 BluetoothDevice sourceDevice, int sourceAdvertisingSid, int broadcastId, 67 int paSyncInterval, boolean isEncrypted, byte[] broadcastCode, int presentationDelay, 68 List<BluetoothLeBroadcastSubgroup> subgroups) { 69 mSourceAddressType = sourceAddressType; 70 mSourceDevice = sourceDevice; 71 mSourceAdvertisingSid = sourceAdvertisingSid; 72 mBroadcastId = broadcastId; 73 mPaSyncInterval = paSyncInterval; 74 mIsEncrypted = isEncrypted; 75 mBroadcastCode = broadcastCode; 76 mPresentationDelayMicros = presentationDelay; 77 mSubgroups = subgroups; 78 } 79 80 @Override equals(@ullable Object o)81 public boolean equals(@Nullable Object o) { 82 if (!(o instanceof BluetoothLeBroadcastMetadata)) { 83 return false; 84 } 85 final BluetoothLeBroadcastMetadata other = (BluetoothLeBroadcastMetadata) o; 86 return mSourceAddressType == other.getSourceAddressType() 87 && mSourceDevice.equals(other.getSourceDevice()) 88 && mSourceAdvertisingSid == other.getSourceAdvertisingSid() 89 && mBroadcastId == other.getBroadcastId() 90 && mPaSyncInterval == other.getPaSyncInterval() 91 && mIsEncrypted == other.isEncrypted() 92 && Arrays.equals(mBroadcastCode, other.getBroadcastCode()) 93 && mPresentationDelayMicros == other.getPresentationDelayMicros() 94 && mSubgroups.equals(other.getSubgroups()); 95 } 96 97 @Override hashCode()98 public int hashCode() { 99 return Objects.hash(mSourceAddressType, mSourceDevice, mSourceAdvertisingSid, 100 mBroadcastId, mPaSyncInterval, mIsEncrypted, Arrays.hashCode(mBroadcastCode), 101 mPresentationDelayMicros, mSubgroups); 102 } 103 104 /** 105 * Get the address type of the Broadcast Source. 106 * 107 * Can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}, 108 * {@link BluetoothDevice#ADDRESS_TYPE_RANDOM} 109 * 110 * @return address type of the Broadcast Source 111 * @hide 112 */ 113 @SystemApi getSourceAddressType()114 public @BluetoothDevice.AddressType int getSourceAddressType() { 115 return mSourceAddressType; 116 } 117 118 /** 119 * Get the MAC address of the Broadcast Source, which can be Public Device Address, 120 * Random Device Address, Public Identity Address or Random (static) Identity Address. 121 * 122 * @return MAC address of the Broadcast Source 123 * @hide 124 */ 125 @SystemApi getSourceDevice()126 public @NonNull BluetoothDevice getSourceDevice() { 127 return mSourceDevice; 128 } 129 130 /** 131 * Get Advertising_SID subfield of the ADI field of the AUX_ADV_IND PDU or the 132 * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the 133 * Broadcast Source. 134 * 135 * @return 1-byte long Advertising_SID of the Broadcast Source 136 * @hide 137 */ 138 @SystemApi getSourceAdvertisingSid()139 public int getSourceAdvertisingSid() { 140 return mSourceAdvertisingSid; 141 } 142 143 /** 144 * Broadcast_ID of the Broadcast Source. 145 * 146 * @return 3-byte long Broadcast_ID of the Broadcast Source 147 * @hide 148 */ 149 @SystemApi getBroadcastId()150 public int getBroadcastId() { 151 return mBroadcastId; 152 } 153 154 /** 155 * Indicated that Periodic Advertising Sync interval is unknown. 156 * @hide 157 */ 158 @SystemApi 159 public static final int PA_SYNC_INTERVAL_UNKNOWN = 0xFFFF; 160 161 /** 162 * Get Periodic Advertising Sync interval of the broadcast Source. 163 * 164 * @return Periodic Advertising Sync interval of the broadcast Source, 165 * {@link #PA_SYNC_INTERVAL_UNKNOWN} if unknown 166 * @hide 167 */ 168 @SystemApi getPaSyncInterval()169 public int getPaSyncInterval() { 170 return mPaSyncInterval; 171 } 172 173 /** 174 * Return true if the Broadcast Source is encrypted. 175 * 176 * @return true if the Broadcast Source is encrypted 177 * @hide 178 */ 179 @SystemApi isEncrypted()180 public boolean isEncrypted() { 181 return mIsEncrypted; 182 } 183 184 /** 185 * Get the Broadcast Code currently set for this Broadcast Source. 186 * 187 * Only needed when encryption is enabled 188 * 189 * <p>As defined in Volume 3, Part C, Section 3.2.6 of Bluetooth Core Specification, Version 190 * 5.3, Broadcast Code is used to encrypt a broadcast audio stream. 191 * <p>It must be a UTF-8 string that has at least 4 octets and should not exceed 16 octets. 192 * 193 * @return Broadcast Code currently set for this Broadcast Source, null if code is not required 194 * or code is currently unknown 195 * @hide 196 */ 197 @SystemApi getBroadcastCode()198 public @Nullable byte[] getBroadcastCode() { 199 return mBroadcastCode; 200 } 201 202 /** 203 * Get the overall presentation delay in microseconds of this Broadcast Source. 204 * 205 * Presentation delay is defined in Section 7 of the Basic Audio Profile. 206 * 207 * @return presentation delay of this Broadcast Source in microseconds 208 * @hide 209 */ 210 @SystemApi getPresentationDelayMicros()211 public @IntRange(from = 0, to = 0xFFFFFF) int getPresentationDelayMicros() { 212 return mPresentationDelayMicros; 213 } 214 215 /** 216 * Get available subgroups in this broadcast source. 217 * 218 * @return list of subgroups in this broadcast source, which should contain at least one 219 * subgroup for each Broadcast Source 220 * @hide 221 */ 222 @SystemApi getSubgroups()223 public @NonNull List<BluetoothLeBroadcastSubgroup> getSubgroups() { 224 return mSubgroups; 225 } 226 227 /** 228 * {@inheritDoc} 229 * @hide 230 */ 231 @Override describeContents()232 public int describeContents() { 233 return 0; 234 } 235 236 /** 237 * {@inheritDoc} 238 * @hide 239 */ 240 @Override writeToParcel(Parcel out, int flags)241 public void writeToParcel(Parcel out, int flags) { 242 out.writeInt(mSourceAddressType); 243 if (mSourceDevice != null) { 244 out.writeInt(1); 245 out.writeTypedObject(mSourceDevice, 0); 246 } else { 247 // zero indicates missing mSourceDevice 248 out.writeInt(0); 249 } 250 out.writeInt(mSourceAdvertisingSid); 251 out.writeInt(mBroadcastId); 252 out.writeInt(mPaSyncInterval); 253 out.writeBoolean(mIsEncrypted); 254 if (mBroadcastCode != null) { 255 out.writeInt(mBroadcastCode.length); 256 out.writeByteArray(mBroadcastCode); 257 } else { 258 // -1 indicates missing broadcast code 259 out.writeInt(-1); 260 } 261 out.writeInt(mPresentationDelayMicros); 262 out.writeTypedList(mSubgroups); 263 } 264 265 /** 266 * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastMetadata} from parcel. 267 * @hide 268 */ 269 @SystemApi 270 public static final @NonNull Parcelable.Creator<BluetoothLeBroadcastMetadata> CREATOR = 271 new Parcelable.Creator<BluetoothLeBroadcastMetadata>() { 272 public @NonNull BluetoothLeBroadcastMetadata createFromParcel(@NonNull Parcel in) { 273 Builder builder = new Builder(); 274 final int sourceAddressType = in.readInt(); 275 final int deviceExist = in.readInt(); 276 BluetoothDevice sourceDevice = null; 277 if (deviceExist == 1) { 278 sourceDevice = in.readTypedObject(BluetoothDevice.CREATOR); 279 } 280 builder.setSourceDevice(sourceDevice, sourceAddressType); 281 builder.setSourceAdvertisingSid(in.readInt()); 282 builder.setBroadcastId(in.readInt()); 283 builder.setPaSyncInterval(in.readInt()); 284 builder.setEncrypted(in.readBoolean()); 285 final int codeLen = in.readInt(); 286 byte[] broadcastCode = null; 287 if (codeLen != -1) { 288 broadcastCode = new byte[codeLen]; 289 if (codeLen > 0) { 290 in.readByteArray(broadcastCode); 291 } 292 } 293 builder.setBroadcastCode(broadcastCode); 294 builder.setPresentationDelayMicros(in.readInt()); 295 final List<BluetoothLeBroadcastSubgroup> subgroups = new ArrayList<>(); 296 in.readTypedList(subgroups, BluetoothLeBroadcastSubgroup.CREATOR); 297 for (BluetoothLeBroadcastSubgroup subgroup : subgroups) { 298 builder.addSubgroup(subgroup); 299 } 300 return builder.build(); 301 } 302 303 public @NonNull BluetoothLeBroadcastMetadata[] newArray(int size) { 304 return new BluetoothLeBroadcastMetadata[size]; 305 } 306 }; 307 308 private static final int UNKNOWN_VALUE_PLACEHOLDER = -1; 309 310 /** 311 * Builder for {@link BluetoothLeBroadcastMetadata}. 312 * @hide 313 */ 314 @SystemApi 315 public static final class Builder { 316 private @BluetoothDevice.AddressType int mSourceAddressType = 317 BluetoothDevice.ADDRESS_TYPE_UNKNOWN; 318 private BluetoothDevice mSourceDevice = null; 319 private int mSourceAdvertisingSid = UNKNOWN_VALUE_PLACEHOLDER; 320 private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER; 321 private int mPaSyncInterval = UNKNOWN_VALUE_PLACEHOLDER; 322 private boolean mIsEncrypted = false; 323 private byte[] mBroadcastCode = null; 324 private int mPresentationDelayMicros = UNKNOWN_VALUE_PLACEHOLDER; 325 private List<BluetoothLeBroadcastSubgroup> mSubgroups = new ArrayList<>(); 326 327 /** 328 * Create an empty builder. 329 * 330 * @hide 331 */ 332 @SystemApi Builder()333 public Builder() {} 334 335 /** 336 * Create a builder with copies of information from original object. 337 * 338 * @param original original object 339 * @hide 340 */ 341 @SystemApi Builder(@onNull BluetoothLeBroadcastMetadata original)342 public Builder(@NonNull BluetoothLeBroadcastMetadata original) { 343 mSourceAddressType = original.getSourceAddressType(); 344 mSourceDevice = original.getSourceDevice(); 345 mSourceAdvertisingSid = original.getSourceAdvertisingSid(); 346 mBroadcastId = original.getBroadcastId(); 347 mPaSyncInterval = original.getPaSyncInterval(); 348 mIsEncrypted = original.isEncrypted(); 349 mBroadcastCode = original.getBroadcastCode(); 350 mPresentationDelayMicros = original.getPresentationDelayMicros(); 351 mSubgroups = original.getSubgroups(); 352 } 353 354 355 /** 356 * Set the address type and MAC address of the Broadcast Source. 357 * 358 * Address type can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}, 359 * {@link BluetoothDevice#ADDRESS_TYPE_RANDOM} 360 * 361 * MAC address can be Public Device Address, Random Device Address, Public Identity Address 362 * or Random (static) Identity Address 363 * 364 * @param sourceDevice source advertiser address 365 * @param sourceAddressType source advertiser address type 366 * @throws IllegalArgumentException if sourceAddressType is invalid 367 * @throws NullPointerException if sourceDevice is null 368 * @return this builder 369 * @hide 370 */ 371 @SystemApi setSourceDevice(@onNull BluetoothDevice sourceDevice, @BluetoothDevice.AddressType int sourceAddressType)372 public @NonNull Builder setSourceDevice(@NonNull BluetoothDevice sourceDevice, 373 @BluetoothDevice.AddressType int sourceAddressType) { 374 if (sourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) { 375 throw new IllegalArgumentException( 376 "sourceAddressType cannot be ADDRESS_TYPE_UNKNOWN"); 377 } 378 if (sourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM 379 && sourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) { 380 throw new IllegalArgumentException("sourceAddressType " + sourceAddressType 381 + " is invalid"); 382 } 383 Objects.requireNonNull(sourceDevice, "sourceDevice cannot be null"); 384 mSourceAddressType = sourceAddressType; 385 mSourceDevice = sourceDevice; 386 return this; 387 } 388 389 /** 390 * Set Advertising_SID that is a subfield of the ADI field of the AUX_ADV_IND PDU or the 391 * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the 392 * Broadcast Source. 393 * 394 * @param sourceAdvertisingSid 1-byte long Advertising_SID of the Broadcast Source 395 * @return this builder 396 * @hide 397 */ 398 @SystemApi setSourceAdvertisingSid(int sourceAdvertisingSid)399 public @NonNull Builder setSourceAdvertisingSid(int sourceAdvertisingSid) { 400 mSourceAdvertisingSid = sourceAdvertisingSid; 401 return this; 402 } 403 404 /** 405 * Set the Broadcast_ID of the Broadcast Source. 406 * 407 * @param broadcastId 3-byte long Broadcast_ID of the Broadcast Source 408 * @return this builder 409 * @hide 410 */ 411 @SystemApi setBroadcastId(int broadcastId)412 public @NonNull Builder setBroadcastId(int broadcastId) { 413 mBroadcastId = broadcastId; 414 return this; 415 } 416 417 /** 418 * Set Periodic Advertising Sync interval of the broadcast Source. 419 * 420 * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source, 421 * {@link #PA_SYNC_INTERVAL_UNKNOWN} if unknown 422 * @return this builder 423 * @hide 424 */ 425 @SystemApi setPaSyncInterval(int paSyncInterval)426 public @NonNull Builder setPaSyncInterval(int paSyncInterval) { 427 mPaSyncInterval = paSyncInterval; 428 return this; 429 } 430 431 /** 432 * Set whether the Broadcast Source should be encrypted. 433 * 434 * When setting up a Broadcast Source, if <var>isEncrypted</var> is true while 435 * <var>broadcastCode</var> is null, the implementation will automatically generate 436 * a Broadcast Code 437 * 438 * @param isEncrypted whether the Broadcast Source is encrypted 439 * @return this builder 440 * @hide 441 */ 442 @SystemApi setEncrypted(boolean isEncrypted)443 public @NonNull Builder setEncrypted(boolean isEncrypted) { 444 mIsEncrypted = isEncrypted; 445 return this; 446 } 447 448 /** 449 * Set the Broadcast Code currently set for this Broadcast Source. 450 * 451 * Only needed when encryption is enabled 452 * 453 * <p>As defined in Volume 3, Part C, Section 3.2.6 of Bluetooth Core Specification, Version 454 * 5.3, Broadcast Code is used to encrypt a broadcast audio stream. 455 * <p>It must be a UTF-8 string that has at least 4 octets and should not exceed 16 octets. 456 * 457 * @param broadcastCode Broadcast Code for this Broadcast Source, null if code is not 458 * required 459 * @return this builder 460 * @hide 461 */ 462 @SystemApi setBroadcastCode(@ullable byte[] broadcastCode)463 public @NonNull Builder setBroadcastCode(@Nullable byte[] broadcastCode) { 464 mBroadcastCode = broadcastCode; 465 return this; 466 } 467 468 /** 469 * Set the overall presentation delay in microseconds of this Broadcast Source. 470 * 471 * Presentation delay is defined in Section 7 of the Basic Audio Profile. 472 * 473 * @param presentationDelayMicros presentation delay of this Broadcast Source in 474 * microseconds 475 * @throws IllegalArgumentException if presentationDelayMicros does not fall in 476 * [0, 0xFFFFFF] 477 * @return this builder 478 * @hide 479 */ 480 @SystemApi setPresentationDelayMicros( @ntRangefrom = 0, to = 0xFFFFFF) int presentationDelayMicros)481 public @NonNull Builder setPresentationDelayMicros( 482 @IntRange(from = 0, to = 0xFFFFFF) int presentationDelayMicros) { 483 if (presentationDelayMicros < 0 || presentationDelayMicros >= 0xFFFFFF) { 484 throw new IllegalArgumentException("presentationDelayMicros " 485 + presentationDelayMicros + " does not fall in [0, 0xFFFFFF]"); 486 } 487 mPresentationDelayMicros = presentationDelayMicros; 488 return this; 489 } 490 491 /** 492 * Add a subgroup to this broadcast source. 493 * 494 * @param subgroup {@link BluetoothLeBroadcastSubgroup} that contains a subgroup's metadata 495 * @throws NullPointerException if subgroup is null 496 * @return this builder 497 * @hide 498 */ 499 @SystemApi addSubgroup(@onNull BluetoothLeBroadcastSubgroup subgroup)500 public @NonNull Builder addSubgroup(@NonNull BluetoothLeBroadcastSubgroup subgroup) { 501 Objects.requireNonNull(subgroup, "subgroup cannot be null"); 502 mSubgroups.add(subgroup); 503 return this; 504 } 505 506 /** 507 * Clear subgroup list so that one can reset the builder after create it from an existing 508 * object. 509 * 510 * @return this builder 511 * @hide 512 */ 513 @SystemApi clearSubgroup()514 public @NonNull Builder clearSubgroup() { 515 mSubgroups.clear(); 516 return this; 517 } 518 519 /** 520 * Build {@link BluetoothLeBroadcastMetadata}. 521 * 522 * @return {@link BluetoothLeBroadcastMetadata} 523 * @throws IllegalArgumentException if the object cannot be built 524 * @throws NullPointerException if {@link NonNull} items are null 525 * @hide 526 */ 527 @SystemApi build()528 public @NonNull BluetoothLeBroadcastMetadata build() { 529 if (mSourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) { 530 throw new IllegalArgumentException("SourceAddressTyp cannot be unknown"); 531 } 532 if (mSourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM 533 && mSourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) { 534 throw new IllegalArgumentException("sourceAddressType " + mSourceAddressType 535 + " is invalid"); 536 } 537 Objects.requireNonNull(mSourceDevice, "mSourceDevice cannot be null"); 538 if (mSubgroups.isEmpty()) { 539 throw new IllegalArgumentException("Must contain at least one subgroup"); 540 } 541 return new BluetoothLeBroadcastMetadata(mSourceAddressType, mSourceDevice, 542 mSourceAdvertisingSid, mBroadcastId, mPaSyncInterval, mIsEncrypted, 543 mBroadcastCode, mPresentationDelayMicros, mSubgroups); 544 } 545 } 546 } 547