1 /* 2 * Copyright 2023 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.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 25 import java.util.ArrayList; 26 import java.util.Arrays; 27 import java.util.List; 28 import java.util.Objects; 29 30 /** 31 * This class contains the broadcast group settings information for this Broadcast Group. 32 * 33 * @hide 34 */ 35 @SystemApi 36 public final class BluetoothLeBroadcastSettings implements Parcelable { 37 private final boolean mIsPublicBroadcast; 38 private final String mBroadcastName; 39 private final byte[] mBroadcastCode; 40 private final BluetoothLeAudioContentMetadata mPublicBroadcastMetadata; 41 private final List<BluetoothLeBroadcastSubgroupSettings> mSubgroupSettings; 42 BluetoothLeBroadcastSettings( boolean isPublicBroadcast, String broadcastName, byte[] broadcastCode, BluetoothLeAudioContentMetadata publicBroadcastMetadata, List<BluetoothLeBroadcastSubgroupSettings> subgroupSettings)43 private BluetoothLeBroadcastSettings( 44 boolean isPublicBroadcast, 45 String broadcastName, 46 byte[] broadcastCode, 47 BluetoothLeAudioContentMetadata publicBroadcastMetadata, 48 List<BluetoothLeBroadcastSubgroupSettings> subgroupSettings) { 49 mIsPublicBroadcast = isPublicBroadcast; 50 mBroadcastName = broadcastName; 51 mBroadcastCode = broadcastCode; 52 mPublicBroadcastMetadata = publicBroadcastMetadata; 53 mSubgroupSettings = subgroupSettings; 54 } 55 56 @Override equals(@ullable Object o)57 public boolean equals(@Nullable Object o) { 58 if (!(o instanceof BluetoothLeBroadcastSettings)) { 59 return false; 60 } 61 final BluetoothLeBroadcastSettings other = (BluetoothLeBroadcastSettings) o; 62 return mIsPublicBroadcast == other.isPublicBroadcast() 63 && Objects.equals(mBroadcastName, other.getBroadcastName()) 64 && Arrays.equals(mBroadcastCode, other.getBroadcastCode()) 65 && Objects.equals(mPublicBroadcastMetadata, other.getPublicBroadcastMetadata()) 66 && mSubgroupSettings.equals(other.getSubgroupSettings()); 67 } 68 69 @Override hashCode()70 public int hashCode() { 71 return Objects.hash( 72 mIsPublicBroadcast, 73 mBroadcastName, 74 Arrays.hashCode(mBroadcastCode), 75 mPublicBroadcastMetadata, 76 mSubgroupSettings); 77 } 78 79 /** 80 * Return {@code true} if this Broadcast Group is set to broadcast Public Broadcast Announcement 81 * otherwise return {@code false}. 82 * 83 * @hide 84 */ 85 @SystemApi isPublicBroadcast()86 public boolean isPublicBroadcast() { 87 return mIsPublicBroadcast; 88 } 89 90 /** 91 * Return the broadcast code for this Broadcast Group. 92 * 93 * @return Broadcast name for this Broadcast Group, null if no name provided 94 * @hide 95 */ 96 @SystemApi 97 @Nullable getBroadcastName()98 public String getBroadcastName() { 99 return mBroadcastName; 100 } 101 102 /** 103 * Get the Broadcast Code currently set for this broadcast group. 104 * 105 * <p>Only needed when encryption is enabled 106 * 107 * <p>As defined in Volume 3, Part C, Section 3.2.6 of Bluetooth Core Specification, Version 108 * 5.3, Broadcast Code is used to encrypt a broadcast audio stream. 109 * 110 * <p>It must be a UTF-8 string that has at least 4 octets and should not exceed 16 octets. 111 * 112 * @return Broadcast Code currently set for this broadcast group, null if code is not required 113 * or code is currently unknown 114 * @hide 115 */ 116 @SystemApi 117 @Nullable getBroadcastCode()118 public byte[] getBroadcastCode() { 119 return mBroadcastCode; 120 } 121 122 /** 123 * Get public broadcast metadata for this Broadcast Group. 124 * 125 * @return public broadcast metadata for this Broadcast Group, null if no public metadata exists 126 * @hide 127 */ 128 @SystemApi 129 @Nullable getPublicBroadcastMetadata()130 public BluetoothLeAudioContentMetadata getPublicBroadcastMetadata() { 131 return mPublicBroadcastMetadata; 132 } 133 134 /** 135 * Get available subgroup settings in the broadcast group. 136 * 137 * @return list of subgroup settings in the broadcast group 138 * @hide 139 */ 140 @SystemApi 141 @NonNull getSubgroupSettings()142 public List<BluetoothLeBroadcastSubgroupSettings> getSubgroupSettings() { 143 return mSubgroupSettings; 144 } 145 146 /** 147 * {@inheritDoc} 148 * 149 * @hide 150 */ 151 @Override describeContents()152 public int describeContents() { 153 return 0; 154 } 155 156 /** 157 * {@inheritDoc} 158 * 159 * @hide 160 */ 161 @Override writeToParcel(Parcel out, int flags)162 public void writeToParcel(Parcel out, int flags) { 163 out.writeBoolean(mIsPublicBroadcast); 164 out.writeString(mBroadcastName); 165 if (mBroadcastCode != null) { 166 out.writeInt(mBroadcastCode.length); 167 out.writeByteArray(mBroadcastCode); 168 } else { 169 // -1 indicates missing broadcast code 170 out.writeInt(-1); 171 } 172 out.writeTypedObject(mPublicBroadcastMetadata, 0); 173 out.writeTypedList(mSubgroupSettings); 174 } 175 176 /** 177 * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastSettings} from parcel. 178 * 179 * @hide 180 */ 181 @SystemApi @NonNull 182 public static final Creator<BluetoothLeBroadcastSettings> CREATOR = 183 new Creator<>() { 184 public @NonNull BluetoothLeBroadcastSettings createFromParcel(@NonNull Parcel in) { 185 Builder builder = new Builder(); 186 builder.setPublicBroadcast(in.readBoolean()); 187 builder.setBroadcastName(in.readString()); 188 final int codeLen = in.readInt(); 189 byte[] broadcastCode = null; 190 if (codeLen != -1) { 191 broadcastCode = new byte[codeLen]; 192 if (codeLen >= 0) { 193 in.readByteArray(broadcastCode); 194 } 195 } 196 builder.setBroadcastCode(broadcastCode); 197 builder.setPublicBroadcastMetadata( 198 in.readTypedObject(BluetoothLeAudioContentMetadata.CREATOR)); 199 final List<BluetoothLeBroadcastSubgroupSettings> subgroupSettings = 200 new ArrayList<>(); 201 in.readTypedList( 202 subgroupSettings, BluetoothLeBroadcastSubgroupSettings.CREATOR); 203 for (BluetoothLeBroadcastSubgroupSettings setting : subgroupSettings) { 204 builder.addSubgroupSettings(setting); 205 } 206 return builder.build(); 207 } 208 209 public @NonNull BluetoothLeBroadcastSettings[] newArray(int size) { 210 return new BluetoothLeBroadcastSettings[size]; 211 } 212 }; 213 214 /** 215 * Builder for {@link BluetoothLeBroadcastSettings}. 216 * 217 * @hide 218 */ 219 @SystemApi 220 public static final class Builder { 221 private boolean mIsPublicBroadcast = false; 222 private String mBroadcastName = null; 223 private byte[] mBroadcastCode = null; 224 private BluetoothLeAudioContentMetadata mPublicBroadcastMetadata = null; 225 private List<BluetoothLeBroadcastSubgroupSettings> mSubgroupSettings = new ArrayList<>(); 226 227 /** 228 * Create an empty builder. 229 * 230 * @hide 231 */ 232 @SystemApi Builder()233 public Builder() {} 234 235 /** 236 * Create a builder with copies of information from original object. 237 * 238 * @param original original object 239 * @hide 240 */ 241 @SystemApi Builder(@onNull BluetoothLeBroadcastSettings original)242 public Builder(@NonNull BluetoothLeBroadcastSettings original) { 243 mIsPublicBroadcast = original.isPublicBroadcast(); 244 mBroadcastName = original.getBroadcastName(); 245 mBroadcastCode = original.getBroadcastCode(); 246 mPublicBroadcastMetadata = original.getPublicBroadcastMetadata(); 247 mSubgroupSettings = original.getSubgroupSettings(); 248 } 249 250 /** 251 * Set whether the Public Broadcast is on for this broadcast group. 252 * 253 * @param isPublicBroadcast whether the Public Broadcast is enabled 254 * @return this builder 255 * @hide 256 */ 257 @SystemApi 258 @NonNull setPublicBroadcast(boolean isPublicBroadcast)259 public Builder setPublicBroadcast(boolean isPublicBroadcast) { 260 mIsPublicBroadcast = isPublicBroadcast; 261 return this; 262 } 263 264 /** 265 * Set broadcast name for the broadcast group. 266 * 267 * <p>As defined in Public Broadcast Profile V1.0, section 5.1. Broadcast_Name AD Type is a 268 * UTF-8 encoded string containing a minimum of 4 characters and a maximum of 32 269 * human-readable characters. 270 * 271 * @param broadcastName Broadcast name for this broadcast group, null if no name provided 272 * @throws IllegalArgumentException if name is non-null and its length is less than 4 273 * characters or greater than 32 characters 274 * @return this builder 275 * @hide 276 */ 277 @SystemApi 278 @NonNull setBroadcastName(@ullable String broadcastName)279 public Builder setBroadcastName(@Nullable String broadcastName) { 280 if (broadcastName != null 281 && ((broadcastName.length() > 32) || (broadcastName.length() < 4))) { 282 throw new IllegalArgumentException("Invalid broadcast name length"); 283 } 284 mBroadcastName = broadcastName; 285 return this; 286 } 287 288 /** 289 * Set the Broadcast Code currently set for this broadcast group. 290 * 291 * <p>Only needed when encryption is enabled As defined in Volume 3, Part C, Section 3.2.6 292 * of Bluetooth Core Specification, Version 5.3, Broadcast Code is used to encrypt a 293 * broadcast audio stream. It must be a UTF-8 string that has at least 4 octets and should 294 * not exceed 16 octets. 295 * 296 * @param broadcastCode Broadcast Code for this broadcast group, null if code is not 297 * required for non-encrypted broadcast 298 * @throws IllegalArgumentException if name is non-null and its length is less than 4 299 * characters or greater than 16 characters 300 * @return this builder 301 * @hide 302 */ 303 @SystemApi 304 @NonNull setBroadcastCode(@ullable byte[] broadcastCode)305 public Builder setBroadcastCode(@Nullable byte[] broadcastCode) { 306 if (broadcastCode != null 307 && ((broadcastCode.length > 16) || (broadcastCode.length < 4))) { 308 throw new IllegalArgumentException("Invalid broadcast code length"); 309 } 310 mBroadcastCode = broadcastCode; 311 return this; 312 } 313 314 /** 315 * Set public broadcast metadata for this Broadcast Group. PBS should include the 316 * Program_Info length-type-value (LTV) structure metadata 317 * 318 * @param publicBroadcastMetadata public broadcast metadata for this Broadcast Group, null 319 * if no public meta data provided 320 * @return this builder 321 * @hide 322 */ 323 @SystemApi 324 @NonNull setPublicBroadcastMetadata( @ullable BluetoothLeAudioContentMetadata publicBroadcastMetadata)325 public Builder setPublicBroadcastMetadata( 326 @Nullable BluetoothLeAudioContentMetadata publicBroadcastMetadata) { 327 mPublicBroadcastMetadata = publicBroadcastMetadata; 328 return this; 329 } 330 331 /** 332 * Add a subgroup settings to the broadcast group. 333 * 334 * @param subgroupSettings contains subgroup's setting data 335 * @return this builder 336 * @hide 337 */ 338 @SystemApi 339 @NonNull addSubgroupSettings( @onNull BluetoothLeBroadcastSubgroupSettings subgroupSettings)340 public Builder addSubgroupSettings( 341 @NonNull BluetoothLeBroadcastSubgroupSettings subgroupSettings) { 342 Objects.requireNonNull(subgroupSettings, "subgroupSettings cannot be null"); 343 mSubgroupSettings.add(subgroupSettings); 344 return this; 345 } 346 347 /** 348 * Clear subgroup settings list so that one can reset the builder 349 * 350 * @return this builder 351 * @hide 352 */ 353 @SystemApi 354 @NonNull clearSubgroupSettings()355 public Builder clearSubgroupSettings() { 356 mSubgroupSettings.clear(); 357 return this; 358 } 359 360 /** 361 * Build {@link BluetoothLeBroadcastSettings}. 362 * 363 * @return {@link BluetoothLeBroadcastSettings} 364 * @throws IllegalArgumentException if the object cannot be built 365 * @hide 366 */ 367 @SystemApi 368 @NonNull build()369 public BluetoothLeBroadcastSettings build() { 370 if (mSubgroupSettings.isEmpty()) { 371 throw new IllegalArgumentException("Must contain at least one subgroup"); 372 } 373 return new BluetoothLeBroadcastSettings( 374 mIsPublicBroadcast, 375 mBroadcastName, 376 mBroadcastCode, 377 mPublicBroadcastMetadata, 378 mSubgroupSettings); 379 } 380 } 381 } 382