• 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     public static final @NonNull Parcelable.Creator<BluetoothLeBroadcastSubgroup> CREATOR =
166             new Parcelable.Creator<BluetoothLeBroadcastSubgroup>() {
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
setCodecSpecificConfig( @onNull BluetoothLeAudioCodecConfigMetadata codecSpecificConfig)251         public @NonNull Builder setCodecSpecificConfig(
252                 @NonNull BluetoothLeAudioCodecConfigMetadata codecSpecificConfig) {
253             Objects.requireNonNull(codecSpecificConfig, "codecSpecificConfig cannot be null");
254             mCodecSpecificConfig = codecSpecificConfig;
255             return this;
256         }
257 
258         /**
259          * Set content metadata for this Broadcast Source subgroup.
260          *
261          * @param contentMetadata content metadata for this Broadcast Source subgroup
262          * @throws NullPointerException if contentMetadata is null
263          * @return this builder
264          * @hide
265          */
266         @SystemApi
setContentMetadata( @onNull BluetoothLeAudioContentMetadata contentMetadata)267         public @NonNull Builder setContentMetadata(
268                 @NonNull BluetoothLeAudioContentMetadata contentMetadata) {
269             Objects.requireNonNull(contentMetadata, "contentMetadata cannot be null");
270             mContentMetadata = contentMetadata;
271             return this;
272         }
273 
274         /**
275          * Add a Broadcast Channel to this Broadcast subgroup.
276          *
277          * Each Broadcast Channel represents a Broadcast Isochronous Stream (BIS)
278          *
279          * A Broadcast subgroup should contain at least 1 Broadcast Channel
280          *
281          * @param channel  a Broadcast Channel to be added to this Broadcast subgroup
282          * @throws NullPointerException if channel is null
283          * @return this builder
284          * @hide
285          */
286         @SystemApi
addChannel(@onNull BluetoothLeBroadcastChannel channel)287         public @NonNull Builder addChannel(@NonNull BluetoothLeBroadcastChannel channel) {
288             Objects.requireNonNull(channel, "channel cannot be null");
289             mChannels.add(channel);
290             return this;
291         }
292 
293         /**
294          * Clear channel list so that one can reset the builder after create it from an existing
295          * object.
296          *
297          * @return this builder
298          * @hide
299          */
300         @SystemApi
clearChannel()301         public @NonNull Builder clearChannel() {
302             mChannels.clear();
303             return this;
304         }
305 
306         /**
307          * Build {@link BluetoothLeBroadcastSubgroup}.
308          *
309          * @return constructed {@link BluetoothLeBroadcastSubgroup}
310          * @throws NullPointerException if {@link NonNull} items are null
311          * @throws IllegalArgumentException if the object cannot be built
312          * @hide
313          */
314         @SystemApi
build()315         public @NonNull BluetoothLeBroadcastSubgroup build() {
316             Objects.requireNonNull(mCodecSpecificConfig, "CodecSpecificConfig is null");
317             Objects.requireNonNull(mContentMetadata, "ContentMetadata is null");
318             if (mChannels.isEmpty()) {
319                 throw new IllegalArgumentException("Must have at least one channel");
320             }
321             return new BluetoothLeBroadcastSubgroup(mCodecId, mCodecSpecificConfig,
322                     mContentMetadata, mChannels);
323         }
324     }
325 }
326