blob: d2a15357aa1fdbf909887c47240b62b40fcadc8f [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.bluetooth;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
* Represents the codec configuration for a Bluetooth A2DP source device.
*
* {@see BluetoothA2dp}
*
* {@hide}
*/
public final class BluetoothCodecConfig implements Parcelable {
// Add an entry for each source codec here.
// NOTE: The values should be same as those listed in the following file:
// hardware/libhardware/include/hardware/bt_av.h
/** @hide */
@IntDef(prefix = "SOURCE_CODEC_TYPE_", value = {
SOURCE_CODEC_TYPE_SBC,
SOURCE_CODEC_TYPE_AAC,
SOURCE_CODEC_TYPE_APTX,
SOURCE_CODEC_TYPE_APTX_HD,
SOURCE_CODEC_TYPE_LDAC,
SOURCE_CODEC_TYPE_MAX,
SOURCE_CODEC_TYPE_INVALID
})
@Retention(RetentionPolicy.SOURCE)
public @interface SourceCodecType {}
public static final int SOURCE_CODEC_TYPE_SBC = 0;
public static final int SOURCE_CODEC_TYPE_AAC = 1;
public static final int SOURCE_CODEC_TYPE_APTX = 2;
public static final int SOURCE_CODEC_TYPE_APTX_HD = 3;
public static final int SOURCE_CODEC_TYPE_LDAC = 4;
public static final int SOURCE_CODEC_TYPE_MAX = 5;
public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
/** @hide */
@IntDef(prefix = "CODEC_PRIORITY_", value = {
CODEC_PRIORITY_DISABLED,
CODEC_PRIORITY_DEFAULT,
CODEC_PRIORITY_HIGHEST
})
@Retention(RetentionPolicy.SOURCE)
public @interface CodecPriority {}
public static final int CODEC_PRIORITY_DISABLED = -1;
public static final int CODEC_PRIORITY_DEFAULT = 0;
public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
/** @hide */
@IntDef(prefix = "SAMPLE_RATE_", value = {
SAMPLE_RATE_NONE,
SAMPLE_RATE_44100,
SAMPLE_RATE_48000,
SAMPLE_RATE_88200,
SAMPLE_RATE_96000,
SAMPLE_RATE_176400,
SAMPLE_RATE_192000
})
@Retention(RetentionPolicy.SOURCE)
public @interface SampleRate {}
public static final int SAMPLE_RATE_NONE = 0;
public static final int SAMPLE_RATE_44100 = 0x1 << 0;
public static final int SAMPLE_RATE_48000 = 0x1 << 1;
public static final int SAMPLE_RATE_88200 = 0x1 << 2;
public static final int SAMPLE_RATE_96000 = 0x1 << 3;
public static final int SAMPLE_RATE_176400 = 0x1 << 4;
public static final int SAMPLE_RATE_192000 = 0x1 << 5;
/** @hide */
@IntDef(prefix = "BITS_PER_SAMPLE_", value = {
BITS_PER_SAMPLE_NONE,
BITS_PER_SAMPLE_16,
BITS_PER_SAMPLE_24,
BITS_PER_SAMPLE_32
})
@Retention(RetentionPolicy.SOURCE)
public @interface BitsPerSample {}
public static final int BITS_PER_SAMPLE_NONE = 0;
public static final int BITS_PER_SAMPLE_16 = 0x1 << 0;
public static final int BITS_PER_SAMPLE_24 = 0x1 << 1;
public static final int BITS_PER_SAMPLE_32 = 0x1 << 2;
/** @hide */
@IntDef(prefix = "CHANNEL_MODE_", value = {
CHANNEL_MODE_NONE,
CHANNEL_MODE_MONO,
CHANNEL_MODE_STEREO
})
@Retention(RetentionPolicy.SOURCE)
public @interface ChannelMode {}
public static final int CHANNEL_MODE_NONE = 0;
public static final int CHANNEL_MODE_MONO = 0x1 << 0;
public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
private final @SourceCodecType int mCodecType;
private @CodecPriority int mCodecPriority;
private final @SampleRate int mSampleRate;
private final @BitsPerSample int mBitsPerSample;
private final @ChannelMode int mChannelMode;
private final long mCodecSpecific1;
private final long mCodecSpecific2;
private final long mCodecSpecific3;
private final long mCodecSpecific4;
public BluetoothCodecConfig(@SourceCodecType int codecType, @CodecPriority int codecPriority,
@SampleRate int sampleRate, @BitsPerSample int bitsPerSample,
@ChannelMode int channelMode, long codecSpecific1,
long codecSpecific2, long codecSpecific3,
long codecSpecific4) {
mCodecType = codecType;
mCodecPriority = codecPriority;
mSampleRate = sampleRate;
mBitsPerSample = bitsPerSample;
mChannelMode = channelMode;
mCodecSpecific1 = codecSpecific1;
mCodecSpecific2 = codecSpecific2;
mCodecSpecific3 = codecSpecific3;
mCodecSpecific4 = codecSpecific4;
}
public BluetoothCodecConfig(@SourceCodecType int codecType) {
mCodecType = codecType;
mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE;
mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE;
mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE;
mCodecSpecific1 = 0;
mCodecSpecific2 = 0;
mCodecSpecific3 = 0;
mCodecSpecific4 = 0;
}
@Override
public boolean equals(Object o) {
if (o instanceof BluetoothCodecConfig) {
BluetoothCodecConfig other = (BluetoothCodecConfig) o;
return (other.mCodecType == mCodecType
&& other.mCodecPriority == mCodecPriority
&& other.mSampleRate == mSampleRate
&& other.mBitsPerSample == mBitsPerSample
&& other.mChannelMode == mChannelMode
&& other.mCodecSpecific1 == mCodecSpecific1
&& other.mCodecSpecific2 == mCodecSpecific2
&& other.mCodecSpecific3 == mCodecSpecific3
&& other.mCodecSpecific4 == mCodecSpecific4);
}
return false;
}
/**
* Returns a hash based on the config values
*
* @return a hash based on the config values
* @hide
*/
@Override
public int hashCode() {
return Objects.hash(mCodecType, mCodecPriority, mSampleRate,
mBitsPerSample, mChannelMode, mCodecSpecific1,
mCodecSpecific2, mCodecSpecific3, mCodecSpecific4);
}
/**
* Checks whether the object contains valid codec configuration.
*
* @return true if the object contains valid codec configuration, otherwise false.
* @hide
*/
public boolean isValid() {
return (mSampleRate != SAMPLE_RATE_NONE)
&& (mBitsPerSample != BITS_PER_SAMPLE_NONE)
&& (mChannelMode != CHANNEL_MODE_NONE);
}
/**
* Adds capability string to an existing string.
*
* @param prevStr the previous string with the capabilities. Can be a null pointer.
* @param capStr the capability string to append to prevStr argument.
* @return the result string in the form "prevStr|capStr".
*/
private static String appendCapabilityToString(String prevStr,
String capStr) {
if (prevStr == null) {
return capStr;
}
return prevStr + "|" + capStr;
}
@Override
public String toString() {
String sampleRateStr = null;
if (mSampleRate == SAMPLE_RATE_NONE) {
sampleRateStr = appendCapabilityToString(sampleRateStr, "NONE");
}
if ((mSampleRate & SAMPLE_RATE_44100) != 0) {
sampleRateStr = appendCapabilityToString(sampleRateStr, "44100");
}
if ((mSampleRate & SAMPLE_RATE_48000) != 0) {
sampleRateStr = appendCapabilityToString(sampleRateStr, "48000");
}
if ((mSampleRate & SAMPLE_RATE_88200) != 0) {
sampleRateStr = appendCapabilityToString(sampleRateStr, "88200");
}
if ((mSampleRate & SAMPLE_RATE_96000) != 0) {
sampleRateStr = appendCapabilityToString(sampleRateStr, "96000");
}
if ((mSampleRate & SAMPLE_RATE_176400) != 0) {
sampleRateStr = appendCapabilityToString(sampleRateStr, "176400");
}
if ((mSampleRate & SAMPLE_RATE_192000) != 0) {
sampleRateStr = appendCapabilityToString(sampleRateStr, "192000");
}
String bitsPerSampleStr = null;
if (mBitsPerSample == BITS_PER_SAMPLE_NONE) {
bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "NONE");
}
if ((mBitsPerSample & BITS_PER_SAMPLE_16) != 0) {
bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "16");
}
if ((mBitsPerSample & BITS_PER_SAMPLE_24) != 0) {
bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "24");
}
if ((mBitsPerSample & BITS_PER_SAMPLE_32) != 0) {
bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "32");
}
String channelModeStr = null;
if (mChannelMode == CHANNEL_MODE_NONE) {
channelModeStr = appendCapabilityToString(channelModeStr, "NONE");
}
if ((mChannelMode & CHANNEL_MODE_MONO) != 0) {
channelModeStr = appendCapabilityToString(channelModeStr, "MONO");
}
if ((mChannelMode & CHANNEL_MODE_STEREO) != 0) {
channelModeStr = appendCapabilityToString(channelModeStr, "STEREO");
}
return "{codecName:" + getCodecName()
+ ",mCodecType:" + mCodecType
+ ",mCodecPriority:" + mCodecPriority
+ ",mSampleRate:" + String.format("0x%x", mSampleRate)
+ "(" + sampleRateStr + ")"
+ ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample)
+ "(" + bitsPerSampleStr + ")"
+ ",mChannelMode:" + String.format("0x%x", mChannelMode)
+ "(" + channelModeStr + ")"
+ ",mCodecSpecific1:" + mCodecSpecific1
+ ",mCodecSpecific2:" + mCodecSpecific2
+ ",mCodecSpecific3:" + mCodecSpecific3
+ ",mCodecSpecific4:" + mCodecSpecific4 + "}";
}
/**
* Always returns 0
*
* @return 0
* @hide
*/
@Override
public int describeContents() {
return 0;
}
public static final @android.annotation.NonNull Parcelable.Creator<BluetoothCodecConfig> CREATOR =
new Parcelable.Creator<BluetoothCodecConfig>() {
public BluetoothCodecConfig createFromParcel(Parcel in) {
final int codecType = in.readInt();
final int codecPriority = in.readInt();
final int sampleRate = in.readInt();
final int bitsPerSample = in.readInt();
final int channelMode = in.readInt();
final long codecSpecific1 = in.readLong();
final long codecSpecific2 = in.readLong();
final long codecSpecific3 = in.readLong();
final long codecSpecific4 = in.readLong();
return new BluetoothCodecConfig(codecType, codecPriority,
sampleRate, bitsPerSample,
channelMode, codecSpecific1,
codecSpecific2, codecSpecific3,
codecSpecific4);
}
public BluetoothCodecConfig[] newArray(int size) {
return new BluetoothCodecConfig[size];
}
};
/**
* Flattens the object to a parcel
*
* @param out The Parcel in which the object should be written.
* @param flags Additional flags about how the object should be written.
*
* @hide
*/
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mCodecType);
out.writeInt(mCodecPriority);
out.writeInt(mSampleRate);
out.writeInt(mBitsPerSample);
out.writeInt(mChannelMode);
out.writeLong(mCodecSpecific1);
out.writeLong(mCodecSpecific2);
out.writeLong(mCodecSpecific3);
out.writeLong(mCodecSpecific4);
}
/**
* Gets the codec name.
*
* @return the codec name
*/
public @NonNull String getCodecName() {
switch (mCodecType) {
case SOURCE_CODEC_TYPE_SBC:
return "SBC";
case SOURCE_CODEC_TYPE_AAC:
return "AAC";
case SOURCE_CODEC_TYPE_APTX:
return "aptX";
case SOURCE_CODEC_TYPE_APTX_HD:
return "aptX HD";
case SOURCE_CODEC_TYPE_LDAC:
return "LDAC";
case SOURCE_CODEC_TYPE_INVALID:
return "INVALID CODEC";
default:
break;
}
return "UNKNOWN CODEC(" + mCodecType + ")";
}
/**
* Gets the codec type.
* See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}.
*
* @return the codec type
*/
public @SourceCodecType int getCodecType() {
return mCodecType;
}
/**
* Checks whether the codec is mandatory.
*
* @return true if the codec is mandatory, otherwise false.
*/
public boolean isMandatoryCodec() {
return mCodecType == SOURCE_CODEC_TYPE_SBC;
}
/**
* Gets the codec selection priority.
* The codec selection priority is relative to other codecs: larger value
* means higher priority. If 0, reset to default.
*
* @return the codec priority
*/
public @CodecPriority int getCodecPriority() {
return mCodecPriority;
}
/**
* Sets the codec selection priority.
* The codec selection priority is relative to other codecs: larger value
* means higher priority. If 0, reset to default.
*
* @param codecPriority the codec priority
* @hide
*/
@UnsupportedAppUsage
public void setCodecPriority(@CodecPriority int codecPriority) {
mCodecPriority = codecPriority;
}
/**
* Gets the codec sample rate. The value can be a bitmask with all
* supported sample rates:
* {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or
* {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or
* {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_48000} or
* {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_88200} or
* {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_96000} or
* {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_176400} or
* {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_192000}
*
* @return the codec sample rate
*/
public @SampleRate int getSampleRate() {
return mSampleRate;
}
/**
* Gets the codec bits per sample. The value can be a bitmask with all
* bits per sample supported:
* {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or
* {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or
* {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_24} or
* {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_32}
*
* @return the codec bits per sample
*/
public @BitsPerSample int getBitsPerSample() {
return mBitsPerSample;
}
/**
* Gets the codec channel mode. The value can be a bitmask with all
* supported channel modes:
* {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or
* {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or
* {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_STEREO}
*
* @return the codec channel mode
* @hide
*/
@UnsupportedAppUsage
public @ChannelMode int getChannelMode() {
return mChannelMode;
}
/**
* Gets a codec specific value1.
*
* @return a codec specific value1.
*/
public long getCodecSpecific1() {
return mCodecSpecific1;
}
/**
* Gets a codec specific value2.
*
* @return a codec specific value2
* @hide
*/
@UnsupportedAppUsage
public long getCodecSpecific2() {
return mCodecSpecific2;
}
/**
* Gets a codec specific value3.
*
* @return a codec specific value3
* @hide
*/
@UnsupportedAppUsage
public long getCodecSpecific3() {
return mCodecSpecific3;
}
/**
* Gets a codec specific value4.
*
* @return a codec specific value4
* @hide
*/
@UnsupportedAppUsage
public long getCodecSpecific4() {
return mCodecSpecific4;
}
/**
* Checks whether a value set presented by a bitmask has zero or single bit
*
* @param valueSet the value set presented by a bitmask
* @return true if the valueSet contains zero or single bit, otherwise false.
* @hide
*/
private static boolean hasSingleBit(int valueSet) {
return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0);
}
/**
* Checks whether the object contains none or single sample rate.
*
* @return true if the object contains none or single sample rate, otherwise false.
* @hide
*/
public boolean hasSingleSampleRate() {
return hasSingleBit(mSampleRate);
}
/**
* Checks whether the object contains none or single bits per sample.
*
* @return true if the object contains none or single bits per sample, otherwise false.
* @hide
*/
public boolean hasSingleBitsPerSample() {
return hasSingleBit(mBitsPerSample);
}
/**
* Checks whether the object contains none or single channel mode.
*
* @return true if the object contains none or single channel mode, otherwise false.
* @hide
*/
public boolean hasSingleChannelMode() {
return hasSingleBit(mChannelMode);
}
/**
* Checks whether the audio feeding parameters are same.
*
* @param other the codec config to compare against
* @return true if the audio feeding parameters are same, otherwise false
* @hide
*/
public boolean sameAudioFeedingParameters(BluetoothCodecConfig other) {
return (other != null && other.mSampleRate == mSampleRate
&& other.mBitsPerSample == mBitsPerSample
&& other.mChannelMode == mChannelMode);
}
/**
* Checks whether another codec config has the similar feeding parameters.
* Any parameters with NONE value will be considered to be a wildcard matching.
*
* @param other the codec config to compare against
* @return true if the audio feeding parameters are similar, otherwise false.
* @hide
*/
public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) {
if (other == null || mCodecType != other.mCodecType) {
return false;
}
int sampleRate = other.mSampleRate;
if (mSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE
|| sampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) {
sampleRate = mSampleRate;
}
int bitsPerSample = other.mBitsPerSample;
if (mBitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE
|| bitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) {
bitsPerSample = mBitsPerSample;
}
int channelMode = other.mChannelMode;
if (mChannelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE
|| channelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE) {
channelMode = mChannelMode;
}
return sameAudioFeedingParameters(new BluetoothCodecConfig(
mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode,
/* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0,
/* specific4 */ 0));
}
/**
* Checks whether the codec specific parameters are the same.
*
* @param other the codec config to compare against
* @return true if the codec specific parameters are the same, otherwise false.
* @hide
*/
public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) {
if (other == null && mCodecType != other.mCodecType) {
return false;
}
// Currently we only care about the LDAC Playback Quality at CodecSpecific1
switch (mCodecType) {
case SOURCE_CODEC_TYPE_LDAC:
if (mCodecSpecific1 != other.mCodecSpecific1) {
return false;
}
// fall through
default:
return true;
}
}
}