/*
 * 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.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;

import com.android.bluetooth.flags.Flags;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;

/**
 * Represents the codec configuration for a Bluetooth A2DP source device.
 *
 * <p>Contains the source codec type, the codec priority, the codec sample rate, the codec bits per
 * sample, and the codec channel mode.
 *
 * <p>The source codec type values are the same as those supported by the device hardware.
 *
 * @see BluetoothA2dp
 */
public final class BluetoothCodecConfig implements Parcelable {
    /** @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_LC3,
                SOURCE_CODEC_TYPE_OPUS,
                SOURCE_CODEC_TYPE_INVALID
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface SourceCodecType {}

    /**
     * Source codec type SBC. This is the mandatory source codec type.
     *
     * @deprecated Use the {@link BluetoothCodecType} values returned by {@link
     *     BluetoothA2dp#getSupportedCodecTypes} instead.
     */
    @Deprecated public static final int SOURCE_CODEC_TYPE_SBC = 0;

    /**
     * Source codec type AAC.
     *
     * @deprecated Use the {@link BluetoothCodecType} values returned by {@link
     *     BluetoothA2dp#getSupportedCodecTypes} instead.
     */
    @Deprecated public static final int SOURCE_CODEC_TYPE_AAC = 1;

    /**
     * Source codec type APTX.
     *
     * @deprecated Use the {@link BluetoothCodecType} values returned by {@link
     *     BluetoothA2dp#getSupportedCodecTypes} instead.
     */
    @Deprecated public static final int SOURCE_CODEC_TYPE_APTX = 2;

    /**
     * Source codec type APTX HD.
     *
     * @deprecated Use the {@link BluetoothCodecType} values returned by {@link
     *     BluetoothA2dp#getSupportedCodecTypes} instead.
     */
    @Deprecated public static final int SOURCE_CODEC_TYPE_APTX_HD = 3;

    /**
     * Source codec type LDAC.
     *
     * @deprecated Use the {@link BluetoothCodecType} values returned by {@link
     *     BluetoothA2dp#getSupportedCodecTypes} instead.
     */
    @Deprecated public static final int SOURCE_CODEC_TYPE_LDAC = 4;

    /**
     * Source codec type LC3.
     *
     * @deprecated Use the {@link BluetoothCodecType} values returned by {@link
     *     BluetoothA2dp#getSupportedCodecTypes} instead.
     */
    @Deprecated public static final int SOURCE_CODEC_TYPE_LC3 = 5;

    /**
     * Source codec type Opus.
     *
     * @deprecated Use the {@link BluetoothCodecType} values returned by {@link
     *     BluetoothA2dp#getSupportedCodecTypes} instead.
     */
    @Deprecated public static final int SOURCE_CODEC_TYPE_OPUS = 6;

    /**
     * Source codec type invalid. This is the default value used for codec type.
     *
     * @deprecated Use the {@link BluetoothCodecType} values returned by {@link
     *     BluetoothA2dp#getSupportedCodecTypes} instead.
     */
    @Deprecated public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;

    /** Represents the count of valid source codec types. */
    static final int SOURCE_CODEC_TYPE_MAX = 7;

    /** @hide */
    @IntDef(
            prefix = "CODEC_PRIORITY_",
            value = {CODEC_PRIORITY_DISABLED, CODEC_PRIORITY_DEFAULT, CODEC_PRIORITY_HIGHEST})
    @Retention(RetentionPolicy.SOURCE)
    public @interface CodecPriority {}

    /**
     * Codec priority disabled. Used to indicate that this codec is disabled and should not be used.
     */
    public static final int CODEC_PRIORITY_DISABLED = -1;

    /** Codec priority default. Default value used for codec priority. */
    public static final int CODEC_PRIORITY_DEFAULT = 0;

    /** Codec priority highest. Used to indicate the highest priority a codec can have. */
    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 {}

    /** Codec sample rate 0 Hz. Default value used for codec sample rate. */
    public static final int SAMPLE_RATE_NONE = 0;

