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.Nullable;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 
24 import java.util.Arrays;
25 import java.util.Objects;
26 
27 /**
28  * Represents the codec status (configuration and capability) for a Bluetooth
29  * A2DP source device.
30  *
31  * {@see BluetoothA2dp}
32  *
33  * {@hide}
34  */
35 public final class BluetoothCodecStatus implements Parcelable {
36     /**
37      * Extra for the codec configuration intents of the individual profiles.
38      *
39      * This extra represents the current codec status of the A2DP
40      * profile.
41      */
42     @UnsupportedAppUsage
43     public static final String EXTRA_CODEC_STATUS =
44             "android.bluetooth.extra.CODEC_STATUS";
45 
46     private final @Nullable BluetoothCodecConfig mCodecConfig;
47     private final BluetoothCodecConfig[] mCodecsLocalCapabilities;
48     private final BluetoothCodecConfig[] mCodecsSelectableCapabilities;
49 
BluetoothCodecStatus(@ullable BluetoothCodecConfig codecConfig, @Nullable BluetoothCodecConfig[] codecsLocalCapabilities, @Nullable BluetoothCodecConfig[] codecsSelectableCapabilities)50     public BluetoothCodecStatus(@Nullable BluetoothCodecConfig codecConfig,
51             @Nullable BluetoothCodecConfig[] codecsLocalCapabilities,
52             @Nullable BluetoothCodecConfig[] codecsSelectableCapabilities) {
53         mCodecConfig = codecConfig;
54         mCodecsLocalCapabilities = codecsLocalCapabilities;
55         mCodecsSelectableCapabilities = codecsSelectableCapabilities;
56     }
57 
58     @Override
equals(@ullable Object o)59     public boolean equals(@Nullable Object o) {
60         if (o instanceof BluetoothCodecStatus) {
61             BluetoothCodecStatus other = (BluetoothCodecStatus) o;
62             return (Objects.equals(other.mCodecConfig, mCodecConfig)
63                     && sameCapabilities(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities)
64                     && sameCapabilities(other.mCodecsSelectableCapabilities,
65                     mCodecsSelectableCapabilities));
66         }
67         return false;
68     }
69 
70     /**
71      * Checks whether two arrays of capabilities contain same capabilities.
72      * The order of the capabilities in each array is ignored.
73      *
74      * @param c1 the first array of capabilities to compare
75      * @param c2 the second array of capabilities to compare
76      * @return true if both arrays contain same capabilities
77      * @hide
78      */
sameCapabilities(BluetoothCodecConfig[] c1, BluetoothCodecConfig[] c2)79     public static boolean sameCapabilities(BluetoothCodecConfig[] c1,
80                                            BluetoothCodecConfig[] c2) {
81         if (c1 == null) {
82             return (c2 == null);
83         }
84         if (c2 == null) {
85             return false;
86         }
87         if (c1.length != c2.length) {
88             return false;
89         }
90         return Arrays.asList(c1).containsAll(Arrays.asList(c2));
91     }
92 
93     /**
94      * Checks whether the codec config matches the selectable capabilities.
95      * Any parameters of the codec config with NONE value will be considered a wildcard matching.
96      *
97      * @param codecConfig the codec config to compare against
98      * @return true if the codec config matches, otherwise false
99      * @hide
100      */
isCodecConfigSelectable(BluetoothCodecConfig codecConfig)101     public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) {
102         if (codecConfig == null || !codecConfig.hasSingleSampleRate()
103                 || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) {
104             return false;
105         }
106         for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) {
107             if (codecConfig.getCodecType() != selectableConfig.getCodecType()) {
108                 continue;
109             }
110             int sampleRate = codecConfig.getSampleRate();
111             if ((sampleRate & selectableConfig.getSampleRate()) == 0
112                     && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) {
113                 continue;
114             }
115             int bitsPerSample = codecConfig.getBitsPerSample();
116             if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0
117                     && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
118                 continue;
119             }
120             int channelMode = codecConfig.getChannelMode();
121             if ((channelMode & selectableConfig.getChannelMode()) == 0
122                     && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) {
123                 continue;
124             }
125             return true;
126         }
127         return false;
128     }
129 
130     /**
131      * Returns a hash based on the codec config and local capabilities
132      *
133      * @return a hash based on the config values
134      * @hide
135      */
136     @Override
hashCode()137     public int hashCode() {
138         return Objects.hash(mCodecConfig, mCodecsLocalCapabilities,
139                 mCodecsLocalCapabilities);
140     }
141 
142     @Override
toString()143     public String toString() {
144         return "{mCodecConfig:" + mCodecConfig
145                 + ",mCodecsLocalCapabilities:" + Arrays.toString(mCodecsLocalCapabilities)
146                 + ",mCodecsSelectableCapabilities:" + Arrays.toString(mCodecsSelectableCapabilities)
147                 + "}";
148     }
149 
150     /**
151      * Always returns 0
152      *
153      * @return 0
154      * @hide
155      */
156     @Override
describeContents()157     public int describeContents() {
158         return 0;
159     }
160 
161     public static final @android.annotation.NonNull Parcelable.Creator<BluetoothCodecStatus> CREATOR =
162             new Parcelable.Creator<BluetoothCodecStatus>() {
163                 public BluetoothCodecStatus createFromParcel(Parcel in) {
164                     final BluetoothCodecConfig codecConfig = in.readTypedObject(
165                             BluetoothCodecConfig.CREATOR);
166                     final BluetoothCodecConfig[] codecsLocalCapabilities = in.createTypedArray(
167                             BluetoothCodecConfig.CREATOR);
168                     final BluetoothCodecConfig[] codecsSelectableCapabilities = in.createTypedArray(
169                             BluetoothCodecConfig.CREATOR);
170 
171                     return new BluetoothCodecStatus(codecConfig,
172                             codecsLocalCapabilities,
173                             codecsSelectableCapabilities);
174                 }
175 
176                 public BluetoothCodecStatus[] newArray(int size) {
177                     return new BluetoothCodecStatus[size];
178                 }
179             };
180 
181     /**
182      * Flattens the object to a parcel
183      *
184      * @param out The Parcel in which the object should be written.
185      * @param flags Additional flags about how the object should be written.
186      *
187      * @hide
188      */
189     @Override
writeToParcel(Parcel out, int flags)190     public void writeToParcel(Parcel out, int flags) {
191         out.writeTypedObject(mCodecConfig, 0);
192         out.writeTypedArray(mCodecsLocalCapabilities, 0);
193         out.writeTypedArray(mCodecsSelectableCapabilities, 0);
194     }
195 
196     /**
197      * Gets the current codec configuration.
198      *
199      * @return the current codec configuration
200      */
201     @UnsupportedAppUsage
getCodecConfig()202     public @Nullable BluetoothCodecConfig getCodecConfig() {
203         return mCodecConfig;
204     }
205 
206     /**
207      * Gets the codecs local capabilities.
208      *
209      * @return an array with the codecs local capabilities
210      */
211     @UnsupportedAppUsage
getCodecsLocalCapabilities()212     public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() {
213         return mCodecsLocalCapabilities;
214     }
215 
216     /**
217      * Gets the codecs selectable capabilities.
218      *
219      * @return an array with the codecs selectable capabilities
220      */
221     @UnsupportedAppUsage
getCodecsSelectableCapabilities()222     public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() {
223         return mCodecsSelectableCapabilities;
224     }
225 }
226