1 /*
2  * Copyright 2023 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.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.List;
28 import java.util.Objects;
29 
30 /**
31  * This class contains the broadcast group settings information for this Broadcast Group.
32  *
33  * @hide
34  */
35 @SystemApi
36 public final class BluetoothLeBroadcastSettings implements Parcelable {
37     private final boolean mIsPublicBroadcast;
38     private final String mBroadcastName;
39     private final byte[] mBroadcastCode;
40     private final BluetoothLeAudioContentMetadata mPublicBroadcastMetadata;
41     private final List<BluetoothLeBroadcastSubgroupSettings> mSubgroupSettings;
42 
BluetoothLeBroadcastSettings( boolean isPublicBroadcast, String broadcastName, byte[] broadcastCode, BluetoothLeAudioContentMetadata publicBroadcastMetadata, List<BluetoothLeBroadcastSubgroupSettings> subgroupSettings)43     private BluetoothLeBroadcastSettings(
44             boolean isPublicBroadcast,
45             String broadcastName,
46             byte[] broadcastCode,
47             BluetoothLeAudioContentMetadata publicBroadcastMetadata,
48             List<BluetoothLeBroadcastSubgroupSettings> subgroupSettings) {
49         mIsPublicBroadcast = isPublicBroadcast;
50         mBroadcastName = broadcastName;
51         mBroadcastCode = broadcastCode;
52         mPublicBroadcastMetadata = publicBroadcastMetadata;
53         mSubgroupSettings = subgroupSettings;
54     }
55 
56     @Override
equals(@ullable Object o)57     public boolean equals(@Nullable Object o) {
58         if (!(o instanceof BluetoothLeBroadcastSettings)) {
59             return false;
60         }
61         final BluetoothLeBroadcastSettings other = (BluetoothLeBroadcastSettings) o;
62         return mIsPublicBroadcast == other.isPublicBroadcast()
63                 && Objects.equals(mBroadcastName, other.getBroadcastName())
64                 && Arrays.equals(mBroadcastCode, other.getBroadcastCode())
65                 && Objects.equals(mPublicBroadcastMetadata, other.getPublicBroadcastMetadata())
66                 && mSubgroupSettings.equals(other.getSubgroupSettings());
67     }
68 
69     @Override
hashCode()70     public int hashCode() {
71         return Objects.hash(
72                 mIsPublicBroadcast,
73                 mBroadcastName,
74                 Arrays.hashCode(mBroadcastCode),
75                 mPublicBroadcastMetadata,
76                 mSubgroupSettings);
77     }
78 
79     /**
80      * Return {@code true} if this Broadcast Group is set to broadcast Public Broadcast Announcement
81      * otherwise return {@code false}.
82      *
83      * @hide
84      */
85     @SystemApi
isPublicBroadcast()86     public boolean isPublicBroadcast() {
87         return mIsPublicBroadcast;
88     }
89 
90     /**
91      * Return the broadcast code for this Broadcast Group.
92      *
93      * @return Broadcast name for this Broadcast Group, null if no name provided
94      * @hide
95      */
96     @SystemApi
97     @Nullable
getBroadcastName()98     public String getBroadcastName() {
99         return mBroadcastName;
100     }
101 
102     /**
103      * Get the Broadcast Code currently set for this broadcast group.
104      *
105      * <p>Only needed when encryption is enabled
106      *
107      * <p>As defined in Volume 3, Part C, Section 3.2.6 of Bluetooth Core Specification, Version
108      * 5.3, Broadcast Code is used to encrypt a broadcast audio stream.
109      *
110      * <p>It must be a UTF-8 string that has at least 4 octets and should not exceed 16 octets.
111      *
112      * @return Broadcast Code currently set for this broadcast group, null if code is not required
113      *     or code is currently unknown
114      * @hide
115      */
116     @SystemApi
117     @Nullable
getBroadcastCode()118     public byte[] getBroadcastCode() {
119         return mBroadcastCode;
120     }
121 
122     /**
123      * Get public broadcast metadata for this Broadcast Group.
124      *
125      * @return public broadcast metadata for this Broadcast Group, null if no public metadata exists
126      * @hide
127      */
128     @SystemApi
129     @Nullable
getPublicBroadcastMetadata()130     public BluetoothLeAudioContentMetadata getPublicBroadcastMetadata() {
131         return mPublicBroadcastMetadata;
132     }
133 
134     /**
135      * Get available subgroup settings in the broadcast group.
136      *
137      * @return list of subgroup settings in the broadcast group
138      * @hide
139      */
140     @SystemApi
141     @NonNull
getSubgroupSettings()142     public List<BluetoothLeBroadcastSubgroupSettings> getSubgroupSettings() {
143         return mSubgroupSettings;
144     }
145 
146     /**
147      * {@inheritDoc}
148      *
149      * @hide
150      */
151     @Override
describeContents()152     public int describeContents() {
153         return 0;
154     }
155 
156     /**
157      * {@inheritDoc}
158      *
159      * @hide
160      */
161     @Override
writeToParcel(Parcel out, int flags)162     public void writeToParcel(Parcel out, int flags) {
163         out.writeBoolean(mIsPublicBroadcast);
164         out.writeString(mBroadcastName);
165         if (mBroadcastCode != null) {
166             out.writeInt(mBroadcastCode.length);
167             out.writeByteArray(mBroadcastCode);
168         } else {
169             // -1 indicates missing broadcast code
170             out.writeInt(-1);
171         }
172         out.writeTypedObject(mPublicBroadcastMetadata, 0);
173         out.writeTypedList(mSubgroupSettings);
174     }
175 
176     /**
177      * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastSettings} from parcel.
178      *
179      * @hide
180      */
181     @SystemApi @NonNull
182     public static final Creator<BluetoothLeBroadcastSettings> CREATOR =
183             new Creator<>() {
184                 public @NonNull BluetoothLeBroadcastSettings createFromParcel(@NonNull Parcel in) {
185                     Builder builder = new Builder();
186                     builder.setPublicBroadcast(in.readBoolean());
187                     builder.setBroadcastName(in.readString());
188                     final int codeLen = in.readInt();
189                     byte[] broadcastCode = null;
190                     if (codeLen != -1) {
191                         broadcastCode = new byte[codeLen];
192                         if (codeLen >= 0) {
193                             in.readByteArray(broadcastCode);
194                         }
195                     }
196                     builder.setBroadcastCode(broadcastCode);
197                     builder.setPublicBroadcastMetadata(
198                             in.readTypedObject(BluetoothLeAudioContentMetadata.CREATOR));
199                     final List<BluetoothLeBroadcastSubgroupSettings> subgroupSettings =
200                             new ArrayList<>();
201                     in.readTypedList(
202                             subgroupSettings, BluetoothLeBroadcastSubgroupSettings.CREATOR);
203                     for (BluetoothLeBroadcastSubgroupSettings setting : subgroupSettings) {
204                         builder.addSubgroupSettings(setting);
205                     }
206                     return builder.build();
207                 }
208 
209                 public @NonNull BluetoothLeBroadcastSettings[] newArray(int size) {
210                     return new BluetoothLeBroadcastSettings[size];
211                 }
212             };
213 
214     /**
215      * Builder for {@link BluetoothLeBroadcastSettings}.
216      *
217      * @hide
218      */
219     @SystemApi
220     public static final class Builder {
221         private boolean mIsPublicBroadcast = false;
222         private String mBroadcastName = null;
223         private byte[] mBroadcastCode = null;
224         private BluetoothLeAudioContentMetadata mPublicBroadcastMetadata = null;
225         private List<BluetoothLeBroadcastSubgroupSettings> mSubgroupSettings = new ArrayList<>();
226 
227         /**
228          * Create an empty builder.
229          *
230          * @hide
231          */
232         @SystemApi
Builder()233         public Builder() {}
234 
235         /**
236          * Create a builder with copies of information from original object.
237          *
238          * @param original original object
239          * @hide
240          */
241         @SystemApi
Builder(@onNull BluetoothLeBroadcastSettings original)242         public Builder(@NonNull BluetoothLeBroadcastSettings original) {
243             mIsPublicBroadcast = original.isPublicBroadcast();
244             mBroadcastName = original.getBroadcastName();
245             mBroadcastCode = original.getBroadcastCode();
246             mPublicBroadcastMetadata = original.getPublicBroadcastMetadata();
247             mSubgroupSettings = original.getSubgroupSettings();
248         }
249 
250         /**
251          * Set whether the Public Broadcast is on for this broadcast group.
252          *
253          * @param isPublicBroadcast whether the Public Broadcast is enabled
254          * @return this builder
255          * @hide
256          */
257         @SystemApi
258         @NonNull
setPublicBroadcast(boolean isPublicBroadcast)259         public Builder setPublicBroadcast(boolean isPublicBroadcast) {
260             mIsPublicBroadcast = isPublicBroadcast;
261             return this;
262         }
263 
264         /**
265          * Set broadcast name for the broadcast group.
266          *
267          * <p>As defined in Public Broadcast Profile V1.0, section 5.1. Broadcast_Name AD Type is a
268          * UTF-8 encoded string containing a minimum of 4 characters and a maximum of 32
269          * human-readable characters.
270          *
271          * @param broadcastName Broadcast name for this broadcast group, null if no name provided
272          * @throws IllegalArgumentException if name is non-null and its length is less than 4
273          *     characters or greater than 32 characters
274          * @return this builder
275          * @hide
276          */
277         @SystemApi
278         @NonNull
setBroadcastName(@ullable String broadcastName)279         public Builder setBroadcastName(@Nullable String broadcastName) {
280             if (broadcastName != null
281                     && ((broadcastName.length() > 32) || (broadcastName.length() < 4))) {
282                 throw new IllegalArgumentException("Invalid broadcast name length");
283             }
284             mBroadcastName = broadcastName;
285             return this;
286         }
287 
288         /**
289          * Set the Broadcast Code currently set for this broadcast group.
290          *
291          * <p>Only needed when encryption is enabled As defined in Volume 3, Part C, Section 3.2.6
292          * of Bluetooth Core Specification, Version 5.3, Broadcast Code is used to encrypt a
293          * broadcast audio stream. It must be a UTF-8 string that has at least 4 octets and should
294          * not exceed 16 octets.
295          *
296          * @param broadcastCode Broadcast Code for this broadcast group, null if code is not
297          *     required for non-encrypted broadcast
298          * @throws IllegalArgumentException if name is non-null and its length is less than 4
299          *     characters or greater than 16 characters
300          * @return this builder
301          * @hide
302          */
303         @SystemApi
304         @NonNull
setBroadcastCode(@ullable byte[] broadcastCode)305         public Builder setBroadcastCode(@Nullable byte[] broadcastCode) {
306             if (broadcastCode != null
307                     && ((broadcastCode.length > 16) || (broadcastCode.length < 4))) {
308                 throw new IllegalArgumentException("Invalid broadcast code length");
309             }
310             mBroadcastCode = broadcastCode;
311             return this;
312         }
313 
314         /**
315          * Set public broadcast metadata for this Broadcast Group. PBS should include the
316          * Program_Info length-type-value (LTV) structure metadata
317          *
318          * @param publicBroadcastMetadata public broadcast metadata for this Broadcast Group, null
319          *     if no public meta data provided
320          * @return this builder
321          * @hide
322          */
323         @SystemApi
324         @NonNull
setPublicBroadcastMetadata( @ullable BluetoothLeAudioContentMetadata publicBroadcastMetadata)325         public Builder setPublicBroadcastMetadata(
326                 @Nullable BluetoothLeAudioContentMetadata publicBroadcastMetadata) {
327             mPublicBroadcastMetadata = publicBroadcastMetadata;
328             return this;
329         }
330 
331         /**
332          * Add a subgroup settings to the broadcast group.
333          *
334          * @param subgroupSettings contains subgroup's setting data
335          * @return this builder
336          * @hide
337          */
338         @SystemApi
339         @NonNull
addSubgroupSettings( @onNull BluetoothLeBroadcastSubgroupSettings subgroupSettings)340         public Builder addSubgroupSettings(
341                 @NonNull BluetoothLeBroadcastSubgroupSettings subgroupSettings) {
342             Objects.requireNonNull(subgroupSettings, "subgroupSettings cannot be null");
343             mSubgroupSettings.add(subgroupSettings);
344             return this;
345         }
346 
347         /**
348          * Clear subgroup settings list so that one can reset the builder
349          *
350          * @return this builder
351          * @hide
352          */
353         @SystemApi
354         @NonNull
clearSubgroupSettings()355         public Builder clearSubgroupSettings() {
356             mSubgroupSettings.clear();
357             return this;
358         }
359 
360         /**
361          * Build {@link BluetoothLeBroadcastSettings}.
362          *
363          * @return {@link BluetoothLeBroadcastSettings}
364          * @throws IllegalArgumentException if the object cannot be built
365          * @hide
366          */
367         @SystemApi
368         @NonNull
build()369         public BluetoothLeBroadcastSettings build() {
370             if (mSubgroupSettings.isEmpty()) {
371                 throw new IllegalArgumentException("Must contain at least one subgroup");
372             }
373             return new BluetoothLeBroadcastSettings(
374                     mIsPublicBroadcast,
375                     mBroadcastName,
376                     mBroadcastCode,
377                     mPublicBroadcastMetadata,
378                     mSubgroupSettings);
379         }
380     }
381 }
382