Merge "Fix admin enable/disable of Bluetooth file sharing" into tm-dev am: c001bc5f22

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/19219300

Change-Id: Ic0beb31a5db2deaab49826a0bb4a93fdc6baba0d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/OWNERS b/OWNERS
index 2d3b648..02b536d 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,6 +1,7 @@
 # Project owners
 sattiraju@google.com
 siyuanh@google.com
+sungsoo@google.com
 zachoverflow@google.com
 
 # Per-file ownership
diff --git a/android/app/jni/com_android_bluetooth_vc.cpp b/android/app/jni/com_android_bluetooth_vc.cpp
index ad31918..4c4103e 100644
--- a/android/app/jni/com_android_bluetooth_vc.cpp
+++ b/android/app/jni/com_android_bluetooth_vc.cpp
@@ -346,7 +346,7 @@
   env->ReleaseByteArrayElements(address, addr, 0);
 }
 
-static void setVolumeGroupNative(JNIEnv* env, jobject object, jint group_id,
+static void setGroupVolumeNative(JNIEnv* env, jobject object, jint group_id,
                                  jint volume) {
   if (!sVolumeControlInterface) {
     LOG(ERROR) << __func__
@@ -547,7 +547,7 @@
     {"disconnectVolumeControlNative", "([B)Z",
      (void*)disconnectVolumeControlNative},
     {"setVolumeNative", "([BI)V", (void*)setVolumeNative},
-    {"setVolumeGroupNative", "(II)V", (void*)setVolumeGroupNative},
+    {"setGroupVolumeNative", "(II)V", (void*)setGroupVolumeNative},
     {"muteNative", "([B)V", (void*)muteNative},
     {"muteGroupNative", "(I)V", (void*)muteGroupNative},
     {"unmuteNative", "([B)V", (void*)unmuteNative},
diff --git a/android/app/res/values-or/strings.xml b/android/app/res/values-or/strings.xml
index a0a3f2f..0392c89 100644
--- a/android/app/res/values-or/strings.xml
+++ b/android/app/res/values-or/strings.xml
@@ -28,7 +28,7 @@
     <string name="bt_enable_title" msgid="4484289159118416315"></string>
     <string name="bt_enable_line1" msgid="8429910585843481489">"ବ୍ଲୁଟୂଥ୍‍‌ ସେବା ବ୍ୟବହାର କରିବା ପାଇଁ, ଆପଣଙ୍କୁ ପ୍ରଥମେ ବ୍ଲୁଟୂଥ୍‍‌ ଅନ୍‌ କରିବାକୁ ପଡ଼ିବ।"</string>
     <string name="bt_enable_line2" msgid="1466367120348920892">"ବ୍ଲୁ-ଟୁଥ୍‌କୁ ଏବେ ଅନ୍‌ କରିବେ?"</string>
-    <string name="bt_enable_cancel" msgid="6770180540581977614">"ବାତିଲ୍ କରନ୍ତୁ"</string>
+    <string name="bt_enable_cancel" msgid="6770180540581977614">"ବାତିଲ କରନ୍ତୁ"</string>
     <string name="bt_enable_ok" msgid="4224374055813566166">"ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="incoming_file_confirm_title" msgid="938251186275547290">"ଫାଇଲ୍‌ ଟ୍ରାନ୍ସଫର୍‌ କରନ୍ତୁ"</string>
     <string name="incoming_file_confirm_content" msgid="6573502088511901157">"ଆସୁଥିବା ଫାଇଲକୁ ଗ୍ରହଣ କରିବେ?"</string>
@@ -117,7 +117,7 @@
     <string name="transfer_clear_dlg_title" msgid="128904516163257225">"ଖାଲି କରନ୍ତୁ"</string>
     <string name="bluetooth_a2dp_sink_queue_name" msgid="7521243473328258997">"ବର୍ତ୍ତମାନ ଚାଲୁଛି"</string>
     <string name="bluetooth_map_settings_save" msgid="8309113239113961550">"ସେଭ୍‌ କରନ୍ତୁ"</string>
-    <string name="bluetooth_map_settings_cancel" msgid="3374494364625947793">"ବାତିଲ୍ କରନ୍ତୁ"</string>
+    <string name="bluetooth_map_settings_cancel" msgid="3374494364625947793">"ବାତିଲ କରନ୍ତୁ"</string>
     <string name="bluetooth_map_settings_intro" msgid="4748160773998753325">"ବ୍ଲୁଟୂଥ୍‍‌ ମାଧ୍ୟମରେ ସେୟାର୍‌ କରିବାକୁ ଚାହୁଁଥିବା ଆକାଉଣ୍ଟଗୁଡ଼ିକୁ ଚୟନ କରନ୍ତୁ। ଆପଣଙ୍କୁ ତଥାପି ସଂଯୋଗ କରୁଥିବା ସମୟରେ ଆକାଉଣ୍ଟଗୁଡ଼ିକ ପ୍ରତି ଯେକୌଣସି ଆକ୍ସେସ୍‌କୁ ସ୍ୱୀକାର କରିବାକୁ ପଡ଼ିବ।"</string>
     <string name="bluetooth_map_settings_count" msgid="183013143617807702">"ବଳକା ସ୍ଲଟ୍‌:"</string>
     <string name="bluetooth_map_settings_app_icon" msgid="3501432663809664982">"ଆପ୍ଲିକେଶନ୍‌ ଆଇକନ୍‌"</string>
diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
index b6321a4..61fbb24 100644
--- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
@@ -217,7 +217,7 @@
                             break;      // The device is already connected
                         }
                         mA2dpConnectedDevices.add(device);
-                        if (mHearingAidActiveDevice == null) {
+                        if (mHearingAidActiveDevice == null && mLeAudioActiveDevice == null) {
                             // New connected device: select it as active
                             setA2dpActiveDevice(device);
                             break;
@@ -277,7 +277,7 @@
                             break;      // The device is already connected
                         }
                         mHfpConnectedDevices.add(device);
-                        if (mHearingAidActiveDevice == null) {
+                        if (mHearingAidActiveDevice == null && mLeAudioActiveDevice == null) {
                             // New connected device: select it as active
                             setHfpActiveDevice(device);
                             break;
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index 0251a22..53ff384 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -5034,7 +5034,8 @@
     private static final String GD_L2CAP_FLAG = "INIT_gd_l2cap";
     private static final String GD_RUST_FLAG = "INIT_gd_rust";
     private static final String GD_LINK_POLICY_FLAG = "INIT_gd_link_policy";
-    private static final String GATT_ROBUST_CACHING_FLAG = "INIT_gatt_robust_caching";
+    private static final String GATT_ROBUST_CACHING_CLIENT_FLAG = "INIT_gatt_robust_caching_client";
+    private static final String GATT_ROBUST_CACHING_SERVER_FLAG = "INIT_gatt_robust_caching_server";
 
     /**
      * Logging flags logic (only applies to DEBUG and VERBOSE levels):
@@ -5088,8 +5089,12 @@
             initFlags.add(String.format("%s=%s", GD_LINK_POLICY_FLAG, "true"));
         }
         if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
-                GATT_ROBUST_CACHING_FLAG, false)) {
-            initFlags.add(String.format("%s=%s", GATT_ROBUST_CACHING_FLAG, "true"));
+                GATT_ROBUST_CACHING_CLIENT_FLAG, false)) {
+            initFlags.add(String.format("%s=%s", GATT_ROBUST_CACHING_CLIENT_FLAG, "true"));
+        }
+        if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
+                GATT_ROBUST_CACHING_SERVER_FLAG, false)) {
+            initFlags.add(String.format("%s=%s", GATT_ROBUST_CACHING_SERVER_FLAG, "true"));
         }
         if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH,
                 LOGGING_DEBUG_ENABLED_FOR_ALL_FLAG, false)) {
diff --git a/android/app/src/com/android/bluetooth/btservice/Config.java b/android/app/src/com/android/bluetooth/btservice/Config.java
index 5dad27b..1c02159 100644
--- a/android/app/src/com/android/bluetooth/btservice/Config.java
+++ b/android/app/src/com/android/bluetooth/btservice/Config.java
@@ -19,6 +19,7 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.res.Resources;
+import android.os.SystemProperties;
 import android.util.Log;
 
 import com.android.bluetooth.R;
@@ -61,6 +62,11 @@
     private static final String FEATURE_BATTERY = "settings_bluetooth_battery";
     private static long sSupportedMask = 0;
 
+    private static final String LE_AUDIO_DYNAMIC_SWITCH_PROPERTY =
+            "ro.bluetooth.leaudio_switcher.supported";
+    private static final String LE_AUDIO_DYNAMIC_ENABLED_PROPERTY =
+            "persist.bluetooth.leaudio_switcher.enabled";
+
     private static class ProfileConfig {
         Class mClass;
         boolean mSupported;
@@ -159,6 +165,19 @@
     private static boolean sIsGdEnabledUptoScanningLayer = false;
 
     static void init(Context ctx) {
+        final boolean leAudioDynamicSwitchSupported =
+                SystemProperties.getBoolean(LE_AUDIO_DYNAMIC_SWITCH_PROPERTY, false);
+
+        if (leAudioDynamicSwitchSupported) {
+            final String leAudioDynamicEnabled = SystemProperties
+                    .get(LE_AUDIO_DYNAMIC_ENABLED_PROPERTY, "none");
+            if (leAudioDynamicEnabled.equals("true")) {
+                setLeAudioProfileStatus(true);
+            } else if (leAudioDynamicEnabled.equals("false")) {
+                setLeAudioProfileStatus(false);
+            }
+        }
+
         ArrayList<Class> profiles = new ArrayList<>(PROFILE_SERVICES_AND_FLAGS.length);
         for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
             Log.i(TAG, "init: profile=" + config.mClass.getSimpleName() + ", enabled="
@@ -179,6 +198,15 @@
         sIsGdEnabledUptoScanningLayer = resources.getBoolean(R.bool.enable_gd_up_to_scanning_layer);
     }
 
+    static void setLeAudioProfileStatus(Boolean enable) {
+        setProfileEnabled(CsipSetCoordinatorService.class, enable);
+        setProfileEnabled(HapClientService.class, enable);
+        setProfileEnabled(LeAudioService.class, enable);
+        setProfileEnabled(TbsService.class, enable);
+        setProfileEnabled(McpService.class, enable);
+        setProfileEnabled(VolumeControlService.class, enable);
+    }
+
     /**
      * Remove the input profiles from the supported list.
      */
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 33fa96f..101dc41 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -128,6 +128,9 @@
     LeAudioTmapGattServer mTmapGattServer;
 
     @VisibleForTesting
+    VolumeControlService mVolumeControlService;
+
+    @VisibleForTesting
     RemoteCallbackList<IBluetoothLeBroadcastCallback> mBroadcastCallbacks;
 
     @VisibleForTesting
@@ -384,6 +387,7 @@
 
         mAdapterService = null;
         mAudioManager = null;
+        mVolumeControlService = null;
 
         return true;
     }
@@ -412,6 +416,18 @@
         sLeAudioService = instance;
     }
 
+    private int getGroupVolume(int groupId) {
+        if (mVolumeControlService == null) {
+            mVolumeControlService = mServiceFactory.getVolumeControlService();
+        }
+        if (mVolumeControlService == null) {
+            Log.e(TAG, "Volume control service is not available");
+            return -1;
+        }
+
+        return mVolumeControlService.getGroupVolume(groupId);
+    }
+
     public boolean connect(BluetoothDevice device) {
         if (DBG) {
             Log.d(TAG, "connect(): " + device);
@@ -830,6 +846,7 @@
                             + previousInDevice + ", mActiveAudioInDevice" + mActiveAudioInDevice
                             + " isLeOutput: false");
             }
+
             mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioInDevice,previousInDevice,
                     BluetoothProfileConnectionInfo.createLeAudioInfo(false, false));
 
@@ -899,9 +916,14 @@
                             + previousOutDevice + ", mActiveOutDevice: " + mActiveAudioOutDevice
                             + " isLeOutput: true");
             }
+            int volume = -1;
+            if (mActiveAudioOutDevice != null) {
+                volume = getGroupVolume(groupId);
+            }
+
             mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioOutDevice,
                     previousOutDevice,
-                    BluetoothProfileConnectionInfo.createLeAudioInfo(suppressNoisyIntent, true));
+                    getLeAudioOutputProfile(suppressNoisyIntent, volume));
             return true;
         }
         Log.d(TAG, "updateActiveOutDevice: Nothing to do.");
@@ -1054,6 +1076,22 @@
         }
     }
 
+    BluetoothProfileConnectionInfo getLeAudioOutputProfile(boolean suppressNoisyIntent,
+            int volume) {
+        /* TODO - b/236618595 */
+        Parcel parcel = Parcel.obtain();
+        parcel.writeInt(BluetoothProfile.LE_AUDIO);
+        parcel.writeBoolean(suppressNoisyIntent);
+        parcel.writeInt(volume);
+        parcel.writeBoolean(true /* isLeOutput */);
+        parcel.setDataPosition(0);
+
+        BluetoothProfileConnectionInfo profileInfo =
+                BluetoothProfileConnectionInfo.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+        return profileInfo;
+    }
+
     BluetoothProfileConnectionInfo getBroadcastProfile(boolean suppressNoisyIntent) {
         Parcel parcel = Parcel.obtain();
         parcel.writeInt(BluetoothProfile.LE_AUDIO_BROADCAST);
@@ -1062,7 +1100,10 @@
         parcel.writeBoolean(true /* mIsLeOutput */);
         parcel.setDataPosition(0);
 
-        return BluetoothProfileConnectionInfo.CREATOR.createFromParcel(parcel);
+        BluetoothProfileConnectionInfo profileInfo =
+                BluetoothProfileConnectionInfo.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+        return profileInfo;
     }
 
     private void clearLostDevicesWhileStreaming(LeAudioGroupDescriptor descriptor) {
@@ -1766,9 +1807,11 @@
             return;
         }
 
-        VolumeControlService service = mServiceFactory.getVolumeControlService();
-        if (service != null) {
-            service.setVolumeGroup(currentlyActiveGroupId, volume);
+        if (mVolumeControlService == null) {
+            mVolumeControlService = mServiceFactory.getVolumeControlService();
+        }
+        if (mVolumeControlService != null) {
+            mVolumeControlService.setGroupVolume(currentlyActiveGroupId, volume);
         }
     }
 
diff --git a/android/app/src/com/android/bluetooth/mcp/McpService.java b/android/app/src/com/android/bluetooth/mcp/McpService.java
index 208997a..dca1c1d 100644
--- a/android/app/src/com/android/bluetooth/mcp/McpService.java
+++ b/android/app/src/com/android/bluetooth/mcp/McpService.java
@@ -176,6 +176,11 @@
     }
 
     public void onDeviceUnauthorized(BluetoothDevice device) {
+        if (Utils.isPtsTestMode()) {
+            Log.d(TAG, "PTS test: setDeviceAuthorized");
+            setDeviceAuthorized(device, true);
+            return;
+        }
         Log.w(TAG, "onDeviceUnauthorized - authorization notification not implemented yet ");
     }
 
@@ -194,9 +199,10 @@
     }
 
     public int getDeviceAuthorization(BluetoothDevice device) {
-        // TODO: For now just reject authorization for other than LeAudio device already authorized.
-        //       Consider intent based authorization mechanism for non-LeAudio devices.
-        return mDeviceAuthorizations.getOrDefault(device, BluetoothDevice.ACCESS_UNKNOWN);
+        // TODO: For now just reject authorization for other than LeAudio device already authorized
+        // except for PTS. Consider intent based authorization mechanism for non-LeAudio devices.
+        return mDeviceAuthorizations.getOrDefault(device, Utils.isPtsTestMode()
+                ? BluetoothDevice.ACCESS_ALLOWED : BluetoothDevice.ACCESS_UNKNOWN);
     }
 
     @GuardedBy("mLock")
diff --git a/android/app/src/com/android/bluetooth/tbs/BluetoothLeCallControlProxy.java b/android/app/src/com/android/bluetooth/tbs/BluetoothLeCallControlProxy.java
index ca0534b..164eadc 100644
--- a/android/app/src/com/android/bluetooth/tbs/BluetoothLeCallControlProxy.java
+++ b/android/app/src/com/android/bluetooth/tbs/BluetoothLeCallControlProxy.java
@@ -17,10 +17,9 @@
 
 package com.android.bluetooth.tbs;
 
-import android.bluetooth.BluetoothLeCallControl;
 import android.bluetooth.BluetoothLeCall;
+import android.bluetooth.BluetoothLeCallControl;
 
-import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
 import java.util.concurrent.Executor;
@@ -36,6 +35,16 @@
 
     private BluetoothLeCallControl mBluetoothLeCallControl;
 
