1/*
2 * Copyright (C) 2016 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
17package android.bluetooth;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21
22import java.util.Objects;
23
24/**
25 * Represents the codec configuration for a Bluetooth A2DP source device.
26 *
27 * {@see BluetoothA2dp}
28 *
29 * {@hide}
30 */
31public final class BluetoothCodecConfig implements Parcelable {
32    // Add an entry for each source codec here.
33    // NOTE: The values should be same as those listed in the following file:
34    //   hardware/libhardware/include/hardware/bt_av.h
35    public static final int SOURCE_CODEC_TYPE_SBC     = 0;
36    public static final int SOURCE_CODEC_TYPE_AAC     = 1;
37    public static final int SOURCE_CODEC_TYPE_APTX    = 2;
38    public static final int SOURCE_CODEC_TYPE_APTX_HD = 3;
39    public static final int SOURCE_CODEC_TYPE_LDAC    = 4;
40    public static final int SOURCE_CODEC_TYPE_MAX     = 5;
41
42    public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
43
44    public static final int CODEC_PRIORITY_DISABLED = -1;
45    public static final int CODEC_PRIORITY_DEFAULT = 0;
46    public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
47
48    public static final int SAMPLE_RATE_NONE   = 0;
49    public static final int SAMPLE_RATE_44100  = 0x1 << 0;
50    public static final int SAMPLE_RATE_48000  = 0x1 << 1;
51    public static final int SAMPLE_RATE_88200  = 0x1 << 2;
52    public static final int SAMPLE_RATE_96000  = 0x1 << 3;
53    public static final int SAMPLE_RATE_176400 = 0x1 << 4;
54    public static final int SAMPLE_RATE_192000 = 0x1 << 5;
55
56    public static final int BITS_PER_SAMPLE_NONE = 0;
57    public static final int BITS_PER_SAMPLE_16   = 0x1 << 0;
58    public static final int BITS_PER_SAMPLE_24   = 0x1 << 1;
59    public static final int BITS_PER_SAMPLE_32   = 0x1 << 2;
60
61    public static final int CHANNEL_MODE_NONE   = 0;
62    public static final int CHANNEL_MODE_MONO   = 0x1 << 0;
63    public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
64
65    private final int mCodecType;
66    private int mCodecPriority;
67    private final int mSampleRate;
68    private final int mBitsPerSample;
69    private final int mChannelMode;
70    private final long mCodecSpecific1;
71    private final long mCodecSpecific2;
72    private final long mCodecSpecific3;
73    private final long mCodecSpecific4;
74
75    public BluetoothCodecConfig(int codecType, int codecPriority,
76                                int sampleRate, int bitsPerSample,
77                                int channelMode, long codecSpecific1,
78                                long codecSpecific2, long codecSpecific3,
79                                long codecSpecific4) {
80        mCodecType = codecType;
81        mCodecPriority = codecPriority;
82        mSampleRate = sampleRate;
83        mBitsPerSample = bitsPerSample;
84        mChannelMode = channelMode;
85        mCodecSpecific1 = codecSpecific1;
86        mCodecSpecific2 = codecSpecific2;
87        mCodecSpecific3 = codecSpecific3;
88        mCodecSpecific4 = codecSpecific4;
89    }
90
91    @Override
92    public boolean equals(Object o) {
93        if (o instanceof BluetoothCodecConfig) {
94            BluetoothCodecConfig other = (BluetoothCodecConfig)o;
95            return (other.mCodecType == mCodecType &&
96                    other.mCodecPriority == mCodecPriority &&
97                    other.mSampleRate == mSampleRate &&
98                    other.mBitsPerSample == mBitsPerSample &&
99                    other.mChannelMode == mChannelMode &&
100                    other.mCodecSpecific1 == mCodecSpecific1 &&
101                    other.mCodecSpecific2 == mCodecSpecific2 &&
102                    other.mCodecSpecific3 == mCodecSpecific3 &&
103                    other.mCodecSpecific4 == mCodecSpecific4);
104        }
105        return false;
106    }
107
108    @Override
109    public int hashCode() {
110        return Objects.hash(mCodecType, mCodecPriority, mSampleRate,
111                            mBitsPerSample, mChannelMode, mCodecSpecific1,
112                            mCodecSpecific2, mCodecSpecific3, mCodecSpecific4);
113    }
114
115    /**
116     * Checks whether the object contains valid codec configuration.
117     *
118     * @return true if the object contains valid codec configuration,
119     * otherwise false.
120     */
121    public boolean isValid() {
122        return (mSampleRate != SAMPLE_RATE_NONE) &&
123            (mBitsPerSample != BITS_PER_SAMPLE_NONE) &&
124            (mChannelMode != CHANNEL_MODE_NONE);
125    }
126
127    /**
128     * Adds capability string to an existing string.
129     *
130     * @param prevStr the previous string with the capabilities. Can be
131     * a null pointer.
132     * @param capStr the capability string to append to prevStr argument.
133     * @return the result string in the form "prevStr|capStr".
134     */
135    private static String appendCapabilityToString(String prevStr,
136                                                   String capStr) {
137        if (prevStr == null) {
138            return capStr;
139        }
140        return prevStr + "|" + capStr;
141    }
142
143    @Override
144    public String toString() {
145        String sampleRateStr = null;
146        if (mSampleRate == SAMPLE_RATE_NONE) {
147            sampleRateStr = appendCapabilityToString(sampleRateStr, "NONE");
148        }
149        if ((mSampleRate & SAMPLE_RATE_44100) != 0) {
150            sampleRateStr = appendCapabilityToString(sampleRateStr, "44100");
151        }
152        if ((mSampleRate & SAMPLE_RATE_48000) != 0) {
153            sampleRateStr = appendCapabilityToString(sampleRateStr, "48000");
154        }
155        if ((mSampleRate & SAMPLE_RATE_88200) != 0) {
156            sampleRateStr = appendCapabilityToString(sampleRateStr, "88200");
157        }
158        if ((mSampleRate & SAMPLE_RATE_96000) != 0) {
159            sampleRateStr = appendCapabilityToString(sampleRateStr, "96000");
160        }
161        if ((mSampleRate & SAMPLE_RATE_176400) != 0) {
162            sampleRateStr = appendCapabilityToString(sampleRateStr, "176400");
163        }
164        if ((mSampleRate & SAMPLE_RATE_192000) != 0) {
165            sampleRateStr = appendCapabilityToString(sampleRateStr, "192000");
166        }
167
168        String bitsPerSampleStr = null;
169        if (mBitsPerSample == BITS_PER_SAMPLE_NONE) {
170            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "NONE");
171        }
172        if ((mBitsPerSample & BITS_PER_SAMPLE_16) != 0) {
173            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "16");
174        }
175        if ((mBitsPerSample & BITS_PER_SAMPLE_24) != 0) {
176            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "24");
177        }
178        if ((mBitsPerSample & BITS_PER_SAMPLE_32) != 0) {
179            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "32");
180        }
181
182        String channelModeStr = null;
183        if (mChannelMode == CHANNEL_MODE_NONE) {
184            channelModeStr = appendCapabilityToString(channelModeStr, "NONE");
185        }
186        if ((mChannelMode & CHANNEL_MODE_MONO) != 0) {
187            channelModeStr = appendCapabilityToString(channelModeStr, "MONO");
188        }
189        if ((mChannelMode & CHANNEL_MODE_STEREO) != 0) {
190            channelModeStr = appendCapabilityToString(channelModeStr, "STEREO");
191        }
192
193        return "{codecName:" + getCodecName() +
194            ",mCodecType:" + mCodecType +
195            ",mCodecPriority:" + mCodecPriority +
196            ",mSampleRate:" + String.format("0x%x", mSampleRate) +
197            "(" + sampleRateStr + ")" +
198            ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) +
199            "(" + bitsPerSampleStr + ")" +
200            ",mChannelMode:" + String.format("0x%x", mChannelMode) +
201            "(" + channelModeStr + ")" +
202            ",mCodecSpecific1:" + mCodecSpecific1 +
203            ",mCodecSpecific2:" + mCodecSpecific2 +
204            ",mCodecSpecific3:" + mCodecSpecific3 +
205            ",mCodecSpecific4:" + mCodecSpecific4 + "}";
206    }
207
208    public int describeContents() {
209        return 0;
210    }
211
212    public static final Parcelable.Creator<BluetoothCodecConfig> CREATOR =
213            new Parcelable.Creator<BluetoothCodecConfig>() {
214        public BluetoothCodecConfig createFromParcel(Parcel in) {
215            final int codecType = in.readInt();
216            final int codecPriority = in.readInt();
217            final int sampleRate = in.readInt();
218            final int bitsPerSample = in.readInt();
219            final int channelMode = in.readInt();
220            final long codecSpecific1 = in.readLong();
221            final long codecSpecific2 = in.readLong();
222            final long codecSpecific3 = in.readLong();
223            final long codecSpecific4 = in.readLong();
224            return new BluetoothCodecConfig(codecType, codecPriority,
225                                            sampleRate, bitsPerSample,
226                                            channelMode, codecSpecific1,
227                                            codecSpecific2, codecSpecific3,
228                                            codecSpecific4);
229        }
230        public BluetoothCodecConfig[] newArray(int size) {
231            return new BluetoothCodecConfig[size];
232        }
233    };
234
235    public void writeToParcel(Parcel out, int flags) {
236        out.writeInt(mCodecType);
237        out.writeInt(mCodecPriority);
238        out.writeInt(mSampleRate);
239        out.writeInt(mBitsPerSample);
240        out.writeInt(mChannelMode);
241        out.writeLong(mCodecSpecific1);
242        out.writeLong(mCodecSpecific2);
243        out.writeLong(mCodecSpecific3);
244        out.writeLong(mCodecSpecific4);
245    }
246
247    /**
248     * Gets the codec name.
249     *
250     * @return the codec name
251     */
252    public String getCodecName() {
253        switch (mCodecType) {
254        case SOURCE_CODEC_TYPE_SBC:
255            return "SBC";
256        case SOURCE_CODEC_TYPE_AAC:
257            return "AAC";
258        case SOURCE_CODEC_TYPE_APTX:
259            return "aptX";
260        case SOURCE_CODEC_TYPE_APTX_HD:
261            return "aptX HD";
262        case SOURCE_CODEC_TYPE_LDAC:
263            return "LDAC";
264        case SOURCE_CODEC_TYPE_INVALID:
265            return "INVALID CODEC";
266        default:
267            break;
268        }
269        return "UNKNOWN CODEC(" + mCodecType + ")";
270    }
271
272    /**
273     * Gets the codec type.
274     * See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}.
275     *
276     * @return the codec type
277     */
278    public int getCodecType() {
279        return mCodecType;
280    }
281
282    /**
283     * Checks whether the codec is mandatory.
284     *
285     * @return true if the codec is mandatory, otherwise false.
286     */
287    public boolean isMandatoryCodec() {
288        return mCodecType == SOURCE_CODEC_TYPE_SBC;
289    }
290
291    /**
292     * Gets the codec selection priority.
293     * The codec selection priority is relative to other codecs: larger value
294     * means higher priority. If 0, reset to default.
295     *
296     * @return the codec priority
297     */
298    public int getCodecPriority() {
299        return mCodecPriority;
300    }
301
302    /**
303     * Sets the codec selection priority.
304     * The codec selection priority is relative to other codecs: larger value
305     * means higher priority. If 0, reset to default.
306     *
307     * @param codecPriority the codec priority
308     */
309    public void setCodecPriority(int codecPriority) {
310        mCodecPriority = codecPriority;
311    }
312
313    /**
314     * Gets the codec sample rate. The value can be a bitmask with all
315     * supported sample rates:
316     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or
317     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or
318     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_48000} or
319     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_88200} or
320     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_96000} or
321     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_176400} or
322     * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_192000}
323     *
324     * @return the codec sample rate
325     */
326    public int getSampleRate() {
327        return mSampleRate;
328    }
329
330    /**
331     * Gets the codec bits per sample. The value can be a bitmask with all
332     * bits per sample supported:
333     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or
334     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or
335     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_24} or
336     * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_32}
337     *
338     * @return the codec bits per sample
339     */
340    public int getBitsPerSample() {
341        return mBitsPerSample;
342    }
343
344    /**
345     * Gets the codec channel mode. The value can be a bitmask with all
346     * supported channel modes:
347     * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or
348     * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or
349     * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_STEREO}
350     *
351     * @return the codec channel mode
352     */
353    public int getChannelMode() {
354        return mChannelMode;
355    }
356
357    /**
358     * Gets a codec specific value1.
359     *
360     * @return a codec specific value1.
361     */
362    public long getCodecSpecific1() {
363        return mCodecSpecific1;
364    }
365
366    /**
367     * Gets a codec specific value2.
368     *
369     * @return a codec specific value2
370     */
371    public long getCodecSpecific2() {
372        return mCodecSpecific2;
373    }
374
375    /**
376     * Gets a codec specific value3.
377     *
378     * @return a codec specific value3
379     */
380    public long getCodecSpecific3() {
381        return mCodecSpecific3;
382    }
383
384    /**
385     * Gets a codec specific value4.
386     *
387     * @return a codec specific value4
388     */
389    public long getCodecSpecific4() {
390        return mCodecSpecific4;
391    }
392
393    /**
394     * Checks whether the audio feeding parameters are same.
395     *
396     * @param other the codec config to compare against
397     * @return true if the audio feeding parameters are same, otherwise false
398     */
399    public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) {
400        return (other != null && other.mSampleRate == mSampleRate &&
401                other.mBitsPerSample == mBitsPerSample &&
402                other.mChannelMode == mChannelMode);
403    }
404}
405