BluetoothLeAudio: Fix API for codec preferences

This patch adjust codec preferences for the Le Audio usage.
API is using group id now and allows to choose configuration for output
and input.
Note: Input and Output shall use same codec type (for now) but different
codec parameters might be used.

Bug: 219875113
Test: atest BluetoothInstrumentationTests
Merged-In: Iee7a0c6d42dd029f9c24a27ec892eb7ef261263b
Change-Id: Iee7a0c6d42dd029f9c24a27ec892eb7ef261263b
diff --git a/android/app/jni/com_android_bluetooth_le_audio.cpp b/android/app/jni/com_android_bluetooth_le_audio.cpp
index e549b83..f67185f 100644
--- a/android/app/jni/com_android_bluetooth_le_audio.cpp
+++ b/android/app/jni/com_android_bluetooth_le_audio.cpp
@@ -160,7 +160,7 @@
   jclass jniBluetoothLeAudioCodecConfigClass =
       env->FindClass("android/bluetooth/BluetoothLeAudioCodecConfig");
   android_bluetooth_BluetoothLeAudioCodecConfig.constructor = env->GetMethodID(
-      jniBluetoothLeAudioCodecConfigClass, "<init>", "(IIIIIII)V");
+      jniBluetoothLeAudioCodecConfigClass, "<init>", "(IIIIIIIII)V");
   android_bluetooth_BluetoothLeAudioCodecConfig.getCodecType = env->GetMethodID(
       jniBluetoothLeAudioCodecConfigClass, "getCodecType", "()I");
 
@@ -357,6 +357,21 @@
   sLeAudioClientInterface->GroupSetActive(group_id);
 }
 
+static void setCodecConfigPreferenceNative(JNIEnv* env, jobject object,
+                                           jint group_id,
+                                           jobject inputCodecConfig,
+                                           jobject outputCodecConfig) {
+  if (!env->IsInstanceOf(inputCodecConfig,
+                         android_bluetooth_BluetoothLeAudioCodecConfig.clazz) ||
+      !env->IsInstanceOf(outputCodecConfig,
+                         android_bluetooth_BluetoothLeAudioCodecConfig.clazz)) {
+    ALOGE("%s: Invalid BluetoothLeAudioCodecConfig instance", __func__);
+    return;
+  }
+
+  // TODO Implement
+}
+
 static JNINativeMethod sMethods[] = {
     {"classInitNative", "()V", (void*)classInitNative},
     {"initNative", "([Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V",
@@ -367,6 +382,10 @@
     {"groupAddNodeNative", "(I[B)Z", (void*)groupAddNodeNative},
     {"groupRemoveNodeNative", "(I[B)Z", (void*)groupRemoveNodeNative},
     {"groupSetActiveNative", "(I)V", (void*)groupSetActiveNative},
+    {"setCodecConfigPreferenceNative",
+     "(ILandroid/bluetooth/BluetoothLeAudioCodecConfig;Landroid/bluetooth/"
+     "BluetoothLeAudioCodecConfig;)V",
+     (void*)setCodecConfigPreferenceNative},
 };
 
 /* Le Audio Broadcaster */
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java
index 46e2db2..6dfc627 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java
@@ -215,6 +215,18 @@
         groupSetActiveNative(groupId);
     }
 
+    /**
+     * Set codec config preference.
+     * @param groupId group ID for the preference
+     * @param inputCodecConfig input codec configuration
+     * @param outputCodecConfig output codec configuration
+     */
+    public void setCodecConfigPreference(int groupId,
+            BluetoothLeAudioCodecConfig inputCodecConfig,
+            BluetoothLeAudioCodecConfig outputCodecConfig) {
+        setCodecConfigPreferenceNative(groupId, inputCodecConfig, outputCodecConfig);
+    }
+
     // Native methods that call into the JNI interface
     private static native void classInitNative();
     private native void initNative(BluetoothLeAudioCodecConfig[] codecConfigOffloading);
@@ -224,4 +236,7 @@
     private native boolean groupAddNodeNative(int groupId, byte[] address);
     private native boolean groupRemoveNodeNative(int groupId, byte[] address);
     private native void groupSetActiveNative(int groupId);
+    private native void setCodecConfigPreferenceNative(int groupId,
+            BluetoothLeAudioCodecConfig inputCodecConfig,
+            BluetoothLeAudioCodecConfig outputCodecConfig);
 }
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
index e6fc534..5e1834e 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -132,11 +132,13 @@
             mIsConnected = false;
             mIsActive = false;
             mActiveContexts = ACTIVE_CONTEXTS_NONE;
+            mCodecStatus = null;
         }
 
         public Boolean mIsConnected;
         public Boolean mIsActive;
         public Integer mActiveContexts;
+        public BluetoothLeAudioCodecStatus mCodecStatus;
     }
 
     private final Map<Integer, LeAudioGroupDescriptor> mGroupDescriptors = new LinkedHashMap<>();
@@ -1726,46 +1728,64 @@
     /**
      * Gets the current codec status (configuration and capability).
      *
-     * @param device the remote Bluetooth device.
+     * @param groupId the group id
      * @return the current codec status
      * @hide
      */