+    public static final int BEARER_TECHNOLOGY_3G = 0x01;
+    public static final int BEARER_TECHNOLOGY_4G = 0x02;
+    public static final int BEARER_TECHNOLOGY_LTE = 0x03;
+    public static final int BEARER_TECHNOLOGY_WIFI = 0x04;
+    public static final int BEARER_TECHNOLOGY_5G = 0x05;
+    public static final int BEARER_TECHNOLOGY_GSM = 0x06;
+    public static final int BEARER_TECHNOLOGY_CDMA = 0x07;
+    public static final int BEARER_TECHNOLOGY_2G = 0x08;
+    public static final int BEARER_TECHNOLOGY_WCDMA = 0x09;
+
     public BluetoothLeCallControlProxy(BluetoothLeCallControl tbs) {
         mBluetoothLeCallControl = tbs;
     }
diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
index b8dbbff..d73ae5b 100644
--- a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
+++ b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java
@@ -52,7 +52,11 @@
 
     private static final String UCI = "GTBS";
     private static final String DEFAULT_PROVIDER_NAME = "none";
-    private static final int DEFAULT_BEARER_TECHNOLOGY = 0x00;
+    /* Use GSM as default technology value. It is used only
+     * when bearer is not registered. It will be updated on the phone call
+     */
+    private static final int DEFAULT_BEARER_TECHNOLOGY =
+            BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM;
     private static final String UNKNOWN_FRIENDLY_NAME = "unknown";
 
     /** Class representing the pending request sent to the application */
diff --git a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
index 2313ff0..85ebcab 100644
--- a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
+++ b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
@@ -417,6 +417,62 @@
         }
     }
 
+    /**
+     * Gets the brearer technology.
+     *
+     * @return bearer technology as defined in Bluetooth Assigned Numbers
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public int getBearerTechnology()  {
+        synchronized (LOCK) {
+            enforceModifyPermission();
+            Log.i(TAG, "getBearerTechnology");
+            // Get the network name from telephony.
+            int dataNetworkType = mTelephonyManager.getDataNetworkType();
+            switch (dataNetworkType) {
+                case TelephonyManager.NETWORK_TYPE_UNKNOWN:
+                case TelephonyManager.NETWORK_TYPE_GSM:
+                    return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM;
+
+                case TelephonyManager.NETWORK_TYPE_GPRS:
+                    return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_2G;
+
+                case TelephonyManager.NETWORK_TYPE_EDGE :
+                case TelephonyManager.NETWORK_TYPE_EVDO_0:
+                case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                case TelephonyManager.NETWORK_TYPE_HSDPA:
+                case TelephonyManager.NETWORK_TYPE_HSUPA:
+                case TelephonyManager.NETWORK_TYPE_HSPA:
+                case TelephonyManager.NETWORK_TYPE_IDEN:
+                case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                    return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_3G;
+
+                case TelephonyManager.NETWORK_TYPE_UMTS:
+                case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+                    return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_WCDMA;
+
+                case TelephonyManager.NETWORK_TYPE_LTE:
+                    return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_LTE;
+
+                case TelephonyManager.NETWORK_TYPE_EHRPD:
+                case TelephonyManager.NETWORK_TYPE_CDMA:
+                case TelephonyManager.NETWORK_TYPE_1xRTT:
+                    return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_CDMA;
+
+                case TelephonyManager.NETWORK_TYPE_HSPAP:
+                    return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_4G;
+
+                case TelephonyManager.NETWORK_TYPE_IWLAN:
+                    return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_WIFI;
+
+                case TelephonyManager.NETWORK_TYPE_NR:
+                    return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_5G;
+            }
+
+            return BluetoothLeCallControlProxy.BEARER_TECHNOLOGY_GSM;
+        }
+    }
+
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public String getSubscriberNumber() {
         synchronized (LOCK) {
@@ -1241,9 +1297,10 @@
         mBluetoothLeCallControl = bluetoothTbs;
 
         if ((mBluetoothLeCallControl) != null && (mTelecomManager != null)) {
-            mBluetoothLeCallControl.registerBearer(TAG, new ArrayList<String>(Arrays.asList("tel")),
-                    BluetoothLeCallControl.CAPABILITY_HOLD_CALL, getNetworkOperator(), 0x01, mExecutor,
-                    mBluetoothLeCallControlCallback);
+            mBluetoothLeCallControl.registerBearer(TAG,
+                    new ArrayList<String>(Arrays.asList("tel")),
+                    BluetoothLeCallControl.CAPABILITY_HOLD_CALL, getNetworkOperator(),
+                    getBearerTechnology(), mExecutor, mBluetoothLeCallControlCallback);
         }
     }
 
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java b/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
index 0a50c6d..2c3c20c 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
@@ -113,8 +113,8 @@
      * @param volume
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void setVolumeGroup(int groupId, int volume) {
-        setVolumeGroupNative(groupId, volume);
+    public void setGroupVolume(int groupId, int volume) {
+        setGroupVolumeNative(groupId, volume);
     }
 
      /**
@@ -370,7 +370,7 @@
     private native boolean connectVolumeControlNative(byte[] address);
     private native boolean disconnectVolumeControlNative(byte[] address);
     private native void setVolumeNative(byte[] address, int volume);
-    private native void setVolumeGroupNative(int groupId, int volume);
+    private native void setGroupVolumeNative(int groupId, int volume);
     private native void muteNative(byte[] address);
     private native void muteGroupNative(int groupId);
     private native void unmuteNative(byte[] address);
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
index e04c2e6..078ebef 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
@@ -179,6 +179,7 @@
     private final Map<BluetoothDevice, VolumeControlStateMachine> mStateMachines = new HashMap<>();
     private final Map<BluetoothDevice, VolumeControlOffsetDescriptor> mAudioOffsets =
                                                                             new HashMap<>();
+    private final Map<Integer, Integer> mGroupVolumeCache = new HashMap<>();
 
     private BroadcastReceiver mBondStateChangedReceiver;
     private BroadcastReceiver mConnectionStateChangedReceiver;
@@ -242,6 +243,7 @@
         registerReceiver(mConnectionStateChangedReceiver, filter);
 
         mAudioOffsets.clear();
+        mGroupVolumeCache.clear();
         mCallbacks = new RemoteCallbackList<IBluetoothVolumeControlCallback>();
 
         // Mark service as started
@@ -296,6 +298,7 @@
         mVolumeControlNativeInterface = null;
 
         mAudioOffsets.clear();
+        mGroupVolumeCache.clear();
 
         // Clear AdapterService, VolumeControlNativeInterface
         mAudioManager = null;
@@ -578,8 +581,17 @@
     /**
      * {@hide}
      */
-    public void setVolumeGroup(int groupId, int volume) {
-        mVolumeControlNativeInterface.setVolumeGroup(groupId, volume);
+    public void setGroupVolume(int groupId, int volume) {
+        mGroupVolumeCache.put(groupId, volume);
+        mVolumeControlNativeInterface.setGroupVolume(groupId, volume);
+    }
+
+    /**
+     * {@hide}
+     * @param groupId
+     */
+    public int getGroupVolume(int groupId) {
+        return mGroupVolumeCache.getOrDefault(groupId, -1);
     }
 
     /**
@@ -618,6 +630,11 @@
         }
         // TODO: Handle the other arguments: device, groupId, mute.
 
+        /* We are interested only in the group volume as any LeAudio device is a part of group */
+        if (device == null) {
+            mGroupVolumeCache.put(groupId, volume);
+        }
+
         int streamType = getBluetoothContextualVolumeStream();
         mAudioManager.setStreamVolume(streamType, getDeviceVolume(streamType, volume),
                 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME);
@@ -1125,7 +1142,7 @@
         }
 
         @Override
-        public void setVolumeGroup(int groupId, int volume, AttributionSource source,
+        public void setGroupVolume(int groupId, int volume, AttributionSource source,
                 SynchronousResultReceiver receiver) {
             try {
                 Objects.requireNonNull(source, "source cannot be null");
@@ -1133,7 +1150,7 @@
 
                 VolumeControlService service = getService(source);
                 if (service != null) {
-                    service.setVolumeGroup(groupId, volume);
+                    service.setGroupVolume(groupId, volume);
                 }
                 receiver.send(null);
             } catch (RuntimeException e) {
@@ -1142,6 +1159,24 @@
         }
 
         @Override
+        public void getGroupVolume(int groupId, AttributionSource source,
+                SynchronousResultReceiver receiver) {
+            try {
+                Objects.requireNonNull(source, "source cannot be null");
+                Objects.requireNonNull(receiver, "receiver cannot be null");
+
+                VolumeControlService service = getService(source);
+                if (service != null) {
+                    service.getGroupVolume(groupId);
+                }
+                receiver.send(null);
+            } catch (RuntimeException e) {
+                receiver.propagateException(e);
+            }
+        }
+
+
+        @Override
         public void mute(BluetoothDevice device,  AttributionSource source,
                 SynchronousResultReceiver receiver) {
             try {
@@ -1271,5 +1306,9 @@
             ProfileService.println(sb, "    Volume offset cnt: " + descriptor.size());
             descriptor.dump(sb);
         }
+        for (Map.Entry<Integer, Integer> entry : mGroupVolumeCache.entrySet()) {
+            ProfileService.println(sb, "    GroupId: " + entry.getKey() + " volume: "
+                            + entry.getValue());
+        }
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
index bc4221e..f6e8015 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
@@ -23,21 +23,21 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.Intent;
 import android.media.AudioManager;
-import android.sysprop.BluetoothProperties;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.a2dp.A2dpService;
 import com.android.bluetooth.hearingaid.HearingAidService;
 import com.android.bluetooth.hfp.HeadsetService;
+import com.android.bluetooth.le_audio.LeAudioService;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -57,6 +57,7 @@
     private BluetoothDevice mHeadsetDevice;
     private BluetoothDevice mA2dpHeadsetDevice;
     private BluetoothDevice mHearingAidDevice;
+    private BluetoothDevice mLeAudioDevice;
     private ActiveDeviceManager mActiveDeviceManager;
     private static final int TIMEOUT_MS = 1000;
 
@@ -65,6 +66,7 @@
     @Mock private A2dpService mA2dpService;
     @Mock private HeadsetService mHeadsetService;
     @Mock private HearingAidService mHearingAidService;
+    @Mock private LeAudioService mLeAudioService;
     @Mock private AudioManager mAudioManager;
 
     @Before
@@ -83,9 +85,11 @@
         when(mServiceFactory.getA2dpService()).thenReturn(mA2dpService);
         when(mServiceFactory.getHeadsetService()).thenReturn(mHeadsetService);
         when(mServiceFactory.getHearingAidService()).thenReturn(mHearingAidService);
+        when(mServiceFactory.getLeAudioService()).thenReturn(mLeAudioService);
         when(mA2dpService.setActiveDevice(any())).thenReturn(true);
         when(mHeadsetService.setActiveDevice(any())).thenReturn(true);
         when(mHearingAidService.setActiveDevice(any())).thenReturn(true);
+        when(mLeAudioService.setActiveDevice(any())).thenReturn(true);
 
         mActiveDeviceManager = new ActiveDeviceManager(mAdapterService, mServiceFactory);
         mActiveDeviceManager.start();
@@ -96,6 +100,7 @@
         mHeadsetDevice = TestUtils.getTestDevice(mAdapter, 1);
         mA2dpHeadsetDevice = TestUtils.getTestDevice(mAdapter, 2);
         mHearingAidDevice = TestUtils.getTestDevice(mAdapter, 3);
+        mLeAudioDevice = TestUtils.getTestDevice(mAdapter, 4);
     }
 
     @After
@@ -212,6 +217,7 @@
         Assert.assertEquals(mHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice());
     }
 
+
     /**
      * A combo (A2DP + Headset) device is connected. Then a Hearing Aid is connected.
      */
@@ -288,6 +294,81 @@
     }
 
     /**
+     * A combo (A2DP + Headset) device is connected. Then an LE Audio is connected.
+     */
+    @Test
+    public void leAudioActive_clearA2dpAndHeadsetActive() {
+        Assume.assumeTrue("Ignore test when LeAudioService is not enabled",
+                LeAudioService.isEnabled());
+
+        a2dpConnected(mA2dpHeadsetDevice);
+        headsetConnected(mA2dpHeadsetDevice);
+        verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice);
+        verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice);
+
+        leAudioActiveDeviceChanged(mLeAudioDevice);
+        verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull());
+        verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull());
+    }
+
+    /**
+     * An LE Audio is connected. Then a combo (A2DP + Headset) device is connected.
+     */
+    @Test
+    public void leAudioActive_dontSetA2dpAndHeadsetActive() {
+        Assume.assumeTrue("Ignore test when LeAudioService is not enabled",
+                LeAudioService.isEnabled());
+
+        leAudioActiveDeviceChanged(mLeAudioDevice);
+        a2dpConnected(mA2dpHeadsetDevice);
+        headsetConnected(mA2dpHeadsetDevice);
+
+        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
+        verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice);
+        verify(mHeadsetService, never()).setActiveDevice(mA2dpHeadsetDevice);
+    }
+
+    /**
+     * An LE Audio is connected. Then an A2DP active device is explicitly set.
+     */
+    @Test
+    public void leAudioActive_setA2dpActiveExplicitly() {
+        Assume.assumeTrue("Ignore test when LeAudioService is not enabled",
+                LeAudioService.isEnabled());
+
+        leAudioActiveDeviceChanged(mLeAudioDevice);
+        a2dpConnected(mA2dpHeadsetDevice);
+        a2dpActiveDeviceChanged(mA2dpHeadsetDevice);
+
+        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
+        verify(mLeAudioService).setActiveDevice(isNull());
+        // Don't call mA2dpService.setActiveDevice()
+        verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice);
+        Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getA2dpActiveDevice());
+        Assert.assertEquals(null, mActiveDeviceManager.getLeAudioActiveDevice());
+    }
+
+    /**
+     * An LE Audio is connected. Then a Headset active device is explicitly set.
+     */
+    @Test
+    public void leAudioActive_setHeadsetActiveExplicitly() {
+        Assume.assumeTrue("Ignore test when LeAudioService is not enabled",
+                LeAudioService.isEnabled());
+
+        leAudioActiveDeviceChanged(mLeAudioDevice);
+        headsetConnected(mA2dpHeadsetDevice);
+        headsetActiveDeviceChanged(mA2dpHeadsetDevice);
+
+        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
+        verify(mLeAudioService).setActiveDevice(isNull());
+        // Don't call mLeAudioService.setActiveDevice()
+        verify(mLeAudioService, never()).setActiveDevice(mA2dpHeadsetDevice);
+        Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice());
+        Assert.assertEquals(null, mActiveDeviceManager.getLeAudioActiveDevice());
+    }
+
+    /**
      * A wired audio device is connected. Then all active devices are set to null.
      */
     @Test
@@ -373,4 +454,14 @@
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
     }
+
+    /**
+     * Helper to indicate LE Audio active device changed for a device.
+     */
+    private void leAudioActiveDeviceChanged(BluetoothDevice device) {
+        Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+    }
+
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
index ae41ce0..b999f6c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
@@ -47,6 +47,7 @@
 import android.content.IntentFilter;
 import android.media.AudioManager;
 import android.media.BluetoothProfileConnectionInfo;
+import android.os.Parcel;
 import android.os.ParcelUuid;
 
 import androidx.test.InstrumentationRegistry;
@@ -58,6 +59,7 @@
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
+import com.android.bluetooth.vc.VolumeControlService;
 
 import org.junit.After;
 import org.junit.Assume;
@@ -65,6 +67,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
@@ -105,6 +108,7 @@
     @Mock private DatabaseManager mDatabaseManager;
     @Mock private LeAudioNativeInterface mNativeInterface;
     @Mock private AudioManager mAudioManager;
+    @Mock private VolumeControlService mVolumeControlService;
     @Mock private LeAudioTmapGattServer mTmapGattServer;
     @Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
 
@@ -179,6 +183,7 @@
         startService();
         mService.mLeAudioNativeInterface = mNativeInterface;
         mService.mAudioManager = mAudioManager;
+        mService.mVolumeControlService = mVolumeControlService;
 
         LeAudioStackEvent stackEvent =
         new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);
@@ -1381,4 +1386,53 @@
         verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(null), eq(leadDevice),
                 any(BluetoothProfileConnectionInfo.class));
     }
