• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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