-    public BluetoothLeAudioCodecStatus getCodecStatus(BluetoothDevice device) {
+    public BluetoothLeAudioCodecStatus getCodecStatus(int groupId) {
         if (DBG) {
-            Log.d(TAG, "getCodecStatus(" + device + ")");
+            Log.d(TAG, "getCodecStatus(" + groupId + ")");
         }
-
+        LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId);
+        if (descriptor != null) {
+            return descriptor.mCodecStatus;
+        }
         return null;
     }
 
     /**
      * Sets the codec configuration preference.
      *
-     * @param device the remote Bluetooth device.
-     * @param codecConfig the codec configuration preference
+     * @param groupId the group id
+     * @param inputCodecConfig the input codec configuration preference
+     * @param outputCodecConfig the output codec configuration preference
      * @hide
      */
-    public void setCodecConfigPreference(BluetoothDevice device,
-                                         BluetoothLeAudioCodecConfig codecConfig) {
+    public void setCodecConfigPreference(int groupId,
+                                         BluetoothLeAudioCodecConfig inputCodecConfig,
+                                         BluetoothLeAudioCodecConfig outputCodecConfig) {
         if (DBG) {
-            Log.d(TAG, "setCodecConfigPreference(" + device + "): "
-                    + Objects.toString(codecConfig));
+            Log.d(TAG, "setCodecConfigPreference(" + groupId + "): "
+                    + Objects.toString(inputCodecConfig)
+                    + Objects.toString(outputCodecConfig));
         }
-        if (device == null) {
-            Log.e(TAG, "setCodecConfigPreference: Invalid device");
+        LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId);
+        if (descriptor == null) {
+            Log.e(TAG, "setCodecConfigPreference: Invalid groupId, " + groupId);
             return;
         }
-        if (codecConfig == null) {
+
+        if (inputCodecConfig == null || outputCodecConfig == null) {
             Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
             return;
         }
-        BluetoothLeAudioCodecStatus codecStatus = getCodecStatus(device);
-        if (codecStatus == null) {
+
+        /* We support different configuration for input and output but codec type
+         * shall be same */
+        if (inputCodecConfig.getCodecType() != outputCodecConfig.getCodecType()) {
+            Log.e(TAG, "setCodecConfigPreference: Input codec type: "
+                    + inputCodecConfig.getCodecType()
+                    + "does not match output codec type: " + outputCodecConfig.getCodecType());
+            return;
+        }
+
+        if (descriptor.mCodecStatus == null) {
             Log.e(TAG, "setCodecConfigPreference: Codec status is null");
             return;
         }
 
-        // TODO: pass the information to bt stack
+        mLeAudioNativeInterface.setCodecConfigPreference(groupId,
+                                inputCodecConfig, outputCodecConfig);
     }
 
 
@@ -2185,14 +2205,14 @@
         }
 
         @Override
-        public void getCodecStatus(BluetoothDevice device,
+        public void getCodecStatus(int groupId,
                 AttributionSource source, SynchronousResultReceiver receiver) {
             try {
                 LeAudioService service = getService(source);
                 BluetoothLeAudioCodecStatus codecStatus = null;
                 if (service != null) {
                     enforceBluetoothPrivilegedPermission(service);
-                    codecStatus = service.getCodecStatus(device);
+                    codecStatus = service.getCodecStatus(groupId);
                 }
                 receiver.send(codecStatus);
             } catch (RuntimeException e) {
@@ -2201,15 +2221,17 @@
         }
 
         @Override
-        public void setCodecConfigPreference(BluetoothDevice device,
-                BluetoothLeAudioCodecConfig codecConfig, AttributionSource source) {
+        public void setCodecConfigPreference(int groupId,
+                BluetoothLeAudioCodecConfig inputCodecConfig,
+                BluetoothLeAudioCodecConfig outputCodecConfig,
+                AttributionSource source) {
             LeAudioService service = getService(source);
             if (service == null) {
                 return;
             }
 
             enforceBluetoothPrivilegedPermission(service);
-            service.setCodecConfigPreference(device, codecConfig);
+            service.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig);
         }
     }
 
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 470011f..b6d998b 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -925,21 +925,23 @@
   public final class BluetoothLeAudioCodecConfig implements android.os.Parcelable {
     method public int describeContents();
     method public int getBitsPerSample();
-    method public int getChannelMode();
+    method public int getChannelCount();
     method @NonNull public String getCodecName();
     method public int getCodecPriority();
     method public int getCodecType();
     method public int getFrameDuration();
+    method public int getMaxOctetsPerFrame();
+    method public int getMinOctetsPerFrame();
     method public int getOctetsPerFrame();
     method public int getSampleRate();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final int BITS_PER_SAMPLE_16 = 1; // 0x1
     field public static final int BITS_PER_SAMPLE_24 = 2; // 0x2
-    field public static final int BITS_PER_SAMPLE_32 = 3; // 0x3
+    field public static final int BITS_PER_SAMPLE_32 = 8; // 0x8
     field public static final int BITS_PER_SAMPLE_NONE = 0; // 0x0
-    field public static final int CHANNEL_MODE_MONO = 1; // 0x1
-    field public static final int CHANNEL_MODE_NONE = 0; // 0x0
-    field public static final int CHANNEL_MODE_STEREO = 2; // 0x2
+    field public static final int CHANNEL_COUNT_1 = 1; // 0x1
+    field public static final int CHANNEL_COUNT_2 = 2; // 0x2
+    field public static final int CHANNEL_COUNT_NONE = 0; // 0x0
     field public static final int CODEC_PRIORITY_DEFAULT = 0; // 0x0
     field public static final int CODEC_PRIORITY_DISABLED = -1; // 0xffffffff
     field public static final int CODEC_PRIORITY_HIGHEST = 1000000; // 0xf4240
@@ -947,11 +949,11 @@
     field public static final int FRAME_DURATION_10000 = 2; // 0x2
     field public static final int FRAME_DURATION_7500 = 1; // 0x1
     field public static final int FRAME_DURATION_NONE = 0; // 0x0
-    field public static final int SAMPLE_RATE_16000 = 2; // 0x2
-    field public static final int SAMPLE_RATE_24000 = 3; // 0x3
-    field public static final int SAMPLE_RATE_32000 = 4; // 0x4
-    field public static final int SAMPLE_RATE_44100 = 5; // 0x5
-    field public static final int SAMPLE_RATE_48000 = 6; // 0x6
+    field public static final int SAMPLE_RATE_16000 = 4; // 0x4
+    field public static final int SAMPLE_RATE_24000 = 16; // 0x10
+    field public static final int SAMPLE_RATE_32000 = 32; // 0x20
+    field public static final int SAMPLE_RATE_44100 = 64; // 0x40
+    field public static final int SAMPLE_RATE_48000 = 128; // 0x80
     field public static final int SAMPLE_RATE_8000 = 1; // 0x1
     field public static final int SAMPLE_RATE_NONE = 0; // 0x0
     field public static final int SOURCE_CODEC_TYPE_INVALID = 1000000; // 0xf4240
@@ -963,21 +965,27 @@
     ctor public BluetoothLeAudioCodecConfig.Builder(@NonNull android.bluetooth.BluetoothLeAudioCodecConfig);
     method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig build();
     method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setBitsPerSample(int);
-    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setChannelMode(int);
+    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setChannelCount(int);
     method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setCodecPriority(int);
     method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setCodecType(int);
     method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setFrameDuration(int);
+    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setMaxOctetsPerFrame(int);
+    method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setMinOctetsPerFrame(int);
     method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setOctetsPerFrame(int);
     method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setSampleRate(int);
   }
 
   public final class BluetoothLeAudioCodecStatus implements android.os.Parcelable {
-    ctor public BluetoothLeAudioCodecStatus(@Nullable android.bluetooth.BluetoothLeAudioCodecConfig, @NonNull java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig>, @NonNull java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig>);
+    ctor public BluetoothLeAudioCodecStatus(@Nullable android.bluetooth.BluetoothLeAudioCodecConfig, @Nullable android.bluetooth.BluetoothLeAudioCodecConfig, @NonNull java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig>, @NonNull java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig>, @NonNull java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig>, @NonNull 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> getCodecLocalCapabilities();
-    method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getCodecSelectableCapabilities();
-    method public boolean isCodecConfigSelectable(@Nullable android.bluetooth.BluetoothLeAudioCodecConfig);
+    method @Nullable public android.bluetooth.BluetoothLeAudioCodecConfig getInputCodecConfig();
+    method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getInputCodecLocalCapabilities();
+    method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getInputCodecSelectableCapabilities();
+    method @Nullable public android.bluetooth.BluetoothLeAudioCodecConfig getOutputCodecConfig();
+    method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getOutputCodecLocalCapabilities();
+    method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getOutputCodecSelectableCapabilities();
+    method public boolean isInputCodecConfigSelectable(@Nullable android.bluetooth.BluetoothLeAudioCodecConfig);
+    method public boolean isOutputCodecConfigSelectable(@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";
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index f19a25c..549bce9 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -330,10 +330,10 @@
 
   public final class BluetoothLeAudio implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getAudioLocation(@NonNull android.bluetooth.BluetoothDevice);
-    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothLeAudioCodecStatus getCodecStatus(@NonNull android.bluetooth.BluetoothDevice);
+    method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothLeAudioCodecStatus getCodecStatus(int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothLeAudio.Callback);
-    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setCodecConfigPreference(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothLeAudioCodecConfig);
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setCodecConfigPreference(int, @NonNull android.bluetooth.BluetoothLeAudioCodecConfig, @NonNull android.bluetooth.BluetoothLeAudioCodecConfig);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void setVolume(int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void unregisterCallback(@NonNull android.bluetooth.BluetoothLeAudio.Callback);
diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java
index b81a8a4..3138ef4 100644
--- a/framework/java/android/bluetooth/BluetoothLeAudio.java
+++ b/framework/java/android/bluetooth/BluetoothLeAudio.java
@@ -1392,7 +1392,7 @@
     /**
      * Gets the current codec status (configuration and capability).
      *
-     * @param device the remote Bluetooth device.
+     * @param groupId The group id
      * @return the current codec status
      * @hide
      */
@@ -1403,9 +1403,9 @@
             android.Manifest.permission.BLUETOOTH_CONNECT,
             android.Manifest.permission.BLUETOOTH_PRIVILEGED
     })
-    public BluetoothLeAudioCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
+    public BluetoothLeAudioCodecStatus getCodecStatus(int groupId) {
         if (DBG) {
-            Log.d(TAG, "getCodecStatus(" + device + ")");
+            Log.d(TAG, "getCodecStatus(" + groupId + ")");
         }
 
         final IBluetoothLeAudio service = getService();
@@ -1414,11 +1414,11 @@
         if (service == null) {
             Log.w(TAG, "Proxy not attached to service");
             if (DBG) log(Log.getStackTraceString(new Throwable()));
-        } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+        } else if (mAdapter.isEnabled()) {
             try {
                 final SynchronousResultReceiver<BluetoothLeAudioCodecStatus> recv =
                         new SynchronousResultReceiver();
-                service.getCodecStatus(device, mAttributionSource, recv);
+                service.getCodecStatus(groupId, mAttributionSource, recv);
                 return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
             } catch (TimeoutException e) {
                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
@@ -1433,9 +1433,11 @@
     /**
      * Sets the codec configuration preference.
      *
-     * @param device the remote Bluetooth device.
-     * @param codecConfig the codec configuration preference
+     * @param groupId the groupId
+     * @param inputCodecConfig the input codec configuration preference
+     * @param outputCodecConfig the output codec configuration preference
      * @throws IllegalStateException if LE Audio Service is null
+     * @throws NullPointerException if any of the configs is null
      * @hide
      */
     @SystemApi
@@ -1444,14 +1446,13 @@
             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 + ")");
+    public void setCodecConfigPreference(int groupId,
+                                         @NonNull BluetoothLeAudioCodecConfig inputCodecConfig,
+                                         @NonNull BluetoothLeAudioCodecConfig outputCodecConfig) {
+        if (DBG) Log.d(TAG, "setCodecConfigPreference(" + groupId + ")");
 
-        if (codecConfig == null) {
-            Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
-            throw new IllegalArgumentException("codecConfig cannot be null");
-        }
+        Objects.requireNonNull(inputCodecConfig, " inputCodecConfig shall not be null");
+        Objects.requireNonNull(outputCodecConfig, " outputCodecConfig shall not be null");
 
         final IBluetoothLeAudio service = getService();
 
@@ -1459,9 +1460,10 @@
             Log.w(TAG, "Proxy not attached to service");
             if (DBG) log(Log.getStackTraceString(new Throwable()));
             throw new IllegalStateException("Service is unavailable");
-        } else if (mAdapter.isEnabled() && isValidDevice(device)) {
+        } else if (mAdapter.isEnabled()) {
             try {
-                service.setCodecConfigPreference(device, codecConfig, mAttributionSource);
+                service.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig,
+                                        mAttributionSource);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
                 e.rethrowFromSystemServer();
diff --git a/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java b/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
index 004bf6b..c91d13f 100644
--- a/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
+++ b/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
@@ -72,7 +72,7 @@
     public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000;
 
     /** @hide */
-    @IntDef(prefix = "SAMPLE_RATE_",
+    @IntDef(flag = true, prefix = "SAMPLE_RATE_",
             value = {SAMPLE_RATE_NONE, SAMPLE_RATE_8000, SAMPLE_RATE_16000, SAMPLE_RATE_24000,
                     SAMPLE_RATE_32000, SAMPLE_RATE_44100, SAMPLE_RATE_48000})
     @Retention(RetentionPolicy.SOURCE)
@@ -81,41 +81,45 @@
     /**
      * Codec sample rate 0 Hz. Default value used for
      * codec sample rate.
+     * Values are the bit mask as defined in the
+     * Bluetooth Assigned Numbers, Generic Audio,
+     * Supported_Sampling_Frequencies table
+     * Note: We use only part of it.
      */
     public static final int SAMPLE_RATE_NONE = 0;
 
     /**
      * Codec sample rate 8000 Hz.
      */
-    public static final int SAMPLE_RATE_8000 = 1;
+    public static final int SAMPLE_RATE_8000 = 0x01 << 0;
 
     /**
      * Codec sample rate 16000 Hz.
      */
-    public static final int SAMPLE_RATE_16000 = 2;
+    public static final int SAMPLE_RATE_16000 = 0x01 << 2;
 
     /**
      * Codec sample rate 24000 Hz.
      */
-    public static final int SAMPLE_RATE_24000 = 3;
+    public static final int SAMPLE_RATE_24000 = 0x01 << 4;
 
     /**
      * Codec sample rate 32000 Hz.
      */
-    public static final int SAMPLE_RATE_32000 = 4;
+    public static final int SAMPLE_RATE_32000 = 0x01 << 5;
 
     /**
      * Codec sample rate 44100 Hz.
      */
-    public static final int SAMPLE_RATE_44100 = 5;
+    public static final int SAMPLE_RATE_44100 = 0x01 << 6;
 
     /**
      * Codec sample rate 48000 Hz.
      */
-    public static final int SAMPLE_RATE_48000 = 6;
+    public static final int SAMPLE_RATE_48000 = 0x01 << 7;
 
     /** @hide */
-    @IntDef(prefix = "BITS_PER_SAMPLE_",
+    @IntDef(flag = true, prefix = "BITS_PER_SAMPLE_",
             value = {BITS_PER_SAMPLE_NONE, BITS_PER_SAMPLE_16, BITS_PER_SAMPLE_24,
                     BITS_PER_SAMPLE_32})
     @Retention(RetentionPolicy.SOURCE)
@@ -130,42 +134,52 @@
     /**
      * Codec bits per sample 16.
      */
-    public static final int BITS_PER_SAMPLE_16 = 1;
+    public static final int BITS_PER_SAMPLE_16 = 0x01 << 0;
 
     /**
      * Codec bits per sample 24.
      */
-    public static final int BITS_PER_SAMPLE_24 = 2;
+    public static final int BITS_PER_SAMPLE_24 = 0x01 << 1;
 
     /**
      * Codec bits per sample 32.
      */
-    public static final int BITS_PER_SAMPLE_32 = 3;
+    public static final int BITS_PER_SAMPLE_32 = 0x01 << 3;
 
-    /** @hide */
-    @IntDef(prefix = "CHANNEL_MODE_",
-            value = {CHANNEL_MODE_NONE, CHANNEL_MODE_MONO, CHANNEL_MODE_STEREO})
+    /**
+     * Values are the bit mask as defined in the
+     * Bluetooth Assigned Numbers, Generic Audio,
+     * Supported_Audio_Channel_Counts table
+     * Note: We use only part of it.
+     * @hide */
+    @IntDef(flag = true, prefix = "CHANNEL_COUNT_",
+            value = {CHANNEL_COUNT_NONE, CHANNEL_COUNT_1, CHANNEL_COUNT_2})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ChannelMode {}
+    public @interface ChannelCount {}
 
     /**
      * Codec channel mode NONE. Default value of the
      * codec channel mode.
      */
-    public static final int CHANNEL_MODE_NONE = 0;
+    public static final int CHANNEL_COUNT_NONE = 0;
 
     /**
      * Codec channel mode MONO.
      */
-    public static final int CHANNEL_MODE_MONO = 1;
+    public static final int CHANNEL_COUNT_1 = 0x01 << 0;
 
     /**
      * Codec channel mode STEREO.
      */
-    public static final int CHANNEL_MODE_STEREO = 2;
+    public static final int CHANNEL_COUNT_2 = 0x01 << 1;
 
-    /** @hide */
-    @IntDef(prefix = "FRAME_DURATION_",
+    /**
+     * Values are the bit mask as defined in the
+     * Bluetooth Assigned Numbers, Generic Audio,
+     * Supported_Frame_Durations table
+     *
+     * @hide */
+    @IntDef(flag = true, prefix = "FRAME_DURATION_",
             value = {FRAME_DURATION_NONE, FRAME_DURATION_7500, FRAME_DURATION_10000})
     @Retention(RetentionPolicy.SOURCE)
     public @interface FrameDuration {}
@@ -178,20 +192,23 @@
     /**
      * Frame duration 7500 us.
      */
-    public static final int FRAME_DURATION_7500 = 1;
+    public static final int FRAME_DURATION_7500 = 0x01 << 0;
 
     /**
      * Frame duration 10000 us.
      */
-    public static final int FRAME_DURATION_10000 = 2;
+    public static final int FRAME_DURATION_10000 = 0x01 << 1;
 
     private final @SourceCodecType int mCodecType;
     private final @CodecPriority int mCodecPriority;
     private final @SampleRate int mSampleRate;
     private final @BitsPerSample int mBitsPerSample;
-    private final @ChannelMode int mChannelMode;
+    private final @ChannelCount int mChannelCount;
     private final @FrameDuration int mFrameDuration;
     private final int mOctetsPerFrame;
+    private final int mMinOctetsPerFrame;
+    private final int mMaxOctetsPerFrame;
+
 
     /**
      * Creates a new BluetoothLeAudioCodecConfig.
@@ -200,21 +217,26 @@
      * @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 channelCount the channel count of this codec
      * @param frameDuration the frame duration of this codec
      * @param octetsPerFrame the octets per frame of this codec
+     * @param minOctetsPerFrame the minimum octets per frame of this codec
+     * @param maxOctetsPerFrame the maximum octets per frame of this codec
      */
     private BluetoothLeAudioCodecConfig(@SourceCodecType int codecType,
             @CodecPriority int codecPriority, @SampleRate int sampleRate,
-            @BitsPerSample int bitsPerSample, @ChannelMode int channelMode,
-            @FrameDuration int frameDuration, int octetsPerFrame) {
+            @BitsPerSample int bitsPerSample, @ChannelCount int channelCount,
+            @FrameDuration int frameDuration, int octetsPerFrame,
+            int minOctetsPerFrame, int maxOctetsPerFrame) {
         mCodecType = codecType;
         mCodecPriority = codecPriority;
         mSampleRate = sampleRate;
         mBitsPerSample = bitsPerSample;
-        mChannelMode = channelMode;
+        mChannelCount = channelCount;
         mFrameDuration = frameDuration;
         mOctetsPerFrame = octetsPerFrame;
+        mMinOctetsPerFrame = minOctetsPerFrame;
+        mMaxOctetsPerFrame = maxOctetsPerFrame;
     }
 
     @Override
@@ -233,11 +255,14 @@
                     int codecPriority = in.readInt();
                     int sampleRate = in.readInt();
                     int bitsPerSample = in.readInt();
-                    int channelMode = in.readInt();
+                    int channelCount = in.readInt();
                     int frameDuration = in.readInt();
                     int octetsPerFrame = in.readInt();
+                    int minOctetsPerFrame = in.readInt();
+                    int maxOctetsPerFrame = in.readInt();
                     return new BluetoothLeAudioCodecConfig(codecType, codecPriority, sampleRate,
-                            bitsPerSample, channelMode, frameDuration, octetsPerFrame);
+                            bitsPerSample, channelCount, frameDuration, octetsPerFrame,
+                            minOctetsPerFrame, maxOctetsPerFrame);
                 }
 
                 public BluetoothLeAudioCodecConfig[] newArray(int size) {
@@ -251,17 +276,21 @@
         out.writeInt(mCodecPriority);
         out.writeInt(mSampleRate);
         out.writeInt(mBitsPerSample);
-        out.writeInt(mChannelMode);
+        out.writeInt(mChannelCount);
         out.writeInt(mFrameDuration);
         out.writeInt(mOctetsPerFrame);
+        out.writeInt(mMinOctetsPerFrame);
+        out.writeInt(mMaxOctetsPerFrame);
     }
 
     @Override
     public String toString() {
         return "{codecName:" + getCodecName() + ",mCodecType:" + mCodecType
                 + ",mCodecPriority:" + mCodecPriority + ",mSampleRate:" + mSampleRate
-                + ",mBitsPerSample:" + mBitsPerSample + ",mChannelMode:" + mChannelMode
-                + ",mFrameDuration:" + mFrameDuration + ",mOctetsPerFrame:" + mOctetsPerFrame + "}";
+                + ",mBitsPerSample:" + mBitsPerSample + ",mChannelCount:" + mChannelCount
+                + ",mFrameDuration:" + mFrameDuration + ",mOctetsPerFrame:" + mOctetsPerFrame
+                + ",mMinOctetsPerFrame:" + mMinOctetsPerFrame
+                + ",mMaxOctetsPerFrame:" + mMaxOctetsPerFrame + "}";
     }
 
     /**
@@ -316,8 +345,8 @@
     /**
      * Returns the codec channel mode.
      */
-    public @ChannelMode int getChannelMode() {
-        return mChannelMode;
+    public @ChannelCount int getChannelCount() {
+        return mChannelCount;
     }
 
     /**
@@ -334,6 +363,20 @@
         return mOctetsPerFrame;
     }
 
+    /**
+     * Returns the minimum octets per frame
+     */
+    public int getMinOctetsPerFrame() {
+        return mMinOctetsPerFrame;
+    }
+
+    /**
+     * Returns the maximum octets per frame
+     */
+    public int getMaxOctetsPerFrame() {
+        return mMaxOctetsPerFrame;
+    }
+
     @Override
     public boolean equals(@NonNull Object o) {
         if (o instanceof BluetoothLeAudioCodecConfig) {
@@ -342,9 +385,11 @@
                     && other.getCodecPriority() == mCodecPriority
                     && other.getSampleRate() == mSampleRate
                     && other.getBitsPerSample() == mBitsPerSample
-                    && other.getChannelMode() == mChannelMode
+                    && other.getChannelCount() == mChannelCount
                     && other.getFrameDuration() == mFrameDuration
-                    && other.getOctetsPerFrame() == mOctetsPerFrame);
+                    && other.getOctetsPerFrame() == mOctetsPerFrame
+                    && other.getMinOctetsPerFrame() == mMinOctetsPerFrame
+                    && other.getMaxOctetsPerFrame() == mMaxOctetsPerFrame);
         }
         return false;
     }
@@ -356,7 +401,8 @@
     @Override
     public int hashCode() {
         return Objects.hash(mCodecType, mCodecPriority, mSampleRate,
-                mBitsPerSample, mChannelMode, mFrameDuration, mOctetsPerFrame);
+                mBitsPerSample, mChannelCount, mFrameDuration, mOctetsPerFrame,
+                mMinOctetsPerFrame, mMaxOctetsPerFrame);
     }
 
     /**
@@ -369,9 +415,11 @@
         private int mCodecPriority = BluetoothLeAudioCodecConfig.CODEC_PRIORITY_DEFAULT;
         private int mSampleRate = BluetoothLeAudioCodecConfig.SAMPLE_RATE_NONE;
         private int mBitsPerSample = BluetoothLeAudioCodecConfig.BITS_PER_SAMPLE_NONE;
-        private int mChannelMode = BluetoothLeAudioCodecConfig.CHANNEL_MODE_NONE;
+        private int mChannelCount = BluetoothLeAudioCodecConfig.CHANNEL_COUNT_NONE;
         private int mFrameDuration = BluetoothLeAudioCodecConfig.FRAME_DURATION_NONE;
         private int mOctetsPerFrame = 0;
+        private int mMinOctetsPerFrame = 0;
+        private int mMaxOctetsPerFrame = 0;
 
         public Builder() {}
 
@@ -380,9 +428,11 @@
             mCodecPriority = config.getCodecPriority();
             mSampleRate = config.getSampleRate();
             mBitsPerSample = config.getBitsPerSample();
-            mChannelMode = config.getChannelMode();
+            mChannelCount = config.getChannelCount();
             mFrameDuration = config.getFrameDuration();
             mOctetsPerFrame = config.getOctetsPerFrame();
+            mMinOctetsPerFrame = config.getMinOctetsPerFrame();
+            mMaxOctetsPerFrame = config.getMaxOctetsPerFrame();
         }
 
         /**
@@ -430,13 +480,13 @@
         }
 
         /**
-         * Set the channel mode for Bluetooth LE audio codec config.
+         * Set the channel count for Bluetooth LE audio codec config.
          *
-         * @param channelMode of this codec
+         * @param channelCount of this codec
          * @return the same Builder instance
          */
-        public @NonNull Builder setChannelMode(@ChannelMode int channelMode) {
-            mChannelMode = channelMode;
+        public @NonNull Builder setChannelCount(@ChannelCount int channelCount) {
+            mChannelCount = channelCount;
             return this;
         }
 
@@ -463,12 +513,35 @@
         }
 
         /**
+         * Set the minimum octets per frame for Bluetooth LE audio codec config.
+         *
+         * @param minOctetsPerFrame of this codec
+         * @return the same Builder instance
+         */
+        public @NonNull Builder setMinOctetsPerFrame(int minOctetsPerFrame) {
+            mMinOctetsPerFrame = minOctetsPerFrame;
+            return this;
+        }
+
+        /**
+         * Set the maximum octets per frame for Bluetooth LE audio codec config.
+         *
+         * @param maxOctetsPerFrame of this codec
+         * @return the same Builder instance
+         */
+        public @NonNull Builder setMaxOctetsPerFrame(int maxOctetsPerFrame) {
+            mMaxOctetsPerFrame = maxOctetsPerFrame;
+            return this;
+        }
+
+        /**
          * Build {@link BluetoothLeAudioCodecConfig}.
          * @return new BluetoothLeAudioCodecConfig built
          */
         public @NonNull BluetoothLeAudioCodecConfig build() {
             return new BluetoothLeAudioCodecConfig(mCodecType, mCodecPriority, mSampleRate,
-                    mBitsPerSample, mChannelMode, mFrameDuration, mOctetsPerFrame);
+                    mBitsPerSample, mChannelCount, mFrameDuration, mOctetsPerFrame,
+                    mMinOctetsPerFrame, mMaxOctetsPerFrame);
         }
     }
 }
diff --git a/framework/java/android/bluetooth/BluetoothLeAudioCodecStatus.java b/framework/java/android/bluetooth/BluetoothLeAudioCodecStatus.java
index 399ffa7..bdb9d5d 100644
--- a/framework/java/android/bluetooth/BluetoothLeAudioCodecStatus.java
+++ b/framework/java/android/bluetooth/BluetoothLeAudioCodecStatus.java
@@ -41,29 +41,47 @@
     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;
+    private final @Nullable BluetoothLeAudioCodecConfig mInputCodecConfig;
+    private final @Nullable BluetoothLeAudioCodecConfig mOutputCodecConfig;
+    private final @Nullable List<BluetoothLeAudioCodecConfig> mInputCodecsLocalCapabilities;
+    private final @Nullable List<BluetoothLeAudioCodecConfig> mOutputCodecsLocalCapabilities;
+    private final @Nullable List<BluetoothLeAudioCodecConfig> mInputCodecsSelectableCapabilities;
+    private final @Nullable List<BluetoothLeAudioCodecConfig> mOutputCodecsSelectableCapabilities;
 
     /**
      * 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.
+     * @param inputCodecConfig the current input code configutration.
+     * @param outputCodecConfig the current output code configutration.
+     * @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 codecConfig,
-            @NonNull List<BluetoothLeAudioCodecConfig> codecsLocalCapabilities,
-            @NonNull List<BluetoothLeAudioCodecConfig> codecsSelectableCapabilities) {
-        mCodecConfig = codecConfig;
-        mCodecsLocalCapabilities = codecsLocalCapabilities;
-        mCodecsSelectableCapabilities = codecsSelectableCapabilities;
+    public BluetoothLeAudioCodecStatus(@Nullable BluetoothLeAudioCodecConfig inputCodecConfig,
+            @Nullable BluetoothLeAudioCodecConfig outputCodecConfig,
+            @NonNull List<BluetoothLeAudioCodecConfig> inputCodecsLocalCapabilities,
+            @NonNull List<BluetoothLeAudioCodecConfig> outputCodecsLocalCapabilities,
+            @NonNull List<BluetoothLeAudioCodecConfig> inputCodecsSelectableCapabilities,
+            @NonNull List<BluetoothLeAudioCodecConfig> outputCodecsSelectableCapabilities) {
+        mInputCodecConfig = inputCodecConfig;
+        mOutputCodecConfig = outputCodecConfig;
+        mInputCodecsLocalCapabilities = inputCodecsLocalCapabilities;
+        mOutputCodecsLocalCapabilities = outputCodecsLocalCapabilities;
+        mInputCodecsSelectableCapabilities = inputCodecsSelectableCapabilities;
+        mOutputCodecsSelectableCapabilities = outputCodecsSelectableCapabilities;
     }
 
     private BluetoothLeAudioCodecStatus(Parcel in) {
-        mCodecConfig = in.readTypedObject(BluetoothLeAudioCodecConfig.CREATOR);
-        mCodecsLocalCapabilities = in.createTypedArrayList(BluetoothLeAudioCodecConfig.CREATOR);
-        mCodecsSelectableCapabilities =
+        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);
     }
 
@@ -71,10 +89,16 @@
     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 (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;
     }
@@ -101,19 +125,67 @@
         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 codec config matches the selectable capabilities.
+     * 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 isCodecConfigSelectable(@Nullable BluetoothLeAudioCodecConfig codecConfig) {
+    public boolean isInputCodecConfigSelectable(@Nullable BluetoothLeAudioCodecConfig codecConfig) {
         if (codecConfig == null) {
             return false;
         }
-        for (BluetoothLeAudioCodecConfig selectableConfig : mCodecsSelectableCapabilities) {
-            if (codecConfig.equals(selectableConfig)) {
+        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;
             }
         }
@@ -125,7 +197,9 @@
      */
     @Override
     public int hashCode() {
-        return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, mCodecsLocalCapabilities);
+        return Objects.hash(mInputCodecConfig, mOutputCodecConfig,
+                        mInputCodecsLocalCapabilities, mOutputCodecsLocalCapabilities,
+                        mInputCodecsSelectableCapabilities, mOutputCodecsSelectableCapabilities);
     }
 
     /**
@@ -134,9 +208,12 @@
      */
     @Override
     public String toString() {
-        return "{mCodecConfig:" + mCodecConfig
-                + ",mCodecsLocalCapabilities:" + mCodecsLocalCapabilities
-                + ",mCodecsSelectableCapabilities:" + mCodecsSelectableCapabilities
+        return "{mInputCodecConfig:" + mInputCodecConfig
+                + ",mOutputCodecConfig:" + mOutputCodecConfig
+                + ",mInputCodecsLocalCapabilities:" + mInputCodecsLocalCapabilities
+                + ",mOutputCodecsLocalCapabilities:" + mOutputCodecsLocalCapabilities
+                + ",mInputCodecsSelectableCapabilities:" + mInputCodecsSelectableCapabilities
+                + ",mOutputCodecsSelectableCapabilities:" + mOutputCodecsSelectableCapabilities
                 + "}";
     }
 
@@ -171,38 +248,71 @@
      */
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeTypedObject(mCodecConfig, flags);
-        out.writeTypedList(mCodecsLocalCapabilities);
-        out.writeTypedList(mCodecsSelectableCapabilities);
+        out.writeTypedObject(mInputCodecConfig, flags);
+        out.writeTypedObject(mOutputCodecConfig, flags);
+        out.writeTypedList(mInputCodecsLocalCapabilities);
+        out.writeTypedList(mOutputCodecsLocalCapabilities);
+        out.writeTypedList(mInputCodecsSelectableCapabilities);
+        out.writeTypedList(mOutputCodecsSelectableCapabilities);
     }
 
     /**
-     * Returns the current codec configuration.
+     * Returns the current Input codec configuration.
      *
-     * @return The current codec config.
+     * @return The current input codec config.
      */
-    public @Nullable BluetoothLeAudioCodecConfig getCodecConfig() {
-        return mCodecConfig;
+    public @Nullable BluetoothLeAudioCodecConfig getInputCodecConfig() {
+        return mInputCodecConfig;
     }
 
     /**
-     * Returns the codecs local capabilities.
+     * 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<BluetoothLeAudioCodecConfig> getCodecLocalCapabilities() {
-        return (mCodecsLocalCapabilities == null)
-                ? Collections.emptyList() : mCodecsLocalCapabilities;
+    public @NonNull List<BluetoothLeAudioCodecConfig> getInputCodecLocalCapabilities() {
+        return (mInputCodecsLocalCapabilities == null)
+                ? Collections.emptyList() : mInputCodecsLocalCapabilities;
     }
 
     /**
-     * Returns the codecs selectable capabilities.
+     * Returns the output codecs local capabilities.
+     *
+     * @return The list of codec config that supported by the local system.
+     */
+    public @NonNull List<BluetoothLeAudioCodecConfig> 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<BluetoothLeAudioCodecConfig> getCodecSelectableCapabilities() {
-        return (mCodecsSelectableCapabilities == null)
-                ? Collections.emptyList() : mCodecsSelectableCapabilities;
+    public @NonNull List<BluetoothLeAudioCodecConfig> 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<BluetoothLeAudioCodecConfig> getOutputCodecSelectableCapabilities() {
+        return (mOutputCodecsSelectableCapabilities == null)
+                ? Collections.emptyList() : mOutputCodecsSelectableCapabilities;
     }
 }
diff --git a/system/binder/android/bluetooth/IBluetoothLeAudio.aidl b/system/binder/android/bluetooth/IBluetoothLeAudio.aidl
index 7039c74..7a3a05a 100644
--- a/system/binder/android/bluetooth/IBluetoothLeAudio.aidl
+++ b/system/binder/android/bluetooth/IBluetoothLeAudio.aidl
@@ -54,9 +54,9 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
     void getConnectedGroupLeadDevice(int groupId, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
-    void getCodecStatus(in BluetoothDevice device, in AttributionSource source, in SynchronousResultReceiver receiver);
+    void getCodecStatus(in int groupId, in AttributionSource source, in SynchronousResultReceiver receiver);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
-    void setCodecConfigPreference(in BluetoothDevice device, in BluetoothLeAudioCodecConfig codecConfig, in AttributionSource source);
+    void setCodecConfigPreference(in int groupId, in BluetoothLeAudioCodecConfig inputCodecConfig, in BluetoothLeAudioCodecConfig outputCodecConfig, in AttributionSource source);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
     void registerCallback(in IBluetoothLeAudioCallback callback, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")