Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5aa4bd22 authored by Patty's avatar Patty Committed by Jack He
Browse files

Add API to set the codec preference for specific device

Add the APIs that would use in developer optin for setting
the codec config preference and getiting the codec status.
Expose the APIs first, some of the implementations
will be done later.
Add equals function to BluetoothLeAudioCodecConfig for
comparison usage

Tag: #feature
Bug: 216276721
Bug: 150670922
Test: atest BluetoothInstrumentationTests
Change-Id: Iaa9b7b1278f06228b7a22a1c61668b879b6f3dc9
Merged-In: Iaa9b7b1278f06228b7a22a1c61668b879b6f3dc9
(cherry picked from commit 7cf686a4)
parent 14c9fcd9
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -960,6 +960,18 @@ package android.bluetooth {
    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setSampleRate(int);
  }

  public final class BluetoothLeAudioCodecStatus implements android.os.Parcelable {
    ctor public BluetoothLeAudioCodecStatus(@Nullable android.bluetooth.BluetoothLeAudioCodecConfig, @Nullable java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig>, @Nullable java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig>);
    method public int describeContents();
    method @Nullable public android.bluetooth.BluetoothLeAudioCodecConfig getCodecConfig();
    method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getCodecsLocalCapabilities();
    method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getCodecsSelectableCapabilities();
    method public boolean isCodecConfigSelectable(@Nullable android.bluetooth.BluetoothLeAudioCodecConfig);
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothLeAudioCodecStatus> CREATOR;
    field public static final String EXTRA_LE_AUDIO_CODEC_STATUS = "android.bluetooth.extra.LE_AUDIO_CODEC_STATUS";
  }

  public final class BluetoothManager {
    method public android.bluetooth.BluetoothAdapter getAdapter();
    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
+67 −0
Original line number Diff line number Diff line
@@ -158,6 +158,24 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
    public static final String ACTION_LE_AUDIO_CONF_CHANGED =
            "android.bluetooth.action.LE_AUDIO_CONF_CHANGED";

    /**
     * Intent used to broadcast the audio codec config changed information.
     *
     * <p>This intent will have 2 extras:
     * <ul>
     * <li> {@link BluetoothLeAudioCodecStatus#EXTRA_LE_AUDIO_CODEC_STATUS} - The codec status.
     * </li>
     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
     * connected, otherwise it is not included.</li>
     * </ul>
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_LE_AUDIO_CODEC_CONFIG_CHANGED =
            "android.bluetooth.action.LE_AUDIO_CODEC_CONFIG_CHANGED";

    /**
     * Indicates unspecified audio content.
     * @hide
@@ -906,4 +924,53 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
    private static void log(String msg) {
        Log.d(TAG, msg);
    }

    /**
     * Gets the current codec status (configuration and capability).
     *
     * @param device the remote Bluetooth device.
     * @return the current codec status
     * @hide
     */
    @RequiresBluetoothConnectPermission
    @RequiresPermission(allOf = {
            android.Manifest.permission.BLUETOOTH_CONNECT,
            android.Manifest.permission.BLUETOOTH_PRIVILEGED
    })
    public BluetoothLeAudioCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
        if (DBG) {
            Log.d(TAG, "getCodecStatus(" + device + ")");
        }

        final BluetoothLeAudioCodecStatus defaultValue = null;

        // TODO: Add the implementation to get codec status
        return defaultValue;
    }

    /**
     * Sets the codec configuration preference.
     *
     * @param device the remote Bluetooth device.
     * @param codecConfig the codec configuration preference
     * @hide
     */
    @RequiresBluetoothConnectPermission
    @RequiresPermission(allOf = {
            android.Manifest.permission.BLUETOOTH_CONNECT,
            android.Manifest.permission.BLUETOOTH_PRIVILEGED
    })
    public void setCodecConfigPreference(@NonNull BluetoothDevice device,
                                         @NonNull BluetoothLeAudioCodecConfig codecConfig) {
        if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");

        if (codecConfig == null) {
            Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
            throw new IllegalArgumentException("codecConfig cannot be null");
        }

        // TODO: Add the implementation to set config preference
        return;
    }

}
+26 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ 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 LE Audio source device.
@@ -346,6 +347,31 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
        return mOctetsPerFrame;
    }

    @Override
    public boolean equals(@NonNull Object o) {
        if (o instanceof BluetoothLeAudioCodecConfig) {
            BluetoothLeAudioCodecConfig other = (BluetoothLeAudioCodecConfig) o;
            return (other.getCodecType() == mCodecType
                    && other.getCodecPriority() == mCodecPriority
                    && other.getSampleRate() == mSampleRate
                    && other.getBitsPerSample() == mBitsPerSample
                    && other.getChannelMode() == mChannelMode
                    && other.getFrameDuration() == mFrameDuration
                    && other.getOctetsPerFrame() == mOctetsPerFrame);
        }
        return false;
    }

    /**
     * Returns a hash representation of this BluetoothLeAudioCodecConfig
     * based on all the config values.
     */
    @Override
    public int hashCode() {
        return Objects.hash(mCodecType, mCodecPriority, mSampleRate,
                mBitsPerSample, mChannelMode, mFrameDuration, mOctetsPerFrame);
    }

    /**
     * Builder for {@link BluetoothLeAudioCodecConfig}.
     * <p> By default, the codec type will be set to
+194 −0
Original line number Diff line number Diff line
/*
 * 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 mCodecConfig;
    private final @Nullable List<BluetoothLeAudioCodecConfig> mCodecsLocalCapabilities;
    private final @Nullable List<BluetoothLeAudioCodecConfig> mCodecsSelectableCapabilities;

    /**
     * Represents the codec status for a Bluetooth LE Audio source device.
     *
     * @param codecConfig the current code configutration.
     * @param codecsLocalCapabilities the local codecs capabilities.
     * @param codecsSelectableCapabilities the selectable codecs capabilities.
     */
    public BluetoothLeAudioCodecStatus(@Nullable BluetoothLeAudioCodecConfig codecConfig,
            @Nullable List<BluetoothLeAudioCodecConfig> codecsLocalCapabilities,
            @Nullable List<BluetoothLeAudioCodecConfig> codecsSelectableCapabilities) {
        mCodecConfig = codecConfig;
        mCodecsLocalCapabilities = codecsLocalCapabilities;
        mCodecsSelectableCapabilities = codecsSelectableCapabilities;
    }

    private BluetoothLeAudioCodecStatus(Parcel in) {
        mCodecConfig = in.readTypedObject(BluetoothLeAudioCodecConfig.CREATOR);
        mCodecsLocalCapabilities = in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR);
        mCodecsSelectableCapabilities =
                in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR);
    }

    @Override
    public boolean equals(@Nullable Object o) {
        if (o instanceof BluetoothLeAudioCodecStatus) {
            BluetoothLeAudioCodecStatus other = (BluetoothLeAudioCodecStatus) o;
            return (Objects.equals(other.mCodecConfig, mCodecConfig)
                    && sameCapabilities(other.mCodecsLocalCapabilities, mCodecsLocalCapabilities)
                    && sameCapabilities(other.mCodecsSelectableCapabilities,
                    mCodecsSelectableCapabilities));
        }
        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<BluetoothLeAudioCodecConfig> c1,
                                           @Nullable List<BluetoothLeAudioCodecConfig> c2) {
        if (c1 == null) {
            return (c2 == null);
        }
        if (c2 == null) {
            return false;
        }
        if (c1.size() != c2.size()) {
            return false;
        }
        return c1.containsAll(c2);
    }

    /**
     * Checks whether the 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 isCodecConfigSelectable(@Nullable BluetoothLeAudioCodecConfig codecConfig) {
        // TODO: Add the implementation to check the config is selectable
        return true;
    }

    /**
     * Returns a hash based on the codec config and local capabilities.
     */
    @Override
    public int hashCode() {
        return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, mCodecsLocalCapabilities);
    }

    /**
     * Returns a {@link String} that describes each BluetoothLeAudioCodecStatus parameter
     * current value.
     */
    @Override
    public String toString() {
        return "{mCodecConfig:" + mCodecConfig
                + ",mCodecsLocalCapabilities:" + mCodecsLocalCapabilities
                + ",mCodecsSelectableCapabilities:" + mCodecsSelectableCapabilities
                + "}";
    }

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

    /**
     * {@link Parcelable.Creator} interface implementation.
     */
    public static final @android.annotation.NonNull
            Parcelable.Creator<BluetoothLeAudioCodecStatus> CREATOR =
            new Parcelable.Creator<BluetoothLeAudioCodecStatus>() {
                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(mCodecConfig, 0);
        out.writeTypedList(mCodecsLocalCapabilities);
        out.writeTypedList(mCodecsSelectableCapabilities);
    }

    /**
     * Returns the current codec configuration.
     */
    public @Nullable BluetoothLeAudioCodecConfig getCodecConfig() {
        return mCodecConfig;
    }

    /**
     * Returns the codecs local capabilities.
     */
    public @NonNull List<BluetoothLeAudioCodecConfig> getCodecsLocalCapabilities() {
        return (mCodecsLocalCapabilities == null)
                ? Collections.emptyList() : mCodecsLocalCapabilities;
    }

    /**
     * Returns the codecs selectable capabilities.
     */
    public @NonNull List<BluetoothLeAudioCodecConfig> getCodecsSelectableCapabilities() {
        return (mCodecsSelectableCapabilities == null)
                ? Collections.emptyList() : mCodecsSelectableCapabilities;
    }
}