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.FlaggedApi;
20 import android.annotation.IntDef;
21 import android.annotation.IntRange;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.SystemApi;
25 import android.os.Parcel;
26 import android.os.Parcelable;
27 
28 import com.android.bluetooth.flags.Flags;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.List;
35 import java.util.Objects;
36 
37 /**
38  * This class represents a Broadcast Source group and the associated information that is needed by
39  * Broadcast Audio Scan Service (BASS) to set up a Broadcast Sink.
40  *
41  * <p>For example, an LE Audio Broadcast Sink can use the information contained within an instance
42  * of this class to synchronize with an LE Audio Broadcast group in order to listen to audio from
43  * Broadcast subgroup using one or more Broadcast Channels.
44  *
45  * @hide
46  */
47 @SystemApi
48 public final class BluetoothLeBroadcastMetadata implements Parcelable {
49     // Information needed for adding broadcast Source
50 
51     // Optional: Identity address type
52     private final @BluetoothDevice.AddressType int mSourceAddressType;
53     // Optional: Must use identity address
54     private final BluetoothDevice mSourceDevice;
55     private final int mSourceAdvertisingSid;
56     private final int mBroadcastId;
57     private final int mPaSyncInterval;
58     private final boolean mIsEncrypted;
59     private final boolean mIsPublicBroadcast;
60     private final String mBroadcastName;
61     private final byte[] mBroadcastCode;
62     private final BluetoothLeAudioContentMetadata mPublicBroadcastMetadata;
63     private final @AudioConfigQuality int mAudioConfigQuality;
64     private final int mRssi;
65 
66     /**
67      * Audio configuration quality for this Broadcast Group. This quality bitmap is used for
68      * presenting the audio stream quality for this BIG, either public broadcast or non-public
69      * broadcast Bit0 indicates at least one broadcast Audio Stream configuration is standard
70      * quality Bit1 indicates at least one broadcast Audio Stream configuration is high quality
71      *
72      * @hide
73      */
74     @IntDef(
75             flag = true,
76             prefix = "AUDIO_CONFIG_QUALITY_",
77             value = {
78                 AUDIO_CONFIG_QUALITY_NONE,
79                 AUDIO_CONFIG_QUALITY_STANDARD,
80                 AUDIO_CONFIG_QUALITY_HIGH,
81             })
82     @Retention(RetentionPolicy.SOURCE)
83     public @interface AudioConfigQuality {}
84 
85     /**
86      * Audio config quality is none, default value used for audio config quality.
87      *
88      * @hide
89      */
90     @SystemApi public static final int AUDIO_CONFIG_QUALITY_NONE = 0;
91 
92     /**
93      * Audio config quality is standard. This indicates the BIG shall include at least one broadcast
94      * Audio Stream configuration defined as Mandatory for a Broadcast Sink in Basic Audio Profile,
95      * Version 1 or later, table 6.4
96      *
97      * @hide
98      */
99     @SystemApi public static final int AUDIO_CONFIG_QUALITY_STANDARD = 0x1 << 0;
100 
101     /**
102      * Audio config quality is standard. This indicates the BIG shall include at least one broadcast
103      * Audio Stream configuration setting listed in Public Broadcast Profile, Version 1 or later,
104      * table 4.2
105      *
106      * @hide
107      */
108     @SystemApi public static final int AUDIO_CONFIG_QUALITY_HIGH = 0x1 << 1;
109 
110     // BASE structure
111 
112     // See Section 7 for description. Range: 0x000000 – 0xFFFFFF Units: μs
113     // All other values: RFU
114     private final int mPresentationDelayMicros;
115     // Number of subgroups used to group BISes present in the BIG
116     // Shall be at least 1, as defined by Rule 1
117     // Sub group info numSubGroup = mSubGroups.length
118     private final List<BluetoothLeBroadcastSubgroup> mSubgroups;
119 
BluetoothLeBroadcastMetadata( int sourceAddressType, BluetoothDevice sourceDevice, int sourceAdvertisingSid, int broadcastId, int paSyncInterval, boolean isEncrypted, boolean isPublicBroadcast, String broadcastName, byte[] broadcastCode, int presentationDelay, @AudioConfigQuality int audioConfigQuality, int rssi, BluetoothLeAudioContentMetadata publicBroadcastMetadata, List<BluetoothLeBroadcastSubgroup> subgroups)120     private BluetoothLeBroadcastMetadata(
121             int sourceAddressType,
122             BluetoothDevice sourceDevice,
123             int sourceAdvertisingSid,
124             int broadcastId,
125             int paSyncInterval,
126             boolean isEncrypted,
127             boolean isPublicBroadcast,
128             String broadcastName,
129             byte[] broadcastCode,
130             int presentationDelay,
131             @AudioConfigQuality int audioConfigQuality,
132             int rssi,
133             BluetoothLeAudioContentMetadata publicBroadcastMetadata,
134             List<BluetoothLeBroadcastSubgroup> subgroups) {
135         mSourceAddressType = sourceAddressType;
136         mSourceDevice = sourceDevice;
137         mSourceAdvertisingSid = sourceAdvertisingSid;
138         mBroadcastId = broadcastId;
139         mPaSyncInterval = paSyncInterval;
140         mIsEncrypted = isEncrypted;
141         mIsPublicBroadcast = isPublicBroadcast;
142         mBroadcastName = broadcastName;
143         mBroadcastCode = broadcastCode;
144         mPresentationDelayMicros = presentationDelay;
145         mAudioConfigQuality = audioConfigQuality;
146         mRssi = rssi;
147         mPublicBroadcastMetadata = publicBroadcastMetadata;
148         mSubgroups = subgroups;
149     }
150 
151     @Override
equals(@ullable Object o)152     public boolean equals(@Nullable Object o) {
153         if (!(o instanceof BluetoothLeBroadcastMetadata)) {
154             return false;
155         }
156         final BluetoothLeBroadcastMetadata other = (BluetoothLeBroadcastMetadata) o;
157         return mSourceAddressType == other.getSourceAddressType()
158                 && mSourceDevice.equals(other.getSourceDevice())
159                 && mSourceAdvertisingSid == other.getSourceAdvertisingSid()
160                 && mBroadcastId == other.getBroadcastId()
161                 && mPaSyncInterval == other.getPaSyncInterval()
162                 && mIsEncrypted == other.isEncrypted()
163                 && mIsPublicBroadcast == other.isPublicBroadcast()
164                 && Objects.equals(mBroadcastName, other.getBroadcastName())
165                 && Arrays.equals(mBroadcastCode, other.getBroadcastCode())
166                 && mPresentationDelayMicros == other.getPresentationDelayMicros()
167                 && mAudioConfigQuality == other.getAudioConfigQuality()
168                 && mRssi == other.getRssi()
169                 && Objects.equals(mPublicBroadcastMetadata, other.getPublicBroadcastMetadata())
170                 && mSubgroups.equals(other.getSubgroups());
171     }
172 
173     @Override
hashCode()174     public int hashCode() {
175         return Objects.hash(
176                 mSourceAddressType,
177                 mSourceDevice,
178                 mSourceAdvertisingSid,
179                 mBroadcastId,
180                 mPaSyncInterval,
181                 mIsEncrypted,
182                 mIsPublicBroadcast,
183                 mBroadcastName,
184                 Arrays.hashCode(mBroadcastCode),
185                 mPresentationDelayMicros,
186                 mAudioConfigQuality,
187                 mRssi,
188                 mPublicBroadcastMetadata,
189                 mSubgroups);
190     }
191 
192     @Override
toString()193     public String toString() {
194         return "BluetoothLeBroadcastMetadata{"
195                 + ("sourceAddressType=" + mSourceAddressType)
196                 + (", sourceDevice=" + mSourceDevice)
197                 + (", sourceAdvertisingSid=" + mSourceAdvertisingSid)
198                 + (", broadcastId=" + mBroadcastId)
199                 + (", paSyncInterval=" + mPaSyncInterval)
200                 + (", isEncrypted=" + mIsEncrypted)
201                 + (", isPublicBroadcast=" + mIsPublicBroadcast)
202                 + (", broadcastName=" + mBroadcastName)
203                 + (", broadcastCode=" + Arrays.toString(mBroadcastCode))
204                 + (", presentationDelayMicros=" + mPresentationDelayMicros)
205                 + (", audioConfigQuality=" + mAudioConfigQuality)
206                 + (", rssi=" + mRssi)
207                 + (", publicBroadcastMetadata=" + mPublicBroadcastMetadata)
208                 + (", subgroups=" + mSubgroups)
209                 + '}';
210     }
211 
212     /**
213      * Get the address type of the Broadcast Source.
214      *
215      * <p>Can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}, {@link
216      * BluetoothDevice#ADDRESS_TYPE_RANDOM}
217      *
218      * @return address type of the Broadcast Source
219      * @hide
220      */
221     @SystemApi
getSourceAddressType()222     public @BluetoothDevice.AddressType int getSourceAddressType() {
223         return mSourceAddressType;
224     }
225 
226     /**
227      * Get the MAC address of the Broadcast Source, which can be Public Device Address, Random
228      * Device Address, Public Identity Address or Random (static) Identity Address.
229      *
230      * @return MAC address of the Broadcast Source
231      * @hide
232      */
233     @SystemApi
getSourceDevice()234     public @NonNull BluetoothDevice getSourceDevice() {
235         return mSourceDevice;
236     }
237 
238     /**
239      * Get Advertising_SID subfield of the ADI field of the AUX_ADV_IND PDU or the
240      * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the
241      * Broadcast Source.
242      *
243      * @return 1-byte long Advertising_SID of the Broadcast Source
244      * @hide
245      */
246     @SystemApi
getSourceAdvertisingSid()247     public int getSourceAdvertisingSid() {
248         return mSourceAdvertisingSid;
249     }
250 
251     /**
252      * Broadcast_ID of the Broadcast Source.
253      *
254      * @return 3-byte long Broadcast_ID of the Broadcast Source
255      * @hide
256      */
257     @SystemApi
getBroadcastId()258     public int getBroadcastId() {
259         return mBroadcastId;
260     }
261 
262     /**
263      * Indicated that Periodic Advertising Sync interval is unknown.
264      *
265      * @hide
266      */
267     @SystemApi public static final int PA_SYNC_INTERVAL_UNKNOWN = 0xFFFF;
268 
269     /**
270      * Get Periodic Advertising Sync interval of the broadcast Source.
271      *
272      * @return Periodic Advertising Sync interval of the broadcast Source, {@link
273      *     #PA_SYNC_INTERVAL_UNKNOWN} if unknown
274      * @hide
275      */
276     @SystemApi
getPaSyncInterval()277     public int getPaSyncInterval() {
278         return mPaSyncInterval;
279     }
280 
281     /**
282      * Return true if the Broadcast Source is encrypted.
283      *
284      * @return true if the Broadcast Source is encrypted
285      * @hide
286      */
287     @SystemApi
isEncrypted()288     public boolean isEncrypted() {
289         return mIsEncrypted;
290     }
291 
292     /**
293      * Return {@code true} if this Broadcast Group is broadcasting Public Broadcast Announcement
294      * otherwise return {@code false}.
295      *
296      * @hide
297      */
298     @SystemApi
isPublicBroadcast()299     public boolean isPublicBroadcast() {
300         return mIsPublicBroadcast;
301     }
302 
303     /**
304      * Get the broadcast name for this Broadcast Group as UTF-8 format.
305      *
306      * @return broadcast name or null for this Broadcast Group
307      * @hide
308      */
309     @SystemApi
getBroadcastName()310     public @Nullable String getBroadcastName() {
311         return mBroadcastName;
312     }
313 
314     /**
315      * Get the Broadcast Code currently set for this Broadcast Source.
316      *
317      * <p>Only needed when encryption is enabled
318      *
319      * <p>As defined in Volume 3, Part C, Section 3.2.6 of Bluetooth Core Specification, Version
320      * 5.3, Broadcast Code is used to encrypt a broadcast audio stream.
321      *
322      * <p>It must be a UTF-8 string that has at least 4 octets and should not exceed 16 octets.
323      *
324      * @return Broadcast Code currently set for this Broadcast Source, {@code null} if code is not
325      *     required or code is currently unknown
326      * @hide
327      */
328     @SystemApi
getBroadcastCode()329     public @Nullable byte[] getBroadcastCode() {
330         return mBroadcastCode;
331     }
332 
333     /**
334      * Get the overall presentation delay in microseconds of this Broadcast Source.
335      *
336      * <p>Presentation delay is defined in Section 7 of the Basic Audio Profile.
337      *
338      * @return presentation delay of this Broadcast Source in microseconds
339      * @hide
340      */
341     @SystemApi
getPresentationDelayMicros()342     public @IntRange(from = 0, to = 0xFFFFFF) int getPresentationDelayMicros() {
343         return mPresentationDelayMicros;
344     }
345 
346     /**
347      * Get broadcast audio config quality for this Broadcast Group.
348      *
349      * @return Broadcast audio config quality for this Broadcast Group
350      * @hide
351      */
352     @SystemApi
getAudioConfigQuality()353     public @AudioConfigQuality int getAudioConfigQuality() {
354         return mAudioConfigQuality;
355     }
356 
357     /**
358      * Indicated that rssi value is unknown.
359      *
360      * @hide
361      */
362     @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_MONITOR_SOURCE_SYNC_STATUS)
363     @SystemApi
364     public static final int RSSI_UNKNOWN = 0x7F;
365 
366     /**
367      * Get the Received Signal Strength Indication (RSSI) value of this Broadcast Source.
368      *
369      * <p>The valid RSSI range is [-127, 126] and as defined in Volume 4, Part E, Section 7.7.65.13
370      * of Bluetooth Core Specification, Version 5.3, value of 0x7F(127) means that the RSSI is not
371      * available.
372      *
373      * @return the RSSI {@link #RSSI_UNKNOWN} if unknown
374      * @hide
375      */
376     @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_MONITOR_SOURCE_SYNC_STATUS)
377     @SystemApi
getRssi()378     public @IntRange(from = -127, to = 127) int getRssi() {
379         return mRssi;
380     }
381 
382     /**
383      * Get public broadcast metadata for this Broadcast Group.
384      *
385      * @return public broadcast metadata for this Broadcast Group, {@code null} if no public
386      *     metadata exists
387      * @hide
388      */
389     @SystemApi
getPublicBroadcastMetadata()390     public @Nullable BluetoothLeAudioContentMetadata getPublicBroadcastMetadata() {
391         return mPublicBroadcastMetadata;
392     }
393 
394     /**
395      * Get available subgroups in this broadcast source.
396      *
397      * @return list of subgroups in this broadcast source, which should contain at least one
398      *     subgroup for each Broadcast Source
399      * @hide
400      */
401     @SystemApi
getSubgroups()402     public @NonNull List<BluetoothLeBroadcastSubgroup> getSubgroups() {
403         return mSubgroups;
404     }
405 
406     /**
407      * {@inheritDoc}
408      *
409      * @hide
410      */
411     @Override
describeContents()412     public int describeContents() {
413         return 0;
414     }
415 
416     /**
417      * {@inheritDoc}
418      *
419      * @hide
420      */
421     @Override
writeToParcel(Parcel out, int flags)422     public void writeToParcel(Parcel out, int flags) {
423         out.writeInt(mSourceAddressType);
424         if (mSourceDevice != null) {
425             out.writeInt(1);
426             out.writeTypedObject(mSourceDevice, 0);
427         } else {
428             // zero indicates missing mSourceDevice
429             out.writeInt(0);
430         }
431         out.writeInt(mSourceAdvertisingSid);
432         out.writeInt(mBroadcastId);
433         out.writeInt(mPaSyncInterval);
434         out.writeBoolean(mIsEncrypted);
435         if (mBroadcastCode != null) {
436             out.writeInt(mBroadcastCode.length);
437             out.writeByteArray(mBroadcastCode);
438         } else {
439             // -1 indicates missing broadcast code
440             out.writeInt(-1);
441         }
442         out.writeInt(mPresentationDelayMicros);
443         out.writeTypedList(mSubgroups);
444         out.writeBoolean(mIsPublicBroadcast);
445         out.writeString(mBroadcastName);
446         out.writeInt(mAudioConfigQuality);
447         out.writeTypedObject(mPublicBroadcastMetadata, 0);
448         out.writeInt(mRssi);
449     }
450 
451     /**
452      * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastMetadata} from parcel.
453      *
454      * @hide
455      */
456     @SystemApi @NonNull
457     public static final Creator<BluetoothLeBroadcastMetadata> CREATOR =
458             new Creator<>() {
459                 public @NonNull BluetoothLeBroadcastMetadata createFromParcel(@NonNull Parcel in) {
460                     Builder builder = new Builder();
461                     final int sourceAddressType = in.readInt();
462                     final int deviceExist = in.readInt();
463                     BluetoothDevice sourceDevice = null;
464                     if (deviceExist == 1) {
465                         sourceDevice = in.readTypedObject(BluetoothDevice.CREATOR);
466                     }
467                     builder.setSourceDevice(sourceDevice, sourceAddressType);
468                     builder.setSourceAdvertisingSid(in.readInt());
469                     builder.setBroadcastId(in.readInt());
470                     builder.setPaSyncInterval(in.readInt());
471                     builder.setEncrypted(in.readBoolean());
472                     final int codeLen = in.readInt();
473                     byte[] broadcastCode = null;
474                     if (codeLen != -1) {
475                         broadcastCode = new byte[codeLen];
476                         if (codeLen >= 0) {
477                             in.readByteArray(broadcastCode);
478                         }
479                     }
480                     builder.setBroadcastCode(broadcastCode);
481                     builder.setPresentationDelayMicros(in.readInt());
482                     final List<BluetoothLeBroadcastSubgroup> subgroups = new ArrayList<>();
483                     in.readTypedList(subgroups, BluetoothLeBroadcastSubgroup.CREATOR);
484                     for (BluetoothLeBroadcastSubgroup subgroup : subgroups) {
485                         builder.addSubgroup(subgroup);
486                     }
487                     builder.setPublicBroadcast(in.readBoolean());
488                     builder.setBroadcastName(in.readString());
489                     builder.setAudioConfigQuality(in.readInt());
490                     builder.setPublicBroadcastMetadata(
491                             in.readTypedObject(BluetoothLeAudioContentMetadata.CREATOR));
492                     builder.setRssi(in.readInt());
493                     return builder.build();
494                 }
495 
496                 public @NonNull BluetoothLeBroadcastMetadata[] newArray(int size) {
497                     return new BluetoothLeBroadcastMetadata[size];
498                 }
499             };
500 
501     private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
502 
503     /**
504      * Builder for {@link BluetoothLeBroadcastMetadata}.
505      *
506      * @hide
507      */
508     @SystemApi
509     public static final class Builder {
510         private @BluetoothDevice.AddressType int mSourceAddressType =
511                 BluetoothDevice.ADDRESS_TYPE_UNKNOWN;
512         private BluetoothDevice mSourceDevice = null;
513         private int mSourceAdvertisingSid = UNKNOWN_VALUE_PLACEHOLDER;
514         private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER;
515         private int mPaSyncInterval = PA_SYNC_INTERVAL_UNKNOWN;
516         private boolean mIsEncrypted = false;
517         private boolean mIsPublicBroadcast = false;
518         private String mBroadcastName = null;
519         private byte[] mBroadcastCode = null;
520         private int mPresentationDelayMicros = UNKNOWN_VALUE_PLACEHOLDER;
521         private @AudioConfigQuality int mAudioConfigQuality = AUDIO_CONFIG_QUALITY_NONE;
522         private int mRssi = RSSI_UNKNOWN;
523         private BluetoothLeAudioContentMetadata mPublicBroadcastMetadata = null;
524         private List<BluetoothLeBroadcastSubgroup> mSubgroups = new ArrayList<>();
525 
526         /**
527          * Create an empty builder.
528          *
529          * @hide
530          */
531         @SystemApi
Builder()532         public Builder() {}
533 
534         /**
535          * Create a builder with copies of information from original object.
536          *
537          * @param original original object
538          * @hide
539          */
540         @SystemApi
Builder(@onNull BluetoothLeBroadcastMetadata original)541         public Builder(@NonNull BluetoothLeBroadcastMetadata original) {
542             mSourceAddressType = original.getSourceAddressType();
543             mSourceDevice = original.getSourceDevice();
544             mSourceAdvertisingSid = original.getSourceAdvertisingSid();
545             mBroadcastId = original.getBroadcastId();
546             mPaSyncInterval = original.getPaSyncInterval();
547             mIsEncrypted = original.isEncrypted();
548             mIsPublicBroadcast = original.isPublicBroadcast();
549             mBroadcastName = original.getBroadcastName();
550             mBroadcastCode = original.getBroadcastCode();
551             mPresentationDelayMicros = original.getPresentationDelayMicros();
552             mAudioConfigQuality = original.getAudioConfigQuality();
553             mRssi = original.getRssi();
554             mPublicBroadcastMetadata = original.getPublicBroadcastMetadata();
555             mSubgroups = original.getSubgroups();
556         }
557 
558         /**
559          * Set the address type and MAC address of the Broadcast Source.
560          *
561          * <p>Address type can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}, {@link
562          * BluetoothDevice#ADDRESS_TYPE_RANDOM}
563          *
564          * <p>MAC address can be Public Device Address, Random Device Address, Public Identity
565          * Address or Random (static) Identity Address
566          *
567          * @param sourceDevice source advertiser address
568          * @param sourceAddressType source advertiser address type
569          * @throws IllegalArgumentException if sourceAddressType is invalid
570          * @throws NullPointerException if sourceDevice is null
571          * @return this builder
572          * @hide
573          */
574         @SystemApi
575         @NonNull
setSourceDevice( @onNull BluetoothDevice sourceDevice, @BluetoothDevice.AddressType int sourceAddressType)576         public Builder setSourceDevice(
577                 @NonNull BluetoothDevice sourceDevice,
578                 @BluetoothDevice.AddressType int sourceAddressType) {
579             if (sourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) {
580                 throw new IllegalArgumentException(
581                         "sourceAddressType cannot be ADDRESS_TYPE_UNKNOWN");
582             }
583             if (sourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM
584                     && sourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) {
585                 throw new IllegalArgumentException(
586                         "sourceAddressType " + sourceAddressType + " is invalid");
587             }
588             Objects.requireNonNull(sourceDevice, "sourceDevice cannot be null");
589             mSourceAddressType = sourceAddressType;
590             mSourceDevice = sourceDevice;
591             return this;
592         }
593 
594         /**
595          * Set Advertising_SID that is a subfield of the ADI field of the AUX_ADV_IND PDU or the
596          * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the
597          * Broadcast Source.
598          *
599          * @param sourceAdvertisingSid 1-byte long Advertising_SID of the Broadcast Source
600          * @return this builder
601          * @hide
602          */
603         @SystemApi
setSourceAdvertisingSid(int sourceAdvertisingSid)604         public @NonNull Builder setSourceAdvertisingSid(int sourceAdvertisingSid) {
605             mSourceAdvertisingSid = sourceAdvertisingSid;
606             return this;
607         }
608 
609         /**
610          * Set the Broadcast_ID of the Broadcast Source.
611          *
612          * @param broadcastId 3-byte long Broadcast_ID of the Broadcast Source
613          * @return this builder
614          * @hide
615          */
616         @SystemApi
setBroadcastId(int broadcastId)617         public @NonNull Builder setBroadcastId(int broadcastId) {
618             mBroadcastId = broadcastId;
619             return this;
620         }
621 
622         /**
623          * Set Periodic Advertising Sync interval of the broadcast Source.
624          *
625          * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source, {@link
626          *     #PA_SYNC_INTERVAL_UNKNOWN} if unknown
627          * @return this builder
628          * @hide
629          */
630         @SystemApi
setPaSyncInterval(int paSyncInterval)631         public @NonNull Builder setPaSyncInterval(int paSyncInterval) {
632             mPaSyncInterval = paSyncInterval;
633             return this;
634         }
635 
636         /**
637          * Set whether the Broadcast Source should be encrypted.
638          *
639          * <p>When setting up a Broadcast Source, if <var>isEncrypted</var> is true while
640          * <var>broadcastCode</var> is null, the implementation will automatically generate a
641          * Broadcast Code
642          *
643          * @param isEncrypted whether the Broadcast Source is encrypted
644          * @return this builder
645          * @hide
646          */
647         @SystemApi
setEncrypted(boolean isEncrypted)648         public @NonNull Builder setEncrypted(boolean isEncrypted) {
649             mIsEncrypted = isEncrypted;
650             return this;
651         }
652 
653         /**
654          * Set whether this Broadcast Group is broadcasting Public Broadcast Announcement.
655          *
656          * @param isPublicBroadcast whether this Broadcast Group is broadcasting Public Broadcast
657          *     Announcement
658          * @return this builder
659          * @hide
660          */
661         @SystemApi
setPublicBroadcast(boolean isPublicBroadcast)662         public @NonNull Builder setPublicBroadcast(boolean isPublicBroadcast) {
663             mIsPublicBroadcast = isPublicBroadcast;
664             return this;
665         }
666 
667         /**
668          * Set broadcast name for this Broadcast Group.
669          *
670          * @param broadcastName Broadcast name for this Broadcast Group, {@code null} if no name
671          *     provided
672          * @return this builder
673          * @hide
674          */
675         @SystemApi
setBroadcastName(@ullable String broadcastName)676         public @NonNull Builder setBroadcastName(@Nullable String broadcastName) {
677             mBroadcastName = broadcastName;
678             return this;
679         }
680 
681         /**
682          * Set the Broadcast Code currently set for this broadcast group.
683          *
684          * <p>Only needed when encryption is enabled
685          *
686          * <p>As defined in Volume 3, Part C, Section 3.2.6 of Bluetooth Core Specification, Version
687          * 5.3, Broadcast Code is used to encrypt a broadcast audio stream.
688          *
689          * <p>It must be a UTF-8 string that has at least 4 octets and should not exceed 16 octets.
690          *
691          * @param broadcastCode Broadcast Code for this Broadcast Source, {@code null} if code is
692          *     not required
693          * @return this builder
694          * @hide
695          */
696         @SystemApi
setBroadcastCode(@ullable byte[] broadcastCode)697         public @NonNull Builder setBroadcastCode(@Nullable byte[] broadcastCode) {
698             mBroadcastCode = broadcastCode;
699             return this;
700         }
701 
702         /**
703          * Set the overall presentation delay in microseconds of this Broadcast Source.
704          *
705          * <p>Presentation delay is defined in Section 7 of the Basic Audio Profile.
706          *
707          * @param presentationDelayMicros presentation delay of this Broadcast Source in
708          *     microseconds
709          * @throws IllegalArgumentException if presentationDelayMicros does not fall in [0,
710          *     0xFFFFFF]
711          * @return this builder
712          * @hide
713          */
714         @SystemApi
715         @NonNull
setPresentationDelayMicros( @ntRangefrom = 0, to = 0xFFFFFF) int presentationDelayMicros)716         public Builder setPresentationDelayMicros(
717                 @IntRange(from = 0, to = 0xFFFFFF) int presentationDelayMicros) {
718             if (presentationDelayMicros < 0 || presentationDelayMicros >= 0xFFFFFF) {
719                 throw new IllegalArgumentException(
720                         "presentationDelayMicros "
721                                 + presentationDelayMicros
722                                 + " does not fall in [0, 0xFFFFFF]");
723             }
724             mPresentationDelayMicros = presentationDelayMicros;
725             return this;
726         }
727 
728         /**
729          * Set broadcast audio config quality for this Broadcast Group.
730          *
731          * @param audioConfigQuality broadcast audio config quality for this Broadcast Group
732          * @return this builder
733          * @hide
734          */
735         @SystemApi
736         @NonNull
setAudioConfigQuality(@udioConfigQuality int audioConfigQuality)737         public Builder setAudioConfigQuality(@AudioConfigQuality int audioConfigQuality) {
738             mAudioConfigQuality = audioConfigQuality;
739             return this;
740         }
741 
742         /**
743          * Set the Received Signal Strength Indication (RSSI) value for this Broadcast metadata.
744          *
745          * <p>The valid RSSI range is [-127, 126] and as defined in Volume 4, Part E, Section
746          * 7.7.65.13 of Bluetooth Core Specification, Version 5.3, value of 0x7F(127) means that the
747          * RSSI is not available.
748          *
749          * @param rssi the RSSI
750          * @return this builder
751          * @throws IllegalArgumentException if rssi is not in the range [-127, 127].
752          * @hide
753          */
754         @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_MONITOR_SOURCE_SYNC_STATUS)
755         @SystemApi
756         @NonNull
setRssi(@ntRangefrom = -127, to = 127) int rssi)757         public Builder setRssi(@IntRange(from = -127, to = 127) int rssi) {
758             if (rssi < -127 || rssi > 127) {
759                 throw new IllegalArgumentException("illegal rssi " + rssi);
760             }
761             mRssi = rssi;
762             return this;
763         }
764 
765         /**
766          * Set public broadcast metadata for this Broadcast Group. PBS should include the
767          * Program_Info length-type-value (LTV) structure metadata
768          *
769          * @param publicBroadcastMetadata public broadcast metadata for this Broadcast Group, {@code
770          *     null} if no public meta data provided
771          * @return this builder
772          * @hide
773          */
774         @SystemApi
775         @NonNull
setPublicBroadcastMetadata( @ullable BluetoothLeAudioContentMetadata publicBroadcastMetadata)776         public Builder setPublicBroadcastMetadata(
777                 @Nullable BluetoothLeAudioContentMetadata publicBroadcastMetadata) {
778             mPublicBroadcastMetadata = publicBroadcastMetadata;
779             return this;
780         }
781 
782         /**
783          * Add a subgroup to this broadcast source.
784          *
785          * @param subgroup {@link BluetoothLeBroadcastSubgroup} that contains a subgroup's metadata
786          * @throws NullPointerException if subgroup is null
787          * @return this builder
788          * @hide
789          */
790         @SystemApi
addSubgroup(@onNull BluetoothLeBroadcastSubgroup subgroup)791         public @NonNull Builder addSubgroup(@NonNull BluetoothLeBroadcastSubgroup subgroup) {
792             Objects.requireNonNull(subgroup, "subgroup cannot be null");
793             mSubgroups.add(subgroup);
794             return this;
795         }
796 
797         /**
798          * Clear subgroup list so that one can reset the builder after create it from an existing
799          * object.
800          *
801          * @return this builder
802          * @hide
803          */
804         @SystemApi
clearSubgroup()805         public @NonNull Builder clearSubgroup() {
806             mSubgroups.clear();
807             return this;
808         }
809 
810         /**
811          * Build {@link BluetoothLeBroadcastMetadata}.
812          *
813          * @return {@link BluetoothLeBroadcastMetadata}
814          * @throws IllegalArgumentException if the object cannot be built
815          * @throws NullPointerException if {@link NonNull} items are null
816          * @hide
817          */
818         @SystemApi
build()819         public @NonNull BluetoothLeBroadcastMetadata build() {
820             if (mSourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) {
821                 throw new IllegalArgumentException("SourceAddressTyp cannot be unknown");
822             }
823             if (mSourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM
824                     && mSourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) {
825                 throw new IllegalArgumentException(
826                         "sourceAddressType " + mSourceAddressType + " is invalid");
827             }
828             Objects.requireNonNull(mSourceDevice, "mSourceDevice cannot be null");
829             if (mSubgroups.isEmpty()) {
830                 throw new IllegalArgumentException("Must contain at least one subgroup");
831             }
832             return new BluetoothLeBroadcastMetadata(
833                     mSourceAddressType,
834                     mSourceDevice,
835                     mSourceAdvertisingSid,
836                     mBroadcastId,
837                     mPaSyncInterval,
838                     mIsEncrypted,
839                     mIsPublicBroadcast,
840                     mBroadcastName,
841                     mBroadcastCode,
842                     mPresentationDelayMicros,
843                     mAudioConfigQuality,
844                     mRssi,
845                     mPublicBroadcastMetadata,
846                     mSubgroups);
847         }
848     }
849 }
850