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.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.List; 27 import java.util.Objects; 28 29 /** 30 * This class contains the subgroup level information as defined in the BASE structure of Basic 31 * Audio profile. 32 * 33 * @hide 34 */ 35 @SystemApi 36 public final class BluetoothLeBroadcastSubgroup implements Parcelable { 37 private final long mCodecId; 38 private final BluetoothLeAudioCodecConfigMetadata mCodecSpecificConfig; 39 private final BluetoothLeAudioContentMetadata mContentMetadata; 40 private final List<BluetoothLeBroadcastChannel> mChannels; 41 BluetoothLeBroadcastSubgroup(long codecId, BluetoothLeAudioCodecConfigMetadata codecSpecificConfig, BluetoothLeAudioContentMetadata contentMetadata, List<BluetoothLeBroadcastChannel> channels)42 private BluetoothLeBroadcastSubgroup(long codecId, 43 BluetoothLeAudioCodecConfigMetadata codecSpecificConfig, 44 BluetoothLeAudioContentMetadata contentMetadata, 45 List<BluetoothLeBroadcastChannel> channels) { 46 mCodecId = codecId; 47 mCodecSpecificConfig = codecSpecificConfig; 48 mContentMetadata = contentMetadata; 49 mChannels = channels; 50 } 51 52 @Override equals(@ullable Object o)53 public boolean equals(@Nullable Object o) { 54 if (!(o instanceof BluetoothLeBroadcastSubgroup)) { 55 return false; 56 } 57 final BluetoothLeBroadcastSubgroup other = (BluetoothLeBroadcastSubgroup) o; 58 return mCodecId == other.getCodecId() 59 && mCodecSpecificConfig.equals(other.getCodecSpecificConfig()) 60 && mContentMetadata.equals(other.getContentMetadata()) 61 && mChannels.equals(other.getChannels()); 62 } 63 64 @Override hashCode()65 public int hashCode() { 66 return Objects.hash(mCodecId, mCodecSpecificConfig, mContentMetadata, mChannels); 67 } 68 69 /** 70 * Get the codec ID field as defined by the Basic Audio Profile. 71 * 72 * The codec ID field has 5 octets, with 73 * - Octet 0: Coding_Format as defined in Bluetooth Assigned Numbers 74 * - Octet 1-2: Company ID as defined in Bluetooth Assigned Numbers 75 * Shall be 0x0000 if octet 0 != 0xFF 76 * - Octet 3-4: Vendor-specific codec ID 77 * Shall be 0x0000 if octet 0 != 0xFF 78 * 79 * @return 5-byte codec ID field in Java long format 80 * @hide 81 */ 82 @SystemApi getCodecId()83 public long getCodecId() { 84 return mCodecId; 85 } 86 87 /** 88 * Get codec specific config metadata for this subgroup. 89 * 90 * @return codec specific config metadata for this subgroup 91 * @hide 92 */ 93 @SystemApi 94 @NonNull getCodecSpecificConfig()95 public BluetoothLeAudioCodecConfigMetadata getCodecSpecificConfig() { 96 return mCodecSpecificConfig; 97 } 98 99 /** 100 * Get content metadata for this Broadcast Source subgroup. 101 * 102 * @return content metadata for this Broadcast Source subgroup 103 * @hide 104 */ 105 @SystemApi getContentMetadata()106 public @NonNull BluetoothLeAudioContentMetadata getContentMetadata() { 107 return mContentMetadata; 108 } 109 110 /** 111 * Indicate if Broadcast Sink should have a preferred Broadcast Channel (BIS). 112 * 113 * Only used by Broadcast Assistant and Sink. Ignored by Broadcast Source 114 * 115 * @return true if Broadcast Sink has at least one preferred Broadcast Channel (BIS) as 116 * indicated by {@link BluetoothLeBroadcastChannel#isSelected()} 117 * @hide 118 */ 119 @SystemApi hasChannelPreference()120 public boolean hasChannelPreference() { 121 return mChannels.stream().anyMatch(BluetoothLeBroadcastChannel::isSelected); 122 } 123 124 /** 125 * Get list of Broadcast Channels included in this Broadcast subgroup. 126 * 127 * Each Broadcast Channel represents a Broadcast Isochronous Stream (BIS) 128 * 129 * A Broadcast subgroup should contain at least 1 Broadcast Channel 130 * 131 * @return list of Broadcast Channels included in this Broadcast subgroup 132 * @hide 133 */ 134 @SystemApi getChannels()135 public @NonNull List<BluetoothLeBroadcastChannel> getChannels() { 136 return mChannels; 137 } 138 139 /** 140 * {@inheritDoc} 141 * @hide 142 */ 143 @Override describeContents()144 public int describeContents() { 145 return 0; 146 } 147 148 /** 149 * {@inheritDoc} 150 * @hide 151 */ 152 @Override writeToParcel(Parcel out, int flags)153 public void writeToParcel(Parcel out, int flags) { 154 out.writeLong(mCodecId); 155 out.writeTypedObject(mCodecSpecificConfig, 0); 156 out.writeTypedObject(mContentMetadata, 0); 157 out.writeTypedList(mChannels); 158 } 159 160 /** 161 * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastSubgroup} from parcel. 162 * @hide 163 */ 164 @SystemApi 165 @NonNull 166 public static final Creator<BluetoothLeBroadcastSubgroup> CREATOR = new Creator<>() { 167 public @NonNull BluetoothLeBroadcastSubgroup createFromParcel(@NonNull Parcel in) { 168 Builder builder = new Builder(); 169 builder.setCodecId(in.readLong()); 170 builder.setCodecSpecificConfig(in.readTypedObject( 171 BluetoothLeAudioCodecConfigMetadata.CREATOR)); 172 builder.setContentMetadata( 173 in.readTypedObject(BluetoothLeAudioContentMetadata.CREATOR)); 174 List<BluetoothLeBroadcastChannel> channels = new ArrayList<>(); 175 in.readTypedList(channels, BluetoothLeBroadcastChannel.CREATOR); 176 for (BluetoothLeBroadcastChannel channel : channels) { 177 builder.addChannel(channel); 178 } 179 return builder.build(); 180 } 181 182 public @NonNull BluetoothLeBroadcastSubgroup[] newArray(int size) { 183 return new BluetoothLeBroadcastSubgroup[size]; 184 } 185 }; 186 187 private static final int UNKNOWN_VALUE_PLACEHOLDER = -1; 188 189 /** 190 * Builder for {@link BluetoothLeBroadcastSubgroup}. 191 * @hide 192 */ 193 @SystemApi 194 public static final class Builder { 195 private long mCodecId = UNKNOWN_VALUE_PLACEHOLDER; 196 private BluetoothLeAudioCodecConfigMetadata mCodecSpecificConfig = null; 197 private BluetoothLeAudioContentMetadata mContentMetadata = null; 198 private List<BluetoothLeBroadcastChannel> mChannels = new ArrayList<>(); 199 200 /** 201 * Create an empty constructor. 202 * 203 * @hide 204 */ 205 @SystemApi Builder()206 public Builder() {} 207 208 /** 209 * Create a builder with copies of information from original object. 210 * 211 * @param original original object 212 * @hide 213 */ 214 @SystemApi Builder(@onNull BluetoothLeBroadcastSubgroup original)215 public Builder(@NonNull BluetoothLeBroadcastSubgroup original) { 216 mCodecId = original.getCodecId(); 217 mCodecSpecificConfig = original.getCodecSpecificConfig(); 218 mContentMetadata = original.getContentMetadata(); 219 mChannels = original.getChannels(); 220 } 221 222 /** 223 * Set the codec ID field as defined by the Basic Audio Profile. 224 * 225 * The codec ID field has 5 octets, with 226 * - Octet 0: Coding_Format as defined in Bluetooth Assigned Numbers 227 * - Octet 1-2: Company ID as defined in Bluetooth Assigned Numbers 228 * Shall be 0x0000 if octet 0 != 0xFF 229 * - Octet 3-4: Vendor-specific codec ID 230 * Shall be 0x0000 if octet 0 != 0xFF 231 * 232 * @param codecId 5-byte codec ID field in Java long format 233 * @return this builder 234 * @hide 235 */ 236 @SystemApi setCodecId(long codecId)237 public @NonNull Builder setCodecId(long codecId) { 238 mCodecId = codecId; 239 return this; 240 } 241 242 /** 243 * Set codec specific config metadata for this subgroup. 244 * 245 * @param codecSpecificConfig codec specific config metadata for this subgroup 246 * @throws {@link NullPointerException} if codecSpecificConfig is null 247 * @return this builder 248 * @hide 249 */ 250 @SystemApi 251 @NonNull setCodecSpecificConfig( @onNull BluetoothLeAudioCodecConfigMetadata codecSpecificConfig)252 public Builder setCodecSpecificConfig( 253 @NonNull BluetoothLeAudioCodecConfigMetadata codecSpecificConfig) { 254 Objects.requireNonNull(codecSpecificConfig, "codecSpecificConfig cannot be null"); 255 mCodecSpecificConfig = codecSpecificConfig; 256 return this; 257 } 258 259 /** 260 * Set content metadata for this Broadcast Source subgroup. 261 * 262 * @param contentMetadata content metadata for this Broadcast Source subgroup 263 * @throws NullPointerException if contentMetadata is null 264 * @return this builder 265 * @hide 266 */ 267 @SystemApi 268 @NonNull setContentMetadata( @onNull BluetoothLeAudioContentMetadata contentMetadata)269 public Builder setContentMetadata( 270 @NonNull BluetoothLeAudioContentMetadata contentMetadata) { 271 Objects.requireNonNull(contentMetadata, "contentMetadata cannot be null"); 272 mContentMetadata = contentMetadata; 273 return this; 274 } 275 276 /** 277 * Add a Broadcast Channel to this Broadcast subgroup. 278 * 279 * Each Broadcast Channel represents a Broadcast Isochronous Stream (BIS) 280 * 281 * A Broadcast subgroup should contain at least 1 Broadcast Channel 282 * 283 * @param channel a Broadcast Channel to be added to this Broadcast subgroup 284 * @throws NullPointerException if channel is null 285 * @return this builder 286 * @hide 287 */ 288 @SystemApi addChannel(@onNull BluetoothLeBroadcastChannel channel)289 public @NonNull Builder addChannel(@NonNull BluetoothLeBroadcastChannel channel) { 290 Objects.requireNonNull(channel, "channel cannot be null"); 291 mChannels.add(channel); 292 return this; 293 } 294 295 /** 296 * Clear channel list so that one can reset the builder after create it from an existing 297 * object. 298 * 299 * @return this builder 300 * @hide 301 */ 302 @SystemApi clearChannel()303 public @NonNull Builder clearChannel() { 304 mChannels.clear(); 305 return this; 306 } 307 308 /** 309 * Build {@link BluetoothLeBroadcastSubgroup}. 310 * 311 * @return constructed {@link BluetoothLeBroadcastSubgroup} 312 * @throws NullPointerException if {@link NonNull} items are null 313 * @throws IllegalArgumentException if the object cannot be built 314 * @hide 315 */ 316 @SystemApi build()317 public @NonNull BluetoothLeBroadcastSubgroup build() { 318 Objects.requireNonNull(mCodecSpecificConfig, "CodecSpecificConfig is null"); 319 Objects.requireNonNull(mContentMetadata, "ContentMetadata is null"); 320 if (mChannels.isEmpty()) { 321 throw new IllegalArgumentException("Must have at least one channel"); 322 } 323 return new BluetoothLeBroadcastSubgroup(mCodecId, mCodecSpecificConfig, 324 mContentMetadata, mChannels); 325 } 326 } 327 } 328