+
+    /**
+     * Test volume caching for the group
+     */
+    @Test
+    public void testVolumeCache() {
+        int groupId = 1;
+        int volume = 100;
+        int availableContexts = 4;
+
+        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
+        connectTestDevice(mLeftDevice, groupId);
+        connectTestDevice(mRightDevice, groupId);
+
+        assertThat(mService.setActiveDevice(mLeftDevice)).isTrue();
+
+        ArgumentCaptor<BluetoothProfileConnectionInfo> profileInfo =
+                        ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class);
+
+        //Add location support.
+        injectAudioConfChanged(groupId, availableContexts);
+
+        doReturn(-1).when(mVolumeControlService).getGroupVolume(groupId);
+        //Set group and device as active.
+        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
+
+        verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(any(), eq(null),
+                        profileInfo.capture());
+        assertThat(profileInfo.getValue().getVolume()).isEqualTo(-1);
+
+        mService.setVolume(volume);
+        verify(mVolumeControlService, times(1)).setGroupVolume(groupId, volume);
+
+        // Set group to inactive.
+        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);
+
+        verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(null), any(),
+                        any(BluetoothProfileConnectionInfo.class));
+
+        doReturn(100).when(mVolumeControlService).getGroupVolume(groupId);
+
+        //Set back to active and check if last volume is restored.
+        injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
+
+        verify(mAudioManager, times(2)).handleBluetoothActiveDeviceChanged(any(), eq(null),
+                        profileInfo.capture());
+
+        assertThat(profileInfo.getValue().getVolume()).isEqualTo(volume);
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java
index f127ede..6808758 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java
@@ -496,6 +496,33 @@
         mService.messageFromNative(stackEvent);
     }
 
+    /**
+     * Test Volume Control cache.
+     */
+    @Test
+    public void testVolumeCache() {
+        int groupId = 1;
+        int volume = 6;
+
+        Assert.assertEquals(-1, mService.getGroupVolume(groupId));
+        mService.setGroupVolume(groupId, volume);
+        Assert.assertEquals(volume, mService.getGroupVolume(groupId));
+
+        volume = 10;
+
+        // Send autonomus volume change.
+        VolumeControlStackEvent stackEvent = new VolumeControlStackEvent(
+                VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
+        stackEvent.device = null;
+        stackEvent.valueInt1 = groupId;
+        stackEvent.valueInt2 = volume;
+        stackEvent.valueBool1 = false;
+        stackEvent.valueBool2 = true; /* autonomus */
+        mService.messageFromNative(stackEvent);
+
+        Assert.assertEquals(volume, mService.getGroupVolume(groupId));
+    }
+
     private void connectDevice(BluetoothDevice device) {
         VolumeControlStackEvent connCompletedEvent;
 
diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java
index dda3f04..2d00f88 100644
--- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java
+++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java
@@ -95,6 +95,7 @@
 import java.io.FileOutputStream;
 import java.io.PrintWriter;
 import java.time.Duration;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -218,6 +219,7 @@
     private final ReentrantReadWriteLock mBluetoothLock = new ReentrantReadWriteLock();
     private boolean mBinding;
     private boolean mUnbinding;
+    private List<Integer> mSupportedProfileList = new ArrayList<>();
 
     private BluetoothModeChangeHelper mBluetoothModeChangeHelper;
 
@@ -872,6 +874,25 @@
         recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
     }
 
+    @GuardedBy("mBluetoothLock")
+    private List<Integer> synchronousGetSupportedProfiles(AttributionSource attributionSource)
+            throws RemoteException, TimeoutException {
+        final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>();
+        if (mBluetooth == null) return supportedProfiles;
+        final SynchronousResultReceiver<Long> recv = SynchronousResultReceiver.get();
+        mBluetooth.getSupportedProfiles(attributionSource, recv);
+        final long supportedProfilesBitMask =
+                recv.awaitResultNoInterrupt(getSyncTimeout()).getValue((long) 0);
+
+        for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) {
+            if ((supportedProfilesBitMask & (1 << i)) != 0) {
+                supportedProfiles.add(i);
+            }
+        }
+
+        return supportedProfiles;
+    }
+
     /**
      * Sends the current foreground user id to the Bluetooth process. This user id is used to
      * determine if Binder calls are coming from the active user.
@@ -1449,10 +1470,10 @@
             ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
             Intent intent;
             if (bluetoothProfile == BluetoothProfile.HEADSET
-                    && BluetoothProperties.isProfileHfpAgEnabled().orElse(false)) {
+                    && mSupportedProfileList.contains(BluetoothProfile.HEADSET)) {
                 intent = new Intent(IBluetoothHeadset.class.getName());
             } else if (bluetoothProfile == BluetoothProfile.LE_CALL_CONTROL
-                    && BluetoothProperties.isProfileCcpServerEnabled().orElse(false)) {
+                    && mSupportedProfileList.contains(BluetoothProfile.LE_CALL_CONTROL)) {
                 intent = new Intent(IBluetoothLeCallControl.class.getName());
             } else {
                 return false;
@@ -2241,6 +2262,14 @@
                         //Inform BluetoothAdapter instances that service is up
                         sendBluetoothServiceUpCallback();
 
+                        // Get the supported profiles list
+                        try {
+                            mSupportedProfileList = synchronousGetSupportedProfiles(
+                                    mContext.getAttributionSource());
+                        } catch (RemoteException | TimeoutException e) {
+                            Log.e(TAG, "Unable to get the supported profiles list", e);
+                        }
+
                         //Do enable request
                         try {
                             if (!synchronousEnable(mQuietEnable, mContext.getAttributionSource())) {
@@ -2319,6 +2348,7 @@
                                 break;
                             }
                             mBluetooth = null;
+                            mSupportedProfileList.clear();
                         } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
                             mBluetoothGatt = null;
                             break;
diff --git a/system/audio_hal_interface/aidl/client_interface_aidl.cc b/system/audio_hal_interface/aidl/client_interface_aidl.cc
index 814c6c7..6c7a687 100644
--- a/system/audio_hal_interface/aidl/client_interface_aidl.cc
+++ b/system/audio_hal_interface/aidl/client_interface_aidl.cc
@@ -46,6 +46,7 @@
 BluetoothAudioClientInterface::BluetoothAudioClientInterface(
     IBluetoothTransportInstance* instance)
     : provider_(nullptr),
+      provider_factory_(nullptr),
       session_started_(false),
       data_mq_(nullptr),
       transport_(instance) {
@@ -134,8 +135,12 @@
   }
   CHECK(provider_ != nullptr);
 
-  AIBinder_linkToDeath(provider_factory->asBinder().get(),
-                       death_recipient_.get(), this);
+  binder_status_t binder_status = AIBinder_linkToDeath(
+      provider_factory->asBinder().get(), death_recipient_.get(), this);
+  if (binder_status != STATUS_OK) {
+    LOG(ERROR) << "Failed to linkToDeath " << static_cast<int>(binder_status);
+  }
+  provider_factory_ = std::move(provider_factory);
 
   LOG(INFO) << "IBluetoothAudioProvidersFactory::openProvider() returned "
             << provider_.get()
@@ -150,8 +155,8 @@
 }
 
 BluetoothAudioSinkClientInterface::~BluetoothAudioSinkClientInterface() {
-  if (provider_ != nullptr) {
-    AIBinder_unlinkToDeath(provider_->asBinder().get(), death_recipient_.get(),
+  if (provider_factory_ != nullptr) {
+    AIBinder_unlinkToDeath(provider_factory_->asBinder().get(), death_recipient_.get(),
                            nullptr);
   }
 }
@@ -164,8 +169,8 @@
 }
 
 BluetoothAudioSourceClientInterface::~BluetoothAudioSourceClientInterface() {
-  if (provider_ != nullptr) {
-    AIBinder_unlinkToDeath(provider_->asBinder().get(), death_recipient_.get(),
+  if (provider_factory_ != nullptr) {
+    AIBinder_unlinkToDeath(provider_factory_->asBinder().get(), death_recipient_.get(),
                            nullptr);
   }
 }
diff --git a/system/audio_hal_interface/aidl/client_interface_aidl.h b/system/audio_hal_interface/aidl/client_interface_aidl.h
index 87dd450..17abefe 100644
--- a/system/audio_hal_interface/aidl/client_interface_aidl.h
+++ b/system/audio_hal_interface/aidl/client_interface_aidl.h
@@ -107,6 +107,8 @@
 
   std::shared_ptr<IBluetoothAudioProvider> provider_;
 
+  std::shared_ptr<IBluetoothAudioProviderFactory> provider_factory_;
+
   bool session_started_;
   std::unique_ptr<DataMQ> data_mq_;
 
diff --git a/system/binder/android/bluetooth/IBluetoothVolumeControl.aidl b/system/binder/android/bluetooth/IBluetoothVolumeControl.aidl
index eb6ddd1..d5f157c 100644
--- a/system/binder/android/bluetooth/IBluetoothVolumeControl.aidl
+++ b/system/binder/android/bluetooth/IBluetoothVolumeControl.aidl
@@ -50,7 +50,9 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
     void setVolumeOffset(in BluetoothDevice device, int volumeOffset, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
-    void setVolumeGroup(int group_id, int volume, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    void setGroupVolume(int group_id, int volume, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
+    void getGroupVolume(int group_id, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
     void mute(in BluetoothDevice device, in AttributionSource attributionSource, in SynchronousResultReceiver receiver);
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index a1199f6..afd1b11 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -142,6 +142,7 @@
     if (!csis_group) {
       if (create_group_if_non_existing) {
         /* Let's create a group */
+        LOG(INFO) << __func__ << ": Create a new group";
         auto g = std::make_shared<CsisGroup>(group_id, uuid);
         csis_groups_.push_back(g);
         csis_group = FindCsisGroup(group_id);
@@ -1340,11 +1341,6 @@
         group_id =
             dev_groups_->AddDevice(device->addr, csis_instance->GetUuid());
         LOG_ASSERT(group_id != -1);
