/* * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.bluetooth; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import com.android.bluetooth.flags.Flags; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; /** * This class represents a Broadcast Source group and the associated information that is needed by * Broadcast Audio Scan Service (BASS) to set up a Broadcast Sink. * *

For example, an LE Audio Broadcast Sink can use the information contained within an instance * of this class to synchronize with an LE Audio Broadcast group in order to listen to audio from * Broadcast subgroup using one or more Broadcast Channels. * * @hide */ @SystemApi public final class BluetoothLeBroadcastMetadata implements Parcelable { // Information needed for adding broadcast Source // Optional: Identity address type private final @BluetoothDevice.AddressType int mSourceAddressType; // Optional: Must use identity address private final BluetoothDevice mSourceDevice; private final int mSourceAdvertisingSid; private final int mBroadcastId; private final int mPaSyncInterval; private final boolean mIsEncrypted; private final boolean mIsPublicBroadcast; private final String mBroadcastName; private final byte[] mBroadcastCode; private final BluetoothLeAudioContentMetadata mPublicBroadcastMetadata; private final @AudioConfigQuality int mAudioConfigQuality; private final int mRssi; /** * Audio configuration quality for this Broadcast Group. This quality bitmap is used for * presenting the audio stream quality for this BIG, either public broadcast or non-public * broadcast Bit0 indicates at least one broadcast Audio Stream configuration is standard * quality Bit1 indicates at least one broadcast Audio Stream configuration is high quality * * @hide */ @IntDef( flag = true, prefix = "AUDIO_CONFIG_QUALITY_", value = { AUDIO_CONFIG_QUALITY_NONE, AUDIO_CONFIG_QUALITY_STANDARD, AUDIO_CONFIG_QUALITY_HIGH, }) @Retention(RetentionPolicy.SOURCE) public @interface AudioConfigQuality {} /** * Audio config quality is none, default value used for audio config quality. * * @hide */ @SystemApi public static final int AUDIO_CONFIG_QUALITY_NONE = 0; /** * Audio config quality is standard. This indicates the BIG shall include at least one broadcast * Audio Stream configuration defined as Mandatory for a Broadcast Sink in Basic Audio Profile, * Version 1 or later, table 6.4 * * @hide */ @SystemApi public static final int AUDIO_CONFIG_QUALITY_STANDARD = 0x1 << 0; /** * Audio config quality is standard. This indicates the BIG shall include at least one broadcast * Audio Stream configuration setting listed in Public Broadcast Profile, Version 1 or later, * table 4.2 * * @hide */ @SystemApi public static final int AUDIO_CONFIG_QUALITY_HIGH = 0x1 << 1; // BASE structure // See Section 7 for description. Range: 0x000000 – 0xFFFFFF Units: μs // All other values: RFU private final int mPresentationDelayMicros; // Number of subgroups used to group BISes present in the BIG // Shall be at least 1, as defined by Rule 1 // Sub group info numSubGroup = mSubGroups.length private final List mSubgroups; private 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 subgroups) { mSourceAddressType = sourceAddressType; mSourceDevice = sourceDevice; mSourceAdvertisingSid = sourceAdvertisingSid; mBroadcastId = broadcastId; mPaSyncInterval = paSyncInterval; mIsEncrypted = isEncrypted; mIsPublicBroadcast = isPublicBroadcast; mBroadcastName = broadcastName; mBroadcastCode = broadcastCode; mPresentationDelayMicros = presentationDelay; mAudioConfigQuality = audioConfigQuality; mRssi = rssi; mPublicBroadcastMetadata = publicBroadcastMetadata; mSubgroups = subgroups; } @Override public boolean equals(@Nullable Object o) { if (!(o instanceof BluetoothLeBroadcastMetadata)) { return false; } final BluetoothLeBroadcastMetadata other = (BluetoothLeBroadcastMetadata) o; return mSourceAddressType == other.getSourceAddressType() && mSourceDevice.equals(other.getSourceDevice()) && mSourceAdvertisingSid == other.getSourceAdvertisingSid() && mBroadcastId == other.getBroadcastId() && mPaSyncInterval == other.getPaSyncInterval() && mIsEncrypted == other.isEncrypted() && mIsPublicBroadcast == other.isPublicBroadcast() && Objects.equals(mBroadcastName, other.getBroadcastName()) && Arrays.equals(mBroadcastCode, other.getBroadcastCode()) && mPresentationDelayMicros == other.getPresentationDelayMicros() && mAudioConfigQuality == other.getAudioConfigQuality() && mRssi == other.getRssi() && Objects.equals(mPublicBroadcastMetadata, other.getPublicBroadcastMetadata()) && mSubgroups.equals(other.getSubgroups()); } @Override public int hashCode() { return Objects.hash( mSourceAddressType, mSourceDevice, mSourceAdvertisingSid, mBroadcastId, mPaSyncInterval, mIsEncrypted, mIsPublicBroadcast, mBroadcastName, Arrays.hashCode(mBroadcastCode), mPresentationDelayMicros, mAudioConfigQuality, mRssi, mPublicBroadcastMetadata, mSubgroups); } @Override public String toString() { return "BluetoothLeBroadcastMetadata{" + ("sourceAddressType=" + mSourceAddressType) + (", sourceDevice=" + mSourceDevice) + (", sourceAdvertisingSid=" + mSourceAdvertisingSid) + (", broadcastId=" + mBroadcastId) + (", paSyncInterval=" + mPaSyncInterval) + (", isEncrypted=" + mIsEncrypted) + (", isPublicBroadcast=" + mIsPublicBroadcast) + (", broadcastName=" + mBroadcastName) + (", broadcastCode=" + Arrays.toString(mBroadcastCode)) + (", presentationDelayMicros=" + mPresentationDelayMicros) + (", audioConfigQuality=" + mAudioConfigQuality) + (", rssi=" + mRssi) + (", publicBroadcastMetadata=" + mPublicBroadcastMetadata) + (", subgroups=" + mSubgroups) + '}'; } /** * Get the address type of the Broadcast Source. * *

Can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}, {@link * BluetoothDevice#ADDRESS_TYPE_RANDOM} * * @return address type of the Broadcast Source * @hide */ @SystemApi public @BluetoothDevice.AddressType int getSourceAddressType() { return mSourceAddressType; } /** * Get the MAC address of the Broadcast Source, which can be Public Device Address, Random * Device Address, Public Identity Address or Random (static) Identity Address. * * @return MAC address of the Broadcast Source * @hide */ @SystemApi public @NonNull BluetoothDevice getSourceDevice() { return mSourceDevice; } /** * Get Advertising_SID subfield of the ADI field of the AUX_ADV_IND PDU or the * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the * Broadcast Source. * * @return 1-byte long Advertising_SID of the Broadcast Source * @hide */ @SystemApi public int getSourceAdvertisingSid() { return mSourceAdvertisingSid; } /** * Broadcast_ID of the Broadcast Source. * * @return 3-byte long Broadcast_ID of the Broadcast Source * @hide */ @SystemApi public int getBroadcastId() { return mBroadcastId; } /** * Indicated that Periodic Advertising Sync interval is unknown. * * @hide */ @SystemApi public static final int PA_SYNC_INTERVAL_UNKNOWN = 0xFFFF; /** * Get Periodic Advertising Sync interval of the broadcast Source. * * @return Periodic Advertising Sync interval of the broadcast Source, {@link * #PA_SYNC_INTERVAL_UNKNOWN} if unknown * @hide */ @SystemApi public int getPaSyncInterval() { return mPaSyncInterval; } /** * Return true if the Broadcast Source is encrypted. * * @return true if the Broadcast Source is encrypted * @hide */ @SystemApi public boolean isEncrypted() { return mIsEncrypted; } /** * Return {@code true} if this Broadcast Group is broadcasting Public Broadcast Announcement * otherwise return {@code false}. * * @hide */ @SystemApi public boolean isPublicBroadcast() { return mIsPublicBroadcast; } /** * Get the broadcast name for this Broadcast Group as UTF-8 format. * * @return broadcast name or null for this Broadcast Group * @hide */ @SystemApi public @Nullable String getBroadcastName() { return mBroadcastName; } /** * Get the Broadcast Code currently set for this Broadcast Source. * *

Only needed when encryption is enabled * *

As defined in Volume 3, Part C, Section 3.2.6 of Bluetooth Core Specification, Version * 5.3, Broadcast Code is used to encrypt a broadcast audio stream. * *

It must be a UTF-8 string that has at least 4 octets and should not exceed 16 octets. * * @return Broadcast Code currently set for this Broadcast Source, {@code null} if code is not * required or code is currently unknown * @hide */ @SystemApi public @Nullable byte[] getBroadcastCode() { return mBroadcastCode; } /** * Get the overall presentation delay in microseconds of this Broadcast Source. * *

Presentation delay is defined in Section 7 of the Basic Audio Profile. * * @return presentation delay of this Broadcast Source in microseconds * @hide */ @SystemApi public @IntRange(from = 0, to = 0xFFFFFF) int getPresentationDelayMicros() { return mPresentationDelayMicros; } /** * Get broadcast audio config quality for this Broadcast Group. * * @return Broadcast audio config quality for this Broadcast Group * @hide */ @SystemApi public @AudioConfigQuality int getAudioConfigQuality() { return mAudioConfigQuality; } /** * Indicated that rssi value is unknown. * * @hide */ @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_MONITOR_SOURCE_SYNC_STATUS) @SystemApi public static final int RSSI_UNKNOWN = 0x7F; /** * Get the Received Signal Strength Indication (RSSI) value of this Broadcast Source. * *

The valid RSSI range is [-127, 126] and as defined in Volume 4, Part E, Section 7.7.65.13 * of Bluetooth Core Specification, Version 5.3, value of 0x7F(127) means that the RSSI is not * available. * * @return the RSSI {@link #RSSI_UNKNOWN} if unknown * @hide */ @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_MONITOR_SOURCE_SYNC_STATUS) @SystemApi public @IntRange(from = -127, to = 127) int getRssi() { return mRssi; } /** * Get public broadcast metadata for this Broadcast Group. * * @return public broadcast metadata for this Broadcast Group, {@code null} if no public * metadata exists * @hide */ @SystemApi public @Nullable BluetoothLeAudioContentMetadata getPublicBroadcastMetadata() { return mPublicBroadcastMetadata; } /** * Get available subgroups in this broadcast source. * * @return list of subgroups in this broadcast source, which should contain at least one * subgroup for each Broadcast Source * @hide */ @SystemApi public @NonNull List getSubgroups() { return mSubgroups; } /** * {@inheritDoc} * * @hide */ @Override public int describeContents() { return 0; } /** * {@inheritDoc} * * @hide */ @Override public void writeToParcel(Parcel out, int flags) { out.writeInt(mSourceAddressType); if (mSourceDevice != null) { out.writeInt(1); out.writeTypedObject(mSourceDevice, 0); } else { // zero indicates missing mSourceDevice out.writeInt(0); } out.writeInt(mSourceAdvertisingSid); out.writeInt(mBroadcastId); out.writeInt(mPaSyncInterval); out.writeBoolean(mIsEncrypted); if (mBroadcastCode != null) { out.writeInt(mBroadcastCode.length); out.writeByteArray(mBroadcastCode); } else { // -1 indicates missing broadcast code out.writeInt(-1); } out.writeInt(mPresentationDelayMicros); out.writeTypedList(mSubgroups); out.writeBoolean(mIsPublicBroadcast); out.writeString(mBroadcastName); out.writeInt(mAudioConfigQuality); out.writeTypedObject(mPublicBroadcastMetadata, 0); out.writeInt(mRssi); } /** * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastMetadata} from parcel. * * @hide */ @SystemApi @NonNull public static final Creator CREATOR = new Creator<>() { public @NonNull BluetoothLeBroadcastMetadata createFromParcel(@NonNull Parcel in) { Builder builder = new Builder(); final int sourceAddressType = in.readInt(); final int deviceExist = in.readInt(); BluetoothDevice sourceDevice = null; if (deviceExist == 1) { sourceDevice = in.readTypedObject(BluetoothDevice.CREATOR); } builder.setSourceDevice(sourceDevice, sourceAddressType); builder.setSourceAdvertisingSid(in.readInt()); builder.setBroadcastId(in.readInt()); builder.setPaSyncInterval(in.readInt()); builder.setEncrypted(in.readBoolean()); final int codeLen = in.readInt(); byte[] broadcastCode = null; if (codeLen != -1) { broadcastCode = new byte[codeLen]; if (codeLen >= 0) { in.readByteArray(broadcastCode); } } builder.setBroadcastCode(broadcastCode); builder.setPresentationDelayMicros(in.readInt()); final List subgroups = new ArrayList<>(); in.readTypedList(subgroups, BluetoothLeBroadcastSubgroup.CREATOR); for (BluetoothLeBroadcastSubgroup subgroup : subgroups) { builder.addSubgroup(subgroup); } builder.setPublicBroadcast(in.readBoolean()); builder.setBroadcastName(in.readString()); builder.setAudioConfigQuality(in.readInt()); builder.setPublicBroadcastMetadata( in.readTypedObject(BluetoothLeAudioContentMetadata.CREATOR)); builder.setRssi(in.readInt()); return builder.build(); } public @NonNull BluetoothLeBroadcastMetadata[] newArray(int size) { return new BluetoothLeBroadcastMetadata[size]; } }; private static final int UNKNOWN_VALUE_PLACEHOLDER = -1; /** * Builder for {@link BluetoothLeBroadcastMetadata}. * * @hide */ @SystemApi public static final class Builder { private @BluetoothDevice.AddressType int mSourceAddressType = BluetoothDevice.ADDRESS_TYPE_UNKNOWN; private BluetoothDevice mSourceDevice = null; private int mSourceAdvertisingSid = UNKNOWN_VALUE_PLACEHOLDER; private int mBroadcastId = UNKNOWN_VALUE_PLACEHOLDER; private int mPaSyncInterval = PA_SYNC_INTERVAL_UNKNOWN; private boolean mIsEncrypted = false; private boolean mIsPublicBroadcast = false; private String mBroadcastName = null; private byte[] mBroadcastCode = null; private int mPresentationDelayMicros = UNKNOWN_VALUE_PLACEHOLDER; private @AudioConfigQuality int mAudioConfigQuality = AUDIO_CONFIG_QUALITY_NONE; private int mRssi = RSSI_UNKNOWN; private BluetoothLeAudioContentMetadata mPublicBroadcastMetadata = null; private List mSubgroups = new ArrayList<>(); /** * Create an empty builder. * * @hide */ @SystemApi public Builder() {} /** * Create a builder with copies of information from original object. * * @param original original object * @hide */ @SystemApi public Builder(@NonNull BluetoothLeBroadcastMetadata original) { mSourceAddressType = original.getSourceAddressType(); mSourceDevice = original.getSourceDevice(); mSourceAdvertisingSid = original.getSourceAdvertisingSid(); mBroadcastId = original.getBroadcastId(); mPaSyncInterval = original.getPaSyncInterval(); mIsEncrypted = original.isEncrypted(); mIsPublicBroadcast = original.isPublicBroadcast(); mBroadcastName = original.getBroadcastName(); mBroadcastCode = original.getBroadcastCode(); mPresentationDelayMicros = original.getPresentationDelayMicros(); mAudioConfigQuality = original.getAudioConfigQuality(); mRssi = original.getRssi(); mPublicBroadcastMetadata = original.getPublicBroadcastMetadata(); mSubgroups = original.getSubgroups(); } /** * Set the address type and MAC address of the Broadcast Source. * *

Address type can be either {@link BluetoothDevice#ADDRESS_TYPE_PUBLIC}, {@link * BluetoothDevice#ADDRESS_TYPE_RANDOM} * *

MAC address can be Public Device Address, Random Device Address, Public Identity * Address or Random (static) Identity Address * * @param sourceDevice source advertiser address * @param sourceAddressType source advertiser address type * @throws IllegalArgumentException if sourceAddressType is invalid * @throws NullPointerException if sourceDevice is null * @return this builder * @hide */ @SystemApi @NonNull public Builder setSourceDevice( @NonNull BluetoothDevice sourceDevice, @BluetoothDevice.AddressType int sourceAddressType) { if (sourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) { throw new IllegalArgumentException( "sourceAddressType cannot be ADDRESS_TYPE_UNKNOWN"); } if (sourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM && sourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) { throw new IllegalArgumentException( "sourceAddressType " + sourceAddressType + " is invalid"); } Objects.requireNonNull(sourceDevice, "sourceDevice cannot be null"); mSourceAddressType = sourceAddressType; mSourceDevice = sourceDevice; return this; } /** * Set Advertising_SID that is a subfield of the ADI field of the AUX_ADV_IND PDU or the * LL_PERIODIC_SYNC_IND containing the SyncInfo that points to the PA transmitted by the * Broadcast Source. * * @param sourceAdvertisingSid 1-byte long Advertising_SID of the Broadcast Source * @return this builder * @hide */ @SystemApi public @NonNull Builder setSourceAdvertisingSid(int sourceAdvertisingSid) { mSourceAdvertisingSid = sourceAdvertisingSid; return this; } /** * Set the Broadcast_ID of the Broadcast Source. * * @param broadcastId 3-byte long Broadcast_ID of the Broadcast Source * @return this builder * @hide */ @SystemApi public @NonNull Builder setBroadcastId(int broadcastId) { mBroadcastId = broadcastId; return this; } /** * Set Periodic Advertising Sync interval of the broadcast Source. * * @param paSyncInterval Periodic Advertising Sync interval of the broadcast Source, {@link * #PA_SYNC_INTERVAL_UNKNOWN} if unknown * @return this builder * @hide */ @SystemApi public @NonNull Builder setPaSyncInterval(int paSyncInterval) { mPaSyncInterval = paSyncInterval; return this; } /** * Set whether the Broadcast Source should be encrypted. * *

When setting up a Broadcast Source, if isEncrypted is true while * broadcastCode is null, the implementation will automatically generate a * Broadcast Code * * @param isEncrypted whether the Broadcast Source is encrypted * @return this builder * @hide */ @SystemApi public @NonNull Builder setEncrypted(boolean isEncrypted) { mIsEncrypted = isEncrypted; return this; } /** * Set whether this Broadcast Group is broadcasting Public Broadcast Announcement. * * @param isPublicBroadcast whether this Broadcast Group is broadcasting Public Broadcast * Announcement * @return this builder * @hide */ @SystemApi public @NonNull Builder setPublicBroadcast(boolean isPublicBroadcast) { mIsPublicBroadcast = isPublicBroadcast; return this; } /** * Set broadcast name for this Broadcast Group. * * @param broadcastName Broadcast name for this Broadcast Group, {@code null} if no name * provided * @return this builder * @hide */ @SystemApi public @NonNull Builder setBroadcastName(@Nullable String broadcastName) { mBroadcastName = broadcastName; return this; } /** * Set the Broadcast Code currently set for this broadcast group. * *

Only needed when encryption is enabled * *

As defined in Volume 3, Part C, Section 3.2.6 of Bluetooth Core Specification, Version * 5.3, Broadcast Code is used to encrypt a broadcast audio stream. * *

It must be a UTF-8 string that has at least 4 octets and should not exceed 16 octets. * * @param broadcastCode Broadcast Code for this Broadcast Source, {@code null} if code is * not required * @return this builder * @hide */ @SystemApi public @NonNull Builder setBroadcastCode(@Nullable byte[] broadcastCode) { mBroadcastCode = broadcastCode; return this; } /** * Set the overall presentation delay in microseconds of this Broadcast Source. * *

Presentation delay is defined in Section 7 of the Basic Audio Profile. * * @param presentationDelayMicros presentation delay of this Broadcast Source in * microseconds * @throws IllegalArgumentException if presentationDelayMicros does not fall in [0, * 0xFFFFFF] * @return this builder * @hide */ @SystemApi @NonNull public Builder setPresentationDelayMicros( @IntRange(from = 0, to = 0xFFFFFF) int presentationDelayMicros) { if (presentationDelayMicros < 0 || presentationDelayMicros >= 0xFFFFFF) { throw new IllegalArgumentException( "presentationDelayMicros " + presentationDelayMicros + " does not fall in [0, 0xFFFFFF]"); } mPresentationDelayMicros = presentationDelayMicros; return this; } /** * Set broadcast audio config quality for this Broadcast Group. * * @param audioConfigQuality broadcast audio config quality for this Broadcast Group * @return this builder * @hide */ @SystemApi @NonNull public Builder setAudioConfigQuality(@AudioConfigQuality int audioConfigQuality) { mAudioConfigQuality = audioConfigQuality; return this; } /** * Set the Received Signal Strength Indication (RSSI) value for this Broadcast metadata. * *

The valid RSSI range is [-127, 126] and as defined in Volume 4, Part E, Section * 7.7.65.13 of Bluetooth Core Specification, Version 5.3, value of 0x7F(127) means that the * RSSI is not available. * * @param rssi the RSSI * @return this builder * @throws IllegalArgumentException if rssi is not in the range [-127, 127]. * @hide */ @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_MONITOR_SOURCE_SYNC_STATUS) @SystemApi @NonNull public Builder setRssi(@IntRange(from = -127, to = 127) int rssi) { if (rssi < -127 || rssi > 127) { throw new IllegalArgumentException("illegal rssi " + rssi); } mRssi = rssi; return this; } /** * Set public broadcast metadata for this Broadcast Group. PBS should include the * Program_Info length-type-value (LTV) structure metadata * * @param publicBroadcastMetadata public broadcast metadata for this Broadcast Group, {@code * null} if no public meta data provided * @return this builder * @hide */ @SystemApi @NonNull public Builder setPublicBroadcastMetadata( @Nullable BluetoothLeAudioContentMetadata publicBroadcastMetadata) { mPublicBroadcastMetadata = publicBroadcastMetadata; return this; } /** * Add a subgroup to this broadcast source. * * @param subgroup {@link BluetoothLeBroadcastSubgroup} that contains a subgroup's metadata * @throws NullPointerException if subgroup is null * @return this builder * @hide */ @SystemApi public @NonNull Builder addSubgroup(@NonNull BluetoothLeBroadcastSubgroup subgroup) { Objects.requireNonNull(subgroup, "subgroup cannot be null"); mSubgroups.add(subgroup); return this; } /** * Clear subgroup list so that one can reset the builder after create it from an existing * object. * * @return this builder * @hide */ @SystemApi public @NonNull Builder clearSubgroup() { mSubgroups.clear(); return this; } /** * Build {@link BluetoothLeBroadcastMetadata}. * * @return {@link BluetoothLeBroadcastMetadata} * @throws IllegalArgumentException if the object cannot be built * @throws NullPointerException if {@link NonNull} items are null * @hide */ @SystemApi public @NonNull BluetoothLeBroadcastMetadata build() { if (mSourceAddressType == BluetoothDevice.ADDRESS_TYPE_UNKNOWN) { throw new IllegalArgumentException("SourceAddressTyp cannot be unknown"); } if (mSourceAddressType != BluetoothDevice.ADDRESS_TYPE_RANDOM && mSourceAddressType != BluetoothDevice.ADDRESS_TYPE_PUBLIC) { throw new IllegalArgumentException( "sourceAddressType " + mSourceAddressType + " is invalid"); } Objects.requireNonNull(mSourceDevice, "mSourceDevice cannot be null"); if (mSubgroups.isEmpty()) { throw new IllegalArgumentException("Must contain at least one subgroup"); } return new BluetoothLeBroadcastMetadata( mSourceAddressType, mSourceDevice, mSourceAdvertisingSid, mBroadcastId, mPaSyncInterval, mIsEncrypted, mIsPublicBroadcast, mBroadcastName, mBroadcastCode, mPresentationDelayMicros, mAudioConfigQuality, mRssi, mPublicBroadcastMetadata, mSubgroups); } } }