1 /*
2  * Copyright (C) 2017 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 A2DP source device.
30  *
31  * @see BluetoothA2dp
32  */
33 public final class BluetoothCodecStatus implements Parcelable {
34     /**
35      * Extra for the codec configuration intents of the individual profiles.
36      *
37      * <p>This extra represents the current codec status of the A2DP profile.
38      */
39     public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS";
40 
41     private final @Nullable BluetoothCodecConfig mCodecConfig;
42     private final @Nullable List<BluetoothCodecConfig> mCodecsLocalCapabilities;
43     private final @Nullable List<BluetoothCodecConfig> mCodecsSelectableCapabilities;
44 
45     /**
46      * Creates a new BluetoothCodecStatus.
47      *
48      * @hide
49      */
BluetoothCodecStatus( @ullable BluetoothCodecConfig codecConfig, @Nullable List<BluetoothCodecConfig> codecsLocalCapabilities, @Nullable List<BluetoothCodecConfig> codecsSelectableCapabilities)50     public BluetoothCodecStatus(
51             @Nullable BluetoothCodecConfig codecConfig,
52             @Nullable List<BluetoothCodecConfig> codecsLocalCapabilities,
53             @Nullable List<BluetoothCodecConfig> codecsSelectableCapabilities) {
54         mCodecConfig = codecConfig;
55         mCodecsLocalCapabilities = codecsLocalCapabilities;
56         mCodecsSelectableCapabilities = codecsSelectableCapabilities;
57     }
58 
BluetoothCodecStatus(Parcel in)59     private BluetoothCodecStatus(Parcel in) {
60         mCodecConfig = in.readTypedObject(BluetoothCodecConfig.CREATOR);
61         mCodecsLocalCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR);
62         mCodecsSelectableCapabilities = in.createTypedArrayList(BluetoothCodecConfig.CREATOR);
63     }
64 
65     @Override
equals(@ullable Object o)66     public boolean equals(@Nullable Object o) {
67         if (o instanceof BluetoothCodecStatus) {
68             BluetoothCodecStatus other = (BluetoothCodecStatus) o;
69             return (Objects.equals(other.mCodecConfig, mCodecConfig)
70                     && sameCapabilities(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities)
71                     && sameCapabilities(
72                             other.mCodecsSelectableCapabilities, mCodecsSelectableCapabilities));
73         }
74         return false;
75     }
76 
77     /**
78      * Checks whether two lists of capabilities contain same capabilities. The order of the
79      * capabilities in each list is ignored.
80      *
81      * @param c1 the first list of capabilities to compare
82      * @param c2 the second list of capabilities to compare
83      * @return {@code true} if both lists contain same capabilities
84      */
sameCapabilities( @ullable List<BluetoothCodecConfig> c1, @Nullable List<BluetoothCodecConfig> c2)85     private static boolean sameCapabilities(
86             @Nullable List<BluetoothCodecConfig> c1, @Nullable List<BluetoothCodecConfig> c2) {
87         if (c1 == null) {
88             return (c2 == null);
89         }
90         if (c2 == null) {
91             return false;
92         }
93         if (c1.size() != c2.size()) {
94             return false;
95         }
96         return c1.containsAll(c2);
97     }
98 
99     /**
100      * Checks whether the codec config matches the selectable capabilities. Any parameters of the
101      * codec config with NONE value will be considered a wildcard matching.
102      *
103      * @param codecConfig the codec config to compare against
104      * @return {@code true} if the codec config matches, {@code false} otherwise
105      */
isCodecConfigSelectable(@ullable BluetoothCodecConfig codecConfig)106     public boolean isCodecConfigSelectable(@Nullable BluetoothCodecConfig codecConfig) {
107         if (codecConfig == null
108                 || !codecConfig.hasSingleSampleRate()
109                 || !codecConfig.hasSingleBitsPerSample()
110                 || !codecConfig.hasSingleChannelMode()) {
111             return false;
112         }
113         for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) {
114             if (codecConfig.getCodecType() != selectableConfig.getCodecType()) {
115                 continue;
116             }
117             int sampleRate = codecConfig.getSampleRate();
118             if ((sampleRate & selectableConfig.getSampleRate()) == 0
119                     && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) {
120                 continue;
121             }
122             int bitsPerSample = codecConfig.getBitsPerSample();
123             if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0
124                     && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
125                 continue;
126             }
127             int channelMode = codecConfig.getChannelMode();
128             if ((channelMode & selectableConfig.getChannelMode()) == 0
129                     && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) {
130                 continue;
131             }
132             return true;
133         }
134         return false;
135     }
136 
137     /** Returns a hash based on the codec config and local capabilities. */
138     @Override
hashCode()139     public int hashCode() {
140         return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, mCodecsLocalCapabilities);
141     }
142 
143     /**
144      * Returns a {@link String} that describes each BluetoothCodecStatus parameter current value.
145      */
146     @Override
toString()147     public String toString() {
148         return "{mCodecConfig:"
149                 + mCodecConfig
150                 + ",mCodecsLocalCapabilities:"
151                 + mCodecsLocalCapabilities
152                 + ",mCodecsSelectableCapabilities:"
153                 + mCodecsSelectableCapabilities
154                 + "}";
155     }
156 
157     /**
158      * @return 0
159      * @hide
160      */
161     @Override
describeContents()162     public int describeContents() {
163         return 0;
164     }
165 
166     public static final @NonNull Creator<BluetoothCodecStatus> CREATOR =
167             new Creator<>() {
168                 public BluetoothCodecStatus createFromParcel(Parcel in) {
169                     return new BluetoothCodecStatus(in);
170                 }
171 
172                 public BluetoothCodecStatus[] newArray(int size) {
173                     return new BluetoothCodecStatus[size];
174                 }
175             };
176 
177     /**
178      * Flattens the object to a parcel.
179      *
180      * @param out The Parcel in which the object should be written
181      * @param flags Additional flags about how the object should be written
182      */
183     @Override
writeToParcel(@onNull Parcel out, int flags)184     public void writeToParcel(@NonNull Parcel out, int flags) {
185         out.writeTypedObject(mCodecConfig, 0);
186         out.writeTypedList(mCodecsLocalCapabilities);
187         out.writeTypedList(mCodecsSelectableCapabilities);
188     }
189 
190     /** Returns the current codec configuration. */
getCodecConfig()191     public @Nullable BluetoothCodecConfig getCodecConfig() {
192         return mCodecConfig;
193     }
194 
195     /** Returns the codecs local capabilities. */
getCodecsLocalCapabilities()196     public @NonNull List<BluetoothCodecConfig> getCodecsLocalCapabilities() {
197         return (mCodecsLocalCapabilities == null)
198                 ? Collections.emptyList()
199                 : mCodecsLocalCapabilities;
200     }
201 
202     /** Returns the codecs selectable capabilities. */
getCodecsSelectableCapabilities()203     public @NonNull List<BluetoothCodecConfig> getCodecsSelectableCapabilities() {
204         return (mCodecsSelectableCapabilities == null)
205                 ? Collections.emptyList()
206                 : mCodecsSelectableCapabilities;
207     }
208 
209     /** Builder for {@link BluetoothCodecStatus}. */
210     public static final class Builder {
211         private BluetoothCodecConfig mCodecConfig = null;
212         private List<BluetoothCodecConfig> mCodecsLocalCapabilities = null;
213         private List<BluetoothCodecConfig> mCodecsSelectableCapabilities = null;
214 
215         /**
216          * Set Bluetooth codec config for this codec status.
217          *
218          * @param codecConfig of this codec status
219          * @return the same Builder instance
220          */
setCodecConfig(@onNull BluetoothCodecConfig codecConfig)221         public @NonNull Builder setCodecConfig(@NonNull BluetoothCodecConfig codecConfig) {
222             mCodecConfig = codecConfig;
223             return this;
224         }
225 
226         /**
227          * Set codec local capabilities list for this codec status.
228          *
229          * @param codecsLocalCapabilities of this codec status
230          * @return the same Builder instance
231          */
setCodecsLocalCapabilities( @onNull List<BluetoothCodecConfig> codecsLocalCapabilities)232         public @NonNull Builder setCodecsLocalCapabilities(
233                 @NonNull List<BluetoothCodecConfig> codecsLocalCapabilities) {
234             mCodecsLocalCapabilities = codecsLocalCapabilities;
235             return this;
236         }
237 
238         /**
239          * Set codec selectable capabilities list for this codec status.
240          *
241          * @param codecsSelectableCapabilities of this codec status
242          * @return the same Builder instance
243          */
setCodecsSelectableCapabilities( @onNull List<BluetoothCodecConfig> codecsSelectableCapabilities)244         public @NonNull Builder setCodecsSelectableCapabilities(
245                 @NonNull List<BluetoothCodecConfig> codecsSelectableCapabilities) {
246             mCodecsSelectableCapabilities = codecsSelectableCapabilities;
247             return this;
248         }
249 
250         /**
251          * Build {@link BluetoothCodecStatus}.
252          *
253          * @return new BluetoothCodecStatus built
254          */
build()255         public @NonNull BluetoothCodecStatus build() {
256             return new BluetoothCodecStatus(
257                     mCodecConfig, mCodecsLocalCapabilities, mCodecsSelectableCapabilities);
258         }
259     }
260 }
261