/* * Copyright 2023 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.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; /** * This class contains the broadcast group settings information for this Broadcast Group. * * @hide */ @SystemApi public final class BluetoothLeBroadcastSettings implements Parcelable { private final boolean mIsPublicBroadcast; private final String mBroadcastName; private final byte[] mBroadcastCode; private final BluetoothLeAudioContentMetadata mPublicBroadcastMetadata; private final List mSubgroupSettings; private BluetoothLeBroadcastSettings( boolean isPublicBroadcast, String broadcastName, byte[] broadcastCode, BluetoothLeAudioContentMetadata publicBroadcastMetadata, List subgroupSettings) { mIsPublicBroadcast = isPublicBroadcast; mBroadcastName = broadcastName; mBroadcastCode = broadcastCode; mPublicBroadcastMetadata = publicBroadcastMetadata; mSubgroupSettings = subgroupSettings; } @Override public boolean equals(@Nullable Object o) { if (!(o instanceof BluetoothLeBroadcastSettings)) { return false; } final BluetoothLeBroadcastSettings other = (BluetoothLeBroadcastSettings) o; return mIsPublicBroadcast == other.isPublicBroadcast() && Objects.equals(mBroadcastName, other.getBroadcastName()) && Arrays.equals(mBroadcastCode, other.getBroadcastCode()) && Objects.equals(mPublicBroadcastMetadata, other.getPublicBroadcastMetadata()) && mSubgroupSettings.equals(other.getSubgroupSettings()); } @Override public int hashCode() { return Objects.hash( mIsPublicBroadcast, mBroadcastName, Arrays.hashCode(mBroadcastCode), mPublicBroadcastMetadata, mSubgroupSettings); } /** * Return {@code true} if this Broadcast Group is set to broadcast Public Broadcast Announcement * otherwise return {@code false}. * * @hide */ @SystemApi public boolean isPublicBroadcast() { return mIsPublicBroadcast; } /** * Return the broadcast code for this Broadcast Group. * * @return Broadcast name for this Broadcast Group, null if no name provided * @hide */ @SystemApi @Nullable public String getBroadcastName() { return mBroadcastName; } /** * Get 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. * * @return Broadcast Code currently set for this broadcast group, null if code is not required * or code is currently unknown * @hide */ @SystemApi @Nullable public byte[] getBroadcastCode() { return mBroadcastCode; } /** * Get public broadcast metadata for this Broadcast Group. * * @return public broadcast metadata for this Broadcast Group, null if no public metadata exists * @hide */ @SystemApi @Nullable public BluetoothLeAudioContentMetadata getPublicBroadcastMetadata() { return mPublicBroadcastMetadata; } /** * Get available subgroup settings in the broadcast group. * * @return list of subgroup settings in the broadcast group * @hide */ @SystemApi @NonNull public List getSubgroupSettings() { return mSubgroupSettings; } /** * {@inheritDoc} * * @hide */ @Override public int describeContents() { return 0; } /** * {@inheritDoc} * * @hide */ @Override public void writeToParcel(Parcel out, int flags) { out.writeBoolean(mIsPublicBroadcast); out.writeString(mBroadcastName); if (mBroadcastCode != null) { out.writeInt(mBroadcastCode.length); out.writeByteArray(mBroadcastCode); } else { // -1 indicates missing broadcast code out.writeInt(-1); } out.writeTypedObject(mPublicBroadcastMetadata, 0); out.writeTypedList(mSubgroupSettings); } /** * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastSettings} from parcel. * * @hide */ @SystemApi @NonNull public static final Creator CREATOR = new Creator<>() { public @NonNull BluetoothLeBroadcastSettings createFromParcel(@NonNull Parcel in) { Builder builder = new Builder(); builder.setPublicBroadcast(in.readBoolean()); builder.setBroadcastName(in.readString()); 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.setPublicBroadcastMetadata( in.readTypedObject(BluetoothLeAudioContentMetadata.CREATOR)); final List subgroupSettings = new ArrayList<>(); in.readTypedList( subgroupSettings, BluetoothLeBroadcastSubgroupSettings.CREATOR); for (BluetoothLeBroadcastSubgroupSettings setting : subgroupSettings) { builder.addSubgroupSettings(setting); } return builder.build(); } public @NonNull BluetoothLeBroadcastSettings[] newArray(int size) { return new BluetoothLeBroadcastSettings[size]; } }; /** * Builder for {@link BluetoothLeBroadcastSettings}. * * @hide */ @SystemApi public static final class Builder { private boolean mIsPublicBroadcast = false; private String mBroadcastName = null; private byte[] mBroadcastCode = null; private BluetoothLeAudioContentMetadata mPublicBroadcastMetadata = null; private List mSubgroupSettings = 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 BluetoothLeBroadcastSettings original) { mIsPublicBroadcast = original.isPublicBroadcast(); mBroadcastName = original.getBroadcastName(); mBroadcastCode = original.getBroadcastCode(); mPublicBroadcastMetadata = original.getPublicBroadcastMetadata(); mSubgroupSettings = original.getSubgroupSettings(); } /** * Set whether the Public Broadcast is on for this broadcast group. * * @param isPublicBroadcast whether the Public Broadcast is enabled * @return this builder * @hide */ @SystemApi @NonNull public Builder setPublicBroadcast(boolean isPublicBroadcast) { mIsPublicBroadcast = isPublicBroadcast; return this; } /** * Set broadcast name for the broadcast group. * *

As defined in Public Broadcast Profile V1.0, section 5.1. Broadcast_Name AD Type is a * UTF-8 encoded string containing a minimum of 4 characters and a maximum of 32 * human-readable characters. * * @param broadcastName Broadcast name for this broadcast group, null if no name provided * @throws IllegalArgumentException if name is non-null and its length is less than 4 * characters or greater than 32 characters * @return this builder * @hide */ @SystemApi @NonNull public Builder setBroadcastName(@Nullable String broadcastName) { if (broadcastName != null && ((broadcastName.length() > 32) || (broadcastName.length() < 4))) { throw new IllegalArgumentException("Invalid broadcast name length"); } 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 group, null if code is not * required for non-encrypted broadcast * @throws IllegalArgumentException if name is non-null and its length is less than 4 * characters or greater than 16 characters * @return this builder * @hide */ @SystemApi @NonNull public Builder setBroadcastCode(@Nullable byte[] broadcastCode) { if (broadcastCode != null && ((broadcastCode.length > 16) || (broadcastCode.length < 4))) { throw new IllegalArgumentException("Invalid broadcast code length"); } mBroadcastCode = broadcastCode; 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, 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 settings to the broadcast group. * * @param subgroupSettings contains subgroup's setting data * @return this builder * @hide */ @SystemApi @NonNull public Builder addSubgroupSettings( @NonNull BluetoothLeBroadcastSubgroupSettings subgroupSettings) { Objects.requireNonNull(subgroupSettings, "subgroupSettings cannot be null"); mSubgroupSettings.add(subgroupSettings); return this; } /** * Clear subgroup settings list so that one can reset the builder * * @return this builder * @hide */ @SystemApi @NonNull public Builder clearSubgroupSettings() { mSubgroupSettings.clear(); return this; } /** * Build {@link BluetoothLeBroadcastSettings}. * * @return {@link BluetoothLeBroadcastSettings} * @throws IllegalArgumentException if the object cannot be built * @hide */ @SystemApi @NonNull public BluetoothLeBroadcastSettings build() { if (mSubgroupSettings.isEmpty()) { throw new IllegalArgumentException("Must contain at least one subgroup"); } return new BluetoothLeBroadcastSettings( mIsPublicBroadcast, mBroadcastName, mBroadcastCode, mPublicBroadcastMetadata, mSubgroupSettings); } } }