/* * Copyright (C) 2022 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.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import java.util.Collections; import java.util.List; import java.util.Objects; /** * Represents the codec status (configuration and capability) for a Bluetooth Le Audio source * device. * * @see BluetoothLeAudio */ public final class BluetoothLeAudioCodecStatus implements Parcelable { /** * Extra for the codec configuration intents of the individual profiles. * *

This extra represents the current codec status of the Le Audio profile. */ public static final String EXTRA_LE_AUDIO_CODEC_STATUS = "android.bluetooth.extra.LE_AUDIO_CODEC_STATUS"; private final @Nullable BluetoothLeAudioCodecConfig mInputCodecConfig; private final @Nullable BluetoothLeAudioCodecConfig mOutputCodecConfig; private final @Nullable List mInputCodecsLocalCapabilities; private final @Nullable List mOutputCodecsLocalCapabilities; private final @Nullable List mInputCodecsSelectableCapabilities; private final @Nullable List mOutputCodecsSelectableCapabilities; /** * Represents the codec status for a Bluetooth LE Audio source device. * * @param inputCodecConfig the current input codec configuration. * @param outputCodecConfig the current output codec configuration. * @param inputCodecsLocalCapabilities the local input codecs capabilities. * @param outputCodecsLocalCapabilities the local output codecs capabilities. * @param inputCodecsSelectableCapabilities the selectable input codecs capabilities. * @param outputCodecsSelectableCapabilities the selectable output codecs capabilities. */ public BluetoothLeAudioCodecStatus( @Nullable BluetoothLeAudioCodecConfig inputCodecConfig, @Nullable BluetoothLeAudioCodecConfig outputCodecConfig, @NonNull List inputCodecsLocalCapabilities, @NonNull List outputCodecsLocalCapabilities, @NonNull List inputCodecsSelectableCapabilities, @NonNull List outputCodecsSelectableCapabilities) { mInputCodecConfig = inputCodecConfig; mOutputCodecConfig = outputCodecConfig; mInputCodecsLocalCapabilities = inputCodecsLocalCapabilities; mOutputCodecsLocalCapabilities = outputCodecsLocalCapabilities; mInputCodecsSelectableCapabilities = inputCodecsSelectableCapabilities; mOutputCodecsSelectableCapabilities = outputCodecsSelectableCapabilities; } private BluetoothLeAudioCodecStatus(Parcel in) { mInputCodecConfig = in.readTypedObject(BluetoothLeAudioCodecConfig.CREATOR); mOutputCodecConfig = in.readTypedObject(BluetoothLeAudioCodecConfig.CREATOR); mInputCodecsLocalCapabilities = in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR); mOutputCodecsLocalCapabilities = in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR); mInputCodecsSelectableCapabilities = in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR); mOutputCodecsSelectableCapabilities = in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR); } @Override public boolean equals(@Nullable Object o) { if (o instanceof BluetoothLeAudioCodecStatus) { BluetoothLeAudioCodecStatus other = (BluetoothLeAudioCodecStatus) o; return (Objects.equals(other.mInputCodecConfig, mInputCodecConfig) && Objects.equals(other.mOutputCodecConfig, mOutputCodecConfig) && sameCapabilities( other.mInputCodecsLocalCapabilities, mInputCodecsLocalCapabilities) && sameCapabilities( other.mOutputCodecsLocalCapabilities, mOutputCodecsLocalCapabilities) && sameCapabilities( other.mInputCodecsSelectableCapabilities, mInputCodecsSelectableCapabilities) && sameCapabilities( other.mOutputCodecsSelectableCapabilities, mOutputCodecsSelectableCapabilities)); } return false; } /** * Checks whether two lists of capabilities contain same capabilities. The order of the * capabilities in each list is ignored. * * @param c1 the first list of capabilities to compare * @param c2 the second list of capabilities to compare * @return {@code true} if both lists contain same capabilities */ private static boolean sameCapabilities( @Nullable List c1, @Nullable List c2) { if (c1 == null) { return (c2 == null); } if (c2 == null) { return false; } if (c1.size() != c2.size()) { return false; } return c1.containsAll(c2); } private boolean isCodecConfigSelectable( BluetoothLeAudioCodecConfig codecConfig, BluetoothLeAudioCodecConfig selectableConfig) { if (codecConfig.getCodecType() != selectableConfig.getCodecType()) { return false; } if ((codecConfig.getFrameDuration() != BluetoothLeAudioCodecConfig.FRAME_DURATION_NONE) && ((codecConfig.getFrameDuration() & selectableConfig.getFrameDuration()) == 0)) { return false; } if ((codecConfig.getChannelCount() != BluetoothLeAudioCodecConfig.CHANNEL_COUNT_NONE) && ((codecConfig.getChannelCount() & selectableConfig.getChannelCount()) == 0)) { return false; } if ((codecConfig.getSampleRate() != BluetoothLeAudioCodecConfig.SAMPLE_RATE_NONE) && ((codecConfig.getSampleRate() & selectableConfig.getSampleRate()) == 0)) { return false; } if ((codecConfig.getBitsPerSample() != BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_NONE) && ((codecConfig.getBitsPerSample() & selectableConfig.getBitsPerSample()) == 0)) { return false; } if ((codecConfig.getOctetsPerFrame() != 0) && ((codecConfig.getOctetsPerFrame() < selectableConfig.getMinOctetsPerFrame()) || (codecConfig.getOctetsPerFrame() > selectableConfig.getMaxOctetsPerFrame()))) { return false; } return true; } /** * Checks whether the Input codec config matches the selectable capabilities. Any parameters of * the codec config with NONE value will be considered a wildcard matching. * * @param codecConfig the codec config to compare against * @return {@code true} if the codec config matches, {@code false} otherwise */ public boolean isInputCodecConfigSelectable(@Nullable BluetoothLeAudioCodecConfig codecConfig) { if (codecConfig == null) { return false; } for (BluetoothLeAudioCodecConfig selectableConfig : mInputCodecsSelectableCapabilities) { if (isCodecConfigSelectable(codecConfig, selectableConfig)) { return true; } } return false; } /** * Checks whether the Output codec config matches the selectable capabilities. Any parameters of * the codec config with NONE value will be considered a wildcard matching. * * @param codecConfig the codec config to compare against * @return {@code true} if the codec config matches, {@code false} otherwise */ public boolean isOutputCodecConfigSelectable( @Nullable BluetoothLeAudioCodecConfig codecConfig) { if (codecConfig == null) { return false; } for (BluetoothLeAudioCodecConfig selectableConfig : mOutputCodecsSelectableCapabilities) { if (isCodecConfigSelectable(codecConfig, selectableConfig)) { return true; } } return false; } /** Returns a hash based on the codec config and local capabilities. */ @Override public int hashCode() { return Objects.hash( mInputCodecConfig, mOutputCodecConfig, mInputCodecsLocalCapabilities, mOutputCodecsLocalCapabilities, mInputCodecsSelectableCapabilities, mOutputCodecsSelectableCapabilities); } /** * Returns a {@link String} that describes each BluetoothLeAudioCodecStatus parameter current * value. */ @Override public String toString() { return "{mInputCodecConfig:" + mInputCodecConfig + ",mOutputCodecConfig:" + mOutputCodecConfig + ",mInputCodecsLocalCapabilities:" + mInputCodecsLocalCapabilities + ",mOutputCodecsLocalCapabilities:" + mOutputCodecsLocalCapabilities + ",mInputCodecsSelectableCapabilities:" + mInputCodecsSelectableCapabilities + ",mOutputCodecsSelectableCapabilities:" + mOutputCodecsSelectableCapabilities + "}"; } /** * @return 0 */ @Override public int describeContents() { return 0; } /** {@link Parcelable.Creator} interface implementation. */ public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public BluetoothLeAudioCodecStatus createFromParcel(Parcel in) { return new BluetoothLeAudioCodecStatus(in); } public BluetoothLeAudioCodecStatus[] newArray(int size) { return new BluetoothLeAudioCodecStatus[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 */ @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeTypedObject(mInputCodecConfig, flags); out.writeTypedObject(mOutputCodecConfig, flags); out.writeTypedList(mInputCodecsLocalCapabilities); out.writeTypedList(mOutputCodecsLocalCapabilities); out.writeTypedList(mInputCodecsSelectableCapabilities); out.writeTypedList(mOutputCodecsSelectableCapabilities); } /** * Returns the current Input codec configuration. * * @return The current input codec config. */ public @Nullable BluetoothLeAudioCodecConfig getInputCodecConfig() { return mInputCodecConfig; } /** * Returns the current Output codec configuration. * * @return The current output codec config. */ public @Nullable BluetoothLeAudioCodecConfig getOutputCodecConfig() { return mOutputCodecConfig; } /** * Returns the input codecs local capabilities. * * @return The list of codec config that supported by the local system. */ public @NonNull List getInputCodecLocalCapabilities() { return (mInputCodecsLocalCapabilities == null) ? Collections.emptyList() : mInputCodecsLocalCapabilities; } /** * Returns the output codecs local capabilities. * * @return The list of codec config that supported by the local system. */ public @NonNull List getOutputCodecLocalCapabilities() { return (mOutputCodecsLocalCapabilities == null) ? Collections.emptyList() : mOutputCodecsLocalCapabilities; } /** * Returns the Input codecs selectable capabilities. * * @return The list of codec config that supported by both of the local system and remote * devices. */ public @NonNull List getInputCodecSelectableCapabilities() { return (mInputCodecsSelectableCapabilities == null) ? Collections.emptyList() : mInputCodecsSelectableCapabilities; } /** * Returns the Output codecs selectable capabilities. * * @return The list of codec config that supported by both of the local system and remote * devices. */ public @NonNull List getOutputCodecSelectableCapabilities() { return (mOutputCodecsSelectableCapabilities == null) ? Collections.emptyList() : mOutputCodecsSelectableCapabilities; } }