• 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.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.List;
27 import java.util.Objects;
28 
29 /**
30  * This class contains the subgroup level information as defined in the BASE structure of Basic
31  * Audio profile.
32  *
33  * @hide
34  */
35 @SystemApi
36 public final class BluetoothLeBroadcastSubgroup implements Parcelable {
37     private final long mCodecId;
38     private final BluetoothLeAudioCodecConfigMetadata mCodecSpecificConfig;
39     private final BluetoothLeAudioContentMetadata mContentMetadata;
40     private final List<BluetoothLeBroadcastChannel> mChannels;
41 
BluetoothLeBroadcastSubgroup(long codecId, BluetoothLeAudioCodecConfigMetadata codecSpecificConfig, BluetoothLeAudioContentMetadata contentMetadata, List<BluetoothLeBroadcastChannel> channels)42     private BluetoothLeBroadcastSubgroup(long codecId,
43             BluetoothLeAudioCodecConfigMetadata codecSpecificConfig,
44             BluetoothLeAudioContentMetadata contentMetadata,
45             List<BluetoothLeBroadcastChannel> channels) {
46         mCodecId = codecId;
47         mCodecSpecificConfig = codecSpecificConfig;
48         mContentMetadata = contentMetadata;
49         mChannels = channels;
50     }
51 
52     @Override
equals(@ullable Object o)53     public boolean equals(@Nullable Object o) {
54         if (!(o instanceof BluetoothLeBroadcastSubgroup)) {
55             return false;
56         }
57         final BluetoothLeBroadcastSubgroup other = (BluetoothLeBroadcastSubgroup) o;
58         return mCodecId == other.getCodecId()
59                 && mCodecSpecificConfig.equals(other.getCodecSpecificConfig())
60                 && mContentMetadata.equals(other.getContentMetadata())
61                 && mChannels.equals(other.getChannels());
62     }
63 
64     @Override
hashCode()65     public int hashCode() {
66         return Objects.hash(mCodecId, mCodecSpecificConfig, mContentMetadata, mChannels);
67     }
68 
69     /**
70      * Get the codec ID field as defined by the Basic Audio Profile.
71      *
72      * The codec ID field has 5 octets, with
73      * - Octet 0: Coding_Format as defined in Bluetooth Assigned Numbers
74      * - Octet 1-2: Company ID as defined in Bluetooth Assigned Numbers
75      *              Shall be 0x0000 if octet 0 != 0xFF
76      * - Octet 3-4: Vendor-specific codec ID
77      *              Shall be 0x0000 if octet 0 != 0xFF
78      *
79      * @return 5-byte codec ID field in Java long format
80      * @hide
81      */
82     @SystemApi
getCodecId()83     public long getCodecId() {
84         return mCodecId;
85     }
86 
87     /**
88      * Get codec specific config metadata for this subgroup.
89      *
90      * @return codec specific config metadata for this subgroup
91      * @hide
92      */
93     @SystemApi
94     @NonNull
getCodecSpecificConfig()95     public BluetoothLeAudioCodecConfigMetadata getCodecSpecificConfig() {
96         return mCodecSpecificConfig;
97     }
98 
99     /**
100      * Get content metadata for this Broadcast Source subgroup.
101      *
102      * @return content metadata for this Broadcast Source subgroup
103      * @hide
104      */
105     @SystemApi
getContentMetadata()106     public @NonNull BluetoothLeAudioContentMetadata getContentMetadata() {
107         return mContentMetadata;
108     }
109 
110     /**
111      * Indicate if Broadcast Sink should have a preferred Broadcast Channel (BIS).
112      *
113      * Only used by Broadcast Assistant and Sink. Ignored by Broadcast Source
114      *
115      * @return true if Broadcast Sink has at least one preferred Broadcast Channel (BIS) as
116      * indicated by {@link BluetoothLeBroadcastChannel#isSelected()}
117      * @hide
118      */
119     @SystemApi
hasChannelPreference()120     public boolean hasChannelPreference() {
121         return mChannels.stream().anyMatch(BluetoothLeBroadcastChannel::isSelected);
122     }
123 
124     /**
125      * Get list of Broadcast Channels included in this Broadcast subgroup.
126      *
127      * Each Broadcast Channel represents a Broadcast Isochronous Stream (BIS)
128      *
129      * A Broadcast subgroup should contain at least 1 Broadcast Channel
130      *
131      * @return list of Broadcast Channels included in this Broadcast subgroup
132      * @hide
133      */
134     @SystemApi
getChannels()135     public @NonNull List<BluetoothLeBroadcastChannel> getChannels() {
136         return mChannels;
137     }
138 
139     /**
140      * {@inheritDoc}
141      * @hide
142      */
143     @Override
describeContents()144     public int describeContents() {
145         return 0;
146     }
147 
148     /**
149      * {@inheritDoc}
150      * @hide
151      */
152     @Override
writeToParcel(Parcel out, int flags)153     public void writeToParcel(Parcel out, int flags) {
154         out.writeLong(mCodecId);
155         out.writeTypedObject(mCodecSpecificConfig, 0);
156         out.writeTypedObject(mContentMetadata, 0);
157         out.writeTypedList(mChannels);
158     }
159 
160     /**
161      * A {@link Parcelable.Creator} to create {@link BluetoothLeBroadcastSubgroup} from parcel.
162      * @hide
163      */
164     @SystemApi
165     @NonNull
166     public static final Creator<BluetoothLeBroadcastSubgroup> CREATOR = new Creator<>() {
167         public @NonNull BluetoothLeBroadcastSubgroup createFromParcel(@NonNull Parcel in) {
168             Builder builder = new Builder();
169             builder.setCodecId(in.readLong());
170             builder.setCodecSpecificConfig(in.readTypedObject(
171                     BluetoothLeAudioCodecConfigMetadata.CREATOR));
172             builder.setContentMetadata(
173                     in.readTypedObject(BluetoothLeAudioContentMetadata.CREATOR));
174             List<BluetoothLeBroadcastChannel> channels = new ArrayList<>();
175             in.readTypedList(channels, BluetoothLeBroadcastChannel.CREATOR);
176             for (BluetoothLeBroadcastChannel channel : channels) {
177                 builder.addChannel(channel);
178             }
179             return builder.build();
180         }
181 
182         public @NonNull BluetoothLeBroadcastSubgroup[] newArray(int size) {
183             return new BluetoothLeBroadcastSubgroup[size];
184         }
185     };
186 
187     private static final int UNKNOWN_VALUE_PLACEHOLDER = -1;
188 
189     /**
190      * Builder for {@link BluetoothLeBroadcastSubgroup}.
191      * @hide
192      */
193     @SystemApi
194     public static final class Builder {
195         private long mCodecId = UNKNOWN_VALUE_PLACEHOLDER;
196         private BluetoothLeAudioCodecConfigMetadata mCodecSpecificConfig = null;
197         private BluetoothLeAudioContentMetadata mContentMetadata = null;
198         private List<BluetoothLeBroadcastChannel> mChannels = new ArrayList<>();
199 
200         /**
201          * Create an empty constructor.
202          *
203          * @hide
204          */
205         @SystemApi
Builder()206         public Builder() {}
207 
208         /**
209          * Create a builder with copies of information from original object.
210          *
211          * @param original original object
212          * @hide
213          */
214         @SystemApi
Builder(@onNull BluetoothLeBroadcastSubgroup original)215         public Builder(@NonNull BluetoothLeBroadcastSubgroup original) {
216             mCodecId = original.getCodecId();
217             mCodecSpecificConfig = original.getCodecSpecificConfig();
218             mContentMetadata = original.getContentMetadata();
219             mChannels = original.getChannels();
220         }
221 
222         /**
223          * Set the codec ID field as defined by the Basic Audio Profile.
224          *
225          * The codec ID field has 5 octets, with
226          * - Octet 0: Coding_Format as defined in Bluetooth Assigned Numbers
227          * - Octet 1-2: Company ID as defined in Bluetooth Assigned Numbers
228          *              Shall be 0x0000 if octet 0 != 0xFF
229          * - Octet 3-4: Vendor-specific codec ID
230          *              Shall be 0x0000 if octet 0 != 0xFF
231          *
232          * @param codecId 5-byte codec ID field in Java long format
233          * @return this builder
234          * @hide
235          */
236         @SystemApi
setCodecId(long codecId)237         public @NonNull Builder setCodecId(long codecId) {
238             mCodecId = codecId;
239             return this;
240         }
241 
242         /**
243          * Set codec specific config metadata for this subgroup.
244          *
245          * @param codecSpecificConfig codec specific config metadata for this subgroup
246          * @throws {@link NullPointerException} if codecSpecificConfig is null
247          * @return this builder
248          * @hide
249          */
250         @SystemApi
251         @NonNull
setCodecSpecificConfig( @onNull BluetoothLeAudioCodecConfigMetadata codecSpecificConfig)252         public Builder setCodecSpecificConfig(
253                 @NonNull BluetoothLeAudioCodecConfigMetadata codecSpecificConfig) {
254             Objects.requireNonNull(codecSpecificConfig, "codecSpecificConfig cannot be null");
255             mCodecSpecificConfig = codecSpecificConfig;
256             return this;
257         }
258 
259         /**
260          * Set content metadata for this Broadcast Source subgroup.
261          *
262          * @param contentMetadata content metadata for this Broadcast Source subgroup
263          * @throws NullPointerException if contentMetadata is null
264          * @return this builder
265          * @hide
266          */
267         @SystemApi
268         @NonNull
setContentMetadata( @onNull BluetoothLeAudioContentMetadata contentMetadata)269         public Builder setContentMetadata(
270                 @NonNull BluetoothLeAudioContentMetadata contentMetadata) {
271             Objects.requireNonNull(contentMetadata, "contentMetadata cannot be null");
272             mContentMetadata = contentMetadata;
273             return this;
274         }
275 
276         /**
277          * Add a Broadcast Channel to this Broadcast subgroup.
278          *
279          * Each Broadcast Channel represents a Broadcast Isochronous Stream (BIS)
280          *
281          * A Broadcast subgroup should contain at least 1 Broadcast Channel
282          *
283          * @param channel  a Broadcast Channel to be added to this Broadcast subgroup
284          * @throws NullPointerException if channel is null
285          * @return this builder
286          * @hide
287          */
288         @SystemApi
addChannel(@onNull BluetoothLeBroadcastChannel channel)289         public @NonNull Builder addChannel(@NonNull BluetoothLeBroadcastChannel channel) {
290             Objects.requireNonNull(channel, "channel cannot be null");
291             mChannels.add(channel);
292             return this;
293         }
294 
295         /**
296          * Clear channel list so that one can reset the builder after create it from an existing
297          * object.
298          *
299          * @return this builder
300          * @hide
301          */
302         @SystemApi
clearChannel()303         public @NonNull Builder clearChannel() {
304             mChannels.clear();
305             return this;
306         }
307 
308         /**
309          * Build {@link BluetoothLeBroadcastSubgroup}.
310          *
311          * @return constructed {@link BluetoothLeBroadcastSubgroup}
312          * @throws NullPointerException if {@link NonNull} items are null
313          * @throws IllegalArgumentException if the object cannot be built
314          * @hide
315          */
316         @SystemApi
build()317         public @NonNull BluetoothLeBroadcastSubgroup build() {
318             Objects.requireNonNull(mCodecSpecificConfig, "CodecSpecificConfig is null");
319             Objects.requireNonNull(mContentMetadata, "ContentMetadata is null");
320             if (mChannels.isEmpty()) {
321                 throw new IllegalArgumentException("Must have at least one channel");
322             }
323             return new BluetoothLeBroadcastSubgroup(mCodecId, mCodecSpecificConfig,
324                     mContentMetadata, mChannels);
325         }
326     }
327 }
328