-
-        /* Create new group */
-        auto g =
-            std::make_shared<CsisGroup>(group_id, csis_instance->GetUuid());
-        csis_groups_.push_back(g);
       } else {
         dev_groups_->AddDevice(device->addr, csis_instance->GetUuid(),
                                group_id);
diff --git a/system/bta/gatt/bta_gattc_utils.cc b/system/bta/gatt/bta_gattc_utils.cc
index f49ae36..1412702 100644
--- a/system/bta/gatt/bta_gattc_utils.cc
+++ b/system/bta/gatt/bta_gattc_utils.cc
@@ -677,5 +677,5 @@
  *
  ******************************************************************************/
 bool bta_gattc_is_robust_caching_enabled() {
-  return bluetooth::common::init_flags::gatt_robust_caching_is_enabled();
+  return bluetooth::common::init_flags::gatt_robust_caching_client_is_enabled();
 }
diff --git a/system/bta/le_audio/audio_set_scenarios.json b/system/bta/le_audio/audio_set_scenarios.json
index c92fbd6..628dfbe 100644
--- a/system/bta/le_audio/audio_set_scenarios.json
+++ b/system/bta/le_audio/audio_set_scenarios.json
@@ -128,7 +128,8 @@
                 "VND_SingleDev_TwoChanStereoSnk_48khz_75octs_TwoChanStereoSrc_16khz_30octs_Server_Preferred_1",
                 "VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_TwoChanStereoSrc_16khz_30octs_R3_L12_1",
                 "VND_SingleDev_TwoChanStereoSnk_48khz_75octs_Server_Preferred_1",
-                "VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_1"
+                "VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_1",
+                "DualDev_OneChanStereoSnk_48_4_Server_Preferred"
             ]
         },
         {
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 5206f7b..fbb07f4 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -82,6 +82,12 @@
 using le_audio::client_parser::ascs::kCtpResponseNoReason;
 
 /* Enums */
+enum class AudioReconfigurationResult {
+  RECONFIGURATION_NEEDED = 0x00,
+  RECONFIGURATION_NOT_NEEDED,
+  RECONFIGURATION_NOT_POSSIBLE
+};
+
 enum class AudioState {
   IDLE = 0x00,
   READY_TO_START,
@@ -90,6 +96,25 @@
   RELEASING,
 };
 
+std::ostream& operator<<(std::ostream& os,
+                         const AudioReconfigurationResult& state) {
+  switch (state) {
+    case AudioReconfigurationResult::RECONFIGURATION_NEEDED:
+      os << "RECONFIGURATION_NEEDED";
+      break;
+    case AudioReconfigurationResult::RECONFIGURATION_NOT_NEEDED:
+      os << "RECONFIGURATION_NOT_NEEDED";
+      break;
+    case AudioReconfigurationResult::RECONFIGURATION_NOT_POSSIBLE:
+      os << "RECONFIGRATION_NOT_POSSIBLE";
+      break;
+    default:
+      os << "UNKNOWN";
+      break;
+  }
+  return os;
+}
+
 std::ostream& operator<<(std::ostream& os, const AudioState& audio_state) {
   switch (audio_state) {
     case AudioState::IDLE:
@@ -487,7 +512,11 @@
       std::optional<AudioContexts> old_group_updated_contexts =
           old_group->UpdateActiveContextsMap(old_group->GetActiveContexts());
 
-      if (old_group_updated_contexts || old_group->ReloadAudioLocations()) {
+      bool group_conf_changed = old_group->ReloadAudioLocations();
+      group_conf_changed |= old_group->ReloadAudioDirections();
+      group_conf_changed |= old_group_updated_contexts.has_value();
+
+      if (group_conf_changed) {
         callbacks_->OnAudioConf(old_group->audio_directions_, old_group_id,
                                 old_group->snk_audio_locations_.to_ulong(),
                                 old_group->src_audio_locations_.to_ulong(),
@@ -550,7 +579,11 @@
     std::optional<AudioContexts> updated_contexts =
         group->UpdateActiveContextsMap(group->GetActiveContexts());
 
-    if (updated_contexts || group->ReloadAudioLocations())
+    bool group_conf_changed = group->ReloadAudioLocations();
+    group_conf_changed |= group->ReloadAudioDirections();
+    group_conf_changed |= updated_contexts.has_value();
+
+    if (group_conf_changed)
       callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                               group->snk_audio_locations_.to_ulong(),
                               group->src_audio_locations_.to_ulong(),
@@ -827,11 +860,24 @@
       }
     }
 
-    /* Configure audio HAL sessions with most frequent context.
-     * If reconfiguration is not needed it means, context type is not supported
+    /* Try configure audio HAL sessions with most frequent context.
+     * If reconfiguration is not needed it means, context type is not supported.
+     * If most frequest scenario is not supported, try to find first supported.
      */
+    LeAudioContextType default_context_type = LeAudioContextType::UNSPECIFIED;
+    if (group->IsContextSupported(LeAudioContextType::MEDIA)) {
+      default_context_type = LeAudioContextType::MEDIA;
+    } else {
+      for (LeAudioContextType context_type :
+           le_audio::types::kLeAudioContextAllTypesArray) {
+        if (group->IsContextSupported(context_type)) {
+          default_context_type = context_type;
+          break;
+        }
+      }
+    }
     UpdateConfigAndCheckIfReconfigurationIsNeeded(group_id,
-                                                  LeAudioContextType::MEDIA);
+                                                  default_context_type);
     if (current_source_codec_config.IsInvalid() &&
         current_sink_codec_config.IsInvalid()) {
       LOG(WARNING) << __func__ << ", unsupported device configurations";
@@ -1144,7 +1190,12 @@
       /* Read of source audio locations during initial attribute discovery.
        * Group would be assigned once service search is completed.
        */
-      if (group && group->ReloadAudioLocations()) {
+      if (!group) return;
+
+      bool group_conf_changed = group->ReloadAudioLocations();
+      group_conf_changed |= group->ReloadAudioDirections();
+
+      if (group_conf_changed) {
         callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                                 group->snk_audio_locations_.to_ulong(),
                                 group->src_audio_locations_.to_ulong(),
@@ -1173,7 +1224,12 @@
       /* Read of source audio locations during initial attribute discovery.
        * Group would be assigned once service search is completed.
        */
-      if (group && group->ReloadAudioLocations()) {
+      if (!group) return;
+
+      bool group_conf_changed = group->ReloadAudioLocations();
+      group_conf_changed |= group->ReloadAudioDirections();
+
+      if (group_conf_changed) {
         callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                                 group->snk_audio_locations_.to_ulong(),
                                 group->src_audio_locations_.to_ulong(),
@@ -1279,10 +1335,6 @@
       BtaGattQueue::ConfigureMtu(leAudioDevice->conn_id_, 240);
     }
 
-    /* If we know services, register for notifications */
-    if (leAudioDevice->known_service_handles_)
-      RegisterKnownNotifications(leAudioDevice);
-
     if (BTM_SecIsSecurityPending(address)) {
       /* if security collision happened, wait for encryption done
        * (BTA_GATTC_ENC_CMPL_CB_EVT) */
@@ -1376,6 +1428,10 @@
       return;
     }
 
+    /* If we know services, register for notifications */
+    if (leAudioDevice->known_service_handles_)
+      RegisterKnownNotifications(leAudioDevice);
+
     if (leAudioDevice->encrypted_) {
       LOG(INFO) << __func__ << " link already encrypted, nothing to do";
       return;
@@ -1993,8 +2049,8 @@
 
     if (bytes_per_sample == 2) {
       int16_t* out = (int16_t*)mono_out.data();
+      const int16_t* in = (int16_t*)(buf.data());
       for (size_t i = 0; i < frames; ++i) {
-        const int16_t* in = (int16_t*)(buf.data());
         int accum = 0;
         accum += *in++;
         accum += *in++;
@@ -2003,8 +2059,8 @@
       }
     } else if (bytes_per_sample == 4) {
       int32_t* out = (int32_t*)mono_out.data();
+      const int32_t* in = (int32_t*)(buf.data());
       for (size_t i = 0; i < frames; ++i) {
-        const int32_t* in = (int32_t*)(buf.data());
         int accum = 0;
         accum += *in++;
         accum += *in++;
@@ -2017,7 +2073,7 @@
     return mono_out;
   }
 
-  void PrepareAndSendToTwoDevices(
+  void PrepareAndSendToTwoCises(
       const std::vector<uint8_t>& data,
       struct le_audio::stream_configuration* stream_conf) {
     uint16_t byte_count = stream_conf->sink_octets_per_codec_frame;
@@ -2087,7 +2143,7 @@
           right_cis_handle, chan_right_enc.data(), chan_right_enc.size());
   }
 
-  void PrepareAndSendToSingleDevice(
+  void PrepareAndSendToSingleCis(
       const std::vector<uint8_t>& data,
       struct le_audio::stream_configuration* stream_conf) {
     int num_channels = stream_conf->sink_num_of_channels;
@@ -2270,9 +2326,12 @@
     }
 
     if (stream_conf.sink_num_of_devices == 2) {
-      PrepareAndSendToTwoDevices(data, &stream_conf);
+      PrepareAndSendToTwoCises(data, &stream_conf);
+    } else if (stream_conf.sink_streams.size() == 2 ) {
+      /* Streaming to one device but 2 CISes */
+      PrepareAndSendToTwoCises(data, &stream_conf);
     } else {
-      PrepareAndSendToSingleDevice(data, &stream_conf);
+      PrepareAndSendToSingleCis(data, &stream_conf);
     }
   }
 
@@ -2701,14 +2760,17 @@
     std::move(cleanupCb).Run();
   }
 
-  bool UpdateConfigAndCheckIfReconfigurationIsNeeded(
+  AudioReconfigurationResult UpdateConfigAndCheckIfReconfigurationIsNeeded(
       int group_id, LeAudioContextType context_type) {
     bool reconfiguration_needed = false;
+    bool sink_cfg_available = true;
+    bool source_cfg_available = true;
+
     auto group = aseGroups_.FindById(group_id);
     if (!group) {
       LOG(ERROR) << __func__
                  << ", Invalid group: " << static_cast<int>(group_id);
-      return reconfiguration_needed;
+      return AudioReconfigurationResult::RECONFIGURATION_NOT_NEEDED;
     }
 
     std::optional<LeAudioCodecConfiguration> source_configuration =
@@ -2728,11 +2790,7 @@
         current_source_codec_config = {0, 0, 0, 0};
         reconfiguration_needed = true;
       }
-
-      LOG(INFO) << __func__
-                << ", group does not supports source direction for"
-                   " context: "
-                << static_cast<int>(context_type);
+      source_cfg_available = false;
     }
 
     if (sink_configuration) {
@@ -2746,20 +2804,28 @@
         reconfiguration_needed = true;
       }
 
-      LOG(INFO) << __func__
-                << ", group does not supports sink direction for"
-                   " context: "
-                << static_cast<int>(context_type);
+      sink_cfg_available = false;
     }
 
-    if (reconfiguration_needed) {
-      LOG(INFO) << __func__
-                << " Session reconfiguration needed group: " << group->group_id_
-                << " for context type: " << static_cast<int>(context_type);
+    LOG_DEBUG(
+        " Context: %s Reconfigufation_needed = %d, sink_cfg_available = %d, "
+        "source_cfg_available = %d",
+        ToString(context_type).c_str(), reconfiguration_needed,
+        sink_cfg_available, source_cfg_available);
+
+    if (!reconfiguration_needed) {
+      return AudioReconfigurationResult::RECONFIGURATION_NOT_NEEDED;
     }
 
+    if (!sink_cfg_available && !source_cfg_available) {
+      return AudioReconfigurationResult::RECONFIGURATION_NOT_POSSIBLE;
+    }
+
+    LOG_INFO(" Session reconfiguration needed group: %d for context type: %s",
+             group->group_id_, ToString(context_type).c_str());
+
     current_context_type_ = context_type;
-    return reconfiguration_needed;
+    return AudioReconfigurationResult::RECONFIGURATION_NEEDED;
   }
 
   bool OnAudioResume(LeAudioDeviceGroup* group) {
@@ -3088,16 +3154,19 @@
   }
 
   LeAudioContextType AudioContentToLeAudioContext(
-      LeAudioContextType current_context_type,
       audio_content_type_t content_type, audio_usage_t usage) {
     /* Check audio attribute usage of stream */
     switch (usage) {
       case AUDIO_USAGE_MEDIA:
         return LeAudioContextType::MEDIA;
       case AUDIO_USAGE_VOICE_COMMUNICATION:
-      case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
       case AUDIO_USAGE_CALL_ASSISTANT:
         return LeAudioContextType::CONVERSATIONAL;
+      case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING:
+        if (content_type == AUDIO_CONTENT_TYPE_SPEECH)
+          return LeAudioContextType::CONVERSATIONAL;
+        else
+          return LeAudioContextType::MEDIA;
       case AUDIO_USAGE_GAME:
         return LeAudioContextType::GAME;
       case AUDIO_USAGE_NOTIFICATION:
@@ -3119,13 +3188,17 @@
 
   LeAudioContextType ChooseContextType(
       std::vector<LeAudioContextType>& available_contents) {
-    /* Mini policy. Voice is prio 1, media is prio 2 */
+    /* Mini policy. Voice is prio 1, game prio 2, media is prio 3 */
     auto iter = find(available_contents.begin(), available_contents.end(),
                      LeAudioContextType::CONVERSATIONAL);
     if (iter != available_contents.end())
       return LeAudioContextType::CONVERSATIONAL;
 
     iter = find(available_contents.begin(), available_contents.end(),
+                LeAudioContextType::GAME);
+    if (iter != available_contents.end()) return LeAudioContextType::GAME;
+
+    iter = find(available_contents.begin(), available_contents.end(),
                 LeAudioContextType::MEDIA);
     if (iter != available_contents.end()) return LeAudioContextType::MEDIA;
 
@@ -3135,10 +3208,19 @@
 
   bool StopStreamIfNeeded(LeAudioDeviceGroup* group,
                           LeAudioContextType new_context_type) {
-    DLOG(INFO) << __func__ << " context type " << int(new_context_type);
-    if (!UpdateConfigAndCheckIfReconfigurationIsNeeded(group->group_id_,
-                                                       new_context_type)) {
-      DLOG(INFO) << __func__ << " reconfiguration not needed";
+    auto reconfig_result = UpdateConfigAndCheckIfReconfigurationIsNeeded(
+        group->group_id_, new_context_type);
+
+    LOG_INFO("group_id %d, context type %s, reconfig_needed %s",
+             group->group_id_, ToString(new_context_type).c_str(),
+             ToString(reconfig_result).c_str());
+    if (reconfig_result ==
+        AudioReconfigurationResult::RECONFIGURATION_NOT_NEEDED) {
+      return false;
+    }
+
+    if (reconfig_result ==
+        AudioReconfigurationResult::RECONFIGURATION_NOT_POSSIBLE) {
       return false;
     }
 
@@ -3159,8 +3241,24 @@
     auto tracks = source_metadata.tracks;
     auto track_count = source_metadata.track_count;
 
+    if (active_group_id_ == bluetooth::groups::kGroupUnknown) {
+      LOG(WARNING) << ", cannot start streaming if no active group set";
+      return;
+    }
+
+    auto group = aseGroups_.FindById(active_group_id_);
+    if (!group) {
+      LOG(ERROR) << __func__
+                 << ", Invalid group: " << static_cast<int>(active_group_id_);
+      return;
+    }
+    bool is_group_streaming =
+        (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+
     std::vector<LeAudioContextType> contexts;
 
+    auto supported_context_type = group->GetActiveContexts();
+
     while (track_count) {
       if (tracks->content_type == 0 && tracks->usage == 0) {
         --track_count;
@@ -3171,37 +3269,31 @@
       LOG_INFO("%s: usage=%d, content_type=%d, gain=%f", __func__,
                tracks->usage, tracks->content_type, tracks->gain);
 
-      auto new_context = AudioContentToLeAudioContext(
-          current_context_type_, tracks->content_type, tracks->usage);
-      contexts.push_back(new_context);
+      auto new_context =
+          AudioContentToLeAudioContext(tracks->content_type, tracks->usage);
+
+      /* Check only supported context types.*/
+      if (static_cast<int>(new_context) & supported_context_type.to_ulong()) {
+        contexts.push_back(new_context);
+      } else {
+        LOG_WARN(" Context type %s not supported by remote device",
+                 ToString(new_context).c_str());
+      }
 
       --track_count;
       ++tracks;
     }
 
     if (contexts.empty()) {
-      DLOG(INFO) << __func__ << " invalid metadata update";
+      LOG_WARN(" invalid/unknown metadata update");
       return;
     }
 
     auto new_context = ChooseContextType(contexts);
-    DLOG(INFO) << __func__
-               << " new_context_type: " << static_cast<int>(new_context);
-
-    auto group = aseGroups_.FindById(active_group_id_);
-    if (!group) {
-      LOG(ERROR) << __func__
-                 << ", Invalid group: " << static_cast<int>(active_group_id_);
-      return;
-    }
+    LOG_DEBUG("new_context_type: %s", ToString(new_context).c_str());
 
     if (new_context == current_context_type_) {
-      LOG(INFO) << __func__ << " Context did not changed.";
-      return;
-    }
-
-    if (active_group_id_ == bluetooth::groups::kGroupUnknown) {
-      LOG(WARNING) << ", cannot start streaming if no active group set";
+      LOG_INFO("Context did not changed.");
       return;
     }
 
@@ -3210,7 +3302,7 @@
       return;
     }
 
-    if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+    if (is_group_streaming) {
       /* Configuration is the same for new context, just will do update
        * metadata of stream
        */
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index 6943f3e..0158b48 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -551,8 +551,8 @@
 
 /* This method returns AudioContext value if support for any type has changed */
 std::optional<AudioContexts> LeAudioDeviceGroup::UpdateActiveContextsMap(void) {
-  DLOG(INFO) << __func__ << " group id: " << group_id_ << " active contexts: "
-             << loghex(active_contexts_mask_.to_ulong());
+  LOG_DEBUG(" group id: %d, active contexts: 0x%04lx", group_id_,
+            active_contexts_mask_.to_ulong());
   return UpdateActiveContextsMap(active_contexts_mask_);
 }
 
@@ -562,10 +562,19 @@
   AudioContexts contexts = 0x0000;
   bool active_contexts_has_been_modified = false;
 
+  if (update_contexts.none()) {
+    LOG_DEBUG("No context updated");
+    return contexts;
+  }
+
   for (LeAudioContextType ctx_type : types::kLeAudioContextAllTypesArray) {
     AudioContexts type_set = static_cast<uint16_t>(ctx_type);
-
+    LOG_DEBUG("Taking context: %s, 0x%04lx",
+              bluetooth::common::ToString(ctx_type).c_str(),
+              update_contexts.to_ulong());
     if ((type_set & update_contexts).none()) {
+      LOG_INFO("Configuration not in updated context %s",
+               bluetooth::common::ToString(ctx_type).c_str());
       /* Fill context bitset for possible returned value if updated */
       if (active_context_to_configuration_map.count(ctx_type) > 0)
         contexts |= type_set;
@@ -575,9 +584,11 @@
 
     auto new_conf = FindFirstSupportedConfiguration(ctx_type);
 
+    bool ctx_previously_not_supported =
+        (active_context_to_configuration_map.count(ctx_type) == 0 ||
+         active_context_to_configuration_map[ctx_type] == nullptr);
     /* Check if support for context type has changed */
-    if (active_context_to_configuration_map.count(ctx_type) == 0 ||
-        active_context_to_configuration_map[ctx_type] == nullptr) {
+    if (ctx_previously_not_supported) {
       /* Current configuration for context type is empty */
       if (new_conf == nullptr) {
         /* Configuration remains empty */
@@ -604,12 +615,14 @@
       }
     }
 
-    LOG(INFO) << __func__ << ", updated context: " << loghex(int(ctx_type))
-              << ", "
-              << (active_context_to_configuration_map[ctx_type] != nullptr
-                      ? active_context_to_configuration_map[ctx_type]->name
-                      : "empty")
-              << " -> " << (new_conf != nullptr ? new_conf->name : "empty");
+    LOG_INFO(
+        "updated context: %s, %s -> %s",
+        bluetooth::common::ToString(ctx_type).c_str(),
+        (ctx_previously_not_supported
+             ? "empty"
+             : active_context_to_configuration_map[ctx_type]->name.c_str()),
+        (new_conf != nullptr ? new_conf->name.c_str() : "empty"));
+
     active_context_to_configuration_map[ctx_type] = new_conf;
   }
 
@@ -646,6 +659,22 @@
   return true;
 }
 
+bool LeAudioDeviceGroup::ReloadAudioDirections(void) {
+  uint8_t updated_audio_directions = 0x00;
+
+  for (const auto& device : leAudioDevices_) {
+    if (device.expired()) continue;
+    updated_audio_directions |= device.lock().get()->audio_directions_;
+  }
+
+  /* Nothing has changed */
+  if (updated_audio_directions == audio_directions_) return false;
+
+  audio_directions_ = updated_audio_directions;
+
+  return true;
+}
+
 bool LeAudioDeviceGroup::IsInTransition(void) {
   return target_state_ != current_state_;
 }
@@ -744,10 +773,9 @@
     types::LeAudioContextType context_type) {
   if (!set_configurations::check_if_may_cover_scenario(
           audio_set_conf, NumOfConnected(context_type))) {
-    DLOG(INFO) << __func__ << " cannot cover scenario "
-               << static_cast<int>(context_type)
-               << " size of for context type: "
-               << +NumOfConnected(context_type);
+    LOG_DEBUG(" cannot cover scenario  %s: size of for context type %d",
+              bluetooth::common::ToString(context_type).c_str(),
+              +NumOfConnected(context_type));
     return false;
   }
 
@@ -759,11 +787,9 @@
    * 3) ASEs should be filled according to performance profile.
    */
   for (const auto& ent : (*audio_set_conf).confs) {
-    DLOG(INFO) << __func__
-               << " Looking for configuration: " << audio_set_conf->name
-               << " - "
-               << (ent.direction == types::kLeAudioDirectionSink ? "snk"
-                                                                 : "src");
+    LOG_DEBUG(" Looking for configuration: %s - %s",
+              audio_set_conf->name.c_str(),
+              (ent.direction == types::kLeAudioDirectionSink ? "snk" : "src"));
 
     uint8_t required_device_cnt = ent.device_cnt;
     uint8_t max_required_ase_per_dev =
@@ -771,10 +797,11 @@
     uint8_t active_ase_num = 0;
     auto strategy = ent.strategy;
 
-    DLOG(INFO) << __func__ << " Number of devices: " << +required_device_cnt
-               << " number of ASEs: " << +ent.ase_cnt
-               << " Max ASE per device: " << +max_required_ase_per_dev
-               << " strategy: " << static_cast<int>(strategy);
+    LOG_DEBUG(
+        " Number of devices: %d, number of ASEs: %d,  Max ASE per device: %d "
+        "strategy: %d",
+        +required_device_cnt, +ent.ase_cnt, +max_required_ase_per_dev,
+        static_cast<int>(strategy));
 
     for (auto* device = GetFirstDeviceWithActiveContext(context_type);
          device != nullptr && required_device_cnt > 0;
@@ -806,8 +833,8 @@
               strategy, audio_locations,
               std::get<LeAudioLc3Config>(ent.codec.config).GetChannelCount(),
               device->GetLc3SupportedChannelCount(ent.direction))) {
-        DLOG(INFO) << __func__ << " insufficient device audio allocation: "
-                   << audio_locations;
+        LOG_DEBUG(" insufficient device audio allocation: %lu",
+                  audio_locations.to_ulong());
         continue;
       }
 
@@ -825,13 +852,13 @@
 
     if (required_device_cnt > 0) {
       /* Don't left any active devices if requirements are not met */
-      DLOG(INFO) << __func__ << " could not configure all the devices";
+      LOG_DEBUG(" could not configure all the devices");
       return false;
     }
   }
 
-  DLOG(INFO) << "Choosed ASE Configuration for group: " << this->group_id_
-             << " configuration: " << audio_set_conf->name;
+  LOG_DEBUG("Chosen ASE Configuration for group: %d, configuration: %s",
+            this->group_id_, audio_set_conf->name.c_str());
   return true;
 }
 
@@ -1205,13 +1232,14 @@
   const set_configurations::AudioSetConfigurations* confs =
       AudioSetConfigurationProvider::Get()->GetConfigurations(context_type);
 
-  DLOG(INFO) << __func__ << " context type: " << (int)context_type
-             << " number of connected devices: " << NumOfConnected();
+  LOG_DEBUG("context type: %s,  number of connected devices: %d",
+            bluetooth::common::ToString(context_type).c_str(),
+            +NumOfConnected());
 
   /* Filter out device set for all scenarios */
   if (!set_configurations::check_if_may_cover_scenario(confs,
                                                        NumOfConnected())) {
-    LOG(ERROR) << __func__ << ", group is unable to cover scenario";
+    LOG_ERROR(", group is unable to cover scenario");
     return nullptr;
   }
 
@@ -1219,7 +1247,7 @@
 
   for (const auto& conf : *confs) {
     if (IsConfigurationSupported(conf, context_type)) {
-      DLOG(INFO) << __func__ << " found: " << conf->name;
+      LOG_DEBUG("found: %s", conf->name.c_str());
       return conf;
     }
   }
@@ -1656,7 +1684,7 @@
       direction == types::kLeAudioDirectionSink ? snk_pacs_ : src_pacs_;
 
   if (pacs.size() == 0) {
-    LOG(ERROR) << __func__ << " missing PAC for direction " << +direction;
+    LOG_ERROR("missing PAC for direction %d", direction);
     return nullptr;
   }
 
@@ -1738,12 +1766,12 @@
   updated_contexts = snk_contexts ^ avail_snk_contexts_;
   updated_contexts |= src_contexts ^ avail_src_contexts_;
 
-  DLOG(INFO) << __func__
-             << "\n\t avail_snk_contexts_: " << avail_snk_contexts_.to_string()
-             << "\n\t avail_src_contexts_: " << avail_src_contexts_.to_string()
-             << "\n\t snk_contexts:" << snk_contexts.to_string()
-             << "\n\t src_contexts: " << src_contexts.to_string()
-             << "\n\t updated_contexts: " << updated_contexts.to_string();
+  LOG_DEBUG(
+      "\n\t avail_snk_contexts_: %s \n\t avail_src_contexts_: %s  \n\t "
+      "snk_contexts: %s \n\t src_contexts: %s \n\t updated_contexts: %s",
+      avail_snk_contexts_.to_string().c_str(),
+      avail_src_contexts_.to_string().c_str(), snk_contexts.to_string().c_str(),
+      src_contexts.to_string().c_str(), updated_contexts.to_string().c_str());
 
   avail_snk_contexts_ = snk_contexts;
   avail_src_contexts_ = src_contexts;
diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h
index 30e4bbc..2a7be7f 100644
--- a/system/bta/le_audio/devices.h
+++ b/system/bta/le_audio/devices.h
@@ -249,6 +249,7 @@
       types::AudioContexts contexts);
   std::optional<types::AudioContexts> UpdateActiveContextsMap(void);
   bool ReloadAudioLocations(void);
+  bool ReloadAudioDirections(void);
   const set_configurations::AudioSetConfiguration* GetActiveConfiguration(void);
   types::LeAudioContextType GetCurrentContextType(void);
   bool IsPendingConfiguration(void);
diff --git a/system/bta/le_audio/devices_test.cc b/system/bta/le_audio/devices_test.cc
index 7628182..706bfa2 100644
--- a/system/bta/le_audio/devices_test.cc
+++ b/system/bta/le_audio/devices_test.cc
@@ -526,6 +526,13 @@
     for (const auto& audio_set_conf : *configurations) {
       // the configuration should fail if there are no active ases expected
       bool success_expected = data_size > 0;
+      bool not_matching_scenario = false;
+      uint8_t snk_ases_cnt = 0;
+      uint8_t src_ases_cnt = 0;
+      PublishedAudioCapabilitiesBuilder snk_pac_builder, src_pac_builder;
+      snk_pac_builder.Reset();
+      src_pac_builder.Reset();
+
       for (int i = 0; i < data_size; i++) {
         success_expected &= (data[i].active_channel_num_snk +
                              data[i].active_channel_num_src) > 0;
@@ -536,19 +543,34 @@
          * DualDev). This is just how the test is created and this limitation
          * should be removed b/230107540
          */
-        PublishedAudioCapabilitiesBuilder snk_pac_builder, src_pac_builder;
         for (const auto& entry : (*audio_set_conf).confs) {
+          /* Configuration requires more devices than are supplied */
+          if (entry.device_cnt > data_size) {
+            not_matching_scenario = true;
+            break;
+          }
           if (entry.direction == kLeAudioDirectionSink) {
+            snk_ases_cnt += entry.ase_cnt;
             snk_pac_builder.Add(entry.codec, data[i].audio_channel_counts_snk);
           } else {
             src_pac_builder.Add(entry.codec, data[i].audio_channel_counts_src);
           }
         }
 
+        /* Scenario requires more ASEs than defined requirement */
+        if (snk_ases_cnt < data[i].audio_channel_counts_snk ||
+            src_ases_cnt < data[i].audio_channel_counts_src) {
+          not_matching_scenario = true;
+        }
+
+        if (not_matching_scenario) break;
+
         data[i].device->snk_pacs_ = snk_pac_builder.Get();
         data[i].device->src_pacs_ = src_pac_builder.Get();
       }
 
+      if (not_matching_scenario) continue;
+
       /* Stimulate update of active context map */
       group_->UpdateActiveContextsMap(static_cast<uint16_t>(context_type));
       ASSERT_EQ(success_expected, group_->Configure(context_type));
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index f2983dc..2525d23 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -808,7 +808,6 @@
 
   void SetUp() override {
     init_message_loop_thread();
-
     ON_CALL(controller_interface_, SupportsBleConnectedIsochronousStreamCentral)
         .WillByDefault(Return(true));
     ON_CALL(controller_interface_,
@@ -3161,5 +3160,47 @@
   LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
   Mock::VerifyAndClearExpectations(mock_unicast_audio_source_);
 }
+
+TEST_F(UnicastTest, StartNotSupportedContextType) {
+  const RawAddress test_address0 = GetTestAddress(0);
+  int group_id = bluetooth::groups::kGroupUnknown;
+
+  SetSampleDatabaseEarbudsValid(
+      1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
+      codec_spec_conf::kLeAudioLocationStereo, 0x0004, false /*add_csis*/,
+      true /*add_cas*/, true /*add_pacs*/, true /*add_ascs*/, 1 /*set_size*/,
+      0 /*rank*/);
+  EXPECT_CALL(mock_client_callbacks_,
+              OnConnectionState(ConnectionState::CONNECTED, test_address0))
+      .Times(1);
+  EXPECT_CALL(mock_client_callbacks_,
+              OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED))
+      .WillOnce(DoAll(SaveArg<1>(&group_id)));
+
+  ConnectLeAudio(test_address0);
+  ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);
+
+  // Start streaming
+  uint8_t cis_count_out = 1;
+  uint8_t cis_count_in = 0;
+
+  // Audio sessions are started only when device gets active
+  EXPECT_CALL(*mock_unicast_audio_source_, Start(_, _)).Times(1);
+  EXPECT_CALL(*mock_audio_sink_, Start(_, _)).Times(1);
+  LeAudioClient::Get()->GroupSetActive(group_id);
+
+  StartStreaming(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
+                 AUDIO_CONTENT_TYPE_UNKNOWN, group_id);
+
+  Mock::VerifyAndClearExpectations(&mock_client_callbacks_);
+  Mock::VerifyAndClearExpectations(mock_unicast_audio_source_);
+  SyncOnMainLoop();
+
+  // Verify Data transfer on one audio source cis
+  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);
+
+  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(0);
+  UpdateMetadata(AUDIO_USAGE_GAME, AUDIO_CONTENT_TYPE_UNKNOWN);
+}
 }  // namespace
 }  // namespace le_audio
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index 44d581b..69e958b 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -104,27 +104,33 @@
   auto req = reqs.Find(codec_spec_conf::kLeAudioCodecLC3TypeSamplingFreq);
   auto pac = pacs.Find(codec_spec_caps::kLeAudioCodecLC3TypeSamplingFreq);
   if (!req || !pac) {
-    DLOG(ERROR) << __func__ << ", lack of sampling frequency fields";
+    LOG_DEBUG(", lack of sampling frequency fields");
     return false;
   }
 
   u8_req_val = VEC_UINT8_TO_UINT8(req.value());
   u16_pac_val = VEC_UINT8_TO_UINT16(pac.value());
 
-  /*
-   * Note: Requirements are in the codec configuration specification which
-   * are values coming from BAP Appendix A1.2.1
-   */
-  DLOG(INFO) << __func__ << " Req:SamplFreq=" << loghex(u8_req_val);
-  /* NOTE: Below is Codec specific cababilities comes form BAP Appendix A A1.1.1
-   * Note this is a bitfield
-   */
-  DLOG(INFO) << __func__ << " Pac:SamplFreq=" << loghex(u16_pac_val);
-
   /* TODO: Integrate with codec capabilities */
   if (!(u16_pac_val &
         codec_spec_caps::SamplingFreqConfig2Capability(u8_req_val))) {
-    DLOG(ERROR) << __func__ << ", sampling frequency not supported";
+    /*
+     * Note: Requirements are in the codec configuration specification which
+     * are values coming from Assigned Numbers: Codec_Specific_Configuration
+     */
+    LOG_DEBUG(
+        " Req:SamplFreq= 0x%04x (Assigned Numbers: "
+        "Codec_Specific_Configuration)",
+        u8_req_val);
+    /* NOTE: Below is Codec specific cababilities comes from Assigned Numbers:
+     * Codec_Specific_Capabilities
+     */
+    LOG_DEBUG(
+        " Pac:SamplFreq= 0x%04x  (Assigned numbers: "
+        "Codec_Specific_Capabilities - bitfield)",
+        u16_pac_val);
+
+    LOG_DEBUG(", sampling frequency not supported");
     return false;
   }
 
@@ -132,20 +138,20 @@
   req = reqs.Find(codec_spec_conf::kLeAudioCodecLC3TypeFrameDuration);
   pac = pacs.Find(codec_spec_caps::kLeAudioCodecLC3TypeFrameDuration);
   if (!req || !pac) {
-    DLOG(ERROR) << __func__ << ", lack of frame duration fields";
+    LOG_DEBUG(", lack of frame duration fields");
     return false;
   }
 
   u8_req_val = VEC_UINT8_TO_UINT8(req.value());
   u8_pac_val = VEC_UINT8_TO_UINT8(pac.value());
-  DLOG(INFO) << __func__ << " Req:FrameDur=" << loghex(u8_req_val);
-  DLOG(INFO) << __func__ << " Pac:FrameDur=" << loghex(u8_pac_val);
 
   if ((u8_req_val != codec_spec_conf::kLeAudioCodecLC3FrameDur7500us &&
        u8_req_val != codec_spec_conf::kLeAudioCodecLC3FrameDur10000us) ||
       !(u8_pac_val &
         (codec_spec_caps::FrameDurationConfig2Capability(u8_req_val)))) {
-    DLOG(ERROR) << __func__ << ", frame duration not supported";
+    LOG_DEBUG(" Req:FrameDur=0x%04x", u8_req_val);
+    LOG_DEBUG(" Pac:FrameDur=0x%04x", u8_pac_val);
+    LOG_DEBUG(", frame duration not supported");
     return false;
   }
 
@@ -162,15 +168,16 @@
    * the Unicast Server supports mandatory one channel.
    */
   if (!pac) {
-    DLOG(WARNING) << __func__ << ", no Audio_Channel_Counts field in PAC";
+    LOG_DEBUG(", no Audio_Channel_Counts field in PAC, using default 0x01");
     u8_pac_val = 0x01;
   } else {
     u8_pac_val = VEC_UINT8_TO_UINT8(pac.value());
   }
 
-  DLOG(INFO) << __func__ << " Pac:AudioChanCnt=" << loghex(u8_pac_val);
   if (!((1 << (required_audio_chan_num - 1)) & u8_pac_val)) {
-    DLOG(ERROR) << __func__ << ", channel count warning";
+    LOG_DEBUG(" Req:AudioChanCnt=0x%04x", 1 << (required_audio_chan_num - 1));
+    LOG_DEBUG(" Pac:AudioChanCnt=0x%04x", u8_pac_val);
+    LOG_DEBUG(", channel count warning");
     return false;
   }
 
@@ -179,26 +186,26 @@
   pac = pacs.Find(codec_spec_caps::kLeAudioCodecLC3TypeOctetPerFrame);
 
   if (!req || !pac) {
-    DLOG(ERROR) << __func__ << ", lack of octet per frame fields";
+    LOG_DEBUG(", lack of octet per frame fields");
     return false;
   }
 
   u16_req_val = VEC_UINT8_TO_UINT16(req.value());
-  DLOG(INFO) << __func__ << " Req:OctetsPerFrame=" << int(u16_req_val);
-
   /* Minimal value 0-1 byte */
   u16_pac_val = VEC_UINT8_TO_UINT16(pac.value());
-  DLOG(INFO) << __func__ << " Pac:MinOctetsPerFrame=" << int(u16_pac_val);
   if (u16_req_val < u16_pac_val) {
-    DLOG(ERROR) << __func__ << ", octet per frame below minimum";
+    LOG_DEBUG(" Req:OctetsPerFrame=%d", int(u16_req_val));
+    LOG_DEBUG(" Pac:MinOctetsPerFrame=%d", int(u16_pac_val));
+    LOG_DEBUG(", octet per frame below minimum");
     return false;
   }
 
   /* Maximal value 2-3 byte */
   u16_pac_val = OFF_VEC_UINT8_TO_UINT16(pac.value(), 2);
-  DLOG(INFO) << __func__ << " Pac:MaxOctetsPerFrame=" << int(u16_pac_val);
   if (u16_req_val > u16_pac_val) {
-    DLOG(ERROR) << __func__ << ", octet per frame above maximum";
+    LOG_DEBUG(" Req:MaxOctetsPerFrame=%d", int(u16_req_val));
+    LOG_DEBUG(" Pac:MaxOctetsPerFrame=%d", int(u16_pac_val));
+    LOG_DEBUG(", octet per frame above maximum");
     return false;
   }
 
@@ -212,7 +219,7 @@
 
   if (codec_id != pac.codec_id) return false;
 
-  DLOG(INFO) << __func__ << ": Settings for format " << +codec_id.coding_format;
+  LOG_DEBUG(": Settings for format: 0x%02x ", codec_id.coding_format);
 
   switch (codec_id.coding_format) {
     case kLeAudioCodingFormatLC3:
@@ -229,7 +236,7 @@
     case kLeAudioCodingFormatLC3:
       return std::get<types::LeAudioLc3Config>(config).GetSamplingFrequencyHz();
     default:
-      DLOG(WARNING) << __func__ << ", invalid codec id";
+      LOG_WARN(", invalid codec id: 0x%02x", id.coding_format);
       return 0;
   }
 };
@@ -239,7 +246,7 @@
     case kLeAudioCodingFormatLC3:
       return std::get<types::LeAudioLc3Config>(config).GetFrameDurationUs();
     default:
-      DLOG(WARNING) << __func__ << ", invalid codec id";
+      LOG_WARN(", invalid codec id: 0x%02x", id.coding_format);
       return 0;
   }
 };
@@ -250,7 +257,7 @@
       /* XXX LC3 supports 16, 24, 32 */
       return 16;
     default:
-      DLOG(WARNING) << __func__ << ", invalid codec id";
+      LOG_WARN(", invalid codec id: 0x%02x", id.coding_format);
       return 0;
   }
 };
@@ -258,12 +265,12 @@
 uint8_t CodecCapabilitySetting::GetConfigChannelCount() const {
   switch (id.coding_format) {
     case kLeAudioCodingFormatLC3:
-      DLOG(INFO) << __func__ << ", count = "
-                 << static_cast<int>(std::get<types::LeAudioLc3Config>(config)
-                                         .channel_count);
+      LOG_DEBUG("count = %d",
+                static_cast<int>(
+                    std::get<types::LeAudioLc3Config>(config).channel_count));
       return std::get<types::LeAudioLc3Config>(config).channel_count;
     default:
-      DLOG(WARNING) << __func__ << ", invalid codec id";
+      LOG_WARN(", invalid codec id: 0x%02x", id.coding_format);
       return 0;
   }
 }
@@ -469,6 +476,49 @@
      << ", AudioChanLoc=" << loghex(*config.audio_channel_allocation) << ")";
   return os;
 }
+std::ostream& operator<<(std::ostream& os, const LeAudioContextType& context) {
+  switch (context) {
+    case LeAudioContextType::UNINITIALIZED:
+      os << "UNINITIALIZED";
+      break;
+    case LeAudioContextType::UNSPECIFIED:
+      os << "UNSPECIFIED";
+      break;
+    case LeAudioContextType::CONVERSATIONAL:
+      os << "CONVERSATIONAL";
+      break;
+    case LeAudioContextType::MEDIA:
+      os << "MEDIA";
+      break;
+    case LeAudioContextType::GAME:
+      os << "GAME";
+      break;
+    case LeAudioContextType::INSTRUCTIONAL:
+      os << "INSTRUCTIONAL";
+      break;
+    case LeAudioContextType::VOICEASSISTANTS:
+      os << "VOICEASSISTANTS";
+      break;
+    case LeAudioContextType::LIVE:
+      os << "LIVE";
+      break;
+    case LeAudioContextType::SOUNDEFFECTS:
+      os << "SOUNDEFFECTS";
+      break;
+    case LeAudioContextType::NOTIFICATIONS:
+      os << "NOTIFICATIONS";
+      break;
+    case LeAudioContextType::RINGTONE:
+      os << "RINGTONE";
+      break;
+    case LeAudioContextType::EMERGENCYALARM:
+      os << "EMERGENCYALARM";
+      break;
+    default:
+      os << "UNKNOWN";
+      break;
+  }
+  return os;
+}
 }  // namespace types
