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.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32 import java.util.Objects;
33 
34 /**
35  * The {@link BluetoothLeBroadcastReceiveState} is used by the BASS server to expose information
36  * about a Broadcast Source.
37  *
38  * <p>It represents the current synchronization state of the server to a PA and/or a BIG containing
39  * one or more subgroups containing one or more BISes transmitted by that Broadcast Source. The
40  * Broadcast Receive State characteristic is also used to inform clients whether the server has
41  * detected that the BIS is encrypted, whether the server requires a Broadcast_Code, and whether the
42  * server is decrypting the BIS.
43  *
44  * @hide
45  */
46 @SystemApi
47 public final class BluetoothLeBroadcastReceiveState implements Parcelable {
48     /**
49      * Periodic Advertising Synchronization state.
50      *
51      * <p>Periodic Advertising (PA) enables the LE Audio Broadcast Assistant to discover broadcast
52      * audio streams as well as the audio stream configuration on behalf of an LE Audio Broadcast
53      * Sink. This information can then be transferred to the LE Audio Broadcast Sink using the
54      * Periodic Advertising Synchronization Transfer (PAST) procedure.
55      *
56      * @hide
57      */
58     @IntDef(
59             prefix = "PA_SYNC_STATE_",
60             value = {
61                 PA_SYNC_STATE_IDLE,
62                 PA_SYNC_STATE_SYNCINFO_REQUEST,
63                 PA_SYNC_STATE_SYNCHRONIZED,
64                 PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE,
65                 PA_SYNC_STATE_NO_PAST
66             })
67     @Retention(RetentionPolicy.SOURCE)
68     public @interface PaSyncState {}
69 
70     /**
71      * Indicates that the Broadcast Sink is not synchronized with the Periodic Advertisements (PA)
72      *
73      * @hide
74      */
75     @SystemApi public static final int PA_SYNC_STATE_IDLE = 0;
76 
77     /**
78      * Indicates that the Broadcast Sink requested the Broadcast Assistant to synchronize with the
79      * Periodic Advertisements (PA).
80      *
81      * <p>This is also known as scan delegation or scan offloading.
82      *
83      * @hide
84      */
85     @SystemApi public static final int PA_SYNC_STATE_SYNCINFO_REQUEST = 1;
86 
87     /**
88      * Indicates that the Broadcast Sink is synchronized with the Periodic Advertisements (PA).
89      *
90      * @hide
91      */
92     @SystemApi public static final int PA_SYNC_STATE_SYNCHRONIZED = 2;
93 
94     /**
95      * Indicates that the Broadcast Sink was unable to synchronize with the Periodic Advertisements
96      * (PA).
97      *
98      * @hide
99      */
100     @SystemApi public static final int PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE = 3;
101 
102     /**
103      * Indicates that the Broadcast Sink should be synchronized with the Periodic Advertisements
104      * (PA) using the Periodic Advertisements Synchronization Transfer (PAST) procedure.
105      *
106      * @hide
107      */
108     @SystemApi public static final int PA_SYNC_STATE_NO_PAST = 4;
109 
110     /**
111      * Indicates that the Broadcast Sink synchronization state is invalid.
112      *
113      * @hide
114      */
115     public static final int PA_SYNC_STATE_INVALID = 0xFFFF;
116 
117     /** @hide */
118     @IntDef(
119             prefix = "BIG_ENCRYPTION_STATE_",
120             value = {
121                 BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
122                 BIG_ENCRYPTION_STATE_CODE_REQUIRED,
123                 BIG_ENCRYPTION_STATE_DECRYPTING,
124                 BIG_ENCRYPTION_STATE_BAD_CODE
125             })
126     @Retention(RetentionPolicy.SOURCE)
127     public @interface BigEncryptionState {}
128 
129     /**
130      * Indicates that the Broadcast Sink is synchronized with an unencrypted audio stream from a
131      * Broadcast Source
132      *
133      * @hide
134      */
135     @SystemApi public static final int BIG_ENCRYPTION_STATE_NOT_ENCRYPTED = 0;
136 
137     /**
138      * Indicates that the Broadcast Sink needs a Broadcast Code to synchronize with an audio stream
139      * from a Broadcast Source, which was not provided when the audio stream from the Broadcast
140      * Source was added.
141      *
142      * @hide
143      */
144     @SystemApi public static final int BIG_ENCRYPTION_STATE_CODE_REQUIRED = 1;
145 
146     /**
147      * Indicates that the Broadcast Sink is synchronized with an encrypted audio stream from a
148      * Broadcast Source.
149      *
150      * @hide
151      */
152     @SystemApi public static final int BIG_ENCRYPTION_STATE_DECRYPTING = 2;
153 
154     /**
155      * Indicates that the Broadcast Sink is unable to decrypt an audio stream from a Broadcast
156      * Source due to an incorrect Broadcast Code.
157      *
158      * @hide
159      */
160     @SystemApi public static final int BIG_ENCRYPTION_STATE_BAD_CODE = 3;
161 
162     /**
163      * Indicates that the Broadcast Sink encryption state is invalid.
164      *
165      * @hide
166      */
167     public static final int BIG_ENCRYPTION_STATE_INVALID = 0xFFFF;
168 
169     private final int mSourceId;
170     private final @BluetoothDevice.AddressType int mSourceAddressType;
171     private final BluetoothDevice mSourceDevice;
172     private final int mSourceAdvertisingSid;
173     private final int mBroadcastId;
174     private final @PaSyncState int mPaSyncState;
175     private final @BigEncryptionState int mBigEncryptionState;
176     private final byte[] mBadCode;
177     private final int mNumSubgroups;
178     private final List<Long> mBisSyncState;
179     private final List<BluetoothLeAudioContentMetadata> mSubgroupMetadata;
180 
paSyncStateToString(int paSyncState)181     private static String paSyncStateToString(int paSyncState) {
182         switch (paSyncState) {
183             case 0x00:
184                 return "Not synchronized to PA: [" + Integer.toString(paSyncState) + "]";
185             case 0x01:
186                 return "SyncInfo Request: [" + Integer.toString(paSyncState) + "]";
187             case 0x02:
188                 return "Synchronized to PA: [" + Integer.toString(paSyncState) + "]";
189             case 0x03:
190                 return "Failed to synchronize to PA: [" + Integer.toString(paSyncState) + "]";
191             case 0x04:
192                 return "No PAST: [" + Integer.toString(paSyncState) + "]";
193             default:
194                 return "RFU: [" + Integer.toString(paSyncState) + "]";
195         }
196     }
197 
bigEncryptionStateToString(int bigEncryptionState)198     private static String bigEncryptionStateToString(int bigEncryptionState) {
199         switch (bigEncryptionState) {
200             case 0x00:
201                 return "Not encrypted: [" + Integer.toString(bigEncryptionState) + "]";
202             case 0x01:
203                 return "Broadcast_Code required: [" + Integer.toString(bigEncryptionState) + "]";
204             case 0x02:
205                 return "Decrypting: [" + Integer.toString(bigEncryptionState) + "]";
206             case 0x03:
207                 return "Bad_Code (incorrect encryption key): ["
208                         + Integer.toString(bigEncryptionState)
209                         + "]";
210             default:
211                 return "RFU: [" + Integer.toString(bigEncryptionState) + "]";
212         }
213     }
214 
bisSyncStateToString(Long bisSyncState, int bisSyncStateIndex)215     private static String bisSyncStateToString(Long bisSyncState, int bisSyncStateIndex) {
216         if (bisSyncState == 0) {
217             return "Not synchronized to BIS_index["
218                     + Integer.toString(bisSyncStateIndex)
219                     + "]: ["
220                     + String.valueOf(bisSyncState)
221                     + "]";
222         } else if (bisSyncState > 0 && bisSyncState < 0xFFFFFFFF) {
223             return "Synchronized to BIS_index["
224                     + Integer.toString(bisSyncStateIndex)
225                     + "]: ["
226                     + String.valueOf(bisSyncState)
227                     + "]";
228         } else if (bisSyncState == 0xFFFFFFFF) {
229             return "Failed to sync to BIG: [" + String.valueOf(bisSyncState) + "]";
230         } else {
231             return "[" + String.valueOf(bisSyncState) + "]";
232         }
233     }
234 
235     /**
236      * Constructor to create a read-only {@link BluetoothLeBroadcastReceiveState} instance.
237      *
238      * @throws NullPointerException if sourceDevice, bisSyncState, or subgroupMetadata is null
239      * @throws IllegalArgumentException if sourceID is not [0, 0xFF] or if sourceAddressType is
240      *     invalid or if bisSyncState.size() != numSubgroups or if subgroupMetadata.size() !=
241      *     numSubgroups or if paSyncState or bigEncryptionState is not recognized bye IntDef
242      * @hide
243      */
BluetoothLeBroadcastReceiveState( @ntRangefrom = 0x00, to = 0xFF) int sourceId, @BluetoothDevice.AddressType int sourceAddressType, @NonNull BluetoothDevice sourceDevice, int sourceAdvertisingSid, int broadcastId, @PaSyncState int paSyncState, @BigEncryptionState int bigEncryptionState, byte[] badCode, @IntRange(from = 0x00) int numSubgroups, @NonNull List<Long> bisSyncState, @NonNull List<BluetoothLeAudioContentMetadata> subgroupMetadata)244     public BluetoothLeBroadcastReceiveState(
245             @IntRange(from = 0x00, to = 0xFF) int sourceId,
246             @BluetoothDevice.AddressType int sourceAddressType,
247             @NonNull BluetoothDevice sourceDevice,
248             int sourceAdvertisingSid,
249             int broadcastId,
250             @PaSyncState int paSyncState,
251             @BigEncryptionState int bigEncryptionState,
252             byte[] badCode,
253             @IntRange(from = 0x00) int numSubgroups,
254             @NonNull List<Long> bisSyncState,
255             @NonNull List<BluetoothLeAudioContentMetadata> subgroupMetadata) {
256         if (sourceId < 0x00 || sourceId > 0xFF) {
257             throw new IllegalArgumentException(
258                     "sourceId " + sourceId + " does not fall between 0x00 and 0xFF");
259         }
260         Objects.requireNonNull(sourceDevice, "sourceDevice cannot be null");
261         if (sourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) {
262             throw new IllegalArgumentException("sourceAddressType cannot be ADDRESS_TYPE_UNKNOWN");
263         }
264         if (sourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM
265                 && sourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) {
266             throw new IllegalArgumentException(
267                     "sourceAddressType " + sourceAddressType + " is invalid");
268         }
269         Objects.requireNonNull(bisSyncState, "bisSyncState cannot be null");
270         if (bisSyncState.size() != numSubgroups) {
271             throw new IllegalArgumentException(
272                     "bisSyncState.size() "
273                             + bisSyncState.size()
274                             + " must be equal to numSubgroups "
275                             + numSubgroups);
276         }
277         Objects.requireNonNull(subgroupMetadata, "subgroupMetadata cannot be null");
278         if (subgroupMetadata.size() != numSubgroups) {
279             throw new IllegalArgumentException(
280                     "subgroupMetadata.size()  "
281                             + subgroupMetadata.size()
282                             + " must be equal to numSubgroups "
283                             + numSubgroups);
284         }
285         if (paSyncState != PA_SYNC_STATE_IDLE
286                 && paSyncState != PA_SYNC_STATE_SYNCINFO_REQUEST
287                 && paSyncState != PA_SYNC_STATE_SYNCHRONIZED
288                 && paSyncState != PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE
289                 && paSyncState != PA_SYNC_STATE_NO_PAST
290                 && paSyncState != PA_SYNC_STATE_INVALID) {
291             throw new IllegalArgumentException("unrecognized paSyncState " + paSyncState);
292         }
293         if (bigEncryptionState != BIG_ENCRYPTION_STATE_NOT_ENCRYPTED
294                 && bigEncryptionState != BIG_ENCRYPTION_STATE_CODE_REQUIRED
295                 && bigEncryptionState != BIG_ENCRYPTION_STATE_DECRYPTING
296                 && bigEncryptionState != BIG_ENCRYPTION_STATE_BAD_CODE
297                 && bigEncryptionState != BIG_ENCRYPTION_STATE_INVALID) {
298             throw new IllegalArgumentException(
299                     "unrecognized bigEncryptionState " + bigEncryptionState);
300         }
301         if (badCode != null && badCode.length != 16) {
302             throw new IllegalArgumentException(
303                     "badCode must be 16 bytes long of null, but is "
304                             + badCode.length
305                             + " + bytes long");
306         }
307         mSourceId = sourceId;
308         mSourceAddressType = sourceAddressType;
309         mSourceDevice = sourceDevice;
310         mSourceAdvertisingSid = sourceAdvertisingSid;
311         mBroadcastId = broadcastId;
312         mPaSyncState = paSyncState;
313         mBigEncryptionState = bigEncryptionState;
314         mBadCode = badCode;
315         mNumSubgroups = numSubgroups;
316         mBisSyncState = bisSyncState;
317         mSubgroupMetadata = subgroupMetadata;
318     }
319 
320     /**
321      * Get the source ID assigned by the BASS server
322      *
323      * <p>Shall be unique for each instance of the Broadcast Receive State characteristic exposed by
324      * the server
325      *
326      * @return source ID assigned by the BASS server
327      * @hide
328      */
329     @SystemApi
getSourceId()330     public @IntRange(from = 0x00, to = 0xFF) int getSourceId() {
331         return mSourceId;
332     }
333 
334     /**
335      * Get the address type of the Broadcast Source
336      *
337      * Can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC} or
338      * {@link BluetoothDevice#ADDRESS_TYPE_RANDOM
339      *
340      * @return address type of the Broadcast Source
341      * @hide
342      */
343     @SystemApi
getSourceAddressType()344     public @BluetoothDevice.AddressType int getSourceAddressType() {
345         return mSourceAddressType;
346     }
347 
348     /**
349      * Get the MAC address of the Broadcast Source, which can be Public Device Address, Random
350      * Device Address, Public Identity Address or Random (static) Identity Address
351      *
352      * @return MAC address of the Broadcast Source
353      * @hide
354      */
355     @SystemApi
getSourceDevice()356     public @NonNull BluetoothDevice getSourceDevice() {
357         return mSourceDevice;
358     }
359 
360     /**
361      * Get Advertising_SID subfield of the ADI field of the AUX_ADV_IND PDU or the
362      * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the
363      * Broadcast Source.
364      *
365      * @return 1-byte long Advertising_SID of the Broadcast Source
366      * @hide
367      */
368     @SystemApi
getSourceAdvertisingSid()369     public int getSourceAdvertisingSid() {
370         return mSourceAdvertisingSid;
371     }
372 
373     /**
374      * Broadcast_ID of the Broadcast Source
375      *
376      * @return 3-byte long Broadcast_ID of the Broadcast Source
377      * @hide
378      */
379     @SystemApi
getBroadcastId()380     public int getBroadcastId() {
381         return mBroadcastId;
382     }
383 
384     /**
385      * Get the Periodic Advertisement synchronization state between the Broadcast Sink and the
386      * Broadcast source
387      *
388      * <p>Possible values are {@link #PA_SYNC_STATE_IDLE}, {@link #PA_SYNC_STATE_SYNCINFO_REQUEST},
389      * {@link #PA_SYNC_STATE_SYNCHRONIZED}, {@link #PA_SYNC_STATE_FAILED_TO_SYNCHRONIZE}, {@link
390      * #PA_SYNC_STATE_NO_PAST}
391      *
392      * @return Periodic Advertisement synchronization state
393      * @hide
394      */
395     @SystemApi
getPaSyncState()396     public @PaSyncState int getPaSyncState() {
397         return mPaSyncState;
398     }
399 
400     /**
401      * Get the encryption state of a Broadcast Isochronous Group (BIG)
402      *
403      * <p>Possible values are {@link #BIG_ENCRYPTION_STATE_NOT_ENCRYPTED}, {@link
404      * #BIG_ENCRYPTION_STATE_CODE_REQUIRED}, {@link #BIG_ENCRYPTION_STATE_DECRYPTING}, {@link
405      * #BIG_ENCRYPTION_STATE_DECRYPTING}, and {@link #BIG_ENCRYPTION_STATE_BAD_CODE}
406      *
407      * @return encryption state of a Broadcast Isochronous Group (BIG)
408      * @hide
409      */
410     @SystemApi
getBigEncryptionState()411     public @BigEncryptionState int getBigEncryptionState() {
412         return mBigEncryptionState;
413     }
414 
415     /**
416      * If {@link #getBigEncryptionState()} returns {@link #BIG_ENCRYPTION_STATE_BAD_CODE}, this
417      * method returns the value of the incorrect 16-octet Broadcast Code that fails to decrypt an
418      * audio stream from a Broadcast Source.
419      *
420      * @return 16-octet Broadcast Code, or null if {@link #getBigEncryptionState()} does not return
421      *     {@link #BIG_ENCRYPTION_STATE_BAD_CODE}
422      * @hide
423      */
424     @SystemApi
getBadCode()425     public @Nullable byte[] getBadCode() {
426         return mBadCode;
427     }
428 
429     /**
430      * Get number of Broadcast subgroups being added to this sink
431      *
432      * @return number of Broadcast subgroups being added to this sink
433      */
getNumSubgroups()434     public int getNumSubgroups() {
435         return mNumSubgroups;
436     }
437 
438     /**
439      * Get a list of bitfield on whether a Broadcast Isochronous Stream (BIS) is synchronized
440      * between the sink and source
441      *
442      * <p>The number of items in the returned list is the same as {@link #getNumSubgroups()}. For
443      * each subgroup, at most 31 BISes are available and their synchronization state is indicated by
444      * its bit value at the particular offset (i.e. Bit 0-30 = BIS_index[1-31])
445      *
446      * <p>For example, if (BisSyncState & 0b1 << 5) != 0, BIS 5 is synchronized between source and
447      * sync
448      *
449      * <p>There is a special case, 0xFFFFFFFF to indicate Broadcast Sink failed to synchronize to a
450      * particular subgroup
451      *
452      * @return a list of bitfield on whether a Broadcast Isochronous Stream (BIS) is synchronized
453      *     between the sink and source
454      * @hide
455      */
456     @SystemApi
getBisSyncState()457     public @NonNull List<Long> getBisSyncState() {
458         return mBisSyncState;
459     }
460 
461     /**
462      * Get metadata for every subgroup added to this Broadcast Sink
463      *
464      * <p>The number of items in the returned list is the same as {@link #getNumSubgroups()}.
465      *
466      * @return metadata for every subgroup added to this Broadcast Sink
467      * @hide
468      */
469     @SystemApi
getSubgroupMetadata()470     public @NonNull List<BluetoothLeAudioContentMetadata> getSubgroupMetadata() {
471         return mSubgroupMetadata;
472     }
473 
474     /**
475      * {@inheritDoc}
476      *
477      * @hide
478      */
479     @Override
describeContents()480     public int describeContents() {
481         return 0;
482     }
483 
484     /**
485      * {@inheritDoc}
486      *
487      * @hide
488      */
489     @Override
writeToParcel(Parcel out, int flags)490     public void writeToParcel(Parcel out, int flags) {
491         out.writeInt(mSourceId);
492         out.writeInt(mSourceAddressType);
493         out.writeTypedObject(mSourceDevice, 0);
494         out.writeInt(mSourceAdvertisingSid);
495         out.writeInt(mBroadcastId);
496         out.writeInt(mPaSyncState);
497         out.writeInt(mBigEncryptionState);
498 
499         if (mBadCode != null) {
500             out.writeInt(mBadCode.length);
501             out.writeByteArray(mBadCode);
502         } else {
503             // -1 indicates that there is no "bad broadcast code"
504             out.writeInt(-1);
505         }
506         out.writeInt(mNumSubgroups);
507         out.writeList(mBisSyncState);
508         out.writeTypedList(mSubgroupMetadata);
509     }
510 
511     /**
512      * {@inheritDoc}
513      *
514      * @hide
515      */
516     @Override
toString()517     public String toString() {
518         String receiveState =
519                 ("Receiver state: "
520                         + "\n  Source ID:"
521                         + mSourceId
522                         + "\n  Source Address Type:"
523                         + (int) mSourceAddressType
524                         + "\n  Source Address:"
525                         + mSourceDevice.toString()
526                         + "\n  Source Adv SID:"
527                         + mSourceAdvertisingSid
528                         + "\n  Broadcast ID:"
529                         + mBroadcastId
530                         + "\n  PA Sync State:"
531                         + paSyncStateToString(mPaSyncState)
532                         + "\n  BIG Encryption Status:"
533                         + bigEncryptionStateToString(mBigEncryptionState)
534                         + "\n  Bad Broadcast Code:"
535                         + Arrays.toString(mBadCode)
536                         + "\n  Number Of Subgroups:"
537                         + mNumSubgroups);
538         for (int i = 0; i < mNumSubgroups; i++) {
539             receiveState +=
540                     ("\n    Subgroup index:"
541                                     + i
542                                     + "\n      BIS Sync State:"
543                                     + bisSyncStateToString(mBisSyncState.get(i), i))
544                             + "\n      Metadata:"
545                             + "\n        ProgramInfo:"
546                             + mSubgroupMetadata.get(i).getProgramInfo()
547                             + "\n        Language:"
548                             + mSubgroupMetadata.get(i).getLanguage()
549                             + "\n        RawData:"
550                             + Arrays.toString(mSubgroupMetadata.get(i).getRawMetadata());
551         }
552         return receiveState;
553     }
554 
555     /**
556      * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastReceiveState} from parcel.
557      *
558      * @hide
559      */
560     @SystemApi @NonNull
561     public static final Creator<BluetoothLeBroadcastReceiveState> CREATOR =
562             new Creator<>() {
563                 public @NonNull BluetoothLeBroadcastReceiveState createFromParcel(
564                         @NonNull Parcel in) {
565                     final int sourceId = in.readInt();
566                     final int sourceAddressType = in.readInt();
567                     final BluetoothDevice sourceDevice =
568                             in.readTypedObject(BluetoothDevice.CREATOR);
569                     final int sourceAdvertisingSid = in.readInt();
570                     final int broadcastId = in.readInt();
571                     final int paSyncState = in.readInt();
572                     final int bigEncryptionState = in.readInt();
573                     final int badCodeLen = in.readInt();
574                     byte[] badCode = null;
575 
576                     if (badCodeLen != -1) {
577                         badCode = new byte[badCodeLen];
578                         if (badCodeLen > 0) {
579                             in.readByteArray(badCode);
580                         }
581                     }
582                     final byte numSubGroups = in.readByte();
583                     final List<Long> bisSyncState =
584                             in.readArrayList(Long.class.getClassLoader(), Long.class);
585                     final List<BluetoothLeAudioContentMetadata> subgroupMetadata =
586                             new ArrayList<>();
587                     in.readTypedList(subgroupMetadata, BluetoothLeAudioContentMetadata.CREATOR);
588 
589                     return new BluetoothLeBroadcastReceiveState(
590                             sourceId,
591                             sourceAddressType,
592                             sourceDevice,
593                             sourceAdvertisingSid,
594                             broadcastId,
595                             paSyncState,
596                             bigEncryptionState,
597                             badCode,
598                             numSubGroups,
599                             bisSyncState,
600                             subgroupMetadata);
601                 }
602 
603                 public @NonNull BluetoothLeBroadcastReceiveState[] newArray(int size) {
604                     return new BluetoothLeBroadcastReceiveState[size];
605                 }
606             };
607 }
608