1 /*
2  * Copyright (C) 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.os.Parcel;
22 import android.os.Parcelable;
23 
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Objects;
27 
28 /**
29  * Represents the codec status (configuration and capability) for a Bluetooth Le Audio source
30  * device.
31  *
32  * @see BluetoothLeAudio
33  */
34 public final class BluetoothLeAudioCodecStatus implements Parcelable {
35     /**
36      * Extra for the codec configuration intents of the individual profiles.
37      *
38      * <p>This extra represents the current codec status of the Le Audio profile.
39      */
40     public static final String EXTRA_LE_AUDIO_CODEC_STATUS =
41             "android.bluetooth.extra.LE_AUDIO_CODEC_STATUS";
42 
43     private final @Nullable BluetoothLeAudioCodecConfig mInputCodecConfig;
44     private final @Nullable BluetoothLeAudioCodecConfig mOutputCodecConfig;
45     private final @Nullable List<BluetoothLeAudioCodecConfig> mInputCodecsLocalCapabilities;
46     private final @Nullable List<BluetoothLeAudioCodecConfig> mOutputCodecsLocalCapabilities;
47     private final @Nullable List<BluetoothLeAudioCodecConfig> mInputCodecsSelectableCapabilities;
48     private final @Nullable List<BluetoothLeAudioCodecConfig> mOutputCodecsSelectableCapabilities;
49 
50     /**
51      * Represents the codec status for a Bluetooth LE Audio source device.
52      *
53      * @param inputCodecConfig the current input codec configuration.
54      * @param outputCodecConfig the current output codec configuration.
55      * @param inputCodecsLocalCapabilities the local input codecs capabilities.
56      * @param outputCodecsLocalCapabilities the local output codecs capabilities.
57      * @param inputCodecsSelectableCapabilities the selectable input codecs capabilities.
58      * @param outputCodecsSelectableCapabilities the selectable output codecs capabilities.
59      */
BluetoothLeAudioCodecStatus( @ullable BluetoothLeAudioCodecConfig inputCodecConfig, @Nullable BluetoothLeAudioCodecConfig outputCodecConfig, @NonNull List<BluetoothLeAudioCodecConfig> inputCodecsLocalCapabilities, @NonNull List<BluetoothLeAudioCodecConfig> outputCodecsLocalCapabilities, @NonNull List<BluetoothLeAudioCodecConfig> inputCodecsSelectableCapabilities, @NonNull List<BluetoothLeAudioCodecConfig> outputCodecsSelectableCapabilities)60     public BluetoothLeAudioCodecStatus(
61             @Nullable BluetoothLeAudioCodecConfig inputCodecConfig,
62             @Nullable BluetoothLeAudioCodecConfig outputCodecConfig,
63             @NonNull List<BluetoothLeAudioCodecConfig> inputCodecsLocalCapabilities,
64             @NonNull List<BluetoothLeAudioCodecConfig> outputCodecsLocalCapabilities,
65             @NonNull List<BluetoothLeAudioCodecConfig> inputCodecsSelectableCapabilities,
66             @NonNull List<BluetoothLeAudioCodecConfig> outputCodecsSelectableCapabilities) {
67         mInputCodecConfig = inputCodecConfig;
68         mOutputCodecConfig = outputCodecConfig;
69         mInputCodecsLocalCapabilities = inputCodecsLocalCapabilities;
70         mOutputCodecsLocalCapabilities = outputCodecsLocalCapabilities;
71         mInputCodecsSelectableCapabilities = inputCodecsSelectableCapabilities;
72         mOutputCodecsSelectableCapabilities = outputCodecsSelectableCapabilities;
73     }
74 
BluetoothLeAudioCodecStatus(Parcel in)75     private BluetoothLeAudioCodecStatus(Parcel in) {
76         mInputCodecConfig = in.readTypedObject(BluetoothLeAudioCodecConfig.CREATOR);
77         mOutputCodecConfig = in.readTypedObject(BluetoothLeAudioCodecConfig.CREATOR);
78         mInputCodecsLocalCapabilities =
79                 in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR);
80         mOutputCodecsLocalCapabilities =
81                 in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR);
82         mInputCodecsSelectableCapabilities =
83                 in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR);
84         mOutputCodecsSelectableCapabilities =
85                 in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR);
86     }
87 
88     @Override
equals(@ullable Object o)89     public boolean equals(@Nullable Object o) {
90         if (o instanceof BluetoothLeAudioCodecStatus) {
91             BluetoothLeAudioCodecStatus other = (BluetoothLeAudioCodecStatus) o;
92             return (Objects.equals(other.mInputCodecConfig, mInputCodecConfig)
93                     && Objects.equals(other.mOutputCodecConfig, mOutputCodecConfig)
94                     && sameCapabilities(
95                             other.mInputCodecsLocalCapabilities, mInputCodecsLocalCapabilities)
96                     && sameCapabilities(
97                             other.mOutputCodecsLocalCapabilities, mOutputCodecsLocalCapabilities)
98                     && sameCapabilities(
99                             other.mInputCodecsSelectableCapabilities,
100                             mInputCodecsSelectableCapabilities)
101                     && sameCapabilities(
102                             other.mOutputCodecsSelectableCapabilities,
103                             mOutputCodecsSelectableCapabilities));
104         }
105         return false;
106     }
107 
108     /**
109      * Checks whether two lists of capabilities contain same capabilities. The order of the
110      * capabilities in each list is ignored.
111      *
112      * @param c1 the first list of capabilities to compare
113      * @param c2 the second list of capabilities to compare
114      * @return {@code true} if both lists contain same capabilities
115      */
sameCapabilities( @ullable List<BluetoothLeAudioCodecConfig> c1, @Nullable List<BluetoothLeAudioCodecConfig> c2)116     private static boolean sameCapabilities(
117             @Nullable List<BluetoothLeAudioCodecConfig> c1,
118             @Nullable List<BluetoothLeAudioCodecConfig> c2) {
119         if (c1 == null) {
120             return (c2 == null);
121         }
122         if (c2 == null) {
123             return false;
124         }
125         if (c1.size() != c2.size()) {
126             return false;
127         }
128         return c1.containsAll(c2);
129     }
130 
isCodecConfigSelectable( BluetoothLeAudioCodecConfig codecConfig, BluetoothLeAudioCodecConfig selectableConfig)131     private boolean isCodecConfigSelectable(
132             BluetoothLeAudioCodecConfig codecConfig, BluetoothLeAudioCodecConfig selectableConfig) {
133         if (codecConfig.getCodecType() != selectableConfig.getCodecType()) {
134             return false;
135         }
136         if ((codecConfig.getFrameDuration() != BluetoothLeAudioCodecConfig.FRAME_DURATION_NONE)
137                 && ((codecConfig.getFrameDuration() & selectableConfig.getFrameDuration()) == 0)) {
138             return false;
139         }
140         if ((codecConfig.getChannelCount() != BluetoothLeAudioCodecConfig.CHANNEL_COUNT_NONE)
141                 && ((codecConfig.getChannelCount() & selectableConfig.getChannelCount()) == 0)) {
142             return false;
143         }
144         if ((codecConfig.getSampleRate() != BluetoothLeAudioCodecConfig.SAMPLE_RATE_NONE)
145                 && ((codecConfig.getSampleRate() & selectableConfig.getSampleRate()) == 0)) {
146             return false;
147         }
148         if ((codecConfig.getBitsPerSample() != BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_NONE)
149                 && ((codecConfig.getBitsPerSample() & selectableConfig.getBitsPerSample()) == 0)) {
150             return false;
151         }
152         if ((codecConfig.getOctetsPerFrame() != 0)
153                 && ((codecConfig.getOctetsPerFrame() < selectableConfig.getMinOctetsPerFrame())
154                         || (codecConfig.getOctetsPerFrame()
155                                 > selectableConfig.getMaxOctetsPerFrame()))) {
156             return false;
157         }
158         return true;
159     }
160 
161     /**
162      * Checks whether the Input codec config matches the selectable capabilities. Any parameters of
163      * the codec config with NONE value will be considered a wildcard matching.
164      *
165      * @param codecConfig the codec config to compare against
166      * @return {@code true} if the codec config matches, {@code false} otherwise
167      */
isInputCodecConfigSelectable(@ullable BluetoothLeAudioCodecConfig codecConfig)168     public boolean isInputCodecConfigSelectable(@Nullable BluetoothLeAudioCodecConfig codecConfig) {
169         if (codecConfig == null) {
170             return false;
171         }
172         for (BluetoothLeAudioCodecConfig selectableConfig : mInputCodecsSelectableCapabilities) {
173             if (isCodecConfigSelectable(codecConfig, selectableConfig)) {
174                 return true;
175             }
176         }
177         return false;
178     }
179 
180     /**
181      * Checks whether the Output codec config matches the selectable capabilities. Any parameters of
182      * the codec config with NONE value will be considered a wildcard matching.
183      *
184      * @param codecConfig the codec config to compare against
185      * @return {@code true} if the codec config matches, {@code false} otherwise
186      */
isOutputCodecConfigSelectable( @ullable BluetoothLeAudioCodecConfig codecConfig)187     public boolean isOutputCodecConfigSelectable(
188             @Nullable BluetoothLeAudioCodecConfig codecConfig) {
189         if (codecConfig == null) {
190             return false;
191         }
192         for (BluetoothLeAudioCodecConfig selectableConfig : mOutputCodecsSelectableCapabilities) {
193             if (isCodecConfigSelectable(codecConfig, selectableConfig)) {
194                 return true;
195             }
196         }
197         return false;
198     }
199 
200     /** Returns a hash based on the codec config and local capabilities. */
201     @Override
hashCode()202     public int hashCode() {
203         return Objects.hash(
204                 mInputCodecConfig,
205                 mOutputCodecConfig,
206                 mInputCodecsLocalCapabilities,
207                 mOutputCodecsLocalCapabilities,
208                 mInputCodecsSelectableCapabilities,
209                 mOutputCodecsSelectableCapabilities);
210     }
211 
212     /**
213      * Returns a {@link String} that describes each BluetoothLeAudioCodecStatus parameter current
214      * value.
215      */
216     @Override
toString()217     public String toString() {
218         return "{mInputCodecConfig:"
219                 + mInputCodecConfig
220                 + ",mOutputCodecConfig:"
221                 + mOutputCodecConfig
222                 + ",mInputCodecsLocalCapabilities:"
223                 + mInputCodecsLocalCapabilities
224                 + ",mOutputCodecsLocalCapabilities:"
225                 + mOutputCodecsLocalCapabilities
226                 + ",mInputCodecsSelectableCapabilities:"
227                 + mInputCodecsSelectableCapabilities
228                 + ",mOutputCodecsSelectableCapabilities:"
229                 + mOutputCodecsSelectableCapabilities
230                 + "}";
231     }
232 
233     /**
234      * @return 0
235      */
236     @Override
describeContents()237     public int describeContents() {
238         return 0;
239     }
240 
241     /** {@link Parcelable.Creator} interface implementation. */
242     public static final @android.annotation.NonNull Parcelable.Creator<BluetoothLeAudioCodecStatus>
243             CREATOR =
244                     new Parcelable.Creator<BluetoothLeAudioCodecStatus>() {
245                         public BluetoothLeAudioCodecStatus createFromParcel(Parcel in) {
246                             return new BluetoothLeAudioCodecStatus(in);
247                         }
248 
249                         public BluetoothLeAudioCodecStatus[] newArray(int size) {
250                             return new BluetoothLeAudioCodecStatus[size];
251                         }
252                     };
253 
254     /**
255      * Flattens the object to a parcel.
256      *
257      * @param out The Parcel in which the object should be written
258      * @param flags Additional flags about how the object should be written
259      */
260     @Override
writeToParcel(@onNull Parcel out, int flags)261     public void writeToParcel(@NonNull Parcel out, int flags) {
262         out.writeTypedObject(mInputCodecConfig, flags);
263         out.writeTypedObject(mOutputCodecConfig, flags);
264         out.writeTypedList(mInputCodecsLocalCapabilities);
265         out.writeTypedList(mOutputCodecsLocalCapabilities);
266         out.writeTypedList(mInputCodecsSelectableCapabilities);
267         out.writeTypedList(mOutputCodecsSelectableCapabilities);
268     }
269 
270     /**
271      * Returns the current Input codec configuration.
272      *
273      * @return The current input codec config.
274      */
getInputCodecConfig()275     public @Nullable BluetoothLeAudioCodecConfig getInputCodecConfig() {
276         return mInputCodecConfig;
277     }
278 
279     /**
280      * Returns the current Output codec configuration.
281      *
282      * @return The current output codec config.
283      */
getOutputCodecConfig()284     public @Nullable BluetoothLeAudioCodecConfig getOutputCodecConfig() {
285         return mOutputCodecConfig;
286     }
287 
288     /**
289      * Returns the input codecs local capabilities.
290      *
291      * @return The list of codec config that supported by the local system.
292      */
getInputCodecLocalCapabilities()293     public @NonNull List<BluetoothLeAudioCodecConfig> getInputCodecLocalCapabilities() {
294         return (mInputCodecsLocalCapabilities == null)
295                 ? Collections.emptyList()
296                 : mInputCodecsLocalCapabilities;
297     }
298 
299     /**
300      * Returns the output codecs local capabilities.
301      *
302      * @return The list of codec config that supported by the local system.
303      */
getOutputCodecLocalCapabilities()304     public @NonNull List<BluetoothLeAudioCodecConfig> getOutputCodecLocalCapabilities() {
305         return (mOutputCodecsLocalCapabilities == null)
306                 ? Collections.emptyList()
307                 : mOutputCodecsLocalCapabilities;
308     }
309 
310     /**
311      * Returns the Input codecs selectable capabilities.
312      *
313      * @return The list of codec config that supported by both of the local system and remote
314      *     devices.
315      */
getInputCodecSelectableCapabilities()316     public @NonNull List<BluetoothLeAudioCodecConfig> getInputCodecSelectableCapabilities() {
317         return (mInputCodecsSelectableCapabilities == null)
318                 ? Collections.emptyList()
319                 : mInputCodecsSelectableCapabilities;
320     }
321 
322     /**
323      * Returns the Output codecs selectable capabilities.
324      *
325      * @return The list of codec config that supported by both of the local system and remote
326      *     devices.
327      */
getOutputCodecSelectableCapabilities()328     public @NonNull List<BluetoothLeAudioCodecConfig> getOutputCodecSelectableCapabilities() {
329         return (mOutputCodecsSelectableCapabilities == null)
330                 ? Collections.emptyList()
331                 : mOutputCodecsSelectableCapabilities;
332     }
333 }
334