-
 }  // namespace le_audio
diff --git a/system/bta/le_audio/le_audio_types.h b/system/bta/le_audio/le_audio_types.h
index 7960359..b64db3b 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -572,6 +572,7 @@
 std::ostream& operator<<(std::ostream& os, const AseState& state);
 std::ostream& operator<<(std::ostream& os, const CigState& state);
 std::ostream& operator<<(std::ostream& os, const LeAudioLc3Config& config);
+std::ostream& operator<<(std::ostream& os, const LeAudioContextType& context);
 }  // namespace types
 
 namespace set_configurations {
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 2265f42..aaeba87 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -579,6 +579,7 @@
     if ((group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) &&
         (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE)) {
       LOG(INFO) << __func__ << " group: " << group->group_id_ << " is in IDLE";
+      group->UpdateActiveContextsMap();
       return;
     }
 
@@ -609,10 +610,10 @@
     /* mark ASEs as not used. */
     leAudioDevice->DeactivateAllAses();
 
-    DLOG(INFO) << __func__ << " device: " << leAudioDevice->address_
-               << " group connected: " << group->IsAnyDeviceConnected()
-               << " all active ase disconnected: "
-               << group->HaveAllActiveDevicesCisDisc();
+    LOG_DEBUG(
+        " device: %s, group connected: %d, all active ase disconnected:: %d",
+        leAudioDevice->address_.ToString().c_str(),
+        group->IsAnyDeviceConnected(), group->HaveAllActiveDevicesCisDisc());
 
     /* Group has changed. Lets update available contexts */
     group->UpdateActiveContextsMap();
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index ff33fd3..7d158cc 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -27,6 +27,7 @@
 #include "btm_api_mock.h"
 #include "client_parser.h"
 #include "fake_osi.h"
+#include "gd/common/init_flags.h"
 #include "le_audio_set_configuration_provider.h"
 #include "mock_codec_manager.h"
 #include "mock_controller.h"
diff --git a/system/btif/avrcp/avrcp_service.cc b/system/btif/avrcp/avrcp_service.cc
index 01b0998..7f33f36 100644
--- a/system/btif/avrcp/avrcp_service.cc
+++ b/system/btif/avrcp/avrcp_service.cc
@@ -106,6 +106,12 @@
                   BT_HDR* p_pkt) override {
     return AVRC_MsgReq(handle, label, ctype, p_pkt);
   }