    /** Codec sample rate 44100 Hz. */
    public static final int SAMPLE_RATE_44100 = 0x1 << 0;

    /** Codec sample rate 48000 Hz. */
    public static final int SAMPLE_RATE_48000 = 0x1 << 1;

    /** Codec sample rate 88200 Hz. */
    public static final int SAMPLE_RATE_88200 = 0x1 << 2;

    /** Codec sample rate 96000 Hz. */
    public static final int SAMPLE_RATE_96000 = 0x1 << 3;

    /** Codec sample rate 176400 Hz. */
    public static final int SAMPLE_RATE_176400 = 0x1 << 4;

    /** Codec sample rate 192000 Hz. */
    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 {}

    /** Codec bits per sample 0. Default value of the codec bits per sample. */
    public static final int BITS_PER_SAMPLE_NONE = 0;

    /** Codec bits per sample 16. */
    public static final int BITS_PER_SAMPLE_16 = 0x1 << 0;

    /** Codec bits per sample 24. */
    public static final int BITS_PER_SAMPLE_24 = 0x1 << 1;

    /** Codec bits per sample 32. */
    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 {}

    /** Codec channel mode NONE. Default value of the codec channel mode. */
    public static final int CHANNEL_MODE_NONE = 0;

    /** Codec channel mode MONO. */
    public static final int CHANNEL_MODE_MONO = 0x1 << 0;

    /** Codec channel mode STEREO. */
    public static final int CHANNEL_MODE_STEREO = 0x1 << 1;

    private final @Nullable BluetoothCodecType 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;

    /**
     * Creates a new BluetoothCodecConfig.
     *
     * @param codecType the source codec type
     * @param codecPriority the priority of this codec
     * @param sampleRate the codec sample rate
     * @param bitsPerSample the bits per sample of this codec
     * @param channelMode the channel mode of this codec
     * @param codecSpecific1 the specific value 1
     * @param codecSpecific2 the specific value 2
     * @param codecSpecific3 the specific value 3
     * @param codecSpecific4 the specific value 4 values to 0.
     * @hide
     */
    @UnsupportedAppUsage
    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) {
        this(
                BluetoothCodecType.createFromType(codecType),
                codecPriority,
                sampleRate,
                bitsPerSample,
                channelMode,
                codecSpecific1,
                codecSpecific2,
                codecSpecific3,
                codecSpecific4);
    }

    /**
     * Creates a new BluetoothCodecConfig.
     *
     * @param codecType the source codec type
     * @param codecPriority the priority of this codec
     * @param sampleRate the codec sample rate
     * @param bitsPerSample the bits per sample of this codec
     * @param channelMode the channel mode of this codec
     * @param codecSpecific1 the specific value 1
     * @param codecSpecific2 the specific value 2
     * @param codecSpecific3 the specific value 3
     * @param codecSpecific4 the specific value 4 values to 0.
     * @hide
     */
    public BluetoothCodecConfig(
            @Nullable BluetoothCodecType 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;
    }

    /**
     * Creates a new BluetoothCodecConfig.
     *
     * <p>By default, the codec priority will be set to {@link
     * BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to {@link
     * BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to {@link
     * BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to {@link
     * BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific values to 0.
     *
     * @param codecType the source codec type
     * @hide
     */
    public BluetoothCodecConfig(@SourceCodecType int codecType) {
        this(
                BluetoothCodecType.createFromType(codecType),
                BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT,
                BluetoothCodecConfig.SAMPLE_RATE_NONE,
                BluetoothCodecConfig.BITS_PER_SAMPLE_NONE,
                BluetoothCodecConfig.CHANNEL_MODE_NONE,
                0,
                0,
                0,
                0);
    }

    private BluetoothCodecConfig(Parcel in) {
        mCodecType = BluetoothCodecType.createFromType(in.readInt());
        mCodecPriority = in.readInt();
        mSampleRate = in.readInt();
        mBitsPerSample = in.readInt();
        mChannelMode = in.readInt();
        mCodecSpecific1 = in.readLong();
        mCodecSpecific2 = in.readLong();
        mCodecSpecific3 = in.readLong();
        mCodecSpecific4 = in.readLong();
    }

    @Override
    public boolean equals(@Nullable Object o) {
        if (o instanceof BluetoothCodecConfig) {
            BluetoothCodecConfig other = (BluetoothCodecConfig) o;
            return (Objects.equals(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 representation of this BluetoothCodecConfig based on all the config values.
     */
    @Override
    public int hashCode() {
        return Objects.hash(
                mCodecType,
                mCodecPriority,
                mSampleRate,
                mBitsPerSample,
                mChannelMode,
                mCodecSpecific1,
                mCodecSpecific2,
                mCodecSpecific3,
                mCodecSpecific4);
    }

    /**
     * Adds capability string to an existing string.
     *
     * @param prevStr the previous string with the capabilities. Can be a {@code 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(
            @Nullable String prevStr, @NonNull String capStr) {
        if (prevStr == null) {
            return capStr;
        }
        return prevStr + "|" + capStr;
    }

    /**
     * Returns a {@link String} that describes each BluetoothCodecConfig parameter current value.
     */
    @Override
    public String toString() {
        String codecName = null;
        int codecType = SOURCE_CODEC_TYPE_INVALID;
        if (mCodecType != null) {
            codecName = mCodecType.getCodecName();
            codecType = mCodecType.getNativeCodecType();
        }

        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:" + codecName)
                + (",mCodecType:" + codecType)
                + (",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)
                + "}";
    }

    /**
     * @return 0
     * @hide
     */
    @Override
    public int describeContents() {
        return 0;
    }

    public static final @NonNull Creator<BluetoothCodecConfig> CREATOR =
            new Creator<>() {
                public BluetoothCodecConfig createFromParcel(Parcel in) {
                    return new BluetoothCodecConfig(in);
                }

                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(getCodecType());
        out.writeInt(mCodecPriority);
        out.writeInt(mSampleRate);
        out.writeInt(mBitsPerSample);
        out.writeInt(mChannelMode);
        out.writeLong(mCodecSpecific1);
        out.writeLong(mCodecSpecific2);
        out.writeLong(mCodecSpecific3);
        out.writeLong(mCodecSpecific4);
    }

    /**
     * Returns the codec name converted to {@link String}.
     *
     * @hide
     */
    public static @NonNull String getCodecName(@SourceCodecType int codecType) {
        switch (codecType) {
            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_LC3:
                return "LC3";
            case SOURCE_CODEC_TYPE_OPUS:
                return "Opus";
            case SOURCE_CODEC_TYPE_INVALID:
                return "INVALID CODEC";
            default:
                break;
        }
        return "UNKNOWN CODEC(" + codecType + ")";
    }

    /**
     * Returns the source codec type of this config.
     *
     * @deprecated use {@link BluetoothCodecConfig#getExtendedCodecType} instead.
     */
    @Deprecated
    @SuppressLint("WrongConstant")
    public @SourceCodecType int getCodecType() {
        return mCodecType == null ? SOURCE_CODEC_TYPE_INVALID : mCodecType.getNativeCodecType();
    }

    /** Returns the source codec type of this config. */
    @FlaggedApi(Flags.FLAG_A2DP_OFFLOAD_CODEC_EXTENSIBILITY)
    public @Nullable BluetoothCodecType getExtendedCodecType() {
        return mCodecType;
    }

    /**
     * Checks whether the codec is mandatory.
     *
     * <p>The actual mandatory codec type for Android Bluetooth audio is SBC. See {@link
     * #SOURCE_CODEC_TYPE_SBC}.
     *
     * @return {@code true} if the codec is mandatory, {@code false} otherwise
     */
    public boolean isMandatoryCodec() {
        return mCodecType == null ? false : mCodecType.isMandatoryCodec();
    }

    /**
     * Returns the codec selection priority.
     *
     * <p>The codec selection priority is relative to other codecs: larger value means higher
     * priority.
     */
    public @CodecPriority int getCodecPriority() {
        return mCodecPriority;
    }

    /**
     * Sets the codec selection priority.
     *
     * <p>The codec selection priority is relative to other codecs: larger value means higher
     * priority.
     *
     * @param codecPriority the priority this codec should have
     * @hide
     */
    public void setCodecPriority(@CodecPriority int codecPriority) {
        mCodecPriority = codecPriority;
    }

    /**
     * Returns the codec sample rate. The value can be a bitmask with all supported sample rates.
     */
    public @SampleRate int getSampleRate() {
        return mSampleRate;
    }

    /**
     * Returns the codec bits per sample. The value can be a bitmask with all bits per sample
     * supported.
     */
    public @BitsPerSample int getBitsPerSample() {
        return mBitsPerSample;
    }

    /**
     * Returns the codec channel mode. The value can be a bitmask with all supported channel modes.
     */
    public @ChannelMode int getChannelMode() {
        return mChannelMode;
    }

    /**
     * Returns the codec specific value1. As the value and usage differ for each codec, please refer
     * to the concerned codec specification to obtain the codec specific information.
     *
     * <p>See section 4.3.2 of the Bluetooth A2dp specification for SBC codec specific information
     * elements.
     *
     * <p>See section 4.4.2 of the Bluetooth A2dp specification for MPEG-1,2 Audio codec specific
     * information elements.
     *
     * <p>See section 4.5.2 of the Bluetooth A2dp specification for MPEG-2, 4 AAC codec specific
     * information elements.
     *
     * <p>See section 4.6.2 of the Bluetooth A2dp specification for ATRAC family codec specific
     * information elements.
     *
     * <p>See section 4.7.2 of the Bluetooth A2dp specification for Vendor Specific A2DP codec
     * specific information elements.
     */
    public long getCodecSpecific1() {
        return mCodecSpecific1;
    }

    /**
     * Returns the codec specific value2. As the value and usage differ for each codec, please refer
     * to the concerned codec specification to obtain the codec specific information.
     *
     * <p>See section 4.3.2 of the Bluetooth A2dp specification for SBC codec specific information
     * elements.
     *
     * <p>See section 4.4.2 of the Bluetooth A2dp specification for MPEG-1,2 Audio codec specific
     * information elements.
     *
     * <p>See section 4.5.2 of the Bluetooth A2dp specification for MPEG-2, 4 AAC codec specific
     * information elements.
     *
     * <p>See section 4.6.2 of the Bluetooth A2dp specification for ATRAC family codec specific
     * information elements.
     *
     * <p>See section 4.7.2 of the Bluetooth A2dp specification for Vendor Specific A2DP codec
     * specific information elements.
     */
    public long getCodecSpecific2() {
        return mCodecSpecific2;
    }

    /**
     * Returns the codec specific value3. As the value and usage differ for each codec, please refer
     * to the concerned codec specification to obtain the codec specific information.
     *
     * <p>See section 4.3.2 of the Bluetooth A2dp specification for SBC codec specific information
     * elements.
     *
     * <p>See section 4.4.2 of the Bluetooth A2dp specification for MPEG-1,2 Audio codec specific
     * information elements.
     *
     * <p>See section 4.5.2 of the Bluetooth A2dp specification for MPEG-2, 4 AAC codec specific
     * information elements.
     *
     * <p>See section 4.6.2 of the Bluetooth A2dp specification for ATRAC family codec specific
     * information elements.
     *
     * <p>See section 4.7.2 of the Bluetooth A2dp specification for Vendor Specific A2DP codec
     * specific information elements.
     */
    public long getCodecSpecific3() {
        return mCodecSpecific3;
    }

    /**
     * Returns the codec specific value4. As the value and usage differ for each codec, please refer
     * to the concerned codec specification to obtain the codec specific information.
     *
     * <p>See section 4.3.2 of the Bluetooth A2dp specification for SBC codec specific information
     * elements.
     *
     * <p>See section 4.4.2 of the Bluetooth A2dp specification for MPEG-1,2 Audio codec specific
     * information elements.
     *
     * <p>See section 4.5.2 of the Bluetooth A2dp specification for MPEG-2, 4 AAC codec specific
     * information elements.
     *
     * <p>See section 4.6.2 of the Bluetooth A2dp specification for ATRAC family codec specific
     * information elements.
     *
     * <p>See section 4.7.2 of the Bluetooth A2dp specification for Vendor Specific A2DP codec
     * specific information elements.
     */
    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 {@code true} if the valueSet contains zero or single bit, {@code false} otherwise
     * @hide
     */
    private static boolean hasSingleBit(int valueSet) {
        return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0);
    }

    /**
     * Returns whether the object contains none or single sample rate.
     *
     * @hide
     */
    public boolean hasSingleSampleRate() {
        return hasSingleBit(mSampleRate);
    }

    /**
     * Returns whether the object contains none or single bits per sample.
     *
     * @hide
     */
    public boolean hasSingleBitsPerSample() {
        return hasSingleBit(mBitsPerSample);
    }

    /**
     * Returns whether the object contains none or single channel mode.
     *
     * @hide
     */
    public boolean hasSingleChannelMode() {
        return hasSingleBit(mChannelMode);
    }

    /**
     * Checks whether the audio feeding parameters are the same.
     *
     * @param other the codec config to compare against
     * @return {@code true} if the audio feeding parameters are same, {@code false} otherwise
     * @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 {@code true} if the audio feeding parameters are similar, {@code false} otherwise
     * @hide
     */
    public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) {
        if (other == null || !Objects.equals(mCodecType, other.mCodecType)) {
            return false;
        }
        int sampleRate = other.mSampleRate;
        if (mSampleRate == SAMPLE_RATE_NONE || sampleRate == SAMPLE_RATE_NONE) {
            sampleRate = mSampleRate;
        }
        int bitsPerSample = other.mBitsPerSample;
        if (mBitsPerSample == BITS_PER_SAMPLE_NONE || bitsPerSample == BITS_PER_SAMPLE_NONE) {
            bitsPerSample = mBitsPerSample;
        }
        int channelMode = other.mChannelMode;
        if (mChannelMode == CHANNEL_MODE_NONE || channelMode == 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.
     *
     * <p>Currently, only AAC VBR and LDAC Playback Quality on CodecSpecific1 are compared.
     *
     * @param other the codec config to compare against
     * @return {@code true} if the codec specific parameters are the same, {@code false} otherwise
     * @hide
     */
    public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) {
        if (other == null && !Objects.equals(mCodecType, other.mCodecType)) {
            return false;
        }
        switch (getCodecType()) {
            case SOURCE_CODEC_TYPE_AAC:
            case SOURCE_CODEC_TYPE_LDAC:
            case SOURCE_CODEC_TYPE_LC3:
            case SOURCE_CODEC_TYPE_OPUS:
                if (mCodecSpecific1 != other.mCodecSpecific1) {
                    return false;
                }
                // fall through
            default:
                return true;
        }
    }

    /**
     * Builder for {@link BluetoothCodecConfig}.
     *
     * <p>By default, the codec type will be set to {@link
     * BluetoothCodecConfig#SOURCE_CODEC_TYPE_INVALID}, the codec priority to {@link
     * BluetoothCodecConfig#CODEC_PRIORITY_DEFAULT}, the sample rate to {@link
     * BluetoothCodecConfig#SAMPLE_RATE_NONE}, the bits per sample to {@link
     * BluetoothCodecConfig#BITS_PER_SAMPLE_NONE}, the channel mode to {@link
     * BluetoothCodecConfig#CHANNEL_MODE_NONE}, and all the codec specific values to 0.
     */
    public static final class Builder {
        private @Nullable BluetoothCodecType mCodecType = null;
        private int mCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
        private int mSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE;
        private int mBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE;
        private int mChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE;
        private long mCodecSpecific1 = 0;
        private long mCodecSpecific2 = 0;
        private long mCodecSpecific3 = 0;
        private long mCodecSpecific4 = 0;

        /**
         * Set codec type for Bluetooth codec config.
         *
         * @param codecType of this codec
         * @return the same Builder instance
         * @deprecated use {@link BluetoothCodecType} instead
         */
        @Deprecated
        public @NonNull Builder setCodecType(@SourceCodecType int codecType) {
            mCodecType = BluetoothCodecType.createFromType(codecType);
            return this;
        }

        /**
         * Set codec type for Bluetooth codec config.
         *
         * @param codecType of this codec
         * @return the same Builder instance
         */
        @FlaggedApi(Flags.FLAG_A2DP_OFFLOAD_CODEC_EXTENSIBILITY)
        public @NonNull Builder setExtendedCodecType(@Nullable BluetoothCodecType codecType) {
            mCodecType = codecType;
            return this;
        }

        /**
         * Set codec priority for Bluetooth codec config.
         *
         * @param codecPriority of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setCodecPriority(@CodecPriority int codecPriority) {
            mCodecPriority = codecPriority;
            return this;
        }

        /**
         * Set sample rate for Bluetooth codec config.
         *
         * @param sampleRate of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setSampleRate(@SampleRate int sampleRate) {
            mSampleRate = sampleRate;
            return this;
        }

        /**
         * Set the bits per sample for Bluetooth codec config.
         *
         * @param bitsPerSample of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setBitsPerSample(@BitsPerSample int bitsPerSample) {
            mBitsPerSample = bitsPerSample;
            return this;
        }

        /**
         * Set the channel mode for Bluetooth codec config.
         *
         * @param channelMode of this codec
         * @return the same Builder instance
         */
        public @NonNull Builder setChannelMode(@ChannelMode int channelMode) {
            mChannelMode = channelMode;
            return this;
        }

        /**
         * Set the first codec specific values for Bluetooth codec config.
         *
         * @param codecSpecific1 codec specific value or 0 if default
         * @return the same Builder instance
         */
        public @NonNull Builder setCodecSpecific1(long codecSpecific1) {
            mCodecSpecific1 = codecSpecific1;
            return this;
        }

        /**
         * Set the second codec specific values for Bluetooth codec config.
         *
         * @param codecSpecific2 codec specific value or 0 if default
         * @return the same Builder instance
         */
        public @NonNull Builder setCodecSpecific2(long codecSpecific2) {
            mCodecSpecific2 = codecSpecific2;
            return this;
        }

        /**
         * Set the third codec specific values for Bluetooth codec config.
         *
         * @param codecSpecific3 codec specific value or 0 if default
         * @return the same Builder instance
         */
        public @NonNull Builder setCodecSpecific3(long codecSpecific3) {
            mCodecSpecific3 = codecSpecific3;
            return this;
        }

        /**
         * Set the fourth codec specific values for Bluetooth codec config.
         *
         * @param codecSpecific4 codec specific value or 0 if default
         * @return the same Builder instance
         */
        public @NonNull Builder setCodecSpecific4(long codecSpecific4) {
            mCodecSpecific4 = codecSpecific4;
            return this;
        }

        /**
         * Build {@link BluetoothCodecConfig}.
         *
         * @return new BluetoothCodecConfig built
         */
        public @NonNull BluetoothCodecConfig build() {
            return new BluetoothCodecConfig(
                    mCodecType,
                    mCodecPriority,
                    mSampleRate,
                    mBitsPerSample,
                    mChannelMode,
                    mCodecSpecific1,
                    mCodecSpecific2,
                    mCodecSpecific3,
                    mCodecSpecific4);
        }
    }
}