+
+  void SaveControllerVersion(const RawAddress& bdaddr,
+                             uint16_t version) override {
+    AVRC_SaveControllerVersion(bdaddr, version);
+  }
+
 } avrcp_interface_;
 
 class SdpInterfaceImpl : public SdpInterface {
diff --git a/system/gd/hci/controller.cc b/system/gd/hci/controller.cc
index da5986f..cd6fa1f 100644
--- a/system/gd/hci/controller.cc
+++ b/system/gd/hci/controller.cc
@@ -146,7 +146,7 @@
       LOG_INFO("LE_READ_PERIODIC_ADVERTISING_LIST_SIZE not supported, defaulting to 0");
       le_periodic_advertiser_list_size_ = 0;
     }
-    if (is_supported(OpCode::LE_SET_HOST_FEATURE)) {
+    if (is_supported(OpCode::LE_SET_HOST_FEATURE) && module_.SupportsBleConnectedIsochronousStreamCentral()) {
       hci_->EnqueueCommand(
           LeSetHostFeatureBuilder::Create(LeHostFeatureBits::CONNECTED_ISO_STREAM_HOST_SUPPORT, Enable::ENABLED),
           handler->BindOnceOn(this, &Controller::impl::le_set_host_feature_handler));
diff --git a/system/gd/hci/le_scanning_manager.cc b/system/gd/hci/le_scanning_manager.cc
index bc80498..1c1b562 100644
--- a/system/gd/hci/le_scanning_manager.cc
+++ b/system/gd/hci/le_scanning_manager.cc
@@ -501,6 +501,16 @@
       return;
     }
 
+    switch (address_type) {
+      case (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS:
+      case (uint8_t)AddressType::PUBLIC_IDENTITY_ADDRESS:
+        address_type = (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS;
+        break;
+      case (uint8_t)AddressType::RANDOM_DEVICE_ADDRESS:
+      case (uint8_t)AddressType::RANDOM_IDENTITY_ADDRESS:
+        address_type = (uint8_t)AddressType::RANDOM_DEVICE_ADDRESS;
+        break;
+    }
     scanning_callbacks_->OnScanResult(
         event_type,
         address_type,
diff --git a/system/gd/rust/common/src/init_flags.rs b/system/gd/rust/common/src/init_flags.rs
index 80fe6da..33a41a1 100644
--- a/system/gd/rust/common/src/init_flags.rs
+++ b/system/gd/rust/common/src/init_flags.rs
@@ -77,7 +77,8 @@
         gd_core,
         gd_security,
         gd_l2cap,
-        gatt_robust_caching,
+        gatt_robust_caching_client,
+        gatt_robust_caching_server,
         btaa_hci,
         gd_rust,
         gd_link_policy
diff --git a/system/gd/rust/shim/src/init_flags.rs b/system/gd/rust/shim/src/init_flags.rs
index fd3015c..688daa6 100644
--- a/system/gd/rust/shim/src/init_flags.rs
+++ b/system/gd/rust/shim/src/init_flags.rs
@@ -7,7 +7,8 @@
         fn gd_core_is_enabled() -> bool;
         fn gd_security_is_enabled() -> bool;
         fn gd_l2cap_is_enabled() -> bool;
-        fn gatt_robust_caching_is_enabled() -> bool;
+        fn gatt_robust_caching_client_is_enabled() -> bool;
+        fn gatt_robust_caching_server_is_enabled() -> bool;
         fn btaa_hci_is_enabled() -> bool;
         fn gd_rust_is_enabled() -> bool;
         fn gd_link_policy_is_enabled() -> bool;
diff --git a/system/profile/avrcp/avrcp_internal.h b/system/profile/avrcp/avrcp_internal.h
index 89dfcb1..74620dc 100644
--- a/system/profile/avrcp/avrcp_internal.h
+++ b/system/profile/avrcp/avrcp_internal.h
@@ -64,6 +64,9 @@
   virtual uint16_t MsgReq(uint8_t handle, uint8_t label, uint8_t ctype,
                           BT_HDR* p_pkt) = 0;
 
+  virtual void SaveControllerVersion(const RawAddress& bdaddr,
+                                     uint16_t version) = 0;
+
   virtual ~AvrcpInterface() = default;
 };
 
diff --git a/system/profile/avrcp/connection_handler.cc b/system/profile/avrcp/connection_handler.cc
index 680f1ac..db86cde 100644
--- a/system/profile/avrcp/connection_handler.cc
+++ b/system/profile/avrcp/connection_handler.cc
@@ -18,6 +18,7 @@
 
 #include <base/bind.h>
 #include <base/logging.h>
+
 #include <map>
 
 #include "avrc_defs.h"
@@ -488,6 +489,10 @@
           }
         }
       }
+
+      if (osi_property_get_bool(AVRC_DYNAMIC_AVRCP_ENABLE_PROPERTY, true)) {
+        avrc_->SaveControllerVersion(bdaddr, peer_avrcp_version);
+      }
     }
   }
 
diff --git a/system/profile/avrcp/tests/avrcp_test_helper.h b/system/profile/avrcp/tests/avrcp_test_helper.h
index 0ed5911..eed3dbc 100644
--- a/system/profile/avrcp/tests/avrcp_test_helper.h
+++ b/system/profile/avrcp/tests/avrcp_test_helper.h
@@ -75,6 +75,7 @@
   MOCK_METHOD1(Close, uint16_t(uint8_t));
   MOCK_METHOD1(CloseBrowse, uint16_t(uint8_t));
   MOCK_METHOD4(MsgReq, uint16_t(uint8_t, uint8_t, uint8_t, BT_HDR*));
+  MOCK_METHOD2(SaveControllerVersion, void(const RawAddress&, uint16_t));
 };
 
 class MockA2dpInterface : public A2dpInterface {
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 85095ae..bcdfc93 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -1265,3 +1265,36 @@
         },
     },
 }
+
+// Bluetooth stack connection multiplexing
+cc_test {
+    name: "net_test_stack_sdp",
+    defaults: ["fluoride_defaults"],
+    local_include_dirs: [
+        "include",
+        "test/common",
+    ],
+    include_dirs: [
+        "packages/modules/Bluetooth/system",
+        "packages/modules/Bluetooth/system/device/include/",
+        "packages/modules/Bluetooth/system/gd",
+        "packages/modules/Bluetooth/system/internal_include",
+        "packages/modules/Bluetooth/system/utils/include",
+    ],
+    srcs: [
+        ":TestCommonMockFunctions",
+        "sdp/sdp_main.cc",
+        "sdp/sdp_utils.cc",
+        "test/common/mock_btif_config.cc",
+        "test/stack_sdp_utils_test.cc",
+    ],
+    shared_libs: [
+        "libcutils",
+    ],
+    static_libs: [
+        "libbt-common",
+        "libbluetooth-types",
+        "liblog",
+        "libgmock",
+    ],
+}
diff --git a/system/stack/avrc/avrc_api.cc b/system/stack/avrc/avrc_api.cc
index d9b9014..c8f2f1c 100644
--- a/system/stack/avrc/avrc_api.cc
+++ b/system/stack/avrc/avrc_api.cc
@@ -27,6 +27,7 @@
 #include <string.h>
 
 #include "avrc_int.h"
+#include "btif/include/btif_config.h"
 #include "osi/include/allocator.h"
 #include "osi/include/fixed_queue.h"
 #include "osi/include/log.h"
@@ -1363,3 +1364,44 @@
   if (p_buf) return AVCT_MsgReq(handle, label, AVCT_RSP, p_buf);
   return AVRC_NO_RESOURCES;
 }
+
+/******************************************************************************
+ *
+ * Function         AVRC_SaveControllerVersion
+ *
+ * Description      Save AVRC controller version of peer device into bt_config.
+ *                  This version is used to send same AVRC target version to
+ *                  peer device to avoid version mismatch IOP issue.
+ *
+ *                  Input Parameters:
+ *                      bdaddr: BD address of peer device.
+ *
+ *                      version: AVRC controller version of peer device.
+ *
+ *                  Output Parameters:
+ *                      None.
+ *
+ * Returns          Nothing
+ *
+ *****************************************************************************/
+void AVRC_SaveControllerVersion(const RawAddress& bdaddr,
+                                uint16_t new_version) {
+  // store AVRC controller version into BT config
+  uint16_t old_version = 0;
+  size_t version_value_size = sizeof(old_version);
+  if (btif_config_get_bin(bdaddr.ToString(),
+                          AVRCP_CONTROLLER_VERSION_CONFIG_KEY,
+                          (uint8_t*)&old_version, &version_value_size) &&
+      new_version == old_version) {
+    LOG_INFO("AVRC controller version same as cached config");
+  } else if (btif_config_set_bin(
+                 bdaddr.ToString(), AVRCP_CONTROLLER_VERSION_CONFIG_KEY,
+                 (const uint8_t*)&new_version, sizeof(new_version))) {
+    btif_config_save();
+    LOG_INFO("store AVRC controller version %x for %s into config.",
+             new_version, bdaddr.ToString().c_str());
+  } else {
+    LOG_WARN("Failed to store AVRC controller version for %s",
+             bdaddr.ToString().c_str());
+  }
+}
diff --git a/system/stack/eatt/eatt_impl.h b/system/stack/eatt/eatt_impl.h
index 0324808..b2ea48d 100644
--- a/system/stack/eatt/eatt_impl.h
+++ b/system/stack/eatt/eatt_impl.h
@@ -118,10 +118,14 @@
      */
     eatt_device* eatt_dev = this->find_device_by_address(bda);
     if (!eatt_dev) {
-      LOG(ERROR) << __func__ << " unknown device: " << bda;
-      L2CA_ConnectCreditBasedRsp(bda, identifier, lcids,
-                                 L2CAP_CONN_NO_RESOURCES, NULL);
-      return;
+      /* If there is no device it means, Android did not read yet Server
+       * supported features, but according to Core 5.3, Vol 3,  Part G, 6.2.1,
+       * for LE case it is not necessary to read it before establish connection.
+       * Therefore assume, device supports EATT since we got request to create
+       * EATT channels. Just create device here. */
+      LOG(INFO) << __func__ << " Adding device: " << bda
+                << " on incoming EATT creation request";
+      eatt_dev = add_eatt_device(bda);
     }
 
     uint16_t max_mps = controller_get_interface()->get_acl_data_size_ble();
diff --git a/system/stack/gatt/att_protocol.cc b/system/stack/gatt/att_protocol.cc
index 63847db..3683d40 100644
--- a/system/stack/gatt/att_protocol.cc
+++ b/system/stack/gatt/att_protocol.cc
@@ -353,8 +353,8 @@
 }
 
 /** Build ATT Server PDUs */
-BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code,
-                          tGATT_SR_MSG* p_msg) {
+BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code, tGATT_SR_MSG* p_msg,
+                          uint16_t payload_size) {
   uint16_t offset = 0;
 
   switch (op_code) {
@@ -370,7 +370,7 @@
     case GATT_HANDLE_VALUE_NOTIF:
     case GATT_HANDLE_VALUE_IND:
       return attp_build_value_cmd(
-          tcb.payload_size, op_code, p_msg->attr_value.handle, offset,
+          payload_size, op_code, p_msg->attr_value.handle, offset,
           p_msg->attr_value.len, p_msg->attr_value.value);
 
     case GATT_RSP_WRITE:
diff --git a/system/stack/gatt/gatt_api.cc b/system/stack/gatt/gatt_api.cc
index 303223f..c523c86 100644
--- a/system/stack/gatt/gatt_api.cc
+++ b/system/stack/gatt/gatt_api.cc
@@ -468,8 +468,10 @@
 
   tGATT_SR_MSG gatt_sr_msg;
   gatt_sr_msg.attr_value = indication;
-  BT_HDR* p_msg =
-      attp_build_sr_msg(*p_tcb, GATT_HANDLE_VALUE_IND, &gatt_sr_msg);
+
+  uint16_t payload_size = gatt_tcb_get_payload_size_tx(*p_tcb, cid);
+  BT_HDR* p_msg = attp_build_sr_msg(*p_tcb, GATT_HANDLE_VALUE_IND, &gatt_sr_msg,
+                                    payload_size);
   if (!p_msg) return GATT_NO_RESOURCES;
 
   tGATT_STATUS cmd_status = attp_send_sr_msg(*p_tcb, cid, p_msg);
@@ -526,9 +528,9 @@
   gatt_sr_msg.attr_value = notif;
 
   uint16_t cid = gatt_tcb_get_att_cid(*p_tcb, p_reg->eatt_support);
-
-  BT_HDR* p_buf =
-      attp_build_sr_msg(*p_tcb, GATT_HANDLE_VALUE_NOTIF, &gatt_sr_msg);
+  uint16_t payload_size = gatt_tcb_get_payload_size_tx(*p_tcb, cid);
+  BT_HDR* p_buf = attp_build_sr_msg(*p_tcb, GATT_HANDLE_VALUE_NOTIF,
+                                    &gatt_sr_msg, payload_size);
   if (p_buf != NULL) {
     cmd_sent = attp_send_sr_msg(*p_tcb, cid, p_buf);
   } else
diff --git a/system/stack/gatt/gatt_attr.cc b/system/stack/gatt/gatt_attr.cc
index ccb9645..c10e157 100644
--- a/system/stack/gatt/gatt_attr.cc
+++ b/system/stack/gatt/gatt_attr.cc
@@ -822,7 +822,7 @@
  *
  ******************************************************************************/
 static bool gatt_sr_is_robust_caching_enabled() {
-  return bluetooth::common::init_flags::gatt_robust_caching_is_enabled();
+  return bluetooth::common::init_flags::gatt_robust_caching_server_is_enabled();
 }
 
 /*******************************************************************************
diff --git a/system/stack/gatt/gatt_int.h b/system/stack/gatt/gatt_int.h
index 0641d4c..ccc8be8 100644
--- a/system/stack/gatt/gatt_int.h
+++ b/system/stack/gatt/gatt_int.h
@@ -495,7 +495,7 @@
 extern tGATT_STATUS attp_send_cl_msg(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
                                      uint8_t op_code, tGATT_CL_MSG* p_msg);
 extern BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code,
-                                 tGATT_SR_MSG* p_msg);
+                                 tGATT_SR_MSG* p_msg, uint16_t payload_size);
 extern tGATT_STATUS attp_send_sr_msg(tGATT_TCB& tcb, uint16_t cid,
                                      BT_HDR* p_msg);
 extern tGATT_STATUS attp_send_msg_to_l2cap(tGATT_TCB& tcb, uint16_t cid,
diff --git a/system/stack/gatt/gatt_sr.cc b/system/stack/gatt/gatt_sr.cc
index 8e642d0..903cdaa 100644
--- a/system/stack/gatt/gatt_sr.cc
+++ b/system/stack/gatt/gatt_sr.cc
@@ -286,7 +286,7 @@
                                      tGATTS_RSP* p_msg,
                                      tGATT_SR_CMD* sr_res_p) {
   tGATT_STATUS ret_code = GATT_SUCCESS;
-  uint16_t payload_size = gatt_tcb_get_payload_size_rx(tcb, sr_res_p->cid);
+  uint16_t payload_size = gatt_tcb_get_payload_size_tx(tcb, sr_res_p->cid);
 
   VLOG(1) << __func__ << " gatt_if=" << +gatt_if;
 
@@ -308,8 +308,8 @@
 
     if (gatt_sr_is_cback_cnt_zero(tcb) && status == GATT_SUCCESS) {
       if (sr_res_p->p_rsp_msg == NULL) {
-        sr_res_p->p_rsp_msg = attp_build_sr_msg(tcb, (uint8_t)(op_code + 1),
-                                                (tGATT_SR_MSG*)p_msg);
+        sr_res_p->p_rsp_msg = attp_build_sr_msg(
+            tcb, (uint8_t)(op_code + 1), (tGATT_SR_MSG*)p_msg, payload_size);
       } else {
         LOG(ERROR) << "Exception!!! already has respond message";
       }
@@ -828,7 +828,8 @@
 
   tGATT_SR_MSG gatt_sr_msg;
   gatt_sr_msg.mtu = tcb.payload_size;
-  BT_HDR* p_buf = attp_build_sr_msg(tcb, GATT_RSP_MTU, &gatt_sr_msg);
+  BT_HDR* p_buf =
+      attp_build_sr_msg(tcb, GATT_RSP_MTU, &gatt_sr_msg, tcb.payload_size);
   attp_send_sr_msg(tcb, cid, p_buf);
 
   tGATTS_DATA gatts_data;
diff --git a/system/stack/gatt/gatt_utils.cc b/system/stack/gatt/gatt_utils.cc
index 991944a..471a5cb 100644
--- a/system/stack/gatt/gatt_utils.cc
+++ b/system/stack/gatt/gatt_utils.cc
@@ -813,7 +813,8 @@
   msg.error.reason = err_code;
   msg.error.handle = handle;
 
-  p_buf = attp_build_sr_msg(tcb, GATT_RSP_ERROR, &msg);
+  uint16_t payload_size = gatt_tcb_get_payload_size_tx(tcb, cid);
+  p_buf = attp_build_sr_msg(tcb, GATT_RSP_ERROR, &msg, payload_size);
   if (p_buf != NULL) {
     status = attp_send_sr_msg(tcb, cid, p_buf);
   } else
@@ -1397,7 +1398,12 @@
 /** Cancel LE Create Connection request */
 bool gatt_cancel_open(tGATT_IF gatt_if, const RawAddress& bda) {
   tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(bda, BT_TRANSPORT_LE);
-  if (!p_tcb) return true;
+  if (!p_tcb) {
+    LOG_WARN(
+        "Unable to cancel open for unknown connection gatt_if:%hhu peer:%s",
+        gatt_if, PRIVATE_ADDRESS(bda));
+    return true;
+  }
 
   if (gatt_get_ch_state(p_tcb) == GATT_CH_OPEN) {
     LOG(ERROR) << __func__ << ": link connected Too late to cancel";
@@ -1406,9 +1412,21 @@
 
   gatt_update_app_use_link_flag(gatt_if, p_tcb, false, false);
 
-  if (p_tcb->app_hold_link.empty()) gatt_disconnect(p_tcb);
+  if (p_tcb->app_hold_link.empty()) {
+    LOG_DEBUG(
+        "Client reference count is zero disconnecting device gatt_if:%hhu "
+        "peer:%s",
+        gatt_if, PRIVATE_ADDRESS(bda));
+    gatt_disconnect(p_tcb);
+  }
 
-  connection_manager::direct_connect_remove(gatt_if, bda);
+  if (!connection_manager::direct_connect_remove(gatt_if, bda)) {
+    BTM_AcceptlistRemove(bda);
+    LOG_INFO(
+        "GATT connection manager has no record but removed filter acceptlist "
+        "gatt_if:%hhu peer:%s",
+        gatt_if, PRIVATE_ADDRESS(bda));
+  }
   return true;
 }
 
diff --git a/system/stack/include/avrc_api.h b/system/stack/include/avrc_api.h
index 134f463..877dafe 100644
--- a/system/stack/include/avrc_api.h
+++ b/system/stack/include/avrc_api.h
@@ -138,6 +138,17 @@
 #define AVRC_DEFAULT_VERSION AVRC_1_5_STRING
 #endif
 
+/* Configurable dynamic avrcp version enable key*/
+#ifndef AVRC_DYNAMIC_AVRCP_ENABLE_PROPERTY
+#define AVRC_DYNAMIC_AVRCP_ENABLE_PROPERTY \
+  "persist.bluetooth.dynamic_avrcp.enable"
+#endif
+
+/* Avrcp controller version key for bt_config.conf */
+#ifndef AVRCP_CONTROLLER_VERSION_CONFIG_KEY
+#define AVRCP_CONTROLLER_VERSION_CONFIG_KEY "AvrcpControllerVersion"
+#endif
+
 /* Supported categories */
 #define AVRC_SUPF_CT_CAT1 0x0001         /* Category 1 */
 #define AVRC_SUPF_CT_CAT2 0x0002         /* Category 2 */
@@ -468,6 +479,28 @@
 
 /******************************************************************************
  *
+ * Function         AVRC_SaveControllerVersion
+ *
+ * Description      Save AVRC controller version of peer device into bt_config.
+ *                  This version is used to send same AVRC target version to
+ *                  peer device to avoid version mismatch IOP issue.
+ *
+ *                  Input Parameters:
+ *                      bdaddr: BD address of peer device.
+ *
+ *                      version: AVRC controller version of peer device.
+ *
+ *                  Output Parameters:
+ *                      None.
+ *
+ * Returns          Nothing
+ *
+ *****************************************************************************/
+extern void AVRC_SaveControllerVersion(const RawAddress& bdaddr,
+                                       uint16_t new_version);
+
+/******************************************************************************
+ *
  * Function         AVRC_UnitCmd
  *
  * Description      Send a UNIT INFO command to the peer device.  This
diff --git a/system/stack/l2cap/l2c_ble.cc b/system/stack/l2cap/l2c_ble.cc
index 5ab208c..0314b6e 100755
--- a/system/stack/l2cap/l2c_ble.cc
+++ b/system/stack/l2cap/l2c_ble.cc
@@ -715,22 +715,28 @@
       for (int i = 0; i < p_lcb->pending_ecoc_conn_cnt; i++) {
         uint16_t cid = p_lcb->pending_ecoc_connection_cids[i];
         STREAM_TO_UINT16(rcid, p);
-        /* if duplicated remote cid then disconnect original channel
-         * and current channel by sending event to upper layer */
-        temp_p_ccb = l2cu_find_ccb_by_remote_cid(p_lcb, rcid);
-        if (temp_p_ccb != nullptr) {
-          L2CAP_TRACE_ERROR(
-              "Already Allocated Destination cid. "
-              "rcid = %d "
-              "send peer_disc_req", rcid);
 
-          l2cu_send_peer_disc_req(temp_p_ccb);
+        if (rcid != 0) {
+          /* If remote cid is duplicated then disconnect original channel
+           * and current channel by sending event to upper layer
+           */
+          temp_p_ccb = l2cu_find_ccb_by_remote_cid(p_lcb, rcid);
+          if (temp_p_ccb != nullptr) {
+            L2CAP_TRACE_ERROR(
+                "Already Allocated Destination cid. "
+                "rcid = %d "
+                "send peer_disc_req",
+                rcid);
 
-          temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
-          con_info.l2cap_result = L2CAP_LE_RESULT_UNACCEPTABLE_PARAMETERS;
-          l2c_csm_execute(temp_p_ccb, L2CEVT_L2CAP_CREDIT_BASED_CONNECT_RSP_NEG,
-                          &con_info);
-          continue;
+            l2cu_send_peer_disc_req(temp_p_ccb);
+
+            temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
+            con_info.l2cap_result = L2CAP_LE_RESULT_UNACCEPTABLE_PARAMETERS;
+            l2c_csm_execute(temp_p_ccb,
+                            L2CEVT_L2CAP_CREDIT_BASED_CONNECT_RSP_NEG,
+                            &con_info);
+            continue;
+          }
         }
 
         temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
diff --git a/system/stack/sdp/sdp_server.cc b/system/stack/sdp/sdp_server.cc
index a486fb1..2ab2784 100644
--- a/system/stack/sdp/sdp_server.cc
+++ b/system/stack/sdp/sdp_server.cc
@@ -23,20 +23,21 @@
  *
  ******************************************************************************/
 
+#include <base/logging.h>
 #include <log/log.h>
 #include <string.h>  // memcpy
 
 #include <cstdint>
 
+#include "btif/include/btif_config.h"
 #include "device/include/interop.h"
 #include "osi/include/allocator.h"
+#include "stack/include/avrc_api.h"
 #include "stack/include/avrc_defs.h"
 #include "stack/include/bt_hdr.h"
 #include "stack/include/sdp_api.h"
 #include "stack/sdp/sdpint.h"
 
-#include <base/logging.h>
-
 /* Maximum number of bytes to reserve out of SDP MTU for response data */
 #define SDP_MAX_SERVICE_RSPHDR_LEN 12
 #define SDP_MAX_SERVATTR_RSPHDR_LEN 10
@@ -400,12 +401,22 @@
     p_ccb->cont_info.attr_offset = 0;
   }
 
+  bool is_service_avrc_target = false;
+  const tSDP_ATTRIBUTE* p_attr_service_id;
+  p_attr_service_id = sdp_db_find_attr_in_rec(
+      p_rec, ATTR_ID_SERVICE_CLASS_ID_LIST, ATTR_ID_SERVICE_CLASS_ID_LIST);
+  if (p_attr_service_id) {
+    is_service_avrc_target = sdpu_is_service_id_avrc_target(p_attr_service_id);
+  }
   /* Search for attributes that match the list given to us */
   for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) {
     p_attr = sdp_db_find_attr_in_rec(p_rec, attr_seq.attr_entry[xx].start,
                                      attr_seq.attr_entry[xx].end);
 
     if (p_attr) {
+      if (is_service_avrc_target) {
+        sdpu_set_avrc_target_version(p_attr, &(p_ccb->device_address));
+      }
       /* Check if attribute fits. Assume 3-byte value type/length */
       rem_len = max_list_len - (int16_t)(p_rsp - &p_ccb->rsp_list[0]);
 
@@ -554,7 +565,6 @@
   const tSDP_RECORD* p_rec;
   tSDP_ATTR_SEQ attr_seq, attr_seq_sav;
   const tSDP_ATTRIBUTE* p_attr;
-  tSDP_ATTRIBUTE attr_sav;
   bool maxxed_out = false, is_cont = false;
   uint8_t* p_seq_start;
   uint16_t seq_len, attr_len;
@@ -647,24 +657,22 @@
       p_rsp += 3;
     }
 
+    bool is_service_avrc_target = false;
+    const tSDP_ATTRIBUTE* p_attr_service_id;
+    p_attr_service_id = sdp_db_find_attr_in_rec(
+        p_rec, ATTR_ID_SERVICE_CLASS_ID_LIST, ATTR_ID_SERVICE_CLASS_ID_LIST);
+    if (p_attr_service_id) {
+      is_service_avrc_target =
+          sdpu_is_service_id_avrc_target(p_attr_service_id);
+    }
     /* Get a list of handles that match the UUIDs given to us */
     for (xx = p_ccb->cont_info.next_attr_index; xx < attr_seq.num_attr; xx++) {
       p_attr = sdp_db_find_attr_in_rec(p_rec, attr_seq.attr_entry[xx].start,
                                        attr_seq.attr_entry[xx].end);
 
       if (p_attr) {
-        // Check if the attribute contain AVRCP profile description list
-        uint16_t avrcp_version = sdpu_is_avrcp_profile_description_list(p_attr);
-        if (avrcp_version > AVRC_REV_1_4 &&
-            interop_match_addr(INTEROP_AVRCP_1_4_ONLY,
-                               &(p_ccb->device_address))) {
-          SDP_TRACE_DEBUG(
-              "%s, device=%s is only accept AVRCP 1.4, reply AVRCP 1.4 "
-              "instead.",
-              __func__, p_ccb->device_address.ToString().c_str());
-          memcpy(&attr_sav, p_attr, sizeof(tSDP_ATTRIBUTE));
-          attr_sav.value_ptr[attr_sav.len - 1] = 0x04;
-          p_attr = &attr_sav;
+        if (is_service_avrc_target) {
+          sdpu_set_avrc_target_version(p_attr, &(p_ccb->device_address));
         }
         /* Check if attribute fits. Assume 3-byte value type/length */
         rem_len = max_list_len - (int16_t)(p_rsp - &p_ccb->rsp_list[0]);
diff --git a/system/stack/sdp/sdp_utils.cc b/system/stack/sdp/sdp_utils.cc
index 761883e..e919298 100644
--- a/system/stack/sdp/sdp_utils.cc
+++ b/system/stack/sdp/sdp_utils.cc
@@ -16,6 +16,8 @@
  *
  ******************************************************************************/
 
+#define LOG_TAG "SDP_Utils"
+
 /******************************************************************************
  *
  *  This file contains SDP utility functions
@@ -28,12 +30,17 @@
 #include <array>
 #include <cstdint>
 #include <cstring>
+#include <ostream>
 #include <type_traits>
 #include <utility>
 #include <vector>
 
 #include "btif/include/btif_config.h"
+#include "device/include/interop.h"
 #include "osi/include/allocator.h"
+#include "osi/include/log.h"
+#include "osi/include/properties.h"
+#include "stack/include/avrc_api.h"
 #include "stack/include/avrc_defs.h"
 #include "stack/include/bt_hdr.h"
 #include "stack/include/sdp_api.h"
@@ -1202,3 +1209,136 @@
       return 0;
   }
 }
+/*******************************************************************************
+ *
+ * Function         sdpu_is_service_id_avrc_target
+ *
+ * Description      This function is to check if attirbute is A/V Remote Control
+ *                  Target
+ *
+ *                  p_attr: attribute to be checked
+ *
+ * Returns          true if service id of attirbute is A/V Remote Control
+ *                  Target, else false
+ *
+ ******************************************************************************/
+bool sdpu_is_service_id_avrc_target(const tSDP_ATTRIBUTE* p_attr) {
+  if (p_attr->id != ATTR_ID_SERVICE_CLASS_ID_LIST || p_attr->len != 3) {
+    return false;
+  }
+
+  uint8_t* p_uuid = p_attr->value_ptr + 1;
+  // check UUID of A/V Remote Control Target
+  if (p_uuid[0] != 0x11 || p_uuid[1] != 0xc) {
+    return false;
+  }
+
+  return true;
+}
+/*******************************************************************************
+ *
+ * Function         spdu_is_avrcp_version_valid
+ *
+ * Description      Check avrcp version is valid
+ *
+ *                  version: the avrcp version to check
+ *
+ * Returns          true if avrcp version is valid, else false
+ *
+ ******************************************************************************/
+bool spdu_is_avrcp_version_valid(const uint16_t version) {
+  return version == AVRC_REV_1_0 || version == AVRC_REV_1_3 ||
+         version == AVRC_REV_1_4 || version == AVRC_REV_1_5 ||
+         version == AVRC_REV_1_6;
+}
+/*******************************************************************************
+ *
+ * Function         sdpu_set_avrc_target_version
+ *
+ * Description      This function is to set AVRCP version of A/V Remote Control
+ *                  Target according to IOP table and cached Bluetooth config
+ *
+ *                  p_attr: attribute to be modified
+ *                  bdaddr: for searching IOP table and BT config
+ *
+ *
+ * Returns          true if service id of attirbute is A/V Remote Control
+ *                  Target, else false
+ *
+ ******************************************************************************/
+void sdpu_set_avrc_target_version(const tSDP_ATTRIBUTE* p_attr,
+                                  const RawAddress* bdaddr) {
+  // Check attribute is AVRCP profile description list and get AVRC Target
+  // version
+  uint16_t avrcp_version = sdpu_is_avrcp_profile_description_list(p_attr);
+  if (avrcp_version == 0) {
+    LOG_INFO("Not AVRCP version attribute or version not valid for device %s",
+             bdaddr->ToString().c_str());
+    return;
+  }
+
+  // Some remote devices will have interoperation issue when receive AVRCP
+  // version 1.5. If those devices are in IOP database and our version high than
+  // 1.4, we reply version 1.4 to them.
+  if (avrcp_version > AVRC_REV_1_4 &&
+      interop_match_addr(INTEROP_AVRCP_1_4_ONLY, bdaddr)) {
+    LOG_INFO(
+        "device=%s is in IOP database. "
+        "Reply AVRC Target version 1.4 instead of %x.",
+        bdaddr->ToString().c_str(), avrcp_version);
+    uint8_t* p_version = p_attr->value_ptr + 6;
+    UINT16_TO_BE_FIELD(p_version, AVRC_REV_1_4);
+    return;
+  }
+
+  // Dynamic ACRCP version. If our version high than remote device's version,
+  // reply version same as its. Otherwise, reply default version.
+  if (!osi_property_get_bool(AVRC_DYNAMIC_AVRCP_ENABLE_PROPERTY, true)) {
+    LOG_INFO(
+        "Dynamic AVRCP version feature is not enabled, skipping this method");
+    return;
+  }
+
+  // Read the remote device's AVRC Controller version from local storage
+  uint16_t cached_version = 0;
+  size_t version_value_size = btif_config_get_bin_length(
+      bdaddr->ToString(), AVRCP_CONTROLLER_VERSION_CONFIG_KEY);
+  if (version_value_size != sizeof(cached_version)) {
+    LOG_ERROR(
+        "cached value len wrong, bdaddr=%s. Len is %zu but should be %zu.",
+        bdaddr->ToString().c_str(), version_value_size, sizeof(cached_version));
+    return;
+  }
+
+  if (!btif_config_get_bin(bdaddr->ToString(),
+                           AVRCP_CONTROLLER_VERSION_CONFIG_KEY,
+                           (uint8_t*)&cached_version, &version_value_size)) {
+    LOG_INFO(
+        "no cached AVRC Controller version for %s. "
+        "Reply default AVRC Target version %x.",
+        bdaddr->ToString().c_str(), avrcp_version);
+    return;
+  }
+
+  if (!spdu_is_avrcp_version_valid(cached_version)) {
+    LOG_ERROR(
+        "cached AVRC Controller version %x of %s is not valid. "
+        "Reply default AVRC Target version %x.",
+        cached_version, bdaddr->ToString().c_str(), avrcp_version);
+    return;
+  }
+
+  if (avrcp_version > cached_version) {
+    LOG_INFO(
+        "read cached AVRC Controller version %x of %s. "
+        "Reply AVRC Target version %x.",
+        cached_version, bdaddr->ToString().c_str(), cached_version);
+    uint8_t* p_version = p_attr->value_ptr + 6;
+    UINT16_TO_BE_FIELD(p_version, cached_version);
+  } else {
+    LOG_INFO(
+        "read cached AVRC Controller version %x of %s. "
+        "Reply default AVRC Target version %x.",
+        cached_version, bdaddr->ToString().c_str(), avrcp_version);
+  }
+}
diff --git a/system/stack/sdp/sdpint.h b/system/stack/sdp/sdpint.h
index 74816dd..7b25bbc 100644
--- a/system/stack/sdp/sdpint.h
+++ b/system/stack/sdp/sdpint.h
@@ -232,6 +232,10 @@
                                                 uint16_t len, uint16_t* offset);
 extern uint16_t sdpu_is_avrcp_profile_description_list(
     const tSDP_ATTRIBUTE* p_attr);
+extern bool sdpu_is_service_id_avrc_target(const tSDP_ATTRIBUTE* p_attr);
+extern bool spdu_is_avrcp_version_valid(const uint16_t version);
+extern void sdpu_set_avrc_target_version(const tSDP_ATTRIBUTE* p_attr,
+                                         const RawAddress* bdaddr);
 
 /* Functions provided by sdp_db.cc
  */
diff --git a/system/stack/test/common/mock_btif_config.cc b/system/stack/test/common/mock_btif_config.cc
new file mode 100644
index 0000000..08fc80d
--- /dev/null
+++ b/system/stack/test/common/mock_btif_config.cc
@@ -0,0 +1,37 @@
+/******************************************************************************
+ *
+ *  Copyright 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.
+ *
+ ******************************************************************************/
+
+#include "mock_btif_config.h"
+
+static bluetooth::manager::MockBtifConfigInterface* btif_config_interface =
+    nullptr;
+
+void bluetooth::manager::SetMockBtifConfigInterface(
+    MockBtifConfigInterface* mock_btif_config_interface) {
+  btif_config_interface = mock_btif_config_interface;
+}
+
+bool btif_config_get_bin(const std::string& section, const std::string& key,
+                         uint8_t* value, size_t* length) {
+  return btif_config_interface->GetBin(section, key, value, length);
+}
+
+size_t btif_config_get_bin_length(const std::string& section,
+                                  const std::string& key) {
+  return btif_config_interface->GetBinLength(section, key);
+}
diff --git a/system/stack/test/common/mock_btif_config.h b/system/stack/test/common/mock_btif_config.h
new file mode 100644
index 0000000..9f11c43
--- /dev/null
+++ b/system/stack/test/common/mock_btif_config.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+ *
+ *  Copyright 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.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include <cstddef>
+
+namespace bluetooth {
+namespace manager {
+
+class BtifConfigInterface {
+ public:
+  virtual bool GetBin(const std::string& section, const std::string& key,
+                      uint8_t* value, size_t* length) = 0;
+  virtual size_t GetBinLength(const std::string& section,
+                              const std::string& key) = 0;
+  virtual ~BtifConfigInterface() = default;
+};
+
+class MockBtifConfigInterface : public BtifConfigInterface {
+ public:
+  MOCK_METHOD4(GetBin, bool(const std::string& section, const std::string& key,
+                            uint8_t* value, size_t* length));
+  MOCK_METHOD2(GetBinLength,
+               size_t(const std::string& section, const std::string& key));
+};
+
+/**
+ * Set the {@link MockBtifConfigInterface} for testing
+ *
+ * @param mock_btif_config_interface pointer to mock btif config interface,
+ * could be null
+ */
+void SetMockBtifConfigInterface(
+    MockBtifConfigInterface* mock_btif_config_interface);
+
+}  // namespace manager
+}  // namespace bluetooth
diff --git a/system/stack/test/eatt/eatt_test.cc b/system/stack/test/eatt/eatt_test.cc
index 3eda92c..32c5848 100644
--- a/system/stack/test/eatt/eatt_test.cc
+++ b/system/stack/test/eatt/eatt_test.cc
@@ -185,13 +185,15 @@
 TEST_F(EattTest, IncomingEattConnectionByUnknownDevice) {
   std::vector<uint16_t> incoming_cids{71, 72, 73, 74, 75};
 
-  EXPECT_CALL(l2cap_interface_,
-              ConnectCreditBasedRsp(test_address, 1, incoming_cids,
-                                    L2CAP_CONN_NO_RESOURCES, _))
+  EXPECT_CALL(
+      l2cap_interface_,
+      ConnectCreditBasedRsp(test_address, 1, incoming_cids, L2CAP_CONN_OK, _))
       .WillOnce(Return(true));
 
   l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(
       test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1);
+
+  DisconnectEattDevice(incoming_cids);
 }
 
 TEST_F(EattTest, IncomingEattConnectionByKnownDevice) {
diff --git a/system/stack/test/gatt/gatt_sr_test.cc b/system/stack/test/gatt/gatt_sr_test.cc
index 913cf61..7e6ab31 100644
--- a/system/stack/test/gatt/gatt_sr_test.cc
+++ b/system/stack/test/gatt/gatt_sr_test.cc
@@ -65,8 +65,8 @@
 }
 }  // namespace connection_manager
 
-BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code,
-                          tGATT_SR_MSG* p_msg) {
+BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code, tGATT_SR_MSG* p_msg,
+                          uint16_t payload_size) {
   test_state_.attp_build_sr_msg.op_code_ = op_code;
   return nullptr;
 }
diff --git a/system/stack/test/gatt/mock_gatt_utils_ref.cc b/system/stack/test/gatt/mock_gatt_utils_ref.cc
index 1c96dd4..cb01fd6 100644
--- a/system/stack/test/gatt/mock_gatt_utils_ref.cc
+++ b/system/stack/test/gatt/mock_gatt_utils_ref.cc
@@ -35,8 +35,8 @@
 }  // namespace connection_manager
 
 /** stack/gatt/att_protocol.cc */
-BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code,
-                          tGATT_SR_MSG* p_msg) {
+BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code, tGATT_SR_MSG* p_msg,
+                          uint16_t payload_size) {
   return nullptr;
 }
 tGATT_STATUS attp_send_cl_confirmation_msg(tGATT_TCB& tcb, uint16_t cid) {
diff --git a/system/stack/test/stack_sdp_utils_test.cc b/system/stack/test/stack_sdp_utils_test.cc
new file mode 100644
index 0000000..200a429
--- /dev/null
+++ b/system/stack/test/stack_sdp_utils_test.cc
@@ -0,0 +1,228 @@
+/*
+ * Copyright 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.
+ */
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <cstddef>
+
+#include "bt_types.h"
+#include "device/include/interop.h"
+#include "mock_btif_config.h"
+#include "stack/include/avrc_defs.h"
+#include "stack/include/sdp_api.h"
+#include "stack/sdp/sdpint.h"
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArrayArgument;
+
+// Global trace level referred in the code under test
+uint8_t appl_trace_level = BT_TRACE_LEVEL_VERBOSE;
+
+extern "C" void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
+
+namespace {
+// convenience mock
+class IopMock {
+ public:
+  MOCK_METHOD2(InteropMatchAddr,
+               bool(const interop_feature_t, const RawAddress*));
+};
+
+std::unique_ptr<IopMock> localIopMock;
+}  // namespace
+
+bool interop_match_addr(const interop_feature_t feature,
+                        const RawAddress* addr) {
+  return localIopMock->InteropMatchAddr(feature, addr);
+}
+
+bool osi_property_get_bool(const char* key, bool default_value) { return true; }
+
+uint8_t avrc_value[8] = {
+    ((DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE),  // data_element
+    6,                                                    // data_len
+    ((UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES),             // uuid_element
+    0,                                                    // uuid
+    0,                                                    // uuid
+    ((UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES),             // version_element
+    0,                                                    // version
+    0                                                     // version
+};
+tSDP_ATTRIBUTE avrcp_attr = {
+    .len = 0,
+    .value_ptr = (uint8_t*)(&avrc_value),
+    .id = 0,
+    .type = 0,
+};
+
+void set_avrcp_attr(uint32_t len, uint16_t id, uint16_t uuid,
+                    uint16_t version) {
+  UINT16_TO_BE_FIELD(avrc_value + 3, uuid);
+  UINT16_TO_BE_FIELD(avrc_value + 6, version);
+  avrcp_attr.len = len;
+  avrcp_attr.id = id;
+}
+
+uint16_t get_avrc_target_version(tSDP_ATTRIBUTE* p_attr) {
+  uint8_t* p_version = p_attr->value_ptr + 6;
+  uint16_t version =
+      (((uint16_t)(*(p_version))) << 8) + ((uint16_t)(*((p_version) + 1)));
+  return version;
+}
+
+class StackSdpUtilsTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    bluetooth::manager::SetMockBtifConfigInterface(&btif_config_interface_);
+    localIopMock = std::make_unique<IopMock>();
+    set_avrcp_attr(8, ATTR_ID_BT_PROFILE_DESC_LIST,
+                   UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_5);
+  }
+
+  void TearDown() override {
+    bluetooth::manager::SetMockBtifConfigInterface(nullptr);
+    localIopMock.reset();
+  }
+  bluetooth::manager::MockBtifConfigInterface btif_config_interface_;
+};
+
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_device_in_iop_table) {
+  RawAddress bdaddr;
+  EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+      .WillOnce(Return(true));
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_4);
+}
+
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_wrong_len) {
+  RawAddress bdaddr;
+  set_avrcp_attr(5, ATTR_ID_BT_PROFILE_DESC_LIST,
+                 UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_5);
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_wrong_attribute_id) {
+  RawAddress bdaddr;
+  set_avrcp_attr(8, ATTR_ID_SERVICE_CLASS_ID_LIST,
+                 UUID_SERVCLASS_AV_REMOTE_CONTROL, AVRC_REV_1_5);
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_wrong_uuid) {
+  RawAddress bdaddr;
+  set_avrcp_attr(8, ATTR_ID_BT_PROFILE_DESC_LIST, UUID_SERVCLASS_AUDIO_SOURCE,
+                 AVRC_REV_1_5);
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// device's controller version older than our target version
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_device_older_version) {
+  RawAddress bdaddr;
+  uint8_t config_0104[2] = {0x04, 0x01};
+  EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+      .WillOnce(Return(false));
+  EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+      .WillOnce(Return(2));
+  EXPECT_CALL(btif_config_interface_, GetBin(bdaddr.ToString(), _, _, _))
+      .WillOnce(DoAll(SetArrayArgument<2>(config_0104, config_0104 + 2),
+                      Return(true)));
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_4);
+}
+
+// device's controller version same as our target version
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_device_same_version) {
+  RawAddress bdaddr;
+  uint8_t config_0105[2] = {0x05, 0x01};
+  EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+      .WillOnce(Return(false));
+  EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+      .WillOnce(Return(2));
+  EXPECT_CALL(btif_config_interface_, GetBin(bdaddr.ToString(), _, _, _))
+      .WillOnce(DoAll(SetArrayArgument<2>(config_0105, config_0105 + 2),
+                      Return(true)));
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// device's controller version higher than our target version
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_device_newer_version) {
+  RawAddress bdaddr;
+  uint8_t config_0106[2] = {0x06, 0x01};
+  EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+      .WillOnce(Return(false));
+  EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+      .WillOnce(Return(2));
+  EXPECT_CALL(btif_config_interface_, GetBin(bdaddr.ToString(), _, _, _))
+      .WillOnce(DoAll(SetArrayArgument<2>(config_0106, config_0106 + 2),
+                      Return(true)));
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// cannot read device's controller version from bt_config
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_no_config_value) {
+  RawAddress bdaddr;
+  EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+      .WillOnce(Return(false));
+  EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+      .WillOnce(Return(0));
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// read device's controller version from bt_config return only 1 byte
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_config_value_1_byte) {
+  RawAddress bdaddr;
+  EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+      .WillOnce(Return(false));
+  EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+      .WillOnce(Return(1));
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// read device's controller version from bt_config return 3 bytes
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_config_value_3_bytes) {
+  RawAddress bdaddr;
+  EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+      .WillOnce(Return(false));
+  EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+      .WillOnce(Return(3));
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
+
+// cached controller version is not valid
+TEST_F(StackSdpUtilsTest, sdpu_set_avrc_target_version_config_value_not_valid) {
+  RawAddress bdaddr;
+  uint8_t config_not_valid[2] = {0x12, 0x34};
+  EXPECT_CALL(*localIopMock, InteropMatchAddr(INTEROP_AVRCP_1_4_ONLY, &bdaddr))
+      .WillOnce(Return(false));
+  EXPECT_CALL(btif_config_interface_, GetBinLength(bdaddr.ToString(), _))
+      .WillOnce(Return(2));
+  EXPECT_CALL(btif_config_interface_, GetBin(bdaddr.ToString(), _, _, _))
+      .WillOnce(
+          DoAll(SetArrayArgument<2>(config_not_valid, config_not_valid + 2),
+                Return(true)));
+  sdpu_set_avrc_target_version(&avrcp_attr, &bdaddr);
+  ASSERT_EQ(get_avrc_target_version(&avrcp_attr), AVRC_REV_1_5);
+}
diff --git a/system/test/mock/mock_stack_avrc_api.cc b/system/test/mock/mock_stack_avrc_api.cc
index 4d86f4a..c9e58a9 100644
--- a/system/test/mock/mock_stack_avrc_api.cc
+++ b/system/test/mock/mock_stack_avrc_api.cc
@@ -66,6 +66,9 @@
   mock_function_count_map[__func__]++;
   return 0;
 }
+void AVRC_SaveControllerVersion(const RawAddress& bdaddr, uint16_t version) {
+  mock_function_count_map[__func__]++;
+}
 uint16_t AVRC_PassCmd(uint8_t handle, uint8_t label, tAVRC_MSG_PASS* p_msg) {
   mock_function_count_map[__func__]++;
   return 0;