Fix an OOB bug in BqrVseSubEvt::ParseBqrLinkQualityEvt am: ecd5a3e85b

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

Change-Id: I39d48d6b0da8de78150a901a5f1ed20e3e326ad4
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_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 1c86ca8..ba69e74 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -71,6 +71,7 @@
 static jmethodID method_sspRequestCallback;
 static jmethodID method_bondStateChangeCallback;
 static jmethodID method_addressConsolidateCallback;
+static jmethodID method_leAddressAssociateCallback;
 static jmethodID method_aclStateChangeCallback;
 static jmethodID method_discoveryStateChangeCallback;
 static jmethodID method_linkQualityReportCallback;
@@ -336,6 +337,34 @@
                                main_addr.get(), secondary_addr.get());
 }
 
+static void le_address_associate_callback(RawAddress* main_bd_addr,
+                                          RawAddress* secondary_bd_addr) {
+  CallbackEnv sCallbackEnv(__func__);
+
+  ScopedLocalRef<jbyteArray> main_addr(
+      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+  if (!main_addr.get()) {
+    ALOGE("Address allocation failed in %s", __func__);
+    return;
+  }
+  sCallbackEnv->SetByteArrayRegion(main_addr.get(), 0, sizeof(RawAddress),
+                                   (jbyte*)main_bd_addr);
+
+  ScopedLocalRef<jbyteArray> secondary_addr(
+      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+  if (!secondary_addr.get()) {
+    ALOGE("Address allocation failed in %s", __func__);
+    return;
+  }
+
+  sCallbackEnv->SetByteArrayRegion(secondary_addr.get(), 0, sizeof(RawAddress),
+                                   (jbyte*)secondary_bd_addr);
+
+  sCallbackEnv->CallVoidMethod(sJniCallbacksObj,
+                               method_leAddressAssociateCallback,
+                               main_addr.get(), secondary_addr.get());
+}
+
 static void acl_state_changed_callback(bt_status_t status, RawAddress* bd_addr,
                                        bt_acl_state_t state,
                                        int transport_link_type,
@@ -696,6 +725,7 @@
                                              ssp_request_callback,
                                              bond_state_changed_callback,
                                              address_consolidate_callback,
+                                             le_address_associate_callback,
                                              acl_state_changed_callback,
                                              callback_thread_event,
                                              dut_mode_recv_callback,
@@ -922,6 +952,9 @@
   method_addressConsolidateCallback = env->GetMethodID(
       jniCallbackClass, "addressConsolidateCallback", "([B[B)V");
 
+  method_leAddressAssociateCallback = env->GetMethodID(
+      jniCallbackClass, "leAddressAssociateCallback", "([B[B)V");
+
   method_aclStateChangeCallback =
       env->GetMethodID(jniCallbackClass, "aclStateChangeCallback", "(I[BIII)V");
 
diff --git a/android/app/jni/com_android_bluetooth_le_audio.cpp b/android/app/jni/com_android_bluetooth_le_audio.cpp
index b97385f..3980d1c 100644
--- a/android/app/jni/com_android_bluetooth_le_audio.cpp
+++ b/android/app/jni/com_android_bluetooth_le_audio.cpp
@@ -25,7 +25,6 @@
 #include "com_android_bluetooth.h"
 #include "hardware/bt_le_audio.h"
 
-using bluetooth::le_audio::BroadcastAudioProfile;
 using bluetooth::le_audio::BroadcastId;
 using bluetooth::le_audio::BroadcastState;
 using bluetooth::le_audio::btle_audio_codec_config_t;
@@ -1125,7 +1124,7 @@
 }
 
 static void CreateBroadcastNative(JNIEnv* env, jobject object,
-                                  jbyteArray metadata, jint audio_profile,
+                                  jbyteArray metadata,
                                   jbyteArray broadcast_code) {
   LOG(INFO) << __func__;
   std::shared_lock<std::shared_timed_mutex> lock(sBroadcasterInterfaceMutex);
@@ -1138,7 +1137,6 @@
   jbyte* meta = env->GetByteArrayElements(metadata, nullptr);
   sLeAudioBroadcasterInterface->CreateBroadcast(
       std::vector<uint8_t>(meta, meta + env->GetArrayLength(metadata)),
-      static_cast<BroadcastAudioProfile>(audio_profile),
       broadcast_code ? std::optional<std::array<uint8_t, 16>>(code_array)
                      : std::nullopt);
   env->ReleaseByteArrayElements(metadata, meta, 0);
@@ -1198,7 +1196,7 @@
     {"initNative", "()V", (void*)BroadcasterInitNative},
     {"stopNative", "()V", (void*)BroadcasterStopNative},
     {"cleanupNative", "()V", (void*)BroadcasterCleanupNative},
-    {"createBroadcastNative", "([BI[B)V", (void*)CreateBroadcastNative},
+    {"createBroadcastNative", "([B[B)V", (void*)CreateBroadcastNative},
     {"updateMetadataNative", "(I[B)V", (void*)UpdateMetadataNative},
     {"startBroadcastNative", "(I)V", (void*)StartBroadcastNative},
     {"stopBroadcastNative", "(I)V", (void*)StopBroadcastNative},
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-ar/strings.xml b/android/app/res/values-ar/strings.xml
index 87b7ca6..423bd41 100644
--- a/android/app/res/values-ar/strings.xml
+++ b/android/app/res/values-ar/strings.xml
@@ -115,7 +115,7 @@
     <string name="transfer_menu_open" msgid="5193344638774400131">"فتح"</string>
     <string name="transfer_menu_clear" msgid="7213491281898188730">"محو من القائمة"</string>
     <string name="transfer_clear_dlg_title" msgid="128904516163257225">"محو"</string>
-    <string name="bluetooth_a2dp_sink_queue_name" msgid="7521243473328258997">"التعرّف التلقائي على الموسيقى"</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_intro" msgid="4748160773998753325">"حدد الحسابات التي تريد مشاركتها عبر البلوتوث. لا يزال يتعين عليك قبول أي دخول إلى الحسابات أثناء الاتصال."</string>
diff --git a/android/app/res/values-as/strings.xml b/android/app/res/values-as/strings.xml
index 4ecc5fb..847be12 100644
--- a/android/app/res/values-as/strings.xml
+++ b/android/app/res/values-as/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="permlab_bluetoothShareManager" msgid="5297865456717871041">"ডাউনল’ড মেনেজাৰ ব্যৱহাৰ কৰিব পাৰে।"</string>
+    <string name="permlab_bluetoothShareManager" msgid="5297865456717871041">"ডাউনল’ড মেনেজাৰ এক্সেছ কৰিব পাৰে।"</string>
     <string name="permdesc_bluetoothShareManager" msgid="1588034776955941477">"এপটোক BluetoothShare মেনেজাৰ ব্যৱহাৰ কৰি ফাইল স্থানান্তৰ কৰিবলৈ অনুমতি দিয়ে।"</string>
     <string name="permlab_bluetoothAcceptlist" msgid="5785922051395856524">"ব্লুটুথ ডিভাইচ এক্সেছ কৰাৰ স্বীকৃতি দিয়ে।"</string>
     <string name="permdesc_bluetoothAcceptlist" msgid="259308920158011885">"এপ্‌টোক এটা ব্লুটুথ ডিভাইচ অস্থায়ীৰূপে স্বীকাৰ কৰাৰ অনুমতি দিয়ে যিয়ে ডিভাইচটোক ব্যৱহাৰকাৰীৰ নিশ্চিতিকৰণৰ অবিহনেই ইয়ালৈ ফাইল পঠিওৱাৰ অনুমতি দিয়ে।"</string>
@@ -118,7 +118,7 @@
     <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_intro" msgid="4748160773998753325">"ব্লুটুথৰ জৰিয়তে শ্বেয়াৰ কৰিব খোজা একাউণ্টসমূহ বাছক। তথাপিও সংযোগ কৰি থাকোঁতে আপুনি একাউণ্টসমূহক সকলো ধৰণৰ অনুমতি দিবই লাগিব।"</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>
     <string name="bluetooth_map_settings_title" msgid="4226030082708590023">"ব্লুটুথৰ জৰিয়তে বাৰ্তা শ্বেয়াৰ কৰাৰ ছেটিং"</string>
diff --git a/android/app/res/values-as/strings_sap.xml b/android/app/res/values-as/strings_sap.xml
index f886e47..f18138b 100644
--- a/android/app/res/values-as/strings_sap.xml
+++ b/android/app/res/values-as/strings_sap.xml
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="bluetooth_sap_notif_title" msgid="7854456947435963346">"ব্লুটুথৰ ছিম ব্যৱহাৰ"</string>
-    <string name="bluetooth_sap_notif_ticker" msgid="7295825445933648498">"ব্লুটুথৰ ছিম ব্যৱহাৰ"</string>
+    <string name="bluetooth_sap_notif_title" msgid="7854456947435963346">"ব্লুটুথৰ ছিম এক্সেছ"</string>
+    <string name="bluetooth_sap_notif_ticker" msgid="7295825445933648498">"ব্লুটুথৰ ছিম এক্সেছ"</string>
     <string name="bluetooth_sap_notif_message" msgid="1004269289836361678">"সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টক অনুৰোধ কৰিবনে?"</string>
     <string name="bluetooth_sap_notif_disconnecting" msgid="6041257463440623400">"সংযোগ বিচ্ছিন্ন কৰিবলৈ ক্লায়েণ্টৰ অপেক্ষা কৰি থকা হৈছে"</string>
     <string name="bluetooth_sap_notif_disconnect_button" msgid="3059012556387692616">"বিচ্ছিন্ন কৰক"</string>
diff --git a/android/app/res/values-de/strings.xml b/android/app/res/values-de/strings.xml
index 719dd95..f432967 100644
--- a/android/app/res/values-de/strings.xml
+++ b/android/app/res/values-de/strings.xml
@@ -80,9 +80,9 @@
     <string name="bt_toast_1" msgid="8791691594887576215">"Die Datei wird empfangen. Überprüfe den Fortschritt in der Benachrichtigungskonsole."</string>
     <string name="bt_toast_2" msgid="2041575937953174042">"Die Datei kann nicht empfangen werden."</string>
     <string name="bt_toast_3" msgid="3053157171297761920">"Der Empfang der Datei von \"<xliff:g id="SENDER">%1$s</xliff:g>\" wurde angehalten."</string>
-    <string name="bt_toast_4" msgid="480365991944956695">"Datei wird an \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" gesendet..."</string>
+    <string name="bt_toast_4" msgid="480365991944956695">"Datei wird an „<xliff:g id="RECIPIENT">%1$s</xliff:g>“ gesendet..."</string>
     <string name="bt_toast_5" msgid="4818264207982268297">"<xliff:g id="NUMBER">%1$s</xliff:g> Dateien werden an \"<xliff:g id="RECIPIENT">%2$s</xliff:g>\" gesendet."</string>
-    <string name="bt_toast_6" msgid="8814166471030694787">"Die Übertragung der Datei an \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" wurde abgebrochen"</string>
+    <string name="bt_toast_6" msgid="8814166471030694787">"Die Übertragung der Datei an „<xliff:g id="RECIPIENT">%1$s</xliff:g>“ wurde abgebrochen"</string>
     <string name="bt_sm_2_1_nosdcard" msgid="288667514869424273">"Auf dem USB-Speicher ist nicht genügend Platz, um die Datei zu speichern."</string>
     <string name="bt_sm_2_1_default" msgid="5070195264206471656">"Auf der SD-Karte ist nicht genügend Platz, um die Datei zu speichern."</string>
     <string name="bt_sm_2_2" msgid="6200119660562110560">"Erforderlicher Speicherplatz: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
diff --git a/android/app/res/values-eu/strings.xml b/android/app/res/values-eu/strings.xml
index d48e5a0..149917e 100644
--- a/android/app/res/values-eu/strings.xml
+++ b/android/app/res/values-eu/strings.xml
@@ -20,14 +20,14 @@
     <string name="permdesc_bluetoothShareManager" msgid="1588034776955941477">"Bluetooth bidezko partekatzeen kudeatzailea atzitzea eta fitxategiak transferitzeko erabiltzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_bluetoothAcceptlist" msgid="5785922051395856524">"Ezarri Bluetooth bidezko gailuak onartutakoen zerrendan."</string>
     <string name="permdesc_bluetoothAcceptlist" msgid="259308920158011885">"Bluetooth bidezko gailu bat aldi baterako onartutakoen zerrendan ezartzeko baimena ematen die aplikazioei, gailu honetara fitxategiak bidaltzeko baimena izan dezan, baina gailu honen erabiltzaileari berrespena eskatu beharrik gabe."</string>
-    <string name="bt_share_picker_label" msgid="7464438494743777696">"Bluetooth-a"</string>
+    <string name="bt_share_picker_label" msgid="7464438494743777696">"Bluetootha"</string>
     <string name="unknown_device" msgid="2317679521750821654">"Identifikatu ezin den gailua"</string>
     <string name="unknownNumber" msgid="1245183329830158661">"Ezezaguna"</string>
     <string name="airplane_error_title" msgid="2570111716678850860">"Hegaldi modua"</string>
-    <string name="airplane_error_msg" msgid="4853111123699559578">"Ezin duzu erabili Bluetooth-a Hegaldi moduan."</string>
+    <string name="airplane_error_msg" msgid="4853111123699559578">"Ezin duzu erabili Bluetootha Hegaldi moduan."</string>
     <string name="bt_enable_title" msgid="4484289159118416315"></string>
-    <string name="bt_enable_line1" msgid="8429910585843481489">"Bluetooth-zerbitzuak erabiltzeko, Bluetooth-a aktibatu behar duzu."</string>
-    <string name="bt_enable_line2" msgid="1466367120348920892">"Bluetooth-a aktibatu nahi duzu?"</string>
+    <string name="bt_enable_line1" msgid="8429910585843481489">"Bluetooth-zerbitzuak erabiltzeko, Bluetootha aktibatu behar duzu."</string>
+    <string name="bt_enable_line2" msgid="1466367120348920892">"Bluetootha aktibatu nahi duzu?"</string>
     <string name="bt_enable_cancel" msgid="6770180540581977614">"Utzi"</string>
     <string name="bt_enable_ok" msgid="4224374055813566166">"Aktibatu"</string>
     <string name="incoming_file_confirm_title" msgid="938251186275547290">"Fitxategi-transferentzia"</string>
@@ -76,7 +76,7 @@
     <string name="not_exist_file" msgid="5097565588949092486">"Ez dago fitxategirik"</string>
     <string name="not_exist_file_desc" msgid="250802392160941265">"Ez dago horrelako fitxategirik. \n"</string>
     <string name="enabling_progress_title" msgid="5262637688863903594">"Itxaron…"</string>
-    <string name="enabling_progress_content" msgid="685427201206684584">"Bluetooth-a aktibatzen…"</string>
+    <string name="enabling_progress_content" msgid="685427201206684584">"Bluetootha aktibatzen…"</string>
     <string name="bt_toast_1" msgid="8791691594887576215">"Fitxategia jasoko da. Egoera kontrolatzeko, joan Jakinarazpenen panelera."</string>
     <string name="bt_toast_2" msgid="2041575937953174042">"Ezin da fitxategia jaso."</string>
     <string name="bt_toast_3" msgid="3053157171297761920">"\"<xliff:g id="SENDER">%1$s</xliff:g>\" igorlearen fitxategia jasotzeari utzi zaio"</string>
@@ -127,5 +127,5 @@
     <string name="bluetooth_disconnected" msgid="6841396291728343534">"Deskonektatu da Bluetooth bidezko audioa"</string>
     <string name="a2dp_sink_mbs_label" msgid="6035366346569127155">"Bluetooth bidezko audioa"</string>
     <string name="bluetooth_opp_file_limit_exceeded" msgid="6612109860149473930">"Ezin dira transferitu 4 GB baino gehiagoko fitxategiak"</string>
-    <string name="bluetooth_connect_action" msgid="2319449093046720209">"Konektatu Bluetooth-era"</string>
+    <string name="bluetooth_connect_action" msgid="2319449093046720209">"Konektatu Bluetoothera"</string>
 </resources>
diff --git a/android/app/res/values-eu/test_strings.xml b/android/app/res/values-eu/test_strings.xml
index e601649..4c501ab 100644
--- a/android/app/res/values-eu/test_strings.xml
+++ b/android/app/res/values-eu/test_strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="7766152617107310582">"Bluetooth-a"</string>
+    <string name="app_name" msgid="7766152617107310582">"Bluetootha"</string>
     <string name="insert_record" msgid="4024416351836939752">"Sartu erregistroa"</string>
     <string name="update_record" msgid="7201772850942641237">"Berretsi erregistroa"</string>
     <string name="ack_record" msgid="2404738476192250210">"ACK erregistroa"</string>
diff --git a/android/app/res/values-fr-rCA/strings.xml b/android/app/res/values-fr-rCA/strings.xml
index 5e10989..1bc3d20 100644
--- a/android/app/res/values-fr-rCA/strings.xml
+++ b/android/app/res/values-fr-rCA/strings.xml
@@ -80,9 +80,9 @@
     <string name="bt_toast_1" msgid="8791691594887576215">"La réception du fichier va commencer. La progression va s\'afficher dans le panneau de notification."</string>
     <string name="bt_toast_2" msgid="2041575937953174042">"Impossible de recevoir le fichier."</string>
     <string name="bt_toast_3" msgid="3053157171297761920">"Réception du fichier de \"<xliff:g id="SENDER">%1$s</xliff:g>\" interrompue"</string>
-    <string name="bt_toast_4" msgid="480365991944956695">"Envoi du fichier à \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string>
+    <string name="bt_toast_4" msgid="480365991944956695">"Envoi du fichier à « <xliff:g id="RECIPIENT">%1$s</xliff:g> »"</string>
     <string name="bt_toast_5" msgid="4818264207982268297">"Envoi de <xliff:g id="NUMBER">%1$s</xliff:g> fichiers à \"<xliff:g id="RECIPIENT">%2$s</xliff:g>\""</string>
-    <string name="bt_toast_6" msgid="8814166471030694787">"Envoi du fichier à \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" interrompu"</string>
+    <string name="bt_toast_6" msgid="8814166471030694787">"Envoi du fichier à « <xliff:g id="RECIPIENT">%1$s</xliff:g> » interrompu"</string>
     <string name="bt_sm_2_1_nosdcard" msgid="288667514869424273">"Espace insuffisant sur la mémoire de stockage USB pour l\'enregistrement du fichier."</string>
     <string name="bt_sm_2_1_default" msgid="5070195264206471656">"Espace insuffisant sur la carte SD pour l\'enregistrement du fichier."</string>
     <string name="bt_sm_2_2" msgid="6200119660562110560">"Espace requis : <xliff:g id="SIZE">%1$s</xliff:g>"</string>
diff --git a/android/app/res/values-pt-rPT/strings.xml b/android/app/res/values-pt-rPT/strings.xml
index c2c09c3..524a95b 100644
--- a/android/app/res/values-pt-rPT/strings.xml
+++ b/android/app/res/values-pt-rPT/strings.xml
@@ -86,7 +86,7 @@
     <string name="bt_sm_2_1_nosdcard" msgid="288667514869424273">"Não existe espaço suficiente na memória USB para guardar o ficheiro."</string>
     <string name="bt_sm_2_1_default" msgid="5070195264206471656">"Não existe espaço suficiente no cartão SD para guardar o ficheiro."</string>
     <string name="bt_sm_2_2" msgid="6200119660562110560">"Espaço necessário: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
-    <string name="ErrorTooManyRequests" msgid="5049670841391761475">"Existem demasiados pedidos em processamento. Tente novamente mais tarde."</string>
+    <string name="ErrorTooManyRequests" msgid="5049670841391761475">"Existem demasiados pedidos em processamento. Tente mais tarde."</string>
     <string name="status_pending" msgid="4781040740237733479">"Ainda não foi iniciada a transferência do ficheiro."</string>
     <string name="status_running" msgid="7419075903776657351">"Transferência do ficheiro em curso."</string>
     <string name="status_success" msgid="7963589000098719541">"Transferência de ficheiros concluída com êxito."</string>
diff --git a/android/app/res/values-te/strings.xml b/android/app/res/values-te/strings.xml
index bdca35f..6e8024e 100644
--- a/android/app/res/values-te/strings.xml
+++ b/android/app/res/values-te/strings.xml
@@ -48,14 +48,14 @@
     <string name="download_title" msgid="6449408649671518102">"ఫైల్ బదిలీ"</string>
     <string name="download_line1" msgid="6449220145685308846">"వీరి నుండి: \"<xliff:g id="SENDER">%1$s</xliff:g>\""</string>
     <string name="download_line2" msgid="7634316500490825390">"ఫైల్: <xliff:g id="FILE">%1$s</xliff:g>"</string>
-    <string name="download_line3" msgid="6722284930665532816">"ఫైల్ పరిమాణం: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
+    <string name="download_line3" msgid="6722284930665532816">"ఫైల్ సైజ్‌: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
     <string name="download_line4" msgid="5234701398884321314"></string>
     <string name="download_line5" msgid="4124272066218470715">"ఫైల్‌ను స్వీకరిస్తోంది…"</string>
     <string name="download_cancel" msgid="1705762428762702342">"ఆపివేయి"</string>
     <string name="download_ok" msgid="2404442707314575833">"దాచు"</string>
     <string name="incoming_line1" msgid="6342300988329482408">"దీని నుండి"</string>
     <string name="incoming_line2" msgid="2199520895444457585">"ఫైల్ పేరు"</string>
-    <string name="incoming_line3" msgid="8630078246326525633">"పరిమాణం"</string>
+    <string name="incoming_line3" msgid="8630078246326525633">"సైజ్‌"</string>
     <string name="download_fail_line1" msgid="3149552664349685007">"ఫైల్ స్వీకరించబడలేదు"</string>
     <string name="download_fail_line2" msgid="4289018531070750414">"ఫైల్: <xliff:g id="FILE">%1$s</xliff:g>"</string>
     <string name="download_fail_line3" msgid="2214989413171231684">"కారణం: <xliff:g id="REASON">%1$s</xliff:g>"</string>
diff --git a/android/app/src/com/android/bluetooth/bas/BatteryService.java b/android/app/src/com/android/bluetooth/bas/BatteryService.java
index 7afa86f..43b4e76 100644
--- a/android/app/src/com/android/bluetooth/bas/BatteryService.java
+++ b/android/app/src/com/android/bluetooth/bas/BatteryService.java
@@ -38,6 +38,7 @@
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.SynchronousResultReceiver;
 
@@ -62,6 +63,7 @@
     private static BatteryService sBatteryService;
 
     private AdapterService mAdapterService;
+    private DatabaseManager mDatabaseManager;
     private HandlerThread mStateMachinesThread;
     private final Map<BluetoothDevice, BatteryStateMachine> mStateMachines = new HashMap<>();
 
@@ -94,6 +96,8 @@
 
         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
                 "AdapterService cannot be null when BatteryService starts");
+        mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
+                "DatabaseManager cannot be null when BatteryService starts");
 
         mStateMachines.clear();
         mStateMachinesThread = new HandlerThread("BatteryService.StateMachines");
@@ -395,8 +399,7 @@
         if (DBG) {
             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
         }
-        mAdapterService.getDatabase()
-                .setProfileConnectionPolicy(device, BluetoothProfile.BATTERY,
+        mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.BATTERY,
                         connectionPolicy);
         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
             connect(device);
@@ -413,8 +416,7 @@
     public int getConnectionPolicy(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
                 "Need BLUETOOTH_PRIVILEGED permission");
-        return mAdapterService.getDatabase()
-                .getProfileConnectionPolicy(device, BluetoothProfile.BATTERY);
+        return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.BATTERY);
     }
     /**
      * Called when the battery level of the device is notified.
diff --git a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
index 8f8007a..878c71e 100644
--- a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
@@ -24,6 +24,7 @@
 import android.bluetooth.BluetoothGatt;
 import android.bluetooth.BluetoothGattCallback;
 import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattDescriptor;
 import android.bluetooth.BluetoothGattService;
 import android.bluetooth.BluetoothProfile;
 import android.os.Looper;
@@ -51,9 +52,10 @@
 
     static final UUID GATT_BATTERY_SERVICE_UUID =
             UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb");
-
     static final UUID GATT_BATTERY_LEVEL_CHARACTERISTIC_UUID =
             UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb");
+    static final UUID CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID =
+            UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
 
     static final int CONNECT = 1;
     static final int DISCONNECT = 2;
@@ -553,7 +555,8 @@
                 return;
             }
 
-            gatt.setCharacteristicNotification(batteryLevel, /*enable=*/true);
+            // This may not trigger onCharacteristicRead if CCCD is already set but then
+            // onCharacteristicChanged will be triggered soon.
             gatt.readCharacteristic(batteryLevel);
         }
 
@@ -575,6 +578,23 @@
 
             if (GATT_BATTERY_LEVEL_CHARACTERISTIC_UUID.equals(characteristic.getUuid())) {
                 updateBatteryLevel(value);
+                BluetoothGattDescriptor cccd =
+                        characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR_UUID);
+                if (cccd != null) {
+                    gatt.setCharacteristicNotification(characteristic, /*enable=*/true);
+                    gatt.writeDescriptor(cccd, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
+                } else {
+                    Log.w(TAG, "No CCCD for battery level characteristic, "
+                            + "it won't be notified");
+                }
+            }
+        }
+
+        @Override
+        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+                int status) {
+            if (status != BluetoothGatt.GATT_SUCCESS) {
+                Log.w(TAG, "Failed to write descriptor " + descriptor.getUuid());
             }
         }
 
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
index 881ef14..584d173 100755
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -17,6 +17,7 @@
 package com.android.bluetooth.bass_client;
 
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
 
 import android.bluetooth.BluetoothAdapter;
@@ -47,6 +48,7 @@
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.nio.ByteBuffer;
@@ -74,6 +76,7 @@
     private HandlerThread mStateMachinesThread;
     private HandlerThread mCallbackHandlerThread;
     private AdapterService mAdapterService;
+    private DatabaseManager mDatabaseManager;
     private BluetoothAdapter mBluetoothAdapter = null;
     private BassUtils mBassUtils = null;
     private Map<BluetoothDevice, BluetoothDevice> mActiveSourceMap;
@@ -218,6 +221,8 @@
         }
         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
                 "AdapterService cannot be null when BassClientService starts");
+        mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
+                "DatabaseManager cannot be null when BassClientService starts");
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
         mStateMachines.clear();
         mStateMachinesThread = new HandlerThread("BassClientService.StateMachines");
@@ -545,7 +550,7 @@
             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
         }
         boolean setSuccessfully =
-                mAdapterService.getDatabase().setProfileConnectionPolicy(device,
+                mDatabaseManager.setProfileConnectionPolicy(device,
                         BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT, connectionPolicy);
         if (setSuccessfully && connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
             connect(device);
@@ -568,8 +573,7 @@
      * @return connection policy of the device
      */
     public int getConnectionPolicy(BluetoothDevice device) {
-        return mAdapterService
-                .getDatabase()
+        return mDatabaseManager
                 .getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
     }
 
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 9e007aa..1f3268b 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -3905,7 +3905,7 @@
 
     public byte[] getByteIdentityAddress(BluetoothDevice device) {
         DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
-        if (deviceProp != null && deviceProp.isConsolidated()) {
+        if (deviceProp != null && deviceProp.getIdentityAddress() != null) {
             return Utils.getBytesFromAddress(deviceProp.getIdentityAddress());
         } else {
             return Utils.getByteAddress(device);
@@ -3923,7 +3923,7 @@
     public String getIdentityAddress(String address) {
         BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address.toUpperCase());
         DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
-        if (deviceProp != null && deviceProp.isConsolidated()) {
+        if (deviceProp != null && deviceProp.getIdentityAddress() != null) {
             return deviceProp.getIdentityAddress();
         } else {
             return address;
@@ -5079,7 +5079,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";
     private static final String IRK_ROTATION_FLAG = "INIT_irk_rotation";
 
     /**
@@ -5134,8 +5135,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, IRK_ROTATION_FLAG, false)) {
             initFlags.add(String.format("%s=%s", IRK_ROTATION_FLAG, "true"));
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/btservice/JniCallbacks.java b/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java
index 47906ac..a5c9869 100644
--- a/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java
+++ b/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java
@@ -71,6 +71,10 @@
         mRemoteDevices.addressConsolidateCallback(mainAddress, secondaryAddress);
     }
 
+    void leAddressAssociateCallback(byte[] mainAddress, byte[] secondaryAddress) {
+        mRemoteDevices.leAddressAssociateCallback(mainAddress, secondaryAddress);
+    }
+
     void aclStateChangeCallback(int status, byte[] address, int newState,
             int transportLinkType, int hciReason) {
         mRemoteDevices.aclStateChangeCallback(status, address, newState,
diff --git a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
index e04d0fd..c81a5fb 100644
--- a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
+++ b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
@@ -36,6 +36,7 @@
 import android.os.Message;
 import android.os.ParcelUuid;
 import android.os.Parcelable;
+import android.os.SystemProperties;
 import android.util.Log;
 
 import com.android.bluetooth.R;
@@ -95,6 +96,9 @@
     private static final int MESSAGE_PROFILE_ACTIVE_DEVICE_CHANGED = 5;
     private static final int MESSAGE_DEVICE_CONNECTED = 6;
 
+    private static final String PREFER_LE_AUDIO_ONLY_MODE =
+            "persist.bluetooth.prefer_le_audio_only_mode";
+
     // Timeouts
     @VisibleForTesting static int sConnectOtherProfilesTimeoutMillis = 6000; // 6s
 
@@ -106,6 +110,8 @@
     private final HashSet<BluetoothDevice> mA2dpRetrySet = new HashSet<>();
     private final HashSet<BluetoothDevice> mConnectOtherProfilesDeviceSet = new HashSet<>();
 
+    private Boolean mPreferLeAudioOnlyMode = false;
+
     // Broadcast receiver for all changes to states of various profiles
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -284,6 +290,7 @@
                 "DatabaseManager cannot be null when PhonePolicy starts");
         mFactory = factory;
         mHandler = new PhonePolicyHandler(service.getMainLooper());
+        mPreferLeAudioOnlyMode = SystemProperties.getBoolean(PREFER_LE_AUDIO_ONLY_MODE, true);
     }
 
     // Policy implementation, all functions MUST be private
@@ -354,6 +361,24 @@
             debugLog("setting le audio profile priority for device " + device);
             mAdapterService.getDatabase().setProfileConnectionPolicy(device,
                     BluetoothProfile.LE_AUDIO, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+            if (mPreferLeAudioOnlyMode) {
+                if (mAdapterService.getDatabase()
+                        .getProfileConnectionPolicy(device, BluetoothProfile.A2DP)
+                        >  BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+                    debugLog("clear a2dp profile priority for the le audio dual mode device "
+                            + device);
+                    mAdapterService.getDatabase().setProfileConnectionPolicy(device,
+                            BluetoothProfile.A2DP, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+                }
+                if (mAdapterService.getDatabase()
+                        .getProfileConnectionPolicy(device, BluetoothProfile.HEADSET)
+                        >  BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
+                    debugLog("clear hfp profile priority for the le audio dual mode device "
+                            + device);
+                    mAdapterService.getDatabase().setProfileConnectionPolicy(device,
+                            BluetoothProfile.HEADSET, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+                }
+            }
         }
 
         if ((hearingAidService != null) && Utils.arrayContains(uuids,
@@ -430,15 +455,42 @@
 
     /**
      * Updates the last connection date in the connection order database for the newly active device
-     * if connected to a2dp profile
+     * if connected to a2dp profile. If the device is LE audio dual mode device, and
+     * mPreferLeAudioOnlyMode be true, A2DP/HFP will be disconnected as LE audio become active one
+     * after pairing.
      *
      * @param device is the device we just made the active device
      */
     private void processActiveDeviceChanged(BluetoothDevice device, int profileId) {
-        debugLog("processActiveDeviceChanged, device=" + device + ", profile=" + profileId);
+        debugLog("processActiveDeviceChanged, device=" + device + ", profile=" + profileId
+                + " mPreferLeAudioOnlyMode: " + mPreferLeAudioOnlyMode);
 
         if (device != null) {
             mDatabaseManager.setConnection(device, profileId == BluetoothProfile.A2DP);
+
+            if (!mPreferLeAudioOnlyMode) return;
+            if (profileId == BluetoothProfile.LE_AUDIO) {
+                HeadsetService hsService = mFactory.getHeadsetService();
+                if (hsService != null) {
+                    if ((hsService.getConnectionPolicy(device)
+                            != BluetoothProfile.CONNECTION_POLICY_ALLOWED)
+                            && (hsService.getConnectionState(device)
+                            == BluetoothProfile.STATE_CONNECTED)) {
+                        debugLog("Disconnect HFP for the LE audio dual mode device " + device);
+                        hsService.disconnect(device);
+                    }
+                }
+                A2dpService a2dpService = mFactory.getA2dpService();
+                if (a2dpService != null) {
+                    if ((a2dpService.getConnectionPolicy(device)
+                            != BluetoothProfile.CONNECTION_POLICY_ALLOWED)
+                            && (a2dpService.getConnectionState(device)
+                            == BluetoothProfile.STATE_CONNECTED)) {
+                        debugLog("Disconnect A2DP for the LE audio dual mode device " + device);
+                        a2dpService.disconnect(device);
+                    }
+                }
+            }
         }
     }
 
diff --git a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
index da78ec6..9c8c88b 100644
--- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -805,6 +805,27 @@
         mDualDevicesMap.put(deviceProperties.getIdentityAddress(), Utils.getAddressStringFromByte(mainAddress));
     }
 
+    /**
+     * Callback to associate an LE-only device's RPA with its identity address
+     *
+     * @param mainAddress the device's RPA
+     * @param secondaryAddress the device's identity address
+     */
+    void leAddressAssociateCallback(byte[] mainAddress, byte[] secondaryAddress) {
+        BluetoothDevice device = getDevice(mainAddress);
+        if (device == null) {
+            errorLog("leAddressAssociateCallback: device is NULL, address="
+                    + Utils.getAddressStringFromByte(mainAddress) + ", secondaryAddress="
+                    + Utils.getAddressStringFromByte(secondaryAddress));
+            return;
+        }
+        Log.d(TAG, "leAddressAssociateCallback device: " + device + ", secondaryAddress:"
+                + Utils.getAddressStringFromByte(secondaryAddress));
+
+        DeviceProperties deviceProperties = getDeviceProperties(device);
+        deviceProperties.mIdentityAddress = Utils.getAddressStringFromByte(secondaryAddress);
+    }
+
     void aclStateChangeCallback(int status, byte[] address, int newState,
                                 int transportLinkType, int hciReason) {
         BluetoothDevice device = getDevice(address);
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
index f094e33..08e7809 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
@@ -47,6 +47,7 @@
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.SynchronousResultReceiver;
 
@@ -76,6 +77,7 @@
     private static CsipSetCoordinatorService sCsipSetCoordinatorService;
 
     private AdapterService mAdapterService;
+    private DatabaseManager mDatabaseManager;
     private HandlerThread mStateMachinesThread;
     private BluetoothDevice mPreviousAudioDevice;
 
@@ -121,10 +123,12 @@
             throw new IllegalStateException("start() called twice");
         }
 
-        // Get AdapterService, CsipSetCoordinatorNativeInterface.
+        // Get AdapterService, DatabaseManager, CsipSetCoordinatorNativeInterface.
         // None of them can be null.
         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
                 "AdapterService cannot be null when CsipSetCoordinatorService starts");
+        mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
+                "DatabaseManager cannot be null when CsipSetCoordinatorService starts");
         mCsipSetCoordinatorNativeInterface = Objects.requireNonNull(
                 CsipSetCoordinatorNativeInterface.getInstance(),
                 "CsipSetCoordinatorNativeInterface cannot be null when"
@@ -461,7 +465,7 @@
         if (DBG) {
             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
         }
-        mAdapterService.getDatabase().setProfileConnectionPolicy(
+        mDatabaseManager.setProfileConnectionPolicy(
                 device, BluetoothProfile.CSIP_SET_COORDINATOR, connectionPolicy);
         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
             connect(device);
@@ -479,7 +483,7 @@
      */
     public int getConnectionPolicy(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
-        return mAdapterService.getDatabase().getProfileConnectionPolicy(
+        return mDatabaseManager.getProfileConnectionPolicy(
                 device, BluetoothProfile.CSIP_SET_COORDINATOR);
     }
 
@@ -494,6 +498,9 @@
     public @Nullable UUID lockGroup(
             int groupId, @NonNull IBluetoothCsipSetCoordinatorLockCallback callback) {
         if (callback == null) {
+            if (DBG) {
+                Log.d(TAG, "lockGroup(): " + groupId + ", callback not provided ");
+            }
             return null;
         }
 
@@ -520,12 +527,18 @@
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
+                if (DBG) {
+                    Log.d(TAG, "lockGroup(): " + groupId + ", ERROR_CSIP_GROUP_LOCKED_BY_OTHER ");
+                }
                 return null;
             }
 
             mLocks.put(groupId, new Pair<>(uuid, callback));
         }
 
+        if (DBG) {
+            Log.d(TAG, "lockGroup(): locking group: " + groupId);
+        }
         mCsipSetCoordinatorNativeInterface.groupLockSet(groupId, true);
         return uuid;
     }
@@ -538,6 +551,9 @@
      */
     public void unlockGroup(@NonNull UUID lockUuid) {
         if (lockUuid == null) {
+            if (DBG) {
+                Log.d(TAG, "unlockGroup(): lockUuid is null");
+            }
             return;
         }
 
@@ -546,6 +562,9 @@
                     mLocks.entrySet()) {
                 Pair<UUID, IBluetoothCsipSetCoordinatorLockCallback> uuidCbPair = entry.getValue();
                 if (uuidCbPair.first.equals(lockUuid)) {
+                    if (DBG) {
+                        Log.d(TAG, "unlockGroup(): unlocking ... " + lockUuid);
+                    }
                     mCsipSetCoordinatorNativeInterface.groupLockSet(entry.getKey(), false);
                     return;
                 }
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
index 0eb5033..756d72b 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientService.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -49,6 +49,7 @@
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.bluetooth.btservice.ServiceFactory;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.bluetooth.csip.CsipSetCoordinatorService;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.SynchronousResultReceiver;
@@ -79,6 +80,7 @@
     @VisibleForTesting
     HapClientNativeInterface mHapClientNativeInterface;
     private AdapterService mAdapterService;
+    private DatabaseManager mDatabaseManager;
     private HandlerThread mStateMachinesThread;
     private BroadcastReceiver mBondStateChangedReceiver;
     private BroadcastReceiver mConnectionStateChangedReceiver;
@@ -151,10 +153,12 @@
             throw new IllegalStateException("start() called twice");
         }
 
-        // Get AdapterService, HapClientNativeInterface, AudioManager.
+        // Get AdapterService, HapClientNativeInterface, DatabaseManager, AudioManager.
         // None of them can be null.
         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
                 "AdapterService cannot be null when HapClientService starts");
+        mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
+                "DatabaseManager cannot be null when HapClientService starts");
         mHapClientNativeInterface = Objects.requireNonNull(
                 HapClientNativeInterface.getInstance(),
                 "HapClientNativeInterface cannot be null when HapClientService starts");
@@ -368,8 +372,7 @@
         if (DBG) {
             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
         }
-        mAdapterService.getDatabase()
-                .setProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT,
+        mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT,
                         connectionPolicy);
         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
             connect(device);
@@ -392,8 +395,7 @@
      * @hide
      */
     public int getConnectionPolicy(BluetoothDevice device) {
-        return mAdapterService.getDatabase()
-                .getProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT);
+        return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT);
     }
 
     /**
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java
index cbacc60..52ffab4 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java
@@ -169,12 +169,11 @@
      * Creates LeAudio Broadcast instance.
      *
      * @param metadata metadata buffer with TLVs
-     * @param audioProfile broadcast audio profile
      * @param broadcastCode optional code if broadcast should be encrypted
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void createBroadcast(byte[] metadata, int audioProfile, byte[] broadcastCode) {
-        createBroadcastNative(metadata, audioProfile, broadcastCode);
+    public void createBroadcast(byte[] metadata, byte[] broadcastCode) {
+        createBroadcastNative(metadata, broadcastCode);
     }
 
     /**
@@ -241,7 +240,7 @@
     private native void initNative();
     private native void stopNative();
     private native void cleanupNative();
-    private native void createBroadcastNative(byte[] metadata, int profile, byte[] broadcastCode);
+    private native void createBroadcastNative(byte[] metadata, byte[] broadcastCode);
     private native void updateMetadataNative(int broadcastId, byte[] metadata);
     private native void startBroadcastNative(int broadcastId);
     private native void stopBroadcastNative(int broadcastId);
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..ffcf22e 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -105,12 +105,6 @@
      */
     private static final int ACTIVE_CONTEXTS_NONE = 0;
 
-    /*
-     * Brodcast profile used by the lower layers
-     */
-    private static final int BROADCAST_PROFILE_SONIFICATION = 0;
-    private static final int BROADCAST_PROFILE_MEDIA = 1;
-
     private AdapterService mAdapterService;
     private DatabaseManager mDatabaseManager;
     private HandlerThread mStateMachinesThread;
@@ -128,6 +122,9 @@
     LeAudioTmapGattServer mTmapGattServer;
 
     @VisibleForTesting
+    VolumeControlService mVolumeControlService;
+
+    @VisibleForTesting
     RemoteCallbackList<IBluetoothLeBroadcastCallback> mBroadcastCallbacks;
 
     @VisibleForTesting
@@ -384,6 +381,7 @@
 
         mAdapterService = null;
         mAudioManager = null;
+        mVolumeControlService = null;
 
         return true;
     }
@@ -412,6 +410,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);
@@ -646,8 +656,6 @@
     /**
      * Creates LeAudio Broadcast instance.
      * @param metadata metadata buffer with TLVs
-     * @param audioProfile broadcast audio profile
-     * @param broadcastCode optional code if broadcast should be encrypted
      */
     public void createBroadcast(BluetoothLeAudioContentMetadata metadata, byte[] broadcastCode) {
         if (mLeAudioBroadcasterNativeInterface == null) {
@@ -655,7 +663,7 @@
             return;
         }
         mLeAudioBroadcasterNativeInterface.createBroadcast(metadata.getRawMetadata(),
-                BROADCAST_PROFILE_MEDIA, broadcastCode);
+                broadcastCode);
     }
 
     /**
@@ -830,6 +838,7 @@
                             + previousInDevice + ", mActiveAudioInDevice" + mActiveAudioInDevice
                             + " isLeOutput: false");
             }
+
             mAudioManager.handleBluetoothActiveDeviceChanged(mActiveAudioInDevice,previousInDevice,
                     BluetoothProfileConnectionInfo.createLeAudioInfo(false, false));
 
@@ -899,9 +908,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 +1068,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 +1092,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 +1799,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..5a6927c 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);
     }
 
      /**
@@ -178,6 +178,10 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public boolean setExtAudioOutVolumeOffset(BluetoothDevice device, int externalOutputId,
                                                     int offset) {
+        if (Utils.isPtsTestMode()) {
+            setVolumeNative(getByteAddress(device), offset);
+            return true;
+        }
         return setExtAudioOutVolumeOffsetNative(getByteAddress(device), externalOutputId, offset);
     }
 
@@ -370,7 +374,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..c1b2587 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
@@ -47,6 +47,7 @@
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.bluetooth.btservice.ServiceFactory;
+import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.SynchronousResultReceiver;
 
@@ -71,6 +72,7 @@
     private static VolumeControlService sVolumeControlService;
 
     private AdapterService mAdapterService;
+    private DatabaseManager mDatabaseManager;
     private HandlerThread mStateMachinesThread;
     private BluetoothDevice mPreviousAudioDevice;
 
@@ -179,6 +181,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;
@@ -210,10 +213,12 @@
             throw new IllegalStateException("start() called twice");
         }
 
-        // Get AdapterService, VolumeControlNativeInterface, AudioManager.
+        // Get AdapterService, VolumeControlNativeInterface, DatabaseManager, AudioManager.
         // None of them can be null.
         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
                 "AdapterService cannot be null when VolumeControlService starts");
+        mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
+                "DatabaseManager cannot be null when VolumeControlService starts");
         mVolumeControlNativeInterface = Objects.requireNonNull(
                 VolumeControlNativeInterface.getInstance(),
                 "VolumeControlNativeInterface cannot be null when VolumeControlService starts");
@@ -242,6 +247,7 @@
         registerReceiver(mConnectionStateChangedReceiver, filter);
 
         mAudioOffsets.clear();
+        mGroupVolumeCache.clear();
         mCallbacks = new RemoteCallbackList<IBluetoothVolumeControlCallback>();
 
         // Mark service as started
@@ -296,6 +302,7 @@
         mVolumeControlNativeInterface = null;
 
         mAudioOffsets.clear();
+        mGroupVolumeCache.clear();
 
         // Clear AdapterService, VolumeControlNativeInterface
         mAudioManager = null;
@@ -529,8 +536,7 @@
         if (DBG) {
             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
         }
-        mAdapterService.getDatabase()
-                .setProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL,
+        mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL,
                         connectionPolicy);
         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
             connect(device);
@@ -544,8 +550,7 @@
     public int getConnectionPolicy(BluetoothDevice device) {
         enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
                 "Need BLUETOOTH_PRIVILEGED permission");
-        return mAdapterService.getDatabase()
-                .getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL);
+        return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL);
     }
 
     boolean isVolumeOffsetAvailable(BluetoothDevice device) {
@@ -578,8 +583,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 +632,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 +1144,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 +1152,7 @@
 
                 VolumeControlService service = getService(source);
                 if (service != null) {
-                    service.setVolumeGroup(groupId, volume);
+                    service.setGroupVolume(groupId, volume);
                 }
                 receiver.send(null);
             } catch (RuntimeException e) {
@@ -1142,6 +1161,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 +1308,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/bas/BatteryServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java
index 46fa2fa..ef909ac 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java
@@ -130,7 +130,6 @@
      */
     @Test
     public void testGetSetPolicy() {
-        when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
         when(mDatabaseManager
                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.BATTERY))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
@@ -138,7 +137,6 @@
                 BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
                 mService.getConnectionPolicy(mDevice));
 
-        when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
         when(mDatabaseManager
                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.BATTERY))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
@@ -146,7 +144,6 @@
                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
                 mService.getConnectionPolicy(mDevice));
 
-        when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
         when(mDatabaseManager
                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.BATTERY))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
@@ -156,6 +153,20 @@
     }
 
     /**
+     * Test if getProfileConnectionPolicy works after the service is stopped.
+     */
+    @Test
+    public void testGetPolicyAfterStopped() {
+        mService.stop();
+        when(mDatabaseManager
+                .getProfileConnectionPolicy(mDevice, BluetoothProfile.BATTERY))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+        Assert.assertEquals("Initial device policy",
+                BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+                mService.getConnectionPolicy(mDevice));
+    }
+
+    /**
      *  Test okToConnect method using various test cases
      */
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
index 95ed30b..55905a7 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java
@@ -47,6 +47,7 @@
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -125,6 +126,10 @@
 
     @After
     public void tearDown() throws Exception {
+        if (mBassClientService == null) {
+            return;
+        }
+
         TestUtils.stopService(mServiceRule, BassClientService.class);
         mBassClientService = BassClientService.getBassClientService();
         assertThat(mBassClientService).isNull();
@@ -147,6 +152,21 @@
     }
 
     /**
+     * Test if getProfileConnectionPolicy works after the service is stopped.
+     */
+    @Test
+    public void testGetPolicyAfterStopped() {
+        mBassClientService.stop();
+        when(mDatabaseManager
+                .getProfileConnectionPolicy(mCurrentDevice,
+                        BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+        Assert.assertEquals("Initial device policy",
+                BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+                mBassClientService.getConnectionPolicy(mCurrentDevice));
+    }
+
+    /**
      * Test connecting to a test device.
      *  - service.connect() should return false
      *  - bassClientStateMachine.sendMessage(CONNECT) should be called.
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/csip/CsipSetCoordinatorServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
index a49db33..caac745 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
@@ -132,6 +132,14 @@
 
     @After
     public void tearDown() throws Exception {
+        if (mService == null) {
+            return;
+        }
+
+        if (Looper.myLooper() == null) {
+            return;
+        }
+
         stopService();
         mTargetContext.unregisterReceiver(mCsipSetCoordinatorIntentReceiver);
         TestUtils.clearAdapterService(mAdapterService);
@@ -205,6 +213,20 @@
     }
 
     /**
+     * Test if getProfileConnectionPolicy works after the service is stopped.
+     */
+    @Test
+    public void testGetPolicyAfterStopped() {
+        mService.stop();
+        when(mDatabaseManager
+                .getProfileConnectionPolicy(mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+        Assert.assertEquals("Initial device policy",
+                BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+                mService.getConnectionPolicy(mTestDevice));
+    }
+
+    /**
      * Test okToConnect method using various test cases
      */
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
index 0d83662..77eb6e9 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
@@ -170,6 +170,10 @@
 
     @After
     public void tearDown() throws Exception {
+        if (mService == null) {
+            return;
+        }
+
         mService.mCallbacks.unregister(mCallback);
 
         stopService();
@@ -247,6 +251,20 @@
     }
 
     /**
+     * Test if getProfileConnectionPolicy works after the service is stopped.
+     */
+    @Test
+    public void testGetPolicyAfterStopped() {
+        mService.stop();
+        when(mDatabaseManager
+                .getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+        Assert.assertEquals("Initial device policy",
+                BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+                mService.getConnectionPolicy(mDevice));
+    }
+
+    /**
      * Test okToConnect method using various test cases
      */
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
index a33824a..9568d4b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java
@@ -193,6 +193,10 @@
 
     @After
     public void tearDown() throws Exception {
+        if (mService == null) {
+            return;
+        }
+
         stopService();
         mTargetContext.unregisterReceiver(mLeAudioIntentReceiver);
         TestUtils.clearAdapterService(mAdapterService);
@@ -239,7 +243,7 @@
             BluetoothLeAudioContentMetadata meta) {
         mService.createBroadcast(meta, code);
 
-        verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()), eq(1),
+        verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()),
                 eq(code));
 
         // Check if broadcast is started automatically when created
@@ -321,8 +325,7 @@
         BluetoothLeAudioContentMetadata meta = meta_builder.build();
         mService.createBroadcast(meta, code);
 
-        verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()), eq(1),
-                eq(code));
+        verify(mNativeInterface, times(1)).createBroadcast(eq(meta.getRawMetadata()), eq(code));
 
         LeAudioStackEvent create_event =
                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
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..26cfebe 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);
@@ -216,6 +221,10 @@
 
     @After
     public void tearDown() throws Exception {
+        if ((mService == null) || (mAdapter == null)) {
+            return;
+        }
+
         mBondedDevices.clear();
         mGroupIntentQueue.clear();
         stopService();
@@ -1381,4 +1390,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/mcp/McpServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java
index 00b4d92..3374607 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java
@@ -81,6 +81,10 @@
 
     @After
     public void tearDown() throws Exception {
+        if (mMcpService == null) {
+            return;
+        }
+
         doReturn(false).when(mAdapterService).isStartedProfile(anyString());
         TestUtils.stopService(mServiceRule, McpService.class);
         mMcpService = McpService.getMcpService();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java
index 002c322..3ec632a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java
@@ -146,6 +146,10 @@
     public void tearDown() throws Exception {
         TestUtils.clearAdapterService(mAdapterService);
 
+        if (mMediaControlProfile == null) {
+            return;
+        }
+
         mMediaControlProfile.cleanup();
         mMediaControlProfile = null;
         reset(mMockMediaPlayerList);
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..2fbe191 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
@@ -118,6 +118,10 @@
 
     @After
     public void tearDown() throws Exception {
+        if (mService == null) {
+            return;
+        }
+
         stopService();
         mTargetContext.unregisterReceiver(mVolumeControlIntentReceiver);
         mDeviceQueueMap.clear();
@@ -232,6 +236,20 @@
     }
 
     /**
+     * Test if getProfileConnectionPolicy works after the service is stopped.
+     */
+    @Test
+    public void testGetPolicyAfterStopped() {
+        mService.stop();
+        when(mDatabaseManager
+                .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+        Assert.assertEquals("Initial device policy",
+                BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
+                mService.getConnectionPolicy(mDevice));
+    }
+
+    /**
      *  Test okToConnect method using various test cases
      */
     @Test
@@ -496,6 +514,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/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BluetoothProxy.java b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BluetoothProxy.java
index 4bd1dee..e4031a1 100644
--- a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BluetoothProxy.java
+++ b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BluetoothProxy.java
@@ -88,7 +88,7 @@
                                                 .equals(groupId))
                                 .collect(Collectors.toList());
             for (LeAudioDeviceStateWrapper dev : valid_devices) {
-                dev.leAudioData.groupStatusMutable.setValue(
+                dev.leAudioData.groupStatusMutable.postValue(
                         new Pair<>(groupId, new Pair<>(groupStatus, 0)));
             }
         }
@@ -112,8 +112,8 @@
             LeAudioDeviceStateWrapper valid_device = valid_device_opt.get();
             LeAudioDeviceStateWrapper.LeAudioData svc_data = valid_device.leAudioData;
 
-            svc_data.nodeStatusMutable.setValue(new Pair<>(groupId, GROUP_NODE_ADDED));
-            svc_data.groupStatusMutable.setValue(new Pair<>(groupId, new Pair<>(-1, -1)));
+            svc_data.nodeStatusMutable.postValue(new Pair<>(groupId, GROUP_NODE_ADDED));
+            svc_data.groupStatusMutable.postValue(new Pair<>(groupId, new Pair<>(-1, -1)));
         }
         @Override
         public void onGroupNodeRemoved(BluetoothDevice device, int groupId) {
@@ -141,8 +141,8 @@
             LeAudioDeviceStateWrapper valid_device = valid_device_opt.get();
             LeAudioDeviceStateWrapper.LeAudioData svc_data = valid_device.leAudioData;
 
-            svc_data.nodeStatusMutable.setValue(new Pair<>(groupId, GROUP_NODE_REMOVED));
-            svc_data.groupStatusMutable.setValue(new Pair<>(groupId, new Pair<>(-1, -1)));
+            svc_data.nodeStatusMutable.postValue(new Pair<>(groupId, GROUP_NODE_REMOVED));
+            svc_data.groupStatusMutable.postValue(new Pair<>(groupId, new Pair<>(-1, -1)));
         }
     };
 
@@ -155,9 +155,9 @@
                 int toState =
                         intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
                 if (toState == BluetoothAdapter.STATE_ON) {
-                    enabledBluetoothMutable.setValue(true);
+                    enabledBluetoothMutable.postValue(true);
                 } else if (toState == BluetoothAdapter.STATE_OFF) {
-                    enabledBluetoothMutable.setValue(false);
+                    enabledBluetoothMutable.postValue(false);
                 }
             }
         }
@@ -192,10 +192,10 @@
                                             .postValue(toState == BluetoothLeAudio.STATE_CONNECTED);
 
                                 group_id = bluetoothLeAudio.getGroupId(device);
-                                svc_data.nodeStatusMutable.setValue(
+                                svc_data.nodeStatusMutable.postValue(
                                         new Pair<>(group_id, GROUP_NODE_ADDED));
                                 svc_data.groupStatusMutable
-                                        .setValue(new Pair<>(group_id, new Pair<>(-1, -1)));
+                                        .postValue(new Pair<>(group_id, new Pair<>(-1, -1)));
                                 break;
                             }
                         }
@@ -669,18 +669,30 @@
                         break;
                     case BluetoothProfile.HAP_CLIENT:
                         bluetoothHapClient = (BluetoothHapClient) bluetoothProfile;
-                        bluetoothHapClient.registerCallback(mExecutor, hapCallback);
+                        try {
+                            bluetoothHapClient.registerCallback(mExecutor, hapCallback);
+                        } catch (IllegalArgumentException e) {
+                            Log.e("HAP", "Application callback already registered.");
+                        }
                         break;
                     case BluetoothProfile.LE_AUDIO_BROADCAST:
                         mBluetoothLeBroadcast = (BluetoothLeBroadcast) bluetoothProfile;
-                        mBluetoothLeBroadcast.registerCallback(mExecutor, mBroadcasterCallback);
+                        try {
+                            mBluetoothLeBroadcast.registerCallback(mExecutor, mBroadcasterCallback);
+                        } catch (IllegalArgumentException e) {
+                            Log.e("Broadcast", "Application callback already registered.");
+                        }
                         break;
                     case BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT:
                         Log.d("BluetoothProxy", "LE_AUDIO_BROADCAST_ASSISTANT Service connected");
                         mBluetoothLeBroadcastAssistant = (BluetoothLeBroadcastAssistant)
                                 bluetoothProfile;
-                        mBluetoothLeBroadcastAssistant.registerCallback(mExecutor,
+                        try {
+                            mBluetoothLeBroadcastAssistant.registerCallback(mExecutor,
                                 mBroadcastAssistantCallback);
+                        } catch (IllegalArgumentException e) {
+                            Log.e("BASS", "Application callback already registered.");
+                        }
                         break;
                 }
                 queryLeAudioDevices();
@@ -973,7 +985,7 @@
 
         try {
             Method groupAddNodeMethod = BluetoothLeAudio.class.getDeclaredMethod("groupAddNode",
-                    Integer.class, BluetoothDevice.class);
+                    int.class, BluetoothDevice.class);
             groupAddNodeMethod.setAccessible(true);
             groupAddNodeMethod.invoke(bluetoothLeAudio, group_id, device);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
@@ -986,7 +998,7 @@
 
         try {
             Method groupRemoveNodeMethod = BluetoothLeAudio.class
-                    .getDeclaredMethod("groupRemoveNode", Integer.class, BluetoothDevice.class);
+                    .getDeclaredMethod("groupRemoveNode", int.class, BluetoothDevice.class);
             groupRemoveNodeMethod.setAccessible(true);
             groupRemoveNodeMethod.invoke(bluetoothLeAudio, group_id, device);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
@@ -999,7 +1011,10 @@
 
         Log.d("Lock", "lock: " + lock);
         if (lock) {
-            if (mGroupLocks.containsKey(group_id)) return;
+            if (mGroupLocks.containsKey(group_id)) {
+                Log.e("Lock", "group" + group_id + " is already in locking process or locked: " + lock);
+                return;
+            }
 
             UUID uuid = bluetoothCsis.lockGroup(group_id, mExecutor,
                     (int group, int op_status, boolean is_locked) -> {
@@ -1121,8 +1136,10 @@
     }
 
     public void setVolume(BluetoothDevice device, int volume) {
-        if (bluetoothLeAudio != null) {
+        if (bluetoothLeAudio != null && !bluetoothLeAudio.getConnectedDevices().isEmpty()) {
             bluetoothLeAudio.setVolume(volume);
+        } else if (bluetoothVolumeControl != null) {
+            bluetoothVolumeControl.setVolumeOffset(device, volume);
         }
     }
 
@@ -1155,7 +1172,7 @@
         // Use hidden API
         try {
             Method getPresetInfoMethod = BluetoothHapClient.class.getDeclaredMethod("getPresetInfo",
-                    BluetoothDevice.class, Integer.class);
+                    BluetoothDevice.class, int.class);
             getPresetInfoMethod.setAccessible(true);
 
             new_preset = (BluetoothHapPresetInfo) getPresetInfoMethod.invoke(bluetoothHapClient,
@@ -1222,7 +1239,7 @@
 
             switchToPreviousPresetMethod.invoke(bluetoothHapClient, device);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            // Do nothing
+            return false;
         }
         return true;
     }
@@ -1239,7 +1256,7 @@
 
             switchToNextPresetMethod.invoke(bluetoothHapClient, device);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            // Do nothing
+            return false;
         }
         return true;
     }
@@ -1251,12 +1268,12 @@
         // Use hidden API
         try {
             Method switchToPreviousPresetForGroupMethod = BluetoothHapClient.class
-                    .getDeclaredMethod("switchToPreviousPresetForGroup", Integer.class);
+                    .getDeclaredMethod("switchToPreviousPresetForGroup", int.class);
             switchToPreviousPresetForGroupMethod.setAccessible(true);
 
             switchToPreviousPresetForGroupMethod.invoke(bluetoothHapClient, group_id);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            // Do nothing
+            return false;
         }
         return true;
     }
@@ -1268,12 +1285,12 @@
         // Use hidden API
         try {
             Method switchToNextPresetForGroupMethod = BluetoothHapClient.class
-                    .getDeclaredMethod("switchToNextPresetForGroup", Integer.class);
+                    .getDeclaredMethod("switchToNextPresetForGroup", int.class);
             switchToNextPresetForGroupMethod.setAccessible(true);
 
             switchToNextPresetForGroupMethod.invoke(bluetoothHapClient, group_id);
         } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            // Do nothing
+            return false;
         }
         return true;
     }
@@ -1335,16 +1352,10 @@
         return mBroadcastStatusMutableLive;
     }
 
-    public boolean startBroadcast(String programInfo, byte[] code) {
+    public boolean startBroadcast(BluetoothLeAudioContentMetadata meta, byte[] code) {
         if (mBluetoothLeBroadcast == null)
             return false;
-
-        BluetoothLeAudioContentMetadata.Builder contentBuilder =
-                new BluetoothLeAudioContentMetadata.Builder();
-        if (!programInfo.isEmpty()) {
-            contentBuilder.setProgramInfo(programInfo);
-        }
-        mBluetoothLeBroadcast.startBroadcast(contentBuilder.build(), code);
+        mBluetoothLeBroadcast.startBroadcast(meta, code);
         return true;
     }
 
diff --git a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcastItemsAdapter.java b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcastItemsAdapter.java
index a53bd46..14f77dd 100644
--- a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcastItemsAdapter.java
+++ b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcastItemsAdapter.java
@@ -66,14 +66,17 @@
             if (isPlaying) {
                 holder.background
                 .setCardBackgroundColor(ColorStateList.valueOf(Color.parseColor("#92b141")));
-                holder.mTextViewBroadcastId.setText("ID: " + broadcastId + " ▶️");
+                holder.mTextViewBroadcastId.setText("ID: " + broadcastId
+                        + "(" + String.format("0x%x", broadcastId) + ") ▶️");
             } else {
                 holder.background.setCardBackgroundColor(ColorStateList.valueOf(Color.WHITE));
-                holder.mTextViewBroadcastId.setText("ID: " + broadcastId + " ⏸");
+                holder.mTextViewBroadcastId.setText("ID: " + broadcastId
+                        + "(" + String.format("0x%x", broadcastId) + ") ⏸");
             }
         } else {
             holder.background.setCardBackgroundColor(ColorStateList.valueOf(Color.WHITE));
-            holder.mTextViewBroadcastId.setText("ID: " + broadcastId);
+            holder.mTextViewBroadcastId.setText("ID: " + broadcastId
+                        + "(" + String.format("0x%x", broadcastId) + ")");
         }
 
         // TODO: Add additional informations to the card
diff --git a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterActivity.java b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterActivity.java
index d2eb1d1..a3a2a61 100644
--- a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterActivity.java
+++ b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterActivity.java
@@ -17,6 +17,7 @@
 
 package com.android.bluetooth.leaudio;
 
+import android.bluetooth.BluetoothLeAudioContentMetadata;
 import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.content.Intent;
 import android.os.Bundle;
@@ -24,6 +25,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.EditText;
+import android.widget.NumberPicker;
 import android.widget.TextView;
 import android.widget.Toast;
 
@@ -37,6 +39,8 @@
 
 import com.android.bluetooth.leaudio.R;
 
+import java.io.ByteArrayOutputStream;
+
 public class BroadcasterActivity extends AppCompatActivity {
     private BroadcasterViewModel mViewModel;
 
@@ -55,12 +59,41 @@
 
                 View alertView = inflater.inflate(R.layout.broadcaster_add_broadcast_dialog, null);
                 final EditText code_input_text = alertView.findViewById(R.id.broadcast_code_input);
-                EditText metadata_input_text = alertView.findViewById(R.id.broadcast_meta_input);
+                final EditText program_info = alertView.findViewById(R.id.broadcast_program_info_input);
+                final NumberPicker contextPicker = alertView.findViewById(R.id.context_picker);
+
+                // Add context type selector
+                contextPicker.setMinValue(1);
+                contextPicker.setMaxValue(
+                        alertView.getResources().getStringArray(R.array.content_types).length - 1);
+                contextPicker.setDisplayedValues(
+                        alertView.getResources().getStringArray(R.array.content_types));
 
                 alert.setView(alertView).setNegativeButton("Cancel", (dialog, which) -> {
                     // Do nothing
                 }).setPositiveButton("Start", (dialog, which) -> {
-                    if (mViewModel.startBroadcast(metadata_input_text.getText().toString(),
+
+                    final BluetoothLeAudioContentMetadata.Builder contentBuilder =
+                            new BluetoothLeAudioContentMetadata.Builder();
+                    final String programInfo = program_info.getText().toString();
+                    if (!programInfo.isEmpty()) {
+                        contentBuilder.setProgramInfo(programInfo);
+                    }
+
+                    // Extract raw metadata
+                    byte[] metaBuffer = contentBuilder.build().getRawMetadata();
+                    ByteArrayOutputStream stream = new ByteArrayOutputStream();
+                    stream.write(metaBuffer, 0 , metaBuffer.length);
+
+                    // Extend raw metadata with context type
+                    final int contextValue = 1 << (contextPicker.getValue() - 1);
+                    stream.write((byte)0x03); // Length
+                    stream.write((byte)0x02); // Type for the Streaming Audio Context
+                    stream.write((byte)(contextValue & 0x00FF)); // Value LSB
+                    stream.write((byte)((contextValue & 0xFF00) >> 8)); // Value MSB
+
+                    if (mViewModel.startBroadcast(
+                                BluetoothLeAudioContentMetadata.fromRawBytes(stream.toByteArray()),
                             code_input_text.getText() == null
                                     || code_input_text.getText().length() == 0 ? null
                                             : code_input_text.getText().toString().getBytes()))
@@ -135,7 +168,7 @@
 
                 LayoutInflater inflater = getLayoutInflater();
                 View alertView = inflater.inflate(R.layout.broadcaster_add_broadcast_dialog, null);
-                EditText metadata_input_text = alertView.findViewById(R.id.broadcast_meta_input);
+                EditText program_info_input_text = alertView.findViewById(R.id.broadcast_program_info_input);
 
                 // The Code cannot be changed, so just hide it
                 final EditText code_input_text = alertView.findViewById(R.id.broadcast_code_input);
@@ -146,7 +179,7 @@
                             // Do nothing
                         }).setPositiveButton("Update", (modifyDialog, modifyWhich) -> {
                             if (mViewModel.updateBroadcast(broadcastId,
-                                    metadata_input_text.getText().toString()))
+                                    program_info_input_text.getText().toString()))
                                 Toast.makeText(BroadcasterActivity.this, "Broadcast was updated.",
                                         Toast.LENGTH_SHORT).show();
                         });
diff --git a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterViewModel.java b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterViewModel.java
index bf22c8e..7469bac 100644
--- a/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterViewModel.java
+++ b/android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BroadcasterViewModel.java
@@ -19,6 +19,7 @@
 
 import android.app.Application;
 import android.bluetooth.BluetoothLeBroadcastMetadata;
+import android.bluetooth.BluetoothLeAudioContentMetadata;
 
 import androidx.annotation.NonNull;
 import androidx.core.util.Pair;
@@ -39,8 +40,8 @@
         mBluetooth.initProfiles();
     }
 
-    public boolean startBroadcast(String programInfo, byte[] code) {
-        return mBluetooth.startBroadcast(programInfo, code);
+    public boolean startBroadcast(BluetoothLeAudioContentMetadata meta, byte[] code) {
+        return mBluetooth.startBroadcast(meta, code);
     }
 
     public boolean stopBroadcast(int broadcastId) {
diff --git a/android/leaudio/app/src/main/res/layout/broadcaster_add_broadcast_dialog.xml b/android/leaudio/app/src/main/res/layout/broadcaster_add_broadcast_dialog.xml
index 5bc50b5..2b320e7 100644
--- a/android/leaudio/app/src/main/res/layout/broadcaster_add_broadcast_dialog.xml
+++ b/android/leaudio/app/src/main/res/layout/broadcaster_add_broadcast_dialog.xml
@@ -42,7 +42,7 @@
             android:text="Program Info:" />
 
         <EditText
-            android:id="@+id/broadcast_meta_input"
+            android:id="@+id/broadcast_program_info_input"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="2"
@@ -51,4 +51,25 @@
             android:inputType="text|textAutoCorrect" />
     </LinearLayout>
 
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/textView26"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Context type:" />
+
+        <NumberPicker
+            android:id="@+id/context_picker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal|bottom"
+            android:orientation="horizontal"
+            max="11"
+            min="1" />
+    </LinearLayout>
+
 </LinearLayout>
\ No newline at end of file
diff --git a/android/leaudio/app/src/main/res/values/donottranslate_strings.xml b/android/leaudio/app/src/main/res/values/donottranslate_strings.xml
index 0b7a060..9665e48 100644
--- a/android/leaudio/app/src/main/res/values/donottranslate_strings.xml
+++ b/android/leaudio/app/src/main/res/values/donottranslate_strings.xml
@@ -32,14 +32,15 @@
         <item>Unspecified</item>
         <item>Conversational</item>
         <item>Media</item>
+        <item>Game</item>
         <item>Instructional</item>
-        <item>AttentionSeeking</item>
-        <item>ImmediateAlert</item>
-        <item>ManMachine</item>
-	<item>EmergencyAlert</item>
-        <item>Ringtone</item>
-        <item>TV</item>
+        <item>Voice Assistant</item>
         <item>Live</item>
+        <item>Sound Effects</item>
+        <item>Notifications</item>
+        <item>Ringtone</item>
+        <item>Alerts</item>
+        <item>Emergency Alarm</item>
     </string-array>
     <string-array name="audio_locations">
         <item>Front Left</item>
diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java
index dda3f04..d3aec71 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;
@@ -2905,16 +2935,21 @@
 
             String launcherActivity = "com.android.bluetooth.opp.BluetoothOppLauncherActivity";
 
-            PackageManager packageManager = mContext.createContextAsUser(userHandle, 0)
+            PackageManager systemPackageManager = mContext.getPackageManager();
+            PackageManager userPackageManager = mContext.createContextAsUser(userHandle, 0)
                                                         .getPackageManager();
-            var allPackages = packageManager.getPackagesForUid(Process.BLUETOOTH_UID);
+            var allPackages = systemPackageManager.getPackagesForUid(Process.BLUETOOTH_UID);
             for (String candidatePackage : allPackages) {
+                Log.v(TAG, "Searching package " + candidatePackage);
                 PackageInfo packageInfo;
                 try {
-                    // note: we need the package manager for the SYSTEM user, not our userHandle
-                    packageInfo = mContext.getPackageManager().getPackageInfo(
+                    packageInfo = systemPackageManager.getPackageInfo(
                         candidatePackage,
-                        PackageManager.PackageInfoFlags.of(PackageManager.GET_ACTIVITIES));
+                        PackageManager.PackageInfoFlags.of(
+                            PackageManager.GET_ACTIVITIES
+                            | PackageManager.MATCH_ANY_USER
+                            | PackageManager.MATCH_UNINSTALLED_PACKAGES
+                            | PackageManager.MATCH_DISABLED_COMPONENTS));
                 } catch (PackageManager.NameNotFoundException e) {
                     // ignore, try next package
                     Log.e(TAG, "Could not find package " + candidatePackage);
@@ -2927,11 +2962,12 @@
                     continue;
                 }
                 for (var activity : packageInfo.activities) {
+                    Log.v(TAG, "Checking activity " + activity.name);
                     if (launcherActivity.equals(activity.name)) {
                         final ComponentName oppLauncherComponent = new ComponentName(
                                 candidatePackage, launcherActivity
                         );
-                        packageManager.setComponentEnabledSetting(
+                        userPackageManager.setComponentEnabledSetting(
                                 oppLauncherComponent, newState, PackageManager.DONT_KILL_APP
                         );
                         return;
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/blueberry/tests/gd/cert/os_utils.py b/system/blueberry/tests/gd/cert/os_utils.py
index 0749145..31e4a0e 100644
--- a/system/blueberry/tests/gd/cert/os_utils.py
+++ b/system/blueberry/tests/gd/cert/os_utils.py
@@ -77,7 +77,6 @@
         logging.warning("Freeing port %d used by %s" % (conn.laddr.port, str(conn)))
         if not conn.pid:
             logging.error("Failed to kill process occupying port %d due to lack of pid" % conn.laddr.port)
-            success = False
             continue
         logging.warning("Killing pid %d that is using port port %d" % (conn.pid, conn.laddr.port))
         if conn.pid in killed_pids:
diff --git a/system/blueberry/tests/sl4a_sl4a/advertising/le_advertising.py b/system/blueberry/tests/sl4a_sl4a/advertising/le_advertising.py
new file mode 100644
index 0000000..4e0f2bc
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/advertising/le_advertising.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+#
+#   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.
+
+import binascii
+import io
+import logging
+import os
+import queue
+
+from blueberry.tests.gd.cert.context import get_current_context
+
+from blueberry.tests.sl4a_sl4a.lib import sl4a_sl4a_base_test
+from blueberry.tests.gd_sl4a.lib.bt_constants import ble_address_types
+
+
+class LeAdvertisingTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass):
+
+    def setup_class(self):
+        super().setup_class()
+
+    def setup_test(self):
+        super().setup_test()
+
+    def teardown_test(self):
+        super().teardown_test()
+
+    def test_advertise_name(self):
+        rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu()
+        self.dut_scanner_.scan_for_name(self.cert_advertiser_.get_local_advertising_name())
+        self.dut_scanner_.stop_scanning()
+        self.cert_advertiser_.stop_advertising()
+
+    def test_advertise_name_stress(self):
+        for i in range(0, 10):
+            self.test_advertise_name()
+
+    def test_advertise_name_twice_no_stop(self):
+        rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu()
+        self.dut_scanner_.scan_for_name(self.cert_advertiser_.get_local_advertising_name())
+        self.dut_scanner_.stop_scanning()
+        rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu()
+        self.dut_scanner_.scan_for_name(self.cert_advertiser_.get_local_advertising_name())
+        self.dut_scanner_.stop_scanning()
+        self.cert_advertiser_.stop_advertising()
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/le_advertiser.py b/system/blueberry/tests/sl4a_sl4a/lib/le_advertiser.py
new file mode 100644
index 0000000..b6d5cfe
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/lib/le_advertiser.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+#
+#   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.
+
+import binascii
+import logging
+import queue
+
+from blueberry.facade import common_pb2 as common
+from blueberry.tests.gd.cert.closable import Closable
+from blueberry.tests.gd.cert.closable import safeClose
+from blueberry.tests.gd.cert.truth import assertThat
+from blueberry.tests.gd_sl4a.lib.ble_lib import generate_ble_advertise_objects
+from blueberry.tests.gd_sl4a.lib.bt_constants import adv_succ
+from blueberry.tests.gd_sl4a.lib.bt_constants import ble_advertise_settings_modes
+from blueberry.tests.sl4a_sl4a.lib import sl4a_sl4a_base_test
+
+
+class LeAdvertiser(Closable):
+
+    is_advertising = False
+    device = None
+    default_timeout = 10  # seconds
+    advertise_callback = None
+    advertise_data = None
+    advertise_settings = None
+
+    def __init__(self, device):
+        self.device = device
+
+    def __wait_for_event(self, expected_event_name):
+        try:
+            event_info = self.device.ed.pop_event(expected_event_name, self.default_timeout)
+            logging.info(event_info)
+        except queue.Empty as error:
+            logging.error("Failed to find event: %s", expected_event_name)
+            return False
+        return True
+
+    def advertise_rpa_public_extended_pdu(self, name="SL4A Device"):
+        if self.is_advertising:
+            logging.info("Already advertising!")
+            return
+        self.is_advertising = True
+        self.device.sl4a.bleSetScanSettingsLegacy(False)
+        self.device.sl4a.bleSetAdvertiseSettingsIsConnectable(True)
+        self.device.sl4a.bleSetAdvertiseDataIncludeDeviceName(True)
+        self.device.sl4a.bleSetAdvertiseSettingsAdvertiseMode(ble_advertise_settings_modes['low_latency'])
+        self.device.sl4a.bleSetAdvertiseSettingsOwnAddressType(common.RANDOM_DEVICE_ADDRESS)
+        self.advertise_callback, self.advertise_data, self.advertise_settings = generate_ble_advertise_objects(
+            self.device.sl4a)
+        self.device.sl4a.bleStartBleAdvertising(self.advertise_callback, self.advertise_data, self.advertise_settings)
+
+        # Wait for SL4A cert to start advertising
+        assertThat(self.__wait_for_event(adv_succ.format(self.advertise_callback))).isTrue()
+        logging.info("Advertising started")
+
+    def get_local_advertising_name(self):
+        return self.device.sl4a.bluetoothGetLocalName()
+
+    def stop_advertising(self):
+        if self.is_advertising:
+            logging.info("Stopping advertisement")
+            self.device.sl4a.bleStopBleAdvertising(self.advertise_callback)
+            self.is_advertising = False
+
+    def close(self):
+        self.stop_advertising()
+        self.device = None
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py b/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py
new file mode 100644
index 0000000..c7874e6
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/lib/le_scanner.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python3
+#
+#   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.
+
+import logging
+import queue
+
+from blueberry.facade import common_pb2 as common
+from blueberry.tests.gd.cert.closable import Closable
+from blueberry.tests.gd.cert.closable import safeClose
+from blueberry.tests.gd.cert.truth import assertThat
+from blueberry.tests.gd_sl4a.lib.ble_lib import generate_ble_scan_objects
+from blueberry.tests.gd_sl4a.lib.bt_constants import ble_scan_settings_modes
+from blueberry.tests.gd_sl4a.lib.bt_constants import scan_result
+from blueberry.tests.sl4a_sl4a.lib import sl4a_sl4a_base_test
+
+
+class LeScanner(Closable):
+
+    is_scanning = False
+    device = None
+    filter_list = None
+    scan_settings = None
+    scan_callback = None
+
+    def __init__(self, device):
+        self.device = device
+
+    def __wait_for_scan_result_event(self, expected_event_name, timeout=60):
+        try:
+            event_info = self.device.ed.pop_event(expected_event_name, timeout)
+        except queue.Empty as error:
+            logging.error("Could not find scan result event: %s", expected_event_name)
+            return None
+        return event_info['data']['Result']['deviceInfo']['address']
+
+    def scan_for_address_expect_none(self, address, addr_type):
+        if self.is_scanning:
+            print("Already scanning!")
+            return
+        self.is_scanning = True
+        logging.info("Start scanning for identity address {} or type {}".format(address, addr_type))
+        self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+        self.device.sl4a.bleSetScanSettingsLegacy(False)
+        self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a)
+        expected_event_name = scan_result.format(self.scan_callback)
+
+        # Start scanning on SL4A DUT
+        self.device.sl4a.bleSetScanFilterDeviceAddressAndType(address, addr_type)
+        self.device.sl4a.bleBuildScanFilter(self.filter_list)
+        self.device.sl4a.bleStartBleScan(self.filter_list, self.scan_settings, self.scan_callback)
+
+        # Verify that scan result is received on SL4A DUT
+        advertising_address = self.__wait_for_scan_result_event(expected_event_name, 1)
+        assertThat(advertising_address).isNone()
+        logging.info("Filter advertisement with address {}".format(advertising_address))
+
+    def scan_for_address(self, address, addr_type):
+        if self.is_scanning:
+            print("Already scanning!")
+            return
+        self.is_scanning = True
+        logging.info("Start scanning for identity address {} or type {}".format(address, addr_type))
+        self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+        self.device.sl4a.bleSetScanSettingsLegacy(False)
+        self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a)
+        expected_event_name = scan_result.format(self.scan_callback)
+
+        # Start scanning on SL4A DUT
+        self.device.sl4a.bleSetScanFilterDeviceAddressAndType(address, addr_type)
+        self.device.sl4a.bleBuildScanFilter(self.filter_list)
+        self.device.sl4a.bleStartBleScan(self.filter_list, self.scan_settings, self.scan_callback)
+
+        # Verify that scan result is received on SL4A DUT
+        advertising_address = self.__wait_for_scan_result_event(expected_event_name)
+        assertThat(advertising_address).isNotNone()
+        logging.info("Filter advertisement with address {}".format(advertising_address))
+
+    def scan_for_address_with_irk(self, address, addr_type, irk):
+        if self.is_scanning:
+            print("Already scanning!")
+            return
+        self.is_scanning = True
+        logging.info("Start scanning for identity address {} or type {} using irk {}".format(address, addr_type, irk))
+        self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+        self.device.sl4a.bleSetScanSettingsLegacy(False)
+        self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a)
+        expected_event_name = scan_result.format(self.scan_callback)
+
+        # Start scanning on SL4A DUT
+        self.device.sl4a.bleSetScanFilterDeviceAddressTypeAndIrkHexString(address, addr_type, irk)
+        self.device.sl4a.bleBuildScanFilter(self.filter_list)
+        self.device.sl4a.bleStartBleScan(self.filter_list, self.scan_settings, self.scan_callback)
+
+        # Verify that scan result is received on SL4A DUT
+        advertising_address = self.__wait_for_scan_result_event(expected_event_name)
+        assertThat(advertising_address).isNotNone()
+        logging.info("Filter advertisement with address {}".format(advertising_address))
+
+    def scan_for_address_with_irk_pending_intent(self, address, addr_type, irk):
+        if self.is_scanning:
+            print("Already scanning!")
+            return
+        self.is_scanning = True
+        logging.info("Start scanning for identity address {} or type {} using irk {}".format(address, addr_type, irk))
+        self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+        self.device.sl4a.bleSetScanSettingsLegacy(False)
+        self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a)
+        # Hard code here since callback index iterates and will cause this to fail if ran
+        # Second as the impl in SL4A sends this since it's a single callback for broadcast.
+        expected_event_name = "BleScan1onScanResults"
+
+        # Start scanning on SL4A DUT
+        self.device.sl4a.bleSetScanFilterDeviceAddressTypeAndIrkHexString(address, addr_type, irk)
+        self.device.sl4a.bleBuildScanFilter(self.filter_list)
+        self.device.sl4a.bleStartBleScanPendingIntent(self.filter_list, self.scan_settings)
+
+        # Verify that scan result is received on SL4A DUT
+        advertising_address = self.__wait_for_scan_result_event(expected_event_name)
+        assertThat(advertising_address).isNotNone()
+        logging.info("Filter advertisement with address {}".format(advertising_address))
+
+    def scan_for_name(self, name):
+        if self.is_scanning:
+            print("Already scanning!")
+            return
+        self.is_scanning = True
+        logging.info("Start scanning for name {}".format(name))
+        self.device.sl4a.bleSetScanSettingsScanMode(ble_scan_settings_modes['low_latency'])
+        self.device.sl4a.bleSetScanSettingsLegacy(False)
+        self.filter_list, self.scan_settings, self.scan_callback = generate_ble_scan_objects(self.device.sl4a)
+        expected_event_name = scan_result.format(1)
+
+        # Start scanning on SL4A DUT
+        self.device.sl4a.bleSetScanFilterDeviceName(name)
+        self.device.sl4a.bleBuildScanFilter(self.filter_list)
+        self.device.sl4a.bleStartBleScanPendingIntent(self.filter_list, self.scan_settings)
+
+        # Verify that scan result is received on SL4A DUT
+        advertising_address = self.__wait_for_scan_result_event(expected_event_name)
+        assertThat(advertising_address).isNotNone()
+        logging.info("Filter advertisement with address {}".format(advertising_address))
+        return advertising_address
+
+    def stop_scanning(self):
+        """
+        Warning: no java callback registered for this
+        """
+        if self.is_scanning:
+            logging.info("Stopping scan")
+            self.device.sl4a.bleStopBleScan(self.scan_callback)
+            self.is_scanning = False
+
+    def close(self):
+        self.stop_scanning()
+        self.device = None
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/oob_data.py b/system/blueberry/tests/sl4a_sl4a/lib/oob_data.py
new file mode 100644
index 0000000..d37f3f4
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/lib/oob_data.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+#
+#   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.
+
+
+class OobData:
+    """
+    This represents the data generated from the device
+    """
+
+    address = None
+    confirmation = None
+    randomizer = None
+
+    def __init__(self, address, confirmation, randomizer):
+        self.address = address
+        self.confirmation = confirmation
+        self.randomizer = randomizer
+
+    def to_sl4a_address(self):
+        oob_address = self.address.upper()
+        address_str_octets = []
+        i = 1
+        buf = ""
+        for c in oob_address:
+            buf += c
+            if i % 2 == 0:
+                address_str_octets.append(buf)
+                buf = ""
+            i += 1
+        address_str_octets = address_str_octets[:6]
+        address_str_octets.reverse()
+        return ":".join(address_str_octets)
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/security.py b/system/blueberry/tests/sl4a_sl4a/lib/security.py
new file mode 100644
index 0000000..05a6645
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/lib/security.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+#
+#   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.
+
+import queue
+import logging
+
+from blueberry.tests.gd.cert.closable import Closable
+from blueberry.tests.gd.cert.truth import assertThat
+from blueberry.tests.sl4a_sl4a.lib.oob_data import OobData
+
+
+class Security:
+
+    # Events sent from SL4A
+    SL4A_EVENT_GENERATED = "GeneratedOobData"
+    SL4A_EVENT_ERROR = "ErrorOobData"
+    SL4A_EVENT_BONDED = "Bonded"
+    SL4A_EVENT_UNBONDED = "Unbonded"
+
+    # Matches tBT_TRANSPORT
+    # Used Strings because ints were causing gRPC problems
+    TRANSPORT_AUTO = "0"
+    TRANSPORT_BREDR = "1"
+    TRANSPORT_LE = "2"
+
+    __default_timeout = 10  # seconds
+    __default_bonding_timeout = 30  # seconds
+    __device = None
+
+    def __init__(self, device):
+        self.__device = device
+        self.__device.sl4a.bluetoothStartPairingHelper(True)
+
+    def generate_oob_data(self, transport):
+        self.__device.sl4a.bluetoothGenerateLocalOobData(transport)
+        try:
+            event_info = self.__device.ed.pop_event(self.SL4A_EVENT_GENERATED, self.__default_timeout)
+        except queue.Empty as error:
+            logging.error("Failed to generate OOB data!")
+            return None
+        return OobData(event_info["data"]["address_with_type"], event_info["data"]["confirmation"],
+                       event_info["data"]["randomizer"])
+
+    def ensure_device_bonded(self):
+        bond_state = None
+        try:
+            bond_state = self.__device.ed.pop_event(self.SL4A_EVENT_BONDED, self.__default_bonding_timeout)
+        except queue.Empty as error:
+            logging.error("Failed to get bond event!")
+
+        assertThat(bond_state).isNotNone()
+        logging.info("Bonded: %s", bond_state["data"]["bonded_state"])
+        assertThat(bond_state["data"]["bonded_state"]).isEqualTo(True)
+
+    def create_bond_out_of_band(self, oob_data):
+        assertThat(oob_data).isNotNone()
+        address = oob_data.to_sl4a_address()
+        self.__device.sl4a.bluetoothCreateBondOutOfBand(address, self.TRANSPORT_LE, oob_data.confirmation,
+                                                        oob_data.randomizer)
+        self.ensure_device_bonded()
+
+    def create_bond_numeric_comparison(self, address, transport=TRANSPORT_LE):
+        assertThat(address).isNotNone()
+        if transport == self.TRANSPORT_LE:
+            self.__device.sl4a.bluetoothLeBond(address)
+        else:
+            self.__device.sl4a.bluetoothBond(address)
+        self.ensure_device_bonded()
+
+    def remove_all_bonded_devices(self):
+        bonded_devices = self.__device.sl4a.bluetoothGetBondedDevices()
+        for device in bonded_devices:
+            self.remove_bond(device["address"])
+
+    def remove_bond(self, address):
+        self.__device.sl4a.bluetoothUnbond(address)
+        bond_state = None
+        try:
+            bond_state = self.__device.ed.pop_event(self.SL4A_EVENT_UNBONDED, self.__default_timeout)
+        except queue.Empty as error:
+            logging.error("Failed to get bond event!")
+
+        assertThat(bond_state).isNotNone()
+        assertThat(bond_state["data"]["bonded_state"]).isEqualTo(False)
+
+    def close(self):
+        self.remove_all_bonded_devices()
+        self.__device.sl4a.bluetoothStartPairingHelper(False)
+        self.__device = None
diff --git a/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py b/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
index 86efbd2..fe37b00 100644
--- a/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
+++ b/system/blueberry/tests/sl4a_sl4a/lib/sl4a_sl4a_base_test.py
@@ -19,10 +19,14 @@
 import traceback
 from functools import wraps
 
+from blueberry.tests.gd.cert.closable import safeClose
 from blueberry.tests.gd.cert.context import get_current_context
 from blueberry.tests.gd_sl4a.lib.ble_lib import BleLib
 from blueberry.tests.gd_sl4a.lib.ble_lib import disable_bluetooth
 from blueberry.tests.gd_sl4a.lib.ble_lib import enable_bluetooth
+from blueberry.tests.sl4a_sl4a.lib.le_advertiser import LeAdvertiser
+from blueberry.tests.sl4a_sl4a.lib.le_scanner import LeScanner
+from blueberry.tests.sl4a_sl4a.lib.security import Security
 from blueberry.utils.mobly_sl4a_utils import setup_sl4a
 from blueberry.utils.mobly_sl4a_utils import teardown_sl4a
 from grpc import RpcError
@@ -35,6 +39,16 @@
 
 class Sl4aSl4aBaseTestClass(BaseTestClass):
 
+    # DUT
+    dut_advertiser_ = None
+    dut_scanner_ = None
+    dut_security_ = None
+
+    # CERT
+    cert_advertiser_ = None
+    cert_scanner_ = None
+    cert_security_ = None
+
     SUBPROCESS_WAIT_TIMEOUT_SECONDS = 10
 
     def setup_class(self):
@@ -94,9 +108,29 @@
     def setup_test(self):
         self.setup_device_for_test(self.dut)
         self.setup_device_for_test(self.cert)
+        self.dut_advertiser_ = LeAdvertiser(self.dut)
+        self.dut_scanner_ = LeScanner(self.dut)
+        self.dut_security_ = Security(self.dut)
+        self.cert_advertiser_ = LeAdvertiser(self.cert)
+        self.cert_scanner_ = LeScanner(self.cert)
+        self.cert_security_ = Security(self.cert)
         return True
 
     def teardown_test(self):
+        # Go ahead and remove everything before turning off the stack
+        safeClose(self.dut_advertiser_)
+        safeClose(self.dut_scanner_)
+        safeClose(self.dut_security_)
+        safeClose(self.cert_advertiser_)
+        safeClose(self.cert_scanner_)
+        safeClose(self.cert_security_)
+        self.dut_advertiser_ = None
+        self.dut_scanner_ = None
+        self.dut_security_ = None
+        self.cert_advertiser_ = None
+        self.cert_scanner_ = None
+        self.cert_security_ = None
+
         # Make sure BLE is disabled and Bluetooth is disabled after test
         self.dut.sl4a.bluetoothDisableBLE()
         disable_bluetooth(self.dut.sl4a, self.dut.ed)
diff --git a/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py b/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py
new file mode 100644
index 0000000..becdbc7
--- /dev/null
+++ b/system/blueberry/tests/sl4a_sl4a/security/oob_pairing_test.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+#
+#   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.
+
+import binascii
+import io
+import logging
+import os
+import queue
+
+from blueberry.tests.gd.cert.context import get_current_context
+from blueberry.tests.gd.cert.truth import assertThat
+from blueberry.tests.gd_sl4a.lib.bt_constants import ble_address_types
+from blueberry.tests.sl4a_sl4a.lib import sl4a_sl4a_base_test
+from blueberry.tests.sl4a_sl4a.lib.security import Security
+
+
+class OobPairingTest(sl4a_sl4a_base_test.Sl4aSl4aBaseTestClass):
+
+    def __get_cert_public_address_and_irk_from_bt_config(self):
+        # Pull IRK from SL4A cert side to pass in from SL4A DUT side when scanning
+        bt_config_file_path = os.path.join(get_current_context().get_full_output_path(),
+                                           "DUT_%s_bt_config.conf" % self.cert.serial)
+        try:
+            self.cert.adb.pull(["/data/misc/bluedroid/bt_config.conf", bt_config_file_path])
+        except AdbError as error:
+            logging.error("Failed to pull SL4A cert BT config")
+            return False
+        logging.debug("Reading SL4A cert BT config")
+        with io.open(bt_config_file_path) as f:
+            for line in f.readlines():
+                stripped_line = line.strip()
+                if (stripped_line.startswith("Address")):
+                    address_fields = stripped_line.split(' ')
+                    # API currently requires public address to be capitalized
+                    address = address_fields[2].upper()
+                    logging.debug("Found cert address: %s" % address)
+                    continue
+                if (stripped_line.startswith("LE_LOCAL_KEY_IRK")):
+                    irk_fields = stripped_line.split(' ')
+                    irk = irk_fields[2]
+                    logging.debug("Found cert IRK: %s" % irk)
+                    continue
+
+        return address, irk
+
+    def setup_class(self):
+        super().setup_class()
+
+    def setup_test(self):
+        assertThat(super().setup_test()).isTrue()
+
+    def teardown_test(self):
+        super().teardown_test()
+
+    def __test_scan(self, address_type="public"):
+        cert_public_address, irk = self.__get_cert_public_address_and_irk_from_bt_config()
+        rpa_address = self.cert_advertiser_.advertise_rpa_public_extended_pdu()
+        self.dut_scanner_.start_identity_address_scan(cert_public_address, ble_address_types[address_type])
+        self.dut_scanner_.stop_scanning()
+        self.cert_advertiser_.stop_advertising()
+
+    def test_classic_generate_local_oob_data(self):
+        oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR)
+        assertThat(oob_data).isNotNone()
+        oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR)
+        assertThat(oob_data).isNotNone()
+
+    def test_classic_generate_local_oob_data_stress(self):
+        for i in range(1, 20):
+            oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR)
+            assertThat(oob_data).isNotNone()
+            oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_BREDR)
+            assertThat(oob_data).isNotNone()
+
+    def test_le_generate_local_oob_data(self):
+        oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_LE)
+        assertThat(oob_data).isNotNone()
+        oob_data = self.cert_security_.generate_oob_data(Security.TRANSPORT_LE)
+        assertThat(oob_data).isNotNone()
+
+    def test_le_generate_local_oob_data_stress(self):
+        for i in range(1, 20):
+            oob_data = self.dut_security_.generate_oob_data(Security.TRANSPORT_LE)
+            assertThat(oob_data).isNotNone()
+            oob_data = self.cert_security_.generate_oob_data(Security.TRANSPORT_LE)
+            assertThat(oob_data).isNotNone()
+
+    def test_le_bond_oob(self):
+        oob_data = self.cert_security_.generate_oob_data(Security.TRANSPORT_LE)
+        assertThat(oob_data).isNotNone()
+        self.dut_security_.create_bond_out_of_band(oob_data)
+
+    def test_le_bond_oob_stress(self):
+        for i in range(0, 10):
+            logging.info("Stress #%d" % i)
+            self.test_le_bond_oob()
+            self.dut_security_.remove_all_bonded_devices()
+            self.cert_security_.remove_all_bonded_devices()
+
+    def test_le_generate_local_oob_data_after_le_bond_oob(self):
+        self.test_le_bond_oob()
+        self.test_le_generate_local_oob_data()
diff --git a/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py b/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py
index 92f5074..c08836d 100644
--- a/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py
+++ b/system/blueberry/tests/sl4a_sl4a/sl4a_sl4a_test_runner.py
@@ -14,9 +14,11 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
+from blueberry.tests.sl4a_sl4a.advertising.le_advertising import LeAdvertisingTest
 from blueberry.tests.sl4a_sl4a.gatt.gatt_connect_test import GattConnectTest
 from blueberry.tests.sl4a_sl4a.gatt.gatt_connect_with_irk_test import GattConnectWithIrkTest
 from blueberry.tests.sl4a_sl4a.gatt.gatt_notify_test import GattNotifyTest
+from blueberry.tests.sl4a_sl4a.security.oob_pairing_test import OobPairingTest
 
 from mobly import suite_runner
 import argparse
@@ -25,6 +27,8 @@
     GattConnectTest,
     GattConnectWithIrkTest,
     GattNotifyTest,
+    LeAdvertisingTest,
+    OobPairingTest,
 ]
 
 
diff --git a/system/blueberry/utils/mobly_sl4a_utils.py b/system/blueberry/utils/mobly_sl4a_utils.py
index 0d06d16..25d86cf 100644
--- a/system/blueberry/utils/mobly_sl4a_utils.py
+++ b/system/blueberry/utils/mobly_sl4a_utils.py
@@ -78,7 +78,10 @@
         # waiting for the future. However, mobly calls it and cause InvalidStateError when it
         # tries to do that after the thread pool has stopped, overriding it here
         # TODO: Resolve this issue in mobly
-        device.sl4a.ed.poller = FakeFuture()
+        try:
+            device.sl4a.ed.poller = FakeFuture()
+        except Exception as e:
+            print(e)
     try:
         # Guarded by is_alive internally
         device.sl4a.stop()
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 939f0be..b6eb470 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -93,6 +93,7 @@
         "le_audio/broadcaster/state_machine.cc",
         "le_audio/client.cc",
         "le_audio/codec_manager.cc",
+        "le_audio/content_control_id_keeper.cc",
         "le_audio/devices.cc",
         "le_audio/hal_verifier.cc",
         "le_audio/state_machine.cc",
@@ -656,6 +657,7 @@
         "le_audio/client.cc",
         "le_audio/client_audio.cc",
         "le_audio/client_parser.cc",
+        "le_audio/content_control_id_keeper.cc",
         "le_audio/devices.cc",
         "le_audio/le_audio_client_test.cc",
         "le_audio/le_audio_set_configuration_provider_json.cc",
@@ -795,6 +797,7 @@
         "le_audio/broadcaster/broadcaster_types.cc",
         "le_audio/broadcaster/mock_ble_advertising_manager.cc",
         "le_audio/broadcaster/mock_state_machine.cc",
+        "le_audio/content_control_id_keeper.cc",
         "le_audio/client_audio.cc",
         "le_audio/le_audio_types.cc",
         "le_audio/mock_iso_manager.cc",
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index a1199f6..9ae6450 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -138,10 +138,12 @@
   std::shared_ptr<bluetooth::csis::CsisGroup> AssignCsisGroup(
       const RawAddress& address, int group_id,
       bool create_group_if_non_existing, const bluetooth::Uuid& uuid) {
+    LOG_DEBUG("Device: %s, group_id: %d", address.ToString().c_str(), group_id);
     auto csis_group = FindCsisGroup(group_id);
     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);
@@ -290,14 +292,14 @@
     csis_group->SetTargetLockState(CsisLockState::CSIS_STATE_UNSET);
 
     int group_id = csis_group->GetGroupId();
-    CsisLockState current_lock_state = csis_group->GetCurrentLockState();
     /* Send unlock to previous devices. It shall be done in reverse order. */
     auto prev_dev = csis_group->GetPrevDevice(csis_device);
     while (prev_dev) {
       if (prev_dev->IsConnected()) {
         auto prev_csis_instance = prev_dev->GetCsisInstanceByGroupId(group_id);
         LOG_ASSERT(prev_csis_instance) << " prev_csis_instance does not exist!";
-        SetLock(prev_dev, prev_csis_instance, current_lock_state);
+        SetLock(prev_dev, prev_csis_instance,
+                CsisLockState::CSIS_STATE_UNLOCKED);
       }
       prev_dev = csis_group->GetPrevDevice(prev_dev);
     }
@@ -321,6 +323,10 @@
     }
 
     CsisLockState target_lock_state = csis_group->GetTargetLockState();
+
+    LOG_DEBUG("Device %s, target lock: %d, status: 0x%02x",
+              device->addr.ToString().c_str(), (int)target_lock_state,
+              (int)status);
     if (target_lock_state == CsisLockState::CSIS_STATE_UNSET) return;
 
     if (status != GATT_SUCCESS &&
@@ -370,14 +376,16 @@
       if (next_dev) {
         auto next_csis_inst = next_dev->GetCsisInstanceByGroupId(group_id);
         LOG_ASSERT(csis_instance) << " csis_instance does not exist!";
+#if CSIP_UPPER_TESTER_FORCE_TO_SEND_LOCK == FALSE
         if (next_csis_inst->GetLockState() ==
             CsisLockState::CSIS_STATE_LOCKED) {
           /* Somebody else managed to lock it.
            * Unlock previous devices
            */
-          HandleCsisLockProcedureError(csis_group, device);
+          HandleCsisLockProcedureError(csis_group, next_dev);
           return;
         }
+#endif
         SetLock(next_dev, next_csis_inst, CsisLockState::CSIS_STATE_LOCKED);
       }
     }
@@ -473,6 +481,7 @@
       return;
     }
 
+#if CSIP_UPPER_TESTER_FORCE_TO_SEND_LOCK == FALSE
     if (lock && !csis_group->IsAvailableForCsisLockOperation()) {
       DLOG(INFO) << __func__ << " Group " << group_id << " locked by other";
       NotifyGroupStatus(group_id, false,
@@ -480,6 +489,7 @@
                         std::move(cb));
       return;
     }
+#endif
 
     csis_group->SetTargetLockState(new_lock_state, std::move(cb));
 
@@ -489,6 +499,10 @@
        * can revert lock previously locked devices as per specification.
        */
       auto csis_device = csis_group->GetFirstDevice();
+      while (!csis_device->IsConnected()) {
+        csis_device = csis_group->GetNextDevice(csis_device);
+      }
+
       auto csis_instance = csis_device->GetCsisInstanceByGroupId(group_id);
       LOG_ASSERT(csis_instance) << " csis_instance does not exist!";
       SetLock(csis_device, csis_instance, new_lock_state);
@@ -675,7 +689,9 @@
         if (!instance) {
           stream << "          No csis instance available\n";
         } else {
-          stream << "          rank: " << instance->GetRank() << "\n";
+          stream << "          service handle: "
+                 << loghex(instance->svc_data.start_handle)
+                 << "          rank: " << +instance->GetRank() << "\n";
         }
 
         if (!device->IsConnected()) {
@@ -1340,11 +1356,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/csis/csis_types.h b/system/bta/csis/csis_types.h
index 0be02fd..466a1d4 100644
--- a/system/bta/csis/csis_types.h
+++ b/system/bta/csis/csis_types.h
@@ -27,6 +27,8 @@
 #include "bta_gatt_api.h"
 #include "bta_groups.h"
 #include "gap_api.h"
+#include "gd/common/init_flags.h"
+#include "gd/common/strings.h"
 #include "stack/crypto_toolbox/crypto_toolbox.h"
 
 namespace bluetooth {
@@ -173,7 +175,7 @@
   uint8_t GetRank(void) const { return rank_; }
   void SetRank(uint8_t rank) {
     DLOG(INFO) << __func__ << " current rank state: " << loghex(rank_)
-               << " new lock state: " << loghex(rank);
+               << " new rank state: " << loghex(rank);
     rank_ = rank;
   }
 
@@ -363,15 +365,34 @@
 
   bool IsAvailableForCsisLockOperation(void) {
     int id = group_id_;
-    auto iter = std::find_if(devices_.begin(), devices_.end(), [id](auto& d) {
-      if (!d->IsConnected()) return false;
-      auto inst = d->GetCsisInstanceByGroupId(id);
-      LOG_ASSERT(inst);
-      return inst->GetLockState() == CsisLockState::CSIS_STATE_LOCKED;
-    });
+    int number_of_connected = 0;
+    auto iter = std::find_if(
+        devices_.begin(), devices_.end(), [id, &number_of_connected](auto& d) {
+          if (!d->IsConnected()) {
+            LOG_DEBUG("Device %s is not connected in group %d",
+                      d->addr.ToString().c_str(), id);
+            return false;
+          }
+          auto inst = d->GetCsisInstanceByGroupId(id);
+          if (!inst) {
+            LOG_DEBUG("Instance not available for group %d", id);
+            return false;
+          }
+          number_of_connected++;
+          LOG_DEBUG("Device %s,  lock state: %d", d->addr.ToString().c_str(),
+                    (int)inst->GetLockState());
+          return inst->GetLockState() == CsisLockState::CSIS_STATE_LOCKED;
+        });
 
+    LOG_DEBUG("Locked set: %d, number of connected %d", iter != devices_.end(),
+              number_of_connected);
     /* If there is no locked device, we are good to go */
-    return iter == devices_.end();
+    if (iter != devices_.end()) {
+      LOG_WARN("Device %s is locked ", (*iter)->addr.ToString().c_str());
+      return false;
+    }
+
+    return (number_of_connected > 0);
   }
 
   void SortByCsisRank(void) {
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index 55f7871..477702b 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -957,7 +957,7 @@
 
     /* Remote name discovery is on going now so BTM cannot notify through
      * "bta_dm_remname_cback" */
-    /* adding callback to get notified that current reading remore name done */
+    /* adding callback to get notified that current reading remote name done */
 
     if (bluetooth::shim::is_gd_security_enabled()) {
       bluetooth::shim::BTM_SecAddRmtNameNotifyCallback(
@@ -1872,6 +1872,7 @@
 
   /* if this is what we are looking for */
   if (bta_dm_search_cb.peer_bdaddr == bd_addr) {
+    rem_name.bd_addr = bd_addr;
     rem_name.length = strlcpy((char*)rem_name.remote_bd_name, (char*)bd_name,
                               BD_NAME_LEN + 1);
     if (rem_name.length > BD_NAME_LEN) {
@@ -1893,6 +1894,9 @@
       APPL_TRACE_WARNING("%s: BTM_ReadRemoteDeviceName returns 0x%02X",
                          __func__, btm_status);
 
+      // needed so our response is not ignored, since this corresponds to the
+      // actual peer_bdaddr
+      rem_name.bd_addr = bta_dm_search_cb.peer_bdaddr;
       rem_name.length = 0;
       rem_name.remote_bd_name[0] = 0;
       rem_name.status = btm_status;
@@ -1915,11 +1919,6 @@
   APPL_TRACE_DEBUG("bta_dm_remname_cback len = %d name=<%s>",
                    p_remote_name->length, p_remote_name->remote_bd_name);
 
-  /* remote name discovery is done but it could be failed */
-  bta_dm_search_cb.name_discover_done = true;
-  strlcpy((char*)bta_dm_search_cb.peer_name,
-          (char*)p_remote_name->remote_bd_name, BD_NAME_LEN + 1);
-
   if (bta_dm_search_cb.peer_bdaddr == p_remote_name->bd_addr) {
     if (bluetooth::shim::is_gd_security_enabled()) {
       bluetooth::shim::BTM_SecDeleteRmtNameNotifyCallback(
@@ -1927,8 +1926,19 @@
     } else {
       BTM_SecDeleteRmtNameNotifyCallback(&bta_dm_service_search_remname_cback);
     }
+  } else {
+    // if we got a different response, ignore it
+    // we will have made a request directly from BTM_ReadRemoteDeviceName so we
+    // expect a dedicated response for us
+    LOG_INFO("ignoring remote name response in DM callback since it's for the wrong bd_addr");
+    return;
   }
 
+  /* remote name discovery is done but it could be failed */
+  bta_dm_search_cb.name_discover_done = true;
+  strlcpy((char*)bta_dm_search_cb.peer_name,
+          (char*)p_remote_name->remote_bd_name, BD_NAME_LEN + 1);
+
   if (bta_dm_search_cb.transport == BT_TRANSPORT_LE) {
     GAP_BleReadPeerPrefConnParams(bta_dm_search_cb.peer_bdaddr);
   }
@@ -3627,6 +3637,12 @@
       }
       break;
 
+    case BTM_LE_ADDR_ASSOC_EVT:
+      sec_event.proc_id_addr.pairing_bda = bda;
+      sec_event.proc_id_addr.id_addr = p_data->id_addr;
+      bta_dm_cb.p_sec_cback(BTA_DM_LE_ADDR_ASSOC_EVT, &sec_event);
+      break;
+
     default:
       status = BTM_NOT_AUTHORIZED;
       break;
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/include/bta_api.h b/system/bta/include/bta_api.h
index 59d6d98..98182f4 100644
--- a/system/bta/include/bta_api.h
+++ b/system/bta/include/bta_api.h
@@ -222,7 +222,8 @@
   BTA_DM_BLE_SC_OOB_REQ_EVT = 29,  /* SMP SC OOB request event */
   BTA_DM_BLE_CONSENT_REQ_EVT = 30, /* SMP consent request event */
   BTA_DM_BLE_SC_CR_LOC_OOB_EVT = 31, /* SMP SC Create Local OOB request event */
-  BTA_DM_REPORT_BONDING_EVT = 32     /*handle for pin or key missing*/
+  BTA_DM_REPORT_BONDING_EVT = 32,    /*handle for pin or key missing*/
+  BTA_DM_LE_ADDR_ASSOC_EVT = 33,     /* identity address association event */
 } tBTA_DM_SEC_EVT;
 
 /* Structure associated with BTA_DM_PIN_REQ_EVT */
@@ -382,6 +383,11 @@
   Octet16 local_oob_r; /* Local OOB Data Randomizer */
 } tBTA_DM_LOC_OOB_DATA;
 
+typedef struct {
+  RawAddress pairing_bda;
+  RawAddress id_addr;
+} tBTA_DM_PROC_ID_ADDR;
+
 /* Union of all security callback structures */
 typedef union {
   tBTA_DM_PIN_REQ pin_req;        /* PIN request. */
@@ -399,6 +405,7 @@
   Octet16 ble_er;                     /* ER event data */
   tBTA_DM_LOC_OOB_DATA local_oob_data; /* Local OOB data generated by us */
   tBTA_DM_RC_UNPAIR delete_key_RC_to_unpair;
+  tBTA_DM_PROC_ID_ADDR proc_id_addr; /* Identity address event */
 } tBTA_DM_SEC;
 
 /* Security callback */
diff --git a/system/bta/include/bta_le_audio_broadcaster_api.h b/system/bta/include/bta_le_audio_broadcaster_api.h
index b918fbe..a8f1daa 100644
--- a/system/bta/include/bta_le_audio_broadcaster_api.h
+++ b/system/bta/include/bta_le_audio_broadcaster_api.h
@@ -28,13 +28,7 @@
 /* Interface class */
 class LeAudioBroadcaster {
  public:
-  enum class AudioProfile {
-    SONIFICATION = 0,
-    MEDIA = 1,
-  };
-
   static constexpr uint8_t kInstanceIdUndefined = 0xFF;
-  static constexpr AudioProfile kDefaultAudioProfile = AudioProfile::MEDIA;
 
   virtual ~LeAudioBroadcaster(void) = default;
 
@@ -50,7 +44,7 @@
   static void DebugDump(int fd);
 
   virtual void CreateAudioBroadcast(
-      std::vector<uint8_t> metadata, AudioProfile profile,
+      std::vector<uint8_t> metadata,
       std::optional<bluetooth::le_audio::BroadcastCode> broadcast_code =
           std::nullopt) = 0;
   virtual void SuspendAudioBroadcast(uint32_t broadcast_id) = 0;
@@ -67,8 +61,6 @@
                           RawAddress /* addr */, bool /* is_valid */)>
           cb) = 0;
 
-  virtual void SetNumRetransmit(uint8_t count) = 0;
-  virtual uint8_t GetNumRetransmit(void) const = 0;
   virtual void SetStreamingPhy(uint8_t phy) = 0;
   virtual uint8_t GetStreamingPhy(void) const = 0;
 };
diff --git a/system/bta/le_audio/audio_set_configurations.json b/system/bta/le_audio/audio_set_configurations.json
index be49dc1..c3b9200 100644
--- a/system/bta/le_audio/audio_set_configurations.json
+++ b/system/bta/le_audio/audio_set_configurations.json
@@ -363,6 +363,26 @@
             "qos_config_name": ["QoS_Config_16_1_1"]
         },
         {
+            "name": "DualDev_OneChanMonoSrc_16_2_Server_Preferred",
+            "codec_config_name": "DualDev_OneChanMonoSrc_16_2",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_OneChanStereoSrc_16_2_Server_Preferred",
+            "codec_config_name": "SingleDev_OneChanStereoSrc_16_2",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSrc_24_2_Server_Preferred",
+            "codec_config_name": "SingleDev_OneChanMonoSrc_24_2",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSrc_16_2_Server_Preferred",
+            "codec_config_name": "SingleDev_OneChanMonoSrc_16_2",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
             "name": "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_2",
             "codec_config_name": "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1",
             "qos_config_name": ["QoS_Config_16_1_2"]
@@ -498,6 +518,36 @@
             "qos_config_name": ["QoS_Config_48_4_2"]
         },
         {
+            "name": "DualDev_OneChanStereoSnk_48_3_Server_Preferred",
+            "codec_config_name": "DualDev_OneChanStereoSnk_48_3",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "DualDev_OneChanStereoSnk_48_3_2",
+            "codec_config_name": "DualDev_OneChanStereoSnk_48_3",
+            "qos_config_name": ["QoS_Config_48_3_2"]
+        },
+        {
+            "name": "DualDev_OneChanStereoSnk_48_2_Server_Preferred",
+            "codec_config_name": "DualDev_OneChanStereoSnk_48_2",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "DualDev_OneChanStereoSnk_48_2_2",
+            "codec_config_name": "DualDev_OneChanStereoSnk_48_2",
+            "qos_config_name": ["QoS_Config_48_2_2"]
+        },
+        {
+            "name": "DualDev_OneChanStereoSnk_48_1_Server_Preferred",
+            "codec_config_name": "DualDev_OneChanStereoSnk_48_1",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "DualDev_OneChanStereoSnk_48_1_2",
+            "codec_config_name": "DualDev_OneChanStereoSnk_48_1",
+            "qos_config_name": ["QoS_Config_48_1_2"]
+        },
+        {
             "name": "SingleDev_OneChanStereoSnk_48_4_Server_Preferred",
             "codec_config_name": "SingleDev_OneChanStereoSnk_48_4",
             "qos_config_name": ["QoS_Config_Server_Preferred"]
@@ -513,6 +563,36 @@
             "qos_config_name": ["QoS_Config_48_4_2"]
         },
         {
+            "name": "SingleDev_OneChanStereoSnk_48_3_Server_Preferred",
+            "codec_config_name": "SingleDev_OneChanStereoSnk_48_3",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_OneChanStereoSnk_48_3_2",
+            "codec_config_name": "SingleDev_OneChanStereoSnk_48_3",
+            "qos_config_name": ["QoS_Config_48_3_2"]
+        },
+        {
+            "name": "SingleDev_OneChanStereoSnk_48_2_Server_Preferred",
+            "codec_config_name": "SingleDev_OneChanStereoSnk_48_2",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_OneChanStereoSnk_48_2_2",
+            "codec_config_name": "SingleDev_OneChanStereoSnk_48_2",
+            "qos_config_name": ["QoS_Config_48_2_2"]
+        },
+        {
+            "name": "SingleDev_OneChanStereoSnk_48_1_Server_Preferred",
+            "codec_config_name": "SingleDev_OneChanStereoSnk_48_1",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_OneChanStereoSnk_48_1_2",
+            "codec_config_name": "SingleDev_OneChanStereoSnk_48_1",
+            "qos_config_name": ["QoS_Config_48_1_2"]
+        },
+        {
             "name": "SingleDev_TwoChanStereoSnk_48_4_Server_Preferred",
             "codec_config_name": "SingleDev_TwoChanStereoSnk_48_4",
             "qos_config_name": ["QoS_Config_Server_Preferred"]
@@ -528,6 +608,36 @@
             "qos_config_name": ["QoS_Config_48_4_2"]
         },
         {
+            "name": "SingleDev_TwoChanStereoSnk_48_3_Server_Preferred",
+            "codec_config_name": "SingleDev_TwoChanStereoSnk_48_3",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_TwoChanStereoSnk_48_3_2",
+            "codec_config_name": "SingleDev_TwoChanStereoSnk_48_3",
+            "qos_config_name": ["QoS_Config_48_3_2"]
+        },
+        {
+            "name": "SingleDev_TwoChanStereoSnk_48_2_Server_Preferred",
+            "codec_config_name": "SingleDev_TwoChanStereoSnk_48_2",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_TwoChanStereoSnk_48_2_2",
+            "codec_config_name": "SingleDev_TwoChanStereoSnk_48_2",
+            "qos_config_name": ["QoS_Config_48_2_2"]
+        },
+        {
+            "name": "SingleDev_TwoChanStereoSnk_48_1_Server_Preferred",
+            "codec_config_name": "SingleDev_TwoChanStereoSnk_48_1",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_TwoChanStereoSnk_48_1_2",
+            "codec_config_name": "SingleDev_TwoChanStereoSnk_48_1",
+            "qos_config_name": ["QoS_Config_48_1_2"]
+        },
+        {
             "name": "SingleDev_OneChanMonoSnk_48_4_Server_Preferred",
             "codec_config_name": "SingleDev_OneChanMonoSnk_48_4",
             "qos_config_name": ["QoS_Config_Server_Preferred"]
@@ -543,6 +653,36 @@
             "qos_config_name": ["QoS_Config_48_4_2"]
         },
         {
+            "name": "SingleDev_OneChanMonoSnk_48_3_Server_Preferred",
+            "codec_config_name": "SingleDev_OneChanMonoSnk_48_3",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSnk_48_3_2",
+            "codec_config_name": "SingleDev_OneChanMonoSnk_48_3",
+            "qos_config_name": ["QoS_Config_48_3_2"]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSnk_48_2_Server_Preferred",
+            "codec_config_name": "SingleDev_OneChanMonoSnk_48_2",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSnk_48_2_2",
+            "codec_config_name": "SingleDev_OneChanMonoSnk_48_2",
+            "qos_config_name": ["QoS_Config_48_2_2"]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSnk_48_1_Server_Preferred",
+            "codec_config_name": "SingleDev_OneChanMonoSnk_48_1",
+            "qos_config_name": ["QoS_Config_Server_Preferred"]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSnk_48_1_2",
+            "codec_config_name": "SingleDev_OneChanMonoSnk_48_1",
+            "qos_config_name": ["QoS_Config_48_1_2"]
+        },
+        {
             "name": "VND_SingleDev_TwoChanStereoSnk_OneChanStereoSrc_32khz_60octs_Server_Preferred_1",
             "codec_config_name": "VND_SingleDev_TwoChanStereoSnk_OneChanStereoSrc_32khz_60octs_1",
             "qos_config_name": ["QoS_Config_Server_Preferred"]
@@ -1227,6 +1367,139 @@
             ]
         },
         {
+            "name": "SingleDev_OneChanStereoSrc_16_2",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 2,
+                    "direction": "SOURCE",
+                    "configuration_strategy": "STEREO_TWO_CISES_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    3
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    40,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSrc_24_2",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 1,
+                    "direction": "SOURCE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    5
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    60,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
             "name": "SingleDev_OneChanMonoSnk_16_2",
             "subconfigurations": [
                 {
@@ -3278,6 +3551,138 @@
             ]
         },
         {
+            "name": "DualDev_OneChanMonoSrc_16_2",
+            "subconfigurations": [
+                {
+                    "device_cnt": 2,
+                    "ase_cnt": 2,
+                    "direction": "SOURCE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    3
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    40,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSrc_16_2",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 1,
+                    "direction": "SOURCE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    3
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    40,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
             "name": "DualDev_OneChanStereoSnk_48_4",
             "subconfigurations": [
                 {
@@ -3345,6 +3750,207 @@
             ]
         },
         {
+            "name": "DualDev_OneChanStereoSnk_48_3",
+            "subconfigurations": [
+                {
+                    "device_cnt": 2,
+                    "ase_cnt": 2,
+                    "direction": "SINK",
+                    "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    90,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "DualDev_OneChanStereoSnk_48_2",
+            "subconfigurations": [
+                {
+                    "device_cnt": 2,
+                    "ase_cnt": 2,
+                    "direction": "SINK",
+                    "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    100,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "DualDev_OneChanStereoSnk_48_1",
+            "subconfigurations": [
+                {
+                    "device_cnt": 2,
+                    "ase_cnt": 2,
+                    "direction": "SINK",
+                    "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    75,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
             "name": "SingleDev_OneChanStereoSnk_48_4",
             "subconfigurations": [
                 {
@@ -3412,6 +4018,207 @@
             ]
         },
         {
+            "name": "SingleDev_OneChanStereoSnk_48_3",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 2,
+                    "direction": "SINK",
+                    "configuration_strategy": "STEREO_TWO_CISES_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    90,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "SingleDev_OneChanStereoSnk_48_2",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 2,
+                    "direction": "SINK",
+                    "configuration_strategy": "STEREO_TWO_CISES_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    100,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "SingleDev_OneChanStereoSnk_48_1",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 2,
+                    "direction": "SINK",
+                    "configuration_strategy": "STEREO_TWO_CISES_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    75,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
             "name": "SingleDev_TwoChanStereoSnk_48_4",
             "subconfigurations": [
                 {
@@ -3479,6 +4286,207 @@
             ]
         },
         {
+            "name": "SingleDev_TwoChanStereoSnk_48_3",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 1,
+                    "direction": "SINK",
+                    "configuration_strategy": "STEREO_ONE_CIS_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    3,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    90,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "SingleDev_TwoChanStereoSnk_48_2",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 1,
+                    "direction": "SINK",
+                    "configuration_strategy": "STEREO_ONE_CIS_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    3,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    100,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "SingleDev_TwoChanStereoSnk_48_1",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 1,
+                    "direction": "SINK",
+                    "configuration_strategy": "STEREO_ONE_CIS_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    3,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    75,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
             "name": "SingleDev_OneChanMonoSnk_48_4",
             "subconfigurations": [
                 {
@@ -3546,6 +4554,207 @@
             ]
         },
         {
+            "name": "SingleDev_OneChanMonoSnk_48_3",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 1,
+                    "direction": "SINK",
+                    "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    90,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSnk_48_2",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 1,
+                    "direction": "SINK",
+                    "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    100,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
+            "name": "SingleDev_OneChanMonoSnk_48_1",
+            "subconfigurations": [
+                {
+                    "device_cnt": 1,
+                    "ase_cnt": 1,
+                    "direction": "SINK",
+                    "configuration_strategy": "MONO_ONE_CIS_PER_DEVICE",
+                    "codec_id": {
+                        "coding_format": 6,
+                        "vendor_company_id": 0,
+                        "vendor_codec_id": 0
+                    },
+                    "codec_configuration": [
+                        {
+                            "name": "sampling_frequency",
+                            "type": 1,
+                            "compound_value": {
+                                "value": [
+                                    8
+                                ]
+                            }
+                        },
+                        {
+                            "name": "frame_duration",
+                            "type": 2,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        },
+                        {
+                            "name": "audio_channel_allocation",
+                            "type": 3,
+                            "compound_value": {
+                                "value": [
+                                    1,
+                                    0,
+                                    0,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "octets_per_codec_frame",
+                            "type": 4,
+                            "compound_value": {
+                                "value": [
+                                    75,
+                                    0
+                                ]
+                            }
+                        },
+                        {
+                            "name": "codec_frame_blocks_per_sdu",
+                            "type": 5,
+                            "compound_value": {
+                                "value": [
+                                    1
+                                ]
+                            }
+                        }
+                    ]
+                }
+            ]
+        },
+        {
             "name": "VND_SingleDev_TwoChanStereoSnk_48khz_100octs_1",
             "subconfigurations": [
                 {
@@ -7885,6 +9094,21 @@
             "max_transport_latency": 10
         },
         {
+            "name": "QoS_Config_48_1_2",
+            "retransmission_number": 13,
+            "max_transport_latency": 75
+        },
+        {
+            "name": "QoS_Config_48_2_2",
+            "retransmission_number": 13,
+            "max_transport_latency": 95
+        },
+        {
+            "name": "QoS_Config_48_3_2",
+            "retransmission_number": 13,
+            "max_transport_latency": 75
+        },
+        {
             "name": "QoS_Config_48_4_1",
             "retransmission_number": 5,
             "max_transport_latency": 20
diff --git a/system/bta/le_audio/audio_set_scenarios.json b/system/bta/le_audio/audio_set_scenarios.json
index c92fbd6..4013e7e 100644
--- a/system/bta/le_audio/audio_set_scenarios.json
+++ b/system/bta/le_audio/audio_set_scenarios.json
@@ -8,6 +8,20 @@
         {
             "name": "Ringtone",
             "configurations": [
+                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Server_Preferred",
+                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
+                "DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Server_Preferred",
+                "DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
+                "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_Server_Preferred",
+                "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_1",
+                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Server_Preferred",
+                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
+                "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Server_Preferred",
+                "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
+                "SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Server_Preferred",
+                "SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
+                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Server_Preferred",
+                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
                 "DualDev_OneChanStereoSnk_16_2_Server_Preferred",
                 "DualDev_OneChanStereoSnk_16_2_1",
                 "DualDev_OneChanStereoSnk_16_1_Server_Preferred",
@@ -75,6 +89,10 @@
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Server_Preferred",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
+                "DualDev_OneChanMonoSrc_16_2_Server_Preferred",
+                "SingleDev_OneChanStereoSrc_16_2_Server_Preferred",
+                "SingleDev_OneChanMonoSrc_24_2_Server_Preferred",
+                "SingleDev_OneChanMonoSrc_16_2_Server_Preferred",
                 "VND_SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32khz_Server_Prefered_1",
                 "VND_SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32khz_60oct_R3_L22_1"
             ]
@@ -84,6 +102,12 @@
             "configurations": [
                 "DualDev_OneChanStereoSnk_48_4_Server_Preferred",
                 "DualDev_OneChanStereoSnk_48_4_2",
+                "DualDev_OneChanStereoSnk_48_3_Server_Preferred",
+                "DualDev_OneChanStereoSnk_48_3_2",
+                "DualDev_OneChanStereoSnk_48_2_Server_Preferred",
+                "DualDev_OneChanStereoSnk_48_2_2",
+                "DualDev_OneChanStereoSnk_48_1_Server_Preferred",
+                "DualDev_OneChanStereoSnk_48_1_2",
                 "DualDev_OneChanStereoSnk_24_2_Server_Preferred",
                 "DualDev_OneChanStereoSnk_24_2_2",
                 "DualDev_OneChanStereoSnk_16_2_Server_Preferred",
@@ -92,6 +116,12 @@
                 "DualDev_OneChanStereoSnk_16_1_2",
                 "SingleDev_OneChanStereoSnk_48_4_Server_Preferred",
                 "SingleDev_OneChanStereoSnk_48_4_2",
+                "SingleDev_OneChanStereoSnk_48_3_Server_Preferred",
+                "SingleDev_OneChanStereoSnk_48_3_2",
+                "SingleDev_OneChanStereoSnk_48_2_Server_Preferred",
+                "SingleDev_OneChanStereoSnk_48_2_2",
+                "SingleDev_OneChanStereoSnk_48_1_Server_Preferred",
+                "SingleDev_OneChanStereoSnk_48_1_2",
                 "SingleDev_OneChanStereoSnk_24_2_Server_Preferred",
                 "SingleDev_OneChanStereoSnk_24_2_2",
                 "SingleDev_OneChanStereoSnk_16_2_Server_Preferred",
@@ -100,6 +130,14 @@
                 "SingleDev_OneChanStereoSnk_16_1_2",
                 "SingleDev_TwoChanStereoSnk_48_4_Server_Preferred",
                 "SingleDev_TwoChanStereoSnk_48_4_2",
+                "SingleDev_TwoChanStereoSnk_48_4_Server_Preferred",
+                "SingleDev_TwoChanStereoSnk_48_4_2",
+                "SingleDev_TwoChanStereoSnk_48_3_Server_Preferred",
+                "SingleDev_TwoChanStereoSnk_48_3_2",
+                "SingleDev_TwoChanStereoSnk_48_2_Server_Preferred",
+                "SingleDev_TwoChanStereoSnk_48_2_2",
+                "SingleDev_TwoChanStereoSnk_48_1_Server_Preferred",
+                "SingleDev_TwoChanStereoSnk_48_1_2",
                 "SingleDev_TwoChanStereoSnk_24_2_Server_Preferred",
                 "SingleDev_TwoChanStereoSnk_24_2_2",
                 "SingleDev_TwoChanStereoSnk_16_2_Server_Preferred",
@@ -108,6 +146,12 @@
                 "SingleDev_TwoChanStereoSnk_16_1_2",
                 "SingleDev_OneChanMonoSnk_48_4_Server_Preferred",
                 "SingleDev_OneChanMonoSnk_48_4_2",
+                "SingleDev_OneChanMonoSnk_48_3_Server_Preferred",
+                "SingleDev_OneChanMonoSnk_48_3_2",
+                "SingleDev_OneChanMonoSnk_48_2_Server_Preferred",
+                "SingleDev_OneChanMonoSnk_48_2_2",
+                "SingleDev_OneChanMonoSnk_48_1_Server_Preferred",
+                "SingleDev_OneChanMonoSnk_48_1_2",
                 "SingleDev_OneChanMonoSnk_24_2_Server_Preferred",
                 "SingleDev_OneChanMonoSnk_24_2_2",
                 "SingleDev_OneChanMonoSnk_16_2_Server_Preferred",
@@ -128,7 +172,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/broadcaster/broadcaster.cc b/system/bta/le_audio/broadcaster/broadcaster.cc
index e639c14..f152957 100644
--- a/system/bta/le_audio/broadcaster/broadcaster.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster.cc
@@ -20,6 +20,7 @@
 #include "bta/include/bta_le_audio_api.h"
 #include "bta/include/bta_le_audio_broadcaster_api.h"
 #include "bta/le_audio/broadcaster/state_machine.h"
+#include "bta/le_audio/content_control_id_keeper.h"
 #include "bta/le_audio/le_audio_types.h"
 #include "device/include/controller.h"
 #include "embdrv/lc3/include/lc3.h"
@@ -36,12 +37,15 @@
 using bluetooth::hci::iso_manager::BigCallbacks;
 using bluetooth::le_audio::BasicAudioAnnouncementData;
 using bluetooth::le_audio::BroadcastId;
+using le_audio::ContentControlIdKeeper;
 using le_audio::broadcaster::BigConfig;
 using le_audio::broadcaster::BroadcastCodecWrapper;
+using le_audio::broadcaster::BroadcastQosConfig;
 using le_audio::broadcaster::BroadcastStateMachine;
 using le_audio::broadcaster::BroadcastStateMachineConfig;
 using le_audio::broadcaster::IBroadcastStateMachineCallbacks;
 using le_audio::types::kLeAudioCodingFormatLC3;
+using le_audio::types::LeAudioContextType;
 using le_audio::types::LeAudioLtvMap;
 
 namespace {
@@ -69,7 +73,6 @@
       bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks_)
       : callbacks_(callbacks_),
         current_phy_(PHY_LE_2M),
-        num_retransmit_(3),
         audio_data_path_state_(AudioDataPathState::INACTIVE),
         audio_instance_(nullptr) {
     LOG_INFO();
@@ -188,17 +191,8 @@
   }
 
   void CreateAudioBroadcast(std::vector<uint8_t> metadata,
-                            LeAudioBroadcaster::AudioProfile profile,
                             std::optional<bluetooth::le_audio::BroadcastCode>
                                 broadcast_code) override {
-    LOG_INFO("Audio profile: %s",
-             profile == LeAudioBroadcaster::AudioProfile::MEDIA
-                 ? "Media"
-                 : "Sonification");
-
-    auto& codec_wrapper =
-        BroadcastCodecWrapper::getCodecConfigForProfile(profile);
-
     auto broadcast_id = available_broadcast_ids_.back();
     available_broadcast_ids_.pop_back();
     if (available_broadcast_ids_.size() == 0) GenerateBroadcastIds();
@@ -212,13 +206,38 @@
       return;
     }
 
+    uint16_t context_type =
+        static_cast<std::underlying_type<LeAudioContextType>::type>(
+            LeAudioContextType::MEDIA);
+    auto stream_context_vec =
+        ltv.Find(le_audio::types::kLeAudioMetadataTypeStreamingAudioContext);
+    if (stream_context_vec) {
+      auto pp = stream_context_vec.value().data();
+      STREAM_TO_UINT16(context_type, pp);
+    }
+
+    // Append the CCID list
+    // TODO: We currently support only one context (and CCID) at a time for both
+    //       Unicast and broadcast.
+    auto ccid = ContentControlIdKeeper::GetInstance()->GetCcid(context_type);
+    if (ccid != -1) {
+      ltv.Add(le_audio::types::kLeAudioMetadataTypeCcidList,
+              {static_cast<uint8_t>(ccid)});
+    }
+
+    auto codec_qos_pair =
+        le_audio::broadcaster::getStreamConfigForContext(context_type);
     BroadcastStateMachineConfig msg = {
         .broadcast_id = broadcast_id,
         .streaming_phy = GetStreamingPhy(),
-        .codec_wrapper = codec_wrapper,
-        .announcement = prepareAnnouncement(codec_wrapper, std::move(ltv)),
+        .codec_wrapper = codec_qos_pair.first,
+        .qos_config = codec_qos_pair.second,
+        .announcement =
+            prepareAnnouncement(codec_qos_pair.first, std::move(ltv)),
         .broadcast_code = std::move(broadcast_code)};
 
+    LOG_INFO("CreateAudioBroadcast");
+
     /* Create the broadcaster instance - we'll receive it's init state in the
      * async callback
      */
@@ -372,10 +391,6 @@
         broadcast_id, addr_type, addr, std::move(cb)));
   }
 
-  void SetNumRetransmit(uint8_t count) override { num_retransmit_ = count; }
-
-  uint8_t GetNumRetransmit(void) const override { return num_retransmit_; }
-
   void SetStreamingPhy(uint8_t phy) override { current_phy_ = phy; }
 
   uint8_t GetStreamingPhy(void) const override { return current_phy_; }
@@ -443,25 +458,6 @@
   }
 
  private:
-  uint8_t GetNumRetransmit(uint32_t broadcast_id) {
-    /* TODO: Should be based on QOS settings */
-    return GetNumRetransmit();
-  }
-
-  uint32_t GetSduItv(uint32_t broadcast_id) {
-    /* TODO: Should be based on QOS settings
-     * currently tuned for media profile (music band)
-     */
-    return 0x002710;
-  }
-
-  uint16_t GetMaxTransportLatency(uint32_t broadcast_id) {
-    /* TODO: Should be based on QOS settings
-     * currently tuned for media profile (music band)
-     */
-    return 0x3C;
-  }
-
   static class BroadcastStateMachineCallbacks
       : public IBroadcastStateMachineCallbacks {
     void OnStateMachineCreateStatus(uint32_t broadcast_id,
@@ -561,26 +557,17 @@
                               RawAddress addr) override {
       /* Not used currently */
     }
-
-    uint8_t GetNumRetransmit(uint32_t broadcast_id) override {
-      return instance->GetNumRetransmit(broadcast_id);
-    }
-
-    uint32_t GetSduItv(uint32_t broadcast_id) override {
-      return instance->GetSduItv(broadcast_id);
-    }
-
-    uint16_t GetMaxTransportLatency(uint32_t broadcast_id) override {
-      return instance->GetMaxTransportLatency(broadcast_id);
-    }
   } state_machine_callbacks_;
 
   static class LeAudioClientAudioSinkReceiverImpl
       : public LeAudioClientAudioSinkReceiver {
    public:
     LeAudioClientAudioSinkReceiverImpl()
-        : codec_wrapper_(BroadcastCodecWrapper::getCodecConfigForProfile(
-              LeAudioBroadcaster::AudioProfile::SONIFICATION)) {}
+        : codec_wrapper_(
+              le_audio::broadcaster::getStreamConfigForContext(
+                  static_cast<std::underlying_type<LeAudioContextType>::type>(
+                      le_audio::types::LeAudioContextType::UNSPECIFIED))
+                  .first) {}
 
     void CheckAndReconfigureEncoders() {
       auto const& codec_id = codec_wrapper_.GetLeAudioCodecId();
@@ -737,7 +724,6 @@
 
   /* Some BIG params are set globally */
   uint8_t current_phy_;
-  uint8_t num_retransmit_;
   AudioDataPathState audio_data_path_state_;
   const void* audio_instance_;
   std::vector<BroadcastId> available_broadcast_ids_;
diff --git a/system/bta/le_audio/broadcaster/broadcaster_test.cc b/system/bta/le_audio/broadcaster/broadcaster_test.cc
index 8c0174f..8aa9147 100644
--- a/system/bta/le_audio/broadcaster/broadcaster_test.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster_test.cc
@@ -23,11 +23,15 @@
 #include "bta/include/bta_le_audio_api.h"
 #include "bta/include/bta_le_audio_broadcaster_api.h"
 #include "bta/le_audio/broadcaster/mock_state_machine.h"
+#include "bta/le_audio/content_control_id_keeper.h"
+#include "bta/le_audio/le_audio_types.h"
 #include "bta/le_audio/mock_iso_manager.h"
 #include "bta/test/common/mock_controller.h"
 #include "device/include/controller.h"
 #include "stack/include/btm_iso_api.h"
 
+using le_audio::types::LeAudioContextType;
+
 using testing::_;
 using testing::AtLeast;
 using testing::DoAll;
@@ -104,12 +108,25 @@
 namespace le_audio {
 namespace broadcaster {
 namespace {
-static constexpr LeAudioBroadcaster::AudioProfile default_profile =
-    LeAudioBroadcaster::AudioProfile::SONIFICATION;
+static constexpr auto default_context =
+    static_cast<std::underlying_type<LeAudioContextType>::type>(
+        LeAudioContextType::ALERTS);
 static constexpr BroadcastCode default_code = {
     0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
     0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};
-static const std::vector<uint8_t> default_metadata = {0x03, 0x02, 0x01, 0x00};
+static const std::vector<uint8_t> default_metadata = {
+    le_audio::types::kLeAudioMetadataStreamingAudioContextLen + 1,
+    le_audio::types::kLeAudioMetadataTypeStreamingAudioContext,
+    default_context & 0x00FF, (default_context & 0xFF00) >> 8};
+
+static constexpr uint8_t media_ccid = 0xC0;
+static constexpr auto media_context =
+    static_cast<std::underlying_type<LeAudioContextType>::type>(
+        LeAudioContextType::MEDIA);
+static const std::vector<uint8_t> media_metadata = {
+    le_audio::types::kLeAudioMetadataStreamingAudioContextLen + 1,
+    le_audio::types::kLeAudioMetadataTypeStreamingAudioContext,
+    media_context & 0x00FF, (media_context & 0xFF00) >> 8};
 
 class MockLeAudioBroadcasterCallbacks
     : public bluetooth::le_audio::LeAudioBroadcasterCallbacks {
@@ -185,6 +202,8 @@
     LeAudioBroadcaster::Initialize(&mock_broadcaster_callbacks_,
                                    base::Bind([]() -> bool { return true; }));
 
+    ContentControlIdKeeper::GetInstance()->Start();
+
     /* Simulate random generator */
     uint8_t random[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
     generator_cb.Run(random);
@@ -208,13 +227,12 @@
   }
 
   uint32_t InstantiateBroadcast(
-      LeAudioBroadcaster::AudioProfile profile = default_profile,
       std::vector<uint8_t> metadata = default_metadata,
       BroadcastCode code = default_code) {
     uint32_t broadcast_id = LeAudioBroadcaster::kInstanceIdUndefined;
     EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastCreated(_, true))
         .WillOnce(SaveArg<0>(&broadcast_id));
-    LeAudioBroadcaster::Get()->CreateAudioBroadcast(metadata, profile, code);
+    LeAudioBroadcaster::Get()->CreateAudioBroadcast(metadata, code);
 
     return broadcast_id;
   }
@@ -232,13 +250,6 @@
   ASSERT_TRUE(LeAudioBroadcaster::IsLeAudioBroadcasterRunning());
 }
 
-TEST_F(BroadcasterTest, GetNumRetransmit) {
-  LeAudioBroadcaster::Get()->SetNumRetransmit(8);
-  ASSERT_EQ(LeAudioBroadcaster::Get()->GetNumRetransmit(), 8);
-  LeAudioBroadcaster::Get()->SetNumRetransmit(12);
-  ASSERT_EQ(LeAudioBroadcaster::Get()->GetNumRetransmit(), 12);
-}
-
 TEST_F(BroadcasterTest, GetStreamingPhy) {
   LeAudioBroadcaster::Get()->SetStreamingPhy(1);
   ASSERT_EQ(LeAudioBroadcaster::Get()->GetStreamingPhy(), 1);
@@ -306,8 +317,7 @@
 }
 
 TEST_F(BroadcasterTest, StartAudioBroadcastMedia) {
-  auto broadcast_id =
-      InstantiateBroadcast(LeAudioBroadcaster::AudioProfile::MEDIA);
+  auto broadcast_id = InstantiateBroadcast(media_metadata);
   LeAudioBroadcaster::Get()->StopAudioBroadcast(broadcast_id);
 
   EXPECT_CALL(mock_broadcaster_callbacks_,
@@ -478,15 +488,6 @@
   ASSERT_EQ(sm->GetAdvertisingSid(), metadata.adv_sid);
 }
 
-TEST_F(BroadcasterTest, SetNumRetransmit) {
-  auto broadcast_id = InstantiateBroadcast();
-  LeAudioBroadcaster::Get()->SetNumRetransmit(9);
-  ASSERT_EQ(MockBroadcastStateMachine::GetLastInstance()->cb->GetNumRetransmit(
-                broadcast_id),
-            9);
-  ASSERT_EQ(LeAudioBroadcaster::Get()->GetNumRetransmit(), 9);
-}
-
 TEST_F(BroadcasterTest, SetStreamingPhy) {
   LeAudioBroadcaster::Get()->SetStreamingPhy(2);
   // From now on new streams should be using Phy = 2.
@@ -500,10 +501,9 @@
   ASSERT_EQ(LeAudioBroadcaster::Get()->GetStreamingPhy(), 1);
 }
 
-TEST_F(BroadcasterTest, StreamParamsSonification) {
+TEST_F(BroadcasterTest, StreamParamsAlerts) {
   uint8_t expected_channels = 1u;
-
-  InstantiateBroadcast(LeAudioBroadcaster::AudioProfile::SONIFICATION);
+  InstantiateBroadcast();
   auto config = MockBroadcastStateMachine::GetLastInstance()->cfg;
 
   // Check audio configuration
@@ -516,15 +516,24 @@
 
 TEST_F(BroadcasterTest, StreamParamsMedia) {
   uint8_t expected_channels = 2u;
-
-  InstantiateBroadcast(LeAudioBroadcaster::AudioProfile::MEDIA);
+  ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid);
+  InstantiateBroadcast(media_metadata);
   auto config = MockBroadcastStateMachine::GetLastInstance()->cfg;
 
   // Check audio configuration
   ASSERT_EQ(config.codec_wrapper.GetNumChannels(), expected_channels);
+
+  auto& subgroup = config.announcement.subgroup_configs[0];
+
   // Matches number of bises in the announcement
-  ASSERT_EQ(config.announcement.subgroup_configs[0].bis_configs.size(),
-            expected_channels);
+  ASSERT_EQ(subgroup.bis_configs.size(), expected_channels);
+  // Verify CCID for Media
+  auto ccid_list_opt = types::LeAudioLtvMap(subgroup.metadata)
+                           .Find(le_audio::types::kLeAudioMetadataTypeCcidList);
+  ASSERT_TRUE(ccid_list_opt.has_value());
+  auto ccid_list = ccid_list_opt.value();
+  ASSERT_EQ(1u, ccid_list.size());
+  ASSERT_EQ(media_ccid, ccid_list[0]);
   // Note: Num of bises at IsoManager level is verified by state machine tests
 }
 
diff --git a/system/bta/le_audio/broadcaster/broadcaster_types.cc b/system/bta/le_audio/broadcaster/broadcaster_types.cc
index 996c167..66048a0 100644
--- a/system/bta/le_audio/broadcaster/broadcaster_types.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster_types.cc
@@ -23,11 +23,13 @@
 #include "bta_le_audio_broadcaster_api.h"
 #include "btm_ble_api_types.h"
 #include "embdrv/lc3/include/lc3.h"
+#include "osi/include/properties.h"
 
 using bluetooth::le_audio::BasicAudioAnnouncementBisConfig;
 using bluetooth::le_audio::BasicAudioAnnouncementCodecConfig;
 using bluetooth::le_audio::BasicAudioAnnouncementData;
 using bluetooth::le_audio::BasicAudioAnnouncementSubgroup;
+using le_audio::types::LeAudioContextType;
 
 namespace le_audio {
 namespace broadcaster {
@@ -197,6 +199,18 @@
     // Frame len.
     40);
 
+static const BroadcastCodecWrapper lc3_stereo_16_2 = BroadcastCodecWrapper(
+    kLeAudioCodecIdLc3,
+    // LeAudioCodecConfiguration
+    {.num_channels = LeAudioCodecConfiguration::kChannelNumberStereo,
+     .sample_rate = LeAudioCodecConfiguration::kSampleRate16000,
+     .bits_per_sample = LeAudioCodecConfiguration::kBitsPerSample16,
+     .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us},
+    // Bitrate
+    32000,
+    // Frame len.
+    40);
+
 static const BroadcastCodecWrapper lc3_stereo_24_2 = BroadcastCodecWrapper(
     kLeAudioCodecIdLc3,
     // LeAudioCodecConfiguration
@@ -209,16 +223,6 @@
     // Frame len.
     60);
 
-const BroadcastCodecWrapper& BroadcastCodecWrapper::getCodecConfigForProfile(
-    LeAudioBroadcaster::AudioProfile profile) {
-  switch (profile) {
-    case LeAudioBroadcaster::AudioProfile::SONIFICATION:
-      return lc3_mono_16_2;
-    case LeAudioBroadcaster::AudioProfile::MEDIA:
-      return lc3_stereo_24_2;
-  };
-}
-
 const std::map<uint32_t, uint8_t> sample_rate_to_sampling_freq_map = {
     {LeAudioCodecConfiguration::kSampleRate8000,
      codec_spec_conf::kLeAudioSamplingFreq8000Hz},
@@ -312,6 +316,77 @@
   return os;
 }
 
+static const BroadcastQosConfig qos_config_2_10 = BroadcastQosConfig(2, 10);
+
+static const BroadcastQosConfig qos_config_4_60 = BroadcastQosConfig(4, 60);
+
+std::ostream& operator<<(
+    std::ostream& os, const le_audio::broadcaster::BroadcastQosConfig& config) {
+  os << " BroadcastQosConfig=[";
+  os << "RTN=" << +config.getRetransmissionNumber();
+  os << ", MaxTransportLatency=" << config.getMaxTransportLatency();
+  os << "]";
+  return os;
+}
+
+static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+    lc3_mono_16_2_1 = {lc3_mono_16_2, qos_config_2_10};
+
+static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+    lc3_mono_16_2_2 = {lc3_mono_16_2, qos_config_4_60};
+
+static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+    lc3_stereo_16_2_2 = {lc3_stereo_16_2, qos_config_4_60};
+
+static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+    lc3_stereo_24_2_1 = {lc3_stereo_24_2, qos_config_2_10};
+
+static const std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+    lc3_stereo_24_2_2 = {lc3_stereo_24_2, qos_config_4_60};
+
+std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+getStreamConfigForContext(uint16_t context) {
+  // High quality, Low Latency
+  auto contexts_stereo_24_2_1 =
+      static_cast<std::underlying_type<LeAudioContextType>::type>(
+          LeAudioContextType::GAME) |
+      static_cast<std::underlying_type<LeAudioContextType>::type>(
+          LeAudioContextType::LIVE);
+  if (context & contexts_stereo_24_2_1) return lc3_stereo_24_2_1;
+
+  // Low quality, Low Latency
+  auto contexts_mono_16_2_1 =
+      static_cast<std::underlying_type<LeAudioContextType>::type>(
+          LeAudioContextType::INSTRUCTIONAL);
+  if (context & contexts_mono_16_2_1) return lc3_mono_16_2_1;
+
+  // Low quality, High Reliability
+  auto contexts_stereo_16_2_2 =
+      static_cast<std::underlying_type<LeAudioContextType>::type>(
+          LeAudioContextType::SOUNDEFFECTS) |
+      static_cast<std::underlying_type<LeAudioContextType>::type>(
+          LeAudioContextType::UNSPECIFIED);
+  if (context & contexts_stereo_16_2_2) return lc3_stereo_16_2_2;
+
+  auto contexts_mono_16_2_2 =
+      static_cast<std::underlying_type<LeAudioContextType>::type>(
+          LeAudioContextType::ALERTS) |
+      static_cast<std::underlying_type<LeAudioContextType>::type>(
+          LeAudioContextType::NOTIFICATIONS) |
+      static_cast<std::underlying_type<LeAudioContextType>::type>(
+          LeAudioContextType::EMERGENCYALARM);
+  if (context & contexts_mono_16_2_2) return lc3_mono_16_2_2;
+
+  // High quality, High Reliability
+  auto contexts_stereo_24_2_2 =
+      static_cast<std::underlying_type<LeAudioContextType>::type>(
+          LeAudioContextType::MEDIA);
+  if (context & contexts_stereo_24_2_2) return lc3_stereo_24_2_2;
+
+  // Defaults: Low quality, High Reliability
+  return lc3_mono_16_2_2;
+}
+
 } /* namespace broadcaster */
 } /* namespace le_audio */
 
diff --git a/system/bta/le_audio/broadcaster/broadcaster_types.h b/system/bta/le_audio/broadcaster/broadcaster_types.h
index 5995c32..9e97da8 100644
--- a/system/bta/le_audio/broadcaster/broadcaster_types.h
+++ b/system/bta/le_audio/broadcaster/broadcaster_types.h
@@ -71,9 +71,6 @@
     return *this;
   };
 
-  static const BroadcastCodecWrapper& getCodecConfigForProfile(
-      LeAudioBroadcaster::AudioProfile profile);
-
   types::LeAudioLtvMap GetSubgroupCodecSpecData() const;
   types::LeAudioLtvMap GetBisCodecSpecData(uint8_t bis_idx) const;
 
@@ -127,6 +124,32 @@
     std::ostream& os,
     const le_audio::broadcaster::BroadcastCodecWrapper& config);
 
+struct BroadcastQosConfig {
+  BroadcastQosConfig(uint8_t retransmission_number,
+                     uint16_t max_transport_latency)
+      : retransmission_number(retransmission_number),
+        max_transport_latency(max_transport_latency) {}
+
+  BroadcastQosConfig& operator=(const BroadcastQosConfig& other) {
+    retransmission_number = other.retransmission_number;
+    max_transport_latency = other.max_transport_latency;
+    return *this;
+  };
+
+  uint8_t getRetransmissionNumber() const { return retransmission_number; }
+  uint16_t getMaxTransportLatency() const { return max_transport_latency; }
+
+ private:
+  uint8_t retransmission_number;
+  uint16_t max_transport_latency;
+};
+
+std::ostream& operator<<(
+    std::ostream& os, const le_audio::broadcaster::BroadcastQosConfig& config);
+
+std::pair<const BroadcastCodecWrapper&, const BroadcastQosConfig&>
+getStreamConfigForContext(uint16_t context);
+
 }  // namespace broadcaster
 }  // namespace le_audio
 
diff --git a/system/bta/le_audio/broadcaster/state_machine.cc b/system/bta/le_audio/broadcaster/state_machine.cc
index 71ed8fc..1014f9a 100644
--- a/system/bta/le_audio/broadcaster/state_machine.cc
+++ b/system/bta/le_audio/broadcaster/state_machine.cc
@@ -29,6 +29,7 @@
 #include "bta/le_audio/le_audio_types.h"
 #include "gd/common/strings.h"
 #include "osi/include/log.h"
+#include "osi/include/properties.h"
 #include "service/common/bluetooth/low_energy_constants.h"
 #include "stack/include/ble_advertiser.h"
 #include "stack/include/btm_iso_api.h"
@@ -365,11 +366,10 @@
     struct bluetooth::hci::iso_manager::big_create_params big_params = {
         .adv_handle = GetAdvertisingSid(),
         .num_bis = sm_config_.codec_wrapper.GetNumChannels(),
-        .sdu_itv = callbacks_->GetSduItv(GetBroadcastId()),
+        .sdu_itv = sm_config_.codec_wrapper.GetDataIntervalUs(),
         .max_sdu_size = sm_config_.codec_wrapper.GetMaxSduSize(),
-        .max_transport_latency =
-            callbacks_->GetMaxTransportLatency(GetBroadcastId()),
-        .rtn = callbacks_->GetNumRetransmit(GetBroadcastId()),
+        .max_transport_latency = sm_config_.qos_config.getMaxTransportLatency(),
+        .rtn = sm_config_.qos_config.getRetransmissionNumber(),
         .phy = sm_config_.streaming_phy,
         .packing = 0x00, /* Sequencial */
         .framing = 0x00, /* Unframed */
@@ -655,6 +655,7 @@
                                     : PHYS[config.streaming_phy])
      << "\n";
   os << "        Codec Wrapper: " << config.codec_wrapper << "\n";
+  os << "        Qos Config: " << config.qos_config << "\n";
   if (config.broadcast_code) {
     os << "        Broadcast Code: [";
     for (auto& el : *config.broadcast_code) {
diff --git a/system/bta/le_audio/broadcaster/state_machine.h b/system/bta/le_audio/broadcaster/state_machine.h
index 10d6a93..c1cca06 100644
--- a/system/bta/le_audio/broadcaster/state_machine.h
+++ b/system/bta/le_audio/broadcaster/state_machine.h
@@ -97,6 +97,7 @@
   bluetooth::le_audio::BroadcastId broadcast_id;
   uint8_t streaming_phy;
   BroadcastCodecWrapper codec_wrapper;
+  BroadcastQosConfig qos_config;
   bluetooth::le_audio::BasicAudioAnnouncementData announcement;
   std::optional<bluetooth::le_audio::BroadcastCode> broadcast_code;
 };
@@ -190,9 +191,6 @@
                                    const void* data = nullptr) = 0;
   virtual void OnOwnAddressResponse(uint32_t broadcast_id, uint8_t addr_type,
                                     RawAddress address) = 0;
-  virtual uint8_t GetNumRetransmit(uint32_t broadcast_id) = 0;
-  virtual uint32_t GetSduItv(uint32_t broadcast_id) = 0;
-  virtual uint16_t GetMaxTransportLatency(uint32_t broadcast_id) = 0;
 };
 
 std::ostream& operator<<(
diff --git a/system/bta/le_audio/broadcaster/state_machine_test.cc b/system/bta/le_audio/broadcaster/state_machine_test.cc
index 9c44117..d2a84ad 100644
--- a/system/bta/le_audio/broadcaster/state_machine_test.cc
+++ b/system/bta/le_audio/broadcaster/state_machine_test.cc
@@ -72,10 +72,6 @@
   MOCK_METHOD((void), OnOwnAddressResponse,
               (uint32_t broadcast_id, uint8_t addr_type, RawAddress addr),
               (override));
-  MOCK_METHOD((uint8_t), GetNumRetransmit, (uint32_t broadcast_id), (override));
-  MOCK_METHOD((uint32_t), GetSduItv, (uint32_t broadcast_id), (override));
-  MOCK_METHOD((uint16_t), GetMaxTransportLatency, (uint32_t broadcast_id),
-              (override));
 };
 
 class StateMachineTest : public Test {
@@ -236,8 +232,8 @@
   }
 
   uint32_t InstantiateStateMachine(
-      LeAudioBroadcaster::AudioProfile profile =
-          LeAudioBroadcaster::AudioProfile::SONIFICATION) {
+      le_audio::types::LeAudioContextType context =
+          le_audio::types::LeAudioContextType::UNSPECIFIED) {
     // We will get the state machine create status update in an async callback
     // so let's wait for it here.
     instance_creation_promise_ = std::promise<uint32_t>();
@@ -246,13 +242,16 @@
 
     static uint8_t broadcast_id_lsb = 1;
 
-    auto codec_wrapper =
-        BroadcastCodecWrapper::getCodecConfigForProfile(profile);
+    auto context_int = static_cast<
+        std::underlying_type<le_audio::types::LeAudioContextType>::type>(
+        context);
+    auto codec_qos_pair = getStreamConfigForContext(context_int);
     auto broadcast_id = broadcast_id_lsb++;
     pending_broadcasts_.push_back(BroadcastStateMachine::CreateInstance({
         .broadcast_id = broadcast_id,
         // .streaming_phy = ,
-        .codec_wrapper = std::move(codec_wrapper),
+        .codec_wrapper = codec_qos_pair.first,
+        .qos_config = codec_qos_pair.second,
         // .announcement = ,
         // .broadcast_code = ,
     }));
@@ -456,10 +455,10 @@
   EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineCreateStatus(_, true))
       .Times(1);
 
-  auto sound_profile = LeAudioBroadcaster::AudioProfile::MEDIA;
+  auto sound_context = le_audio::types::LeAudioContextType::MEDIA;
   uint8_t num_channels = 2;
 
-  auto broadcast_id = InstantiateStateMachine(sound_profile);
+  auto broadcast_id = InstantiateStateMachine(sound_context);
   ASSERT_EQ(broadcasts_[broadcast_id]->GetState(),
             BroadcastStateMachine::State::CONFIGURED);
 
@@ -509,7 +508,7 @@
       .Times(1);
 
   auto broadcast_id =
-      InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+      InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
   ASSERT_EQ(broadcasts_[broadcast_id]->GetState(),
             BroadcastStateMachine::State::CONFIGURED);
 
@@ -535,7 +534,7 @@
       .Times(1);
 
   auto broadcast_id =
-      InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+      InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
   ASSERT_EQ(broadcasts_[broadcast_id]->GetState(),
             BroadcastStateMachine::State::CONFIGURED);
 
@@ -552,7 +551,7 @@
 
 TEST_F(StateMachineTest, ProcessMessageStartWhenStreaming) {
   auto broadcast_id =
-      InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+      InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
 
   broadcasts_[broadcast_id]->ProcessMessage(
       BroadcastStateMachine::Message::START);
@@ -573,7 +572,7 @@
 
 TEST_F(StateMachineTest, ProcessMessageStopWhenStreaming) {
   auto broadcast_id =
-      InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+      InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
 
   broadcasts_[broadcast_id]->ProcessMessage(
       BroadcastStateMachine::Message::START);
@@ -599,7 +598,7 @@
 
 TEST_F(StateMachineTest, ProcessMessageSuspendWhenStreaming) {
   auto broadcast_id =
-      InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+      InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
 
   broadcasts_[broadcast_id]->ProcessMessage(
       BroadcastStateMachine::Message::START);
@@ -621,7 +620,7 @@
 
 TEST_F(StateMachineTest, ProcessMessageStartWhenStopped) {
   auto broadcast_id =
-      InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+      InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
 
   broadcasts_[broadcast_id]->ProcessMessage(
       BroadcastStateMachine::Message::STOP);
@@ -647,7 +646,7 @@
 
 TEST_F(StateMachineTest, ProcessMessageStopWhenStopped) {
   auto broadcast_id =
-      InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+      InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
 
   broadcasts_[broadcast_id]->ProcessMessage(
       BroadcastStateMachine::Message::STOP);
@@ -668,7 +667,7 @@
 
 TEST_F(StateMachineTest, ProcessMessageSuspendWhenStopped) {
   auto broadcast_id =
-      InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+      InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
 
   broadcasts_[broadcast_id]->ProcessMessage(
       BroadcastStateMachine::Message::STOP);
@@ -692,7 +691,7 @@
       .Times(1);
 
   auto broadcast_id =
-      InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+      InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
   ASSERT_EQ(broadcasts_[broadcast_id]->GetState(),
             BroadcastStateMachine::State::CONFIGURED);
 
@@ -751,7 +750,7 @@
 
 TEST_F(StateMachineTest, OnRemoveIsoDataPathError) {
   auto broadcast_id =
-      InstantiateStateMachine(LeAudioBroadcaster::AudioProfile::MEDIA);
+      InstantiateStateMachine(le_audio::types::LeAudioContextType::MEDIA);
 
   broadcasts_[broadcast_id]->ProcessMessage(
       BroadcastStateMachine::Message::START);
@@ -801,10 +800,10 @@
   EXPECT_CALL(*(sm_callbacks_.get()), OnStateMachineCreateStatus(_, true))
       .Times(1);
 
-  auto sound_profile = LeAudioBroadcaster::AudioProfile::MEDIA;
+  auto sound_context = le_audio::types::LeAudioContextType::MEDIA;
   uint8_t num_channels = 2;
 
-  auto broadcast_id = InstantiateStateMachine(sound_profile);
+  auto broadcast_id = InstantiateStateMachine(sound_context);
   ASSERT_EQ(broadcasts_[broadcast_id]->GetState(),
             BroadcastStateMachine::State::CONFIGURED);
 
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 5206f7b..f12a078 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -32,6 +32,7 @@
 #include "client_parser.h"
 #include "codec_manager.h"
 #include "common/time_util.h"
+#include "content_control_id_keeper.h"
 #include "device/include/controller.h"
 #include "devices.h"
 #include "embdrv/lc3/include/lc3.h"
@@ -61,6 +62,7 @@
 using bluetooth::le_audio::GroupStatus;
 using bluetooth::le_audio::GroupStreamStatus;
 using le_audio::CodecManager;
+using le_audio::ContentControlIdKeeper;
 using le_audio::LeAudioDevice;
 using le_audio::LeAudioDeviceGroup;
 using le_audio::LeAudioDeviceGroups;
@@ -82,6 +84,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 +98,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:
@@ -308,12 +335,8 @@
     group_remove_node(group, address);
   }
 
-  int GetCcid(le_audio::types::LeAudioContextType context_type) {
-    if (ccids_.count(context_type) == 0) {
-      return -1;
-    }
-
-    return ccids_[context_type];
+  int GetCcid(uint16_t context_type) {
+    return ContentControlIdKeeper::GetInstance()->GetCcid(context_type);
   }
 
   /* This callback happens if kLeAudioDeviceSetStateTimeoutMs timeout happens
@@ -364,7 +387,7 @@
       callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                               group->snk_audio_locations_.to_ulong(),
                               group->src_audio_locations_.to_ulong(),
-                              new_group_updated_contexts->to_ulong());
+                              group->GetActiveContexts().to_ulong());
     }
   }
 
@@ -487,7 +510,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 +577,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(),
@@ -622,7 +653,7 @@
 
     bool result = groupStateMachine_->StartStream(
         group, static_cast<LeAudioContextType>(final_context_type),
-        GetCcid(static_cast<LeAudioContextType>(final_context_type)));
+        GetCcid(final_context_type));
     if (result)
       stream_setup_start_timestamp_ =
           bluetooth::common::time_get_os_boottime_us();
@@ -713,17 +744,7 @@
 
   void SetCcidInformation(int ccid, int context_type) override {
     LOG_DEBUG("Ccid: %d, context type %d", ccid, context_type);
-
-    std::bitset<16> test{static_cast<uint16_t>(context_type)};
-    auto ctx_type =
-        static_cast<le_audio::types::LeAudioContextType>(context_type);
-    if (test.count() > 1 ||
-        ctx_type >= le_audio::types::LeAudioContextType::RFU) {
-      LOG_ERROR("Unknownd context type %d", context_type);
-      return;
-    }
-
-    ccids_[ctx_type] = ccid;
+    ContentControlIdKeeper::GetInstance()->SetCcid(context_type, ccid);
   }
 
   void StartAudioSession(LeAudioDeviceGroup* group,
@@ -827,11 +848,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";
@@ -1086,9 +1120,13 @@
        * Read of available context during initial attribute discovery.
        * Group would be assigned once service search is completed.
        */
-      if (group)
-        group->UpdateActiveContextsMap(leAudioDevice->GetAvailableContexts());
-
+      if (group && group->UpdateActiveContextsMap(
+                       leAudioDevice->GetAvailableContexts())) {
+        callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
+                                group->snk_audio_locations_.to_ulong(),
+                                group->src_audio_locations_.to_ulong(),
+                                group->GetActiveContexts().to_ulong());
+      }
       return;
     }
 
@@ -1113,9 +1151,13 @@
        * Read of available context during initial attribute discovery.
        * Group would be assigned once service search is completed.
        */
-      if (group)
-        group->UpdateActiveContextsMap(leAudioDevice->GetAvailableContexts());
-
+      if (group && group->UpdateActiveContextsMap(
+                       leAudioDevice->GetAvailableContexts())) {
+        callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
+                                group->snk_audio_locations_.to_ulong(),
+                                group->src_audio_locations_.to_ulong(),
+                                group->GetActiveContexts().to_ulong());
+      }
       return;
     }
 
@@ -1144,7 +1186,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 +1220,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(),
@@ -1234,6 +1286,9 @@
 
       if (ParseAseCtpNotification(*ntf, len, value))
         ControlPointNotificationHandler(*ntf);
+    } else if (hdl == leAudioDevice->tmap_role_hdl_) {
+      le_audio::client_parser::tmap::ParseTmapRole(leAudioDevice->tmap_role_,
+                                                   len, value);
     } else {
       LOG(ERROR) << __func__ << ", Unknown attribute read: " << loghex(hdl);
     }
@@ -1279,10 +1334,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 +1427,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;
@@ -1529,6 +1584,7 @@
 
     const gatt::Service* pac_svc = nullptr;
     const gatt::Service* ase_svc = nullptr;
+    const gatt::Service* tmas_svc = nullptr;
 
     std::vector<uint16_t> csis_primary_handles;
     uint16_t cas_csis_included_handle = 0;
@@ -1559,6 +1615,10 @@
             break;
           }
         }
+      } else if (tmp.uuid == le_audio::uuid::kTelephonyMediaAudioServiceUuid) {
+        LOG_INFO(", Found Telephony and Media Audio service, handle: %04x",
+                 tmp.handle);
+        tmas_svc = &tmp;
       }
     }
 
@@ -1815,6 +1875,25 @@
       }
     }
 
+    if (tmas_svc) {
+      for (const gatt::Characteristic& charac : tmas_svc->characteristics) {
+        if (charac.uuid ==
+            le_audio::uuid::kTelephonyMediaAudioProfileRoleCharacteristicUuid) {
+          leAudioDevice->tmap_role_hdl_ = charac.value_handle;
+
+          /* Obtain initial state of TMAP role */
+          BtaGattQueue::ReadCharacteristic(conn_id,
+                                           leAudioDevice->tmap_role_hdl_,
+                                           OnGattReadRspStatic, NULL);
+
+          LOG_INFO(
+              ", Found Telephony and Media Profile characteristic, "
+              "handle: %04x",
+              leAudioDevice->tmap_role_hdl_);
+        }
+      }
+    }
+
     leAudioDevice->known_service_handles_ = true;
     leAudioDevice->notify_connected_after_read_ = true;
 
@@ -1993,8 +2072,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 +2082,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 +2096,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 +2166,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 +2349,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 +2783,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 +2813,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 +2827,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 +3177,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 +3211,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 +3231,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 +3264,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 +3292,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 +3325,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
        */
@@ -3430,7 +3545,7 @@
         callbacks_->OnAudioConf(group->audio_directions_, group->group_id_,
                                 group->snk_audio_locations_.to_ulong(),
                                 group->src_audio_locations_.to_ulong(),
-                                updated_contexts->to_ulong());
+                                group->GetActiveContexts().to_ulong());
       }
 
       group->SetPendingUpdateAvailableContexts(std::nullopt);
@@ -3499,7 +3614,9 @@
           SuspendedForReconfiguration();
           if (groupStateMachine_->ConfigureStream(
                   group, current_context_type_,
-                  GetCcid(current_context_type_))) {
+                  GetCcid(static_cast<
+                          std::underlying_type<LeAudioContextType>::type>(
+                      current_context_type_)))) {
             /* If configuration succeed wait for new status. */
             return;
           }
@@ -3541,10 +3658,6 @@
   /* Speaker(s) */
   AudioState audio_sender_state_;
 
-  /* Ccid informations */
-  std::map<le_audio::types::LeAudioContextType /* context */, int /*ccid */>
-      ccids_;
-
   /* Current stream configuration */
   LeAudioCodecConfiguration current_source_codec_config;
   LeAudioCodecConfiguration current_sink_codec_config;
@@ -3846,6 +3959,7 @@
 
   IsoManager::GetInstance()->RegisterCigCallbacks(stateMachineHciCallbacks);
   CodecManager::GetInstance()->Start(offloading_preference);
+  ContentControlIdKeeper::GetInstance()->Start();
 
   callbacks_->OnInitialized();
 }
@@ -3888,6 +4002,7 @@
   }
 
   CodecManager::GetInstance()->Stop();
+  ContentControlIdKeeper::GetInstance()->Stop();
   LeAudioGroupStateMachine::Cleanup();
   IsoManager::GetInstance()->Stop();
   le_audio::MetricsCollector::Get()->Flush();
diff --git a/system/bta/le_audio/client_parser.cc b/system/bta/le_audio/client_parser.cc
index e7d6461..ddd744f 100644
--- a/system/bta/le_audio/client_parser.cc
+++ b/system/bta/le_audio/client_parser.cc
@@ -657,5 +657,26 @@
 }
 }  // namespace pacs
 
+namespace tmap {
+
+bool ParseTmapRole(std::bitset<16>& role, uint16_t len, const uint8_t* value) {
+  if (len != kTmapRoleLen) {
+    LOG_ERROR(
+        ", Wrong len of Telephony Media Audio Profile Role, "
+        "characteristic");
+    return false;
+  }
+
+  STREAM_TO_UINT16(role, value);
+
+  LOG_INFO(
+      ", Telephony Media Audio Profile Role:"
+      "\n\tRole: %s",
+      role.to_string().c_str());
+
+  return true;
+}
+}  // namespace tmap
+
 }  // namespace client_parser
 }  // namespace le_audio
diff --git a/system/bta/le_audio/client_parser.h b/system/bta/le_audio/client_parser.h
index 159f452..8974989 100644
--- a/system/bta/le_audio/client_parser.h
+++ b/system/bta/le_audio/client_parser.h
@@ -240,5 +240,13 @@
 bool ParseSupportedAudioContexts(struct acs_supported_audio_contexts& rsp,
                                  uint16_t len, const uint8_t* value);
 }  // namespace pacs
+
+namespace tmap {
+
+constexpr uint16_t kTmapRoleLen = 2;
+
+bool ParseTmapRole(std::bitset<16>& role, uint16_t len, const uint8_t* value);
+
+}  // namespace tmap
 }  // namespace client_parser
 }  // namespace le_audio
diff --git a/system/bta/le_audio/client_parser_test.cc b/system/bta/le_audio/client_parser_test.cc
index 0dd98c8..c54c237 100644
--- a/system/bta/le_audio/client_parser_test.cc
+++ b/system/bta/le_audio/client_parser_test.cc
@@ -1645,5 +1645,25 @@
 
 }  // namespace ascs
 
+namespace tmap {
+
+TEST(LeAudioClientParserTest, testParseTmapRoleValid) {
+  std::bitset<16> role;
+  const uint8_t value[] = {0x3F, 0x00};
+
+  ASSERT_TRUE(ParseTmapRole(role, 2, value));
+
+  ASSERT_EQ(role, 0x003F);  // All possible TMAP roles
+}
+
+TEST(LeAudioClientParserTest, testParseTmapRoleInvalidLen) {
+  std::bitset<16> role;
+  const uint8_t value[] = {0x00, 0x3F};
+
+  ASSERT_FALSE(ParseTmapRole(role, 3, value));
+}
+
+}  // namespace tmap
+
 }  // namespace client_parser
 }  // namespace le_audio
diff --git a/system/bta/le_audio/content_control_id_keeper.cc b/system/bta/le_audio/content_control_id_keeper.cc
new file mode 100644
index 0000000..73657d9
--- /dev/null
+++ b/system/bta/le_audio/content_control_id_keeper.cc
@@ -0,0 +1,123 @@
+/*
+ * 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 "content_control_id_keeper.h"
+
+#include <bitset>
+#include <map>
+
+#include "le_audio_types.h"
+#include "osi/include/log.h"
+
+namespace {
+
+using le_audio::types::LeAudioContextType;
+
+}  // namespace
+
+namespace le_audio {
+
+struct ccid_keeper {
+ public:
+  ccid_keeper() {}
+
+  ~ccid_keeper() {}
+
+  void SetCcid(uint16_t context_type, int ccid) {
+    LOG_DEBUG("Ccid: %d, context type %d", ccid, context_type);
+
+    std::bitset<16> test{context_type};
+    if (test.count() > 1 ||
+        context_type >=
+            static_cast<std::underlying_type<LeAudioContextType>::type>(
+                LeAudioContextType::RFU)) {
+      LOG_ERROR("Unknownd context type %d", context_type);
+      return;
+    }
+
+    auto ctx_type = static_cast<LeAudioContextType>(context_type);
+    ccids_.insert_or_assign(ctx_type, ccid);
+  }
+
+  int GetCcid(uint16_t context_type) const {
+    std::bitset<16> test{context_type};
+    if (test.count() > 1 ||
+        context_type >=
+            static_cast<std::underlying_type<LeAudioContextType>::type>(
+                LeAudioContextType::RFU)) {
+      LOG_ERROR("Unknownd context type %d", context_type);
+      return -1;
+    }
+
+    auto ctx_type = static_cast<LeAudioContextType>(context_type);
+
+    if (ccids_.count(ctx_type) == 0) {
+      return -1;
+    }
+
+    return ccids_.at(ctx_type);
+  }
+
+ private:
+  /* Ccid informations */
+  std::map<LeAudioContextType /* context */, int /*ccid */> ccids_;
+};
+
+struct ContentControlIdKeeper::impl {
+  impl(const ContentControlIdKeeper& ccid_keeper) : ccid_keeper_(ccid_keeper) {}
+
+  void Start() {
+    LOG_ASSERT(!ccid_keeper_impl_);
+    ccid_keeper_impl_ = std::make_unique<ccid_keeper>();
+  }
+
+  void Stop() {
+    LOG_ASSERT(ccid_keeper_impl_);
+    ccid_keeper_impl_.reset();
+  }
+
+  bool IsRunning() { return ccid_keeper_impl_ ? true : false; }
+
+  const ContentControlIdKeeper& ccid_keeper_;
+  std::unique_ptr<ccid_keeper> ccid_keeper_impl_;
+};
+
+ContentControlIdKeeper::ContentControlIdKeeper()
+    : pimpl_(std::make_unique<impl>(*this)) {}
+
+void ContentControlIdKeeper::Start() {
+  if (!pimpl_->IsRunning()) pimpl_->Start();
+}
+
+void ContentControlIdKeeper::Stop() {
+  if (pimpl_->IsRunning()) pimpl_->Stop();
+}
+
+int ContentControlIdKeeper::GetCcid(uint16_t context_type) const {
+  if (!pimpl_->IsRunning()) {
+    return -1;
+  }
+
+  return pimpl_->ccid_keeper_impl_->GetCcid(context_type);
+}
+
+void ContentControlIdKeeper::SetCcid(uint16_t context_type, int ccid) {
+  if (pimpl_->IsRunning()) {
+    pimpl_->ccid_keeper_impl_->SetCcid(context_type, ccid);
+  }
+}
+
+}  // namespace le_audio
diff --git a/system/bta/le_audio/content_control_id_keeper.h b/system/bta/le_audio/content_control_id_keeper.h
new file mode 100644
index 0000000..697c19c
--- /dev/null
+++ b/system/bta/le_audio/content_control_id_keeper.h
@@ -0,0 +1,40 @@
+/*
+ * 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 <memory>
+
+namespace le_audio {
+
+class ContentControlIdKeeper {
+ public:
+  ContentControlIdKeeper();
+  virtual ~ContentControlIdKeeper() = default;
+  static ContentControlIdKeeper* GetInstance(void) {
+    static ContentControlIdKeeper* instance = new ContentControlIdKeeper();
+    return instance;
+  }
+  void Start(void);
+  void Stop(void);
+  virtual void SetCcid(uint16_t context_type, int ccid);
+  virtual int GetCcid(uint16_t context_type) const;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+};
+}  // namespace le_audio
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index 6943f3e..a74c027 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -152,11 +152,11 @@
   }
 }
 
-void LeAudioDeviceGroup::Activate(void) {
+void LeAudioDeviceGroup::Activate(LeAudioContextType context_type) {
   for (auto leAudioDevice : leAudioDevices_) {
     if (leAudioDevice.expired()) continue;
 
-    leAudioDevice.lock()->ActivateConfiguredAses();
+    leAudioDevice.lock()->ActivateConfiguredAses(context_type);
   }
 }
 
@@ -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;
 }
 
@@ -996,6 +1023,7 @@
 
   for (; needed_ase && ase; needed_ase--) {
     ase->active = true;
+    ase->configured_for_context_type = context_type;
     active_ases++;
 
     if (ase->state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED)
@@ -1205,13 +1233,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 +1248,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 +1685,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 +1767,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;
@@ -1751,7 +1780,7 @@
   return updated_contexts;
 }
 
-void LeAudioDevice::ActivateConfiguredAses(void) {
+void LeAudioDevice::ActivateConfiguredAses(LeAudioContextType context_type) {
   if (conn_id_ == GATT_INVALID_CONN_ID) {
     LOG_DEBUG(" Device %s is not connected ", address_.ToString().c_str());
     return;
@@ -1760,7 +1789,8 @@
   LOG_DEBUG(" Configuring device %s", address_.ToString().c_str());
   for (auto& ase : ases_) {
     if (!ase.active &&
-        ase.state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) {
+        ase.state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED &&
+        ase.configured_for_context_type == context_type) {
       LOG_DEBUG(" Ase id %d, cis id %d activated.", ase.id, ase.cis_id);
       ase.active = true;
     }
diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h
index 30e4bbc..60881d0 100644
--- a/system/bta/le_audio/devices.h
+++ b/system/bta/le_audio/devices.h
@@ -62,6 +62,7 @@
   bool encrypted_;
   int group_id_;
   bool csis_member_;
+  std::bitset<16> tmap_role_;
 
   uint8_t audio_directions_;
   types::AudioLocations snk_audio_locations_;
@@ -76,6 +77,7 @@
   struct types::hdl_pair audio_supp_cont_hdls_;
   std::vector<struct types::ase> ases_;
   struct types::hdl_pair ctp_hdls_;
+  uint16_t tmap_role_hdl_;
 
   alarm_t* link_quality_timer;
   uint16_t link_quality_timer_data;
@@ -141,7 +143,7 @@
   types::AudioContexts SetAvailableContexts(types::AudioContexts snk_cont_val,
                                             types::AudioContexts src_cont_val);
   void DeactivateAllAses(void);
-  void ActivateConfiguredAses(void);
+  void ActivateConfiguredAses(types::LeAudioContextType context_type);
   void Dump(int fd);
   void DisconnectAcl(void);
   std::vector<uint8_t> GetMetadata(types::LeAudioContextType context_type,
@@ -215,7 +217,7 @@
   int Size(void);
   int NumOfConnected(
       types::LeAudioContextType context_type = types::LeAudioContextType::RFU);
-  void Activate(void);
+  void Activate(types::LeAudioContextType context_type);
   void Deactivate(void);
   void Cleanup(void);
   LeAudioDevice* GetFirstDevice(void);
@@ -249,6 +251,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..2cb62d6 100644
--- a/system/bta/le_audio/devices_test.cc
+++ b/system/bta/le_audio/devices_test.cc
@@ -199,14 +199,15 @@
   /* Update those values, on any change of codec linked with content type */
   switch (context_type) {
     case LeAudioContextType::RINGTONE:
-      if (id == Lc3SettingId::LC3_16_1 || id == Lc3SettingId::LC3_16_2)
+      if (id == Lc3SettingId::LC3_16_2 || id == Lc3SettingId::LC3_16_1 ||
+          id == Lc3SettingId::LC3_32_2)
         return true;
 
       break;
 
     case LeAudioContextType::CONVERSATIONAL:
       if (id == Lc3SettingId::LC3_16_1 || id == Lc3SettingId::LC3_16_2 ||
-          id == Lc3SettingId::LC3_32_2)
+          id == Lc3SettingId::LC3_24_2 || id == Lc3SettingId::LC3_32_2)
         return true;
 
       break;
@@ -526,6 +527,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 +544,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));
@@ -564,9 +587,15 @@
 
   void TestAsesActive(LeAudioCodecId codec_id, uint8_t sampling_frequency,
                       uint8_t frame_duration, uint16_t octets_per_frame) {
+    bool active_ase = false;
+
     for (const auto& device : devices_) {
       for (const auto& ase : device->ases_) {
-        ASSERT_TRUE(ase.active);
+        if (!ase.active) continue;
+
+        /* Configure may request only partial ases to be activated */
+        if (!active_ase && ase.active) active_ase = true;
+
         ASSERT_EQ(ase.codec_id, codec_id);
 
         /* FIXME: Validate other codec parameters than LC3 if any */
@@ -578,6 +607,8 @@
         }
       }
     }
+
+    ASSERT_TRUE(active_ase);
   }
 
   void TestActiveAses(void) {
@@ -664,10 +695,10 @@
 };
 
 TEST_F(LeAudioAseConfigurationTest, test_mono_speaker_ringtone) {
-  LeAudioDevice* mono_speaker = AddTestDevice(1, 0);
-  TestGroupAseConfigurationData data({mono_speaker,
-                                      kLeAudioCodecLC3ChannelCountSingleChannel,
-                                      kLeAudioCodecLC3ChannelCountNone, 1, 0});
+  LeAudioDevice* mono_speaker = AddTestDevice(1, 1);
+  TestGroupAseConfigurationData data(
+      {mono_speaker, kLeAudioCodecLC3ChannelCountSingleChannel,
+       kLeAudioCodecLC3ChannelCountSingleChannel, 1, 0});
 
   TestGroupAseConfiguration(LeAudioContextType::RINGTONE, &data, 1);
 }
@@ -691,10 +722,10 @@
 }
 
 TEST_F(LeAudioAseConfigurationTest, test_bounded_headphones_ringtone) {
-  LeAudioDevice* bounded_headphones = AddTestDevice(2, 0);
-  TestGroupAseConfigurationData data({bounded_headphones,
-                                      kLeAudioCodecLC3ChannelCountTwoChannel,
-                                      kLeAudioCodecLC3ChannelCountNone, 2, 0});
+  LeAudioDevice* bounded_headphones = AddTestDevice(2, 1);
+  TestGroupAseConfigurationData data(
+      {bounded_headphones, kLeAudioCodecLC3ChannelCountTwoChannel,
+       kLeAudioCodecLC3ChannelCountSingleChannel, 2, 0});
 
   TestGroupAseConfiguration(LeAudioContextType::RINGTONE, &data, 1);
 }
@@ -827,7 +858,7 @@
 }
 
 TEST_F(LeAudioAseConfigurationTest, test_lc3_config_ringtone) {
-  AddTestDevice(1, 0);
+  AddTestDevice(1, 1);
 
   TestLc3CodecConfig(LeAudioContextType::RINGTONE);
 }
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..a41742c 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -68,6 +68,9 @@
 static const bluetooth::Uuid kAudioStreamControlServiceUuid =
     bluetooth::Uuid::From16Bit(0x184E);
 
+static const bluetooth::Uuid kTelephonyMediaAudioServiceUuid =
+    bluetooth::Uuid::From16Bit(0x1855);
+
 /* Published Audio Capabilities Service Characteristics */
 static const bluetooth::Uuid kSinkPublishedAudioCapabilityCharacteristicUuid =
     bluetooth::Uuid::From16Bit(0x2BC9);
@@ -92,6 +95,10 @@
 static const bluetooth::Uuid
     kAudioStreamEndpointControlPointCharacteristicUuid =
         bluetooth::Uuid::From16Bit(0x2BC6);
+
+/* Telephony and Media Audio Service Characteristics */
+static const bluetooth::Uuid kTelephonyMediaAudioProfileRoleCharacteristicUuid =
+    bluetooth::Uuid::From16Bit(0x2B51);
 }  // namespace uuid
 
 namespace codec_spec_conf {
@@ -391,6 +398,9 @@
       : values(std::move(values)) {}
 
   std::optional<std::vector<uint8_t>> Find(uint8_t type) const;
+  void Add(uint8_t type, std::vector<uint8_t> value) {
+    values.insert_or_assign(type, std::move(value));
+  }
   bool IsEmpty() const { return values.empty(); }
   void Clear() { values.clear(); }
   size_t Size() const { return values.size(); }
@@ -519,6 +529,7 @@
         active(false),
         reconfigure(false),
         data_path_state(AudioStreamDataPathState::IDLE),
+        configured_for_context_type(LeAudioContextType::UNINITIALIZED),
         preferred_phy(0),
         state(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {}
 
@@ -532,6 +543,7 @@
   bool active;
   bool reconfigure;
   AudioStreamDataPathState data_path_state;
+  LeAudioContextType configured_for_context_type;
 
   /* Codec configuration */
   LeAudioCodecId codec_id;
@@ -572,6 +584,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..686d9a0 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -153,7 +153,7 @@
     switch (group->GetState()) {
       case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
         if (group->GetCurrentContextType() == context_type) {
-          group->Activate();
+          group->Activate(context_type);
           SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
           CigCreate(group);
           return true;
@@ -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();
@@ -1115,6 +1116,8 @@
       LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
     switch (ase->state) {
       case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
+      case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
+      case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
         if (ase->id == 0x00) {
           /* Initial state of Ase - update id */
           LOG(INFO) << __func__
@@ -1126,6 +1129,8 @@
         LeAudioDevice* leAudioDeviceNext;
         ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_IDLE;
         ase->active = false;
+        ase->configured_for_context_type =
+            le_audio::types::LeAudioContextType::UNINITIALIZED;
 
         if (!leAudioDevice->HaveAllActiveAsesSameState(
                 AseState::BTA_LE_AUDIO_ASE_STATE_IDLE)) {
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index ff33fd3..62af3b1 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"
@@ -643,6 +644,7 @@
 
   void MultipleTestDevicePrepare(int leaudio_group_id, uint16_t context_type,
                                  uint16_t device_cnt,
+                                 uint16_t update_context_type,
                                  bool insert_default_pac_records = true) {
     // Prepare fake connected device group
     bool first_connections = true;
@@ -679,8 +681,10 @@
         uint16_t attr_handle = ATTR_HANDLE_PACS_POOL_START;
 
         /* As per spec, unspecified shall be supported */
-        types::AudioContexts snk_context_type = kContextTypeUnspecified;
-        types::AudioContexts src_context_type = kContextTypeUnspecified;
+        types::AudioContexts snk_context_type =
+            kContextTypeUnspecified | update_context_type;
+        types::AudioContexts src_context_type =
+            kContextTypeUnspecified | update_context_type;
 
         // Prepare Sink Published Audio Capability records
         if ((context_type & kContextTypeRingtone) ||
@@ -742,17 +746,20 @@
     }
 
     /* Stimulate update of active context map */
-    types::AudioContexts type_set = static_cast<uint16_t>(context_type);
+    types::AudioContexts type_set = static_cast<uint16_t>(
+        update_context_type == 0 ? context_type
+                                 : context_type | update_context_type);
     group->UpdateActiveContextsMap(type_set);
 
     ASSERT_NE(group, nullptr);
     ASSERT_EQ(group->Size(), total_devices);
   }
 
-  LeAudioDeviceGroup* PrepareSingleTestDeviceGroup(int leaudio_group_id,
-                                                   uint16_t context_type,
-                                                   uint16_t device_cnt = 1) {
-    MultipleTestDevicePrepare(leaudio_group_id, context_type, device_cnt);
+  LeAudioDeviceGroup* PrepareSingleTestDeviceGroup(
+      int leaudio_group_id, uint16_t context_type, uint16_t device_cnt = 1,
+      uint16_t update_context_type = 0) {
+    MultipleTestDevicePrepare(leaudio_group_id, context_type, device_cnt,
+                              update_context_type);
     return le_audio_device_groups_.count(leaudio_group_id)
                ? le_audio_device_groups_[leaudio_group_id].get()
                : nullptr;
@@ -1895,6 +1902,92 @@
             types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
 }
 
+TEST_F(StateMachineTest, testActivateStreamCachingSingle) {
+  auto context_type = kContextTypeConversational;
+  const int leaudio_group_id = 4;
+
+  additional_ases = 2;
+  /* Prepare fake connected device group with update of Media and Conversational
+   * contexts
+   */
+  auto* group = PrepareSingleTestDeviceGroup(
+      leaudio_group_id, context_type, 1,
+      kContextTypeConversational | kContextTypeMedia);
+
+  /* Since we prepared device with Conversational context in mind, only one ASE
+   * should have been configured.
+   */
+  PrepareConfigureCodecHandler(group, 0, true);
+  PrepareConfigureQosHandler(group, 0, true);
+  PrepareEnableHandler(group);
+  PrepareReceiverStartReady(group);
+  PrepareReleaseHandler(group);
+
+  /* Ctp messages we expect:
+   * 1. Codec Config
+   * 2. QoS Config
+   * 3. Enable
+   * 4. Release
+   * 5. QoS Config (because device stays in Configured state)
+   * 6. Enable
+   */
+  auto* leAudioDevice = group->GetFirstDevice();
+  EXPECT_CALL(gatt_queue,
+              WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _,
+                                  GATT_WRITE_NO_RSP, _, _))
+      .Times(8);
+
+  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(2);
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(2);
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(5);
+  EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(2);
+  EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(2);
+  EXPECT_CALL(*mock_iso_manager_, RemoveCig(_)).Times(1);
+
+  InjectInitialIdleNotification(group);
+
+  // Validate GroupStreamStatus
+  EXPECT_CALL(
+      mock_callbacks_,
+      StatusReportCb(leaudio_group_id,
+                     bluetooth::le_audio::GroupStreamStatus::RELEASING));
+
+  EXPECT_CALL(
+      mock_callbacks_,
+      StatusReportCb(
+          leaudio_group_id,
+          bluetooth::le_audio::GroupStreamStatus::CONFIGURED_AUTONOMOUS));
+
+  EXPECT_CALL(mock_callbacks_,
+              StatusReportCb(leaudio_group_id,
+                             bluetooth::le_audio::GroupStreamStatus::STREAMING))
+      .Times(2);
+
+  // Start the configuration and stream Conversational content
+  LeAudioGroupStateMachine::Get()->StartStream(
+      group, static_cast<types::LeAudioContextType>(context_type));
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+
+  // Stop the stream
+  LeAudioGroupStateMachine::Get()->StopStream(group);
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
+
+  // Start the configuration and stream Media content
+  context_type = kContextTypeMedia;
+  LeAudioGroupStateMachine::Get()->StartStream(
+      group, static_cast<types::LeAudioContextType>(context_type));
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+}
+
 TEST_F(StateMachineTest, testReleaseMultiple) {
   const auto context_type = kContextTypeMedia;
   const auto leaudio_group_id = 6;
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/btif/include/btif_common.h b/system/btif/include/btif_common.h
index abd7964..a0ee3c1 100644
--- a/system/btif/include/btif_common.h
+++ b/system/btif/include/btif_common.h
@@ -220,6 +220,8 @@
                                   bt_bond_state_t state, int fail_reason);
 void invoke_address_consolidate_cb(RawAddress main_bd_addr,
                                    RawAddress secondary_bd_addr);
+void invoke_le_address_associate_cb(RawAddress main_bd_addr,
+                                    RawAddress secondary_bd_addr);
 void invoke_acl_state_changed_cb(bt_status_t status, RawAddress bd_addr,
                                  bt_acl_state_t state, int transport_link_type,
                                  bt_hci_error_code_t hci_reason);
diff --git a/system/btif/include/btif_storage.h b/system/btif/include/btif_storage.h
index 3e154b1..0d3225f 100644
--- a/system/btif/include/btif_storage.h
+++ b/system/btif/include/btif_storage.h
@@ -171,15 +171,17 @@
 
 /*******************************************************************************
  *
- * Function         btif_storage_load_consolidate_devices
+ * Function         btif_storage_load_le_devices
  *
- * Description      BTIF storage API - Load the consolidate devices from NVRAM
- *                  Additionally, this API also invokes the adaper_properties_cb
- *                  and invoke_address_consolidate_cb for each of the
- *                  consolidate devices.
+ * Description      BTIF storage API - Loads all LE-only and Dual Mode devices
+ *                  from NVRAM. This API invokes the adaper_properties_cb.
+ *                  It also invokes invoke_address_consolidate_cb
+ *                  to consolidate each Dual Mode device and
+ *                  invoke_le_address_associate_cb to associate each LE-only
+ *                  device between its RPA and identity address.
  *
  ******************************************************************************/
-void btif_storage_load_consolidate_devices(void);
+void btif_storage_load_le_devices(void);
 
 /*******************************************************************************
  *
diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc
index 2be88b2..58d6da0 100644
--- a/system/btif/src/bluetooth.cc
+++ b/system/btif/src/bluetooth.cc
@@ -883,6 +883,16 @@
                      main_bd_addr, secondary_bd_addr));
 }
 
+void invoke_le_address_associate_cb(RawAddress main_bd_addr,
+                                    RawAddress secondary_bd_addr) {
+  do_in_jni_thread(
+      FROM_HERE, base::BindOnce(
+                     [](RawAddress main_bd_addr, RawAddress secondary_bd_addr) {
+                       HAL_CBACK(bt_hal_cbacks, le_address_associate_cb,
+                                 &main_bd_addr, &secondary_bd_addr);
+                     },
+                     main_bd_addr, secondary_bd_addr));
+}
 void invoke_acl_state_changed_cb(bt_status_t status, RawAddress bd_addr,
                                  bt_acl_state_t state, int transport_link_type,
                                  bt_hci_error_code_t hci_reason) {
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index 4c7bd52..ee6c2f2 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -78,6 +78,7 @@
 #include "common/metrics.h"
 #include "device/include/controller.h"
 #include "device/include/interop.h"
+#include "gd/common/lru_cache.h"
 #include "internal_include/stack_config.h"
 #include "main/shim/dumpsys.h"
 #include "main/shim/shim.h"
@@ -164,9 +165,6 @@
   bool is_le_nc; /* LE Numeric comparison */
   btif_dm_ble_cb_t ble;
   uint8_t fail_reason;
-  Uuid::UUID128Bit eir_uuids[32];
-  uint8_t num_eir_uuids;
-  std::set<Uuid::UUID128Bit> uuids;
 } btif_dm_pairing_cb_t;
 
 // TODO(jpawlowski): unify ?
@@ -214,6 +212,11 @@
 
 #define MAX_BTIF_BOND_EVENT_ENTRIES 15
 
+#define MAX_NUM_DEVICES_IN_EIR_UUID_CACHE 128
+
+static bluetooth::common::LruCache<RawAddress, std::set<Uuid>> eir_uuids_cache(
+    MAX_NUM_DEVICES_IN_EIR_UUID_CACHE);
+
 static skip_sdp_entry_t sdp_rejectlist[] = {{76}};  // Apple Mouse and Keyboard
 
 /* This flag will be true if HCI_Inquiry is in progress */
@@ -1302,13 +1305,16 @@
         /* Cache EIR queried services */
         if (num_uuids > 0) {
           uint16_t* p_uuid16 = (uint16_t*)uuid_list;
-          pairing_cb.num_eir_uuids = 0;
-          LOG_INFO("EIR UUIDS:");
+          auto uuid_iter = eir_uuids_cache.find(bdaddr);
+          if (uuid_iter == eir_uuids_cache.end()) {
+            auto triple = eir_uuids_cache.try_emplace(bdaddr, std::set<Uuid>{});
+            uuid_iter = std::get<0>(triple);
+          }
+          LOG_INFO("EIR UUIDs for %s:", bdaddr.ToString().c_str());
           for (int i = 0; i < num_uuids; ++i) {
             Uuid uuid = Uuid::From16Bit(p_uuid16[i]);
             LOG_INFO("        %s", uuid.ToString().c_str());
-            pairing_cb.eir_uuids[i] = uuid.To128BitBE();
-            pairing_cb.num_eir_uuids++;
+            uuid_iter->second.insert(uuid);
           }
         }
 
@@ -1438,6 +1444,8 @@
       /* onUuidChanged requires getBondedDevices to be populated.
       ** bond_state_changed needs to be sent prior to remote_device_property
       */
+      auto num_eir_uuids = 0;
+      Uuid uuid = {};
       if (pairing_cb.state == BT_BOND_STATE_BONDED && pairing_cb.sdp_attempts &&
           (p_data->disc_res.bd_addr == pairing_cb.bd_addr ||
            p_data->disc_res.bd_addr == pairing_cb.static_bdaddr)) {
@@ -1448,33 +1456,33 @@
         // when SDP failed or no UUID is discovered
         if (p_data->disc_res.result != BTA_SUCCESS ||
             p_data->disc_res.num_uuids == 0) {
-          LOG_INFO("SDP failed, send %d EIR UUIDs to unblock bonding %s",
-                   pairing_cb.num_eir_uuids, bd_addr.ToString().c_str());
-          bt_property_t prop_uuids;
-          Uuid uuid = {};
-          prop_uuids.type = BT_PROPERTY_UUIDS;
-          if (pairing_cb.num_eir_uuids > 0) {
-            prop_uuids.val = pairing_cb.eir_uuids;
-            prop_uuids.len = pairing_cb.num_eir_uuids * Uuid::kNumBytes128;
-          } else {
-            prop_uuids.val = &uuid;
-            prop_uuids.len = Uuid::kNumBytes128;
+          auto uuids_iter = eir_uuids_cache.find(bd_addr);
+          if (uuids_iter != eir_uuids_cache.end()) {
+            num_eir_uuids = static_cast<int>(uuids_iter->second.size());
+            LOG_INFO("SDP failed, send %d EIR UUIDs to unblock bonding %s",
+                     num_eir_uuids, bd_addr.ToString().c_str());
+            for (auto eir_uuid : uuids_iter->second) {
+              auto uuid_128bit = eir_uuid.To128BitBE();
+              property_value.insert(property_value.end(), uuid_128bit.begin(),
+                                    uuid_128bit.end());
+            }
+            eir_uuids_cache.erase(uuids_iter);
           }
-
-          /* Send the event to the BTIF
-           * prop_uuids will be deep copied by this call
-           */
-          invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr, 1,
-                                             &prop_uuids);
-          pairing_cb = {};
-          break;
+          if (num_eir_uuids > 0) {
+            prop.val = (void*)property_value.data();
+            prop.len = num_eir_uuids * Uuid::kNumBytes128;
+          } else {
+            LOG_WARN("SDP failed and we have no EIR UUIDs to report either");
+            prop.val = &uuid;
+            prop.len = Uuid::kNumBytes128;
+          }
         }
         // Both SDP and bonding are done, clear pairing control block in case
         // it is not already cleared
         pairing_cb = {};
       }
 
-      if (p_data->disc_res.num_uuids != 0) {
+      if (p_data->disc_res.num_uuids != 0 || num_eir_uuids != 0) {
         /* Also write this to the NVRAM */
         ret = btif_storage_set_remote_device_property(&bd_addr, &prop);
         ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed",
@@ -1634,7 +1642,7 @@
   pairing_cb.bond_type = tBTM_SEC_DEV_REC::BOND_TYPE_PERSISTENT;
   if (enable_address_consolidate) {
     LOG_INFO("enable address consolidate");
-    btif_storage_load_consolidate_devices();
+    btif_storage_load_le_devices();
   }
 
   /* This function will also trigger the adapter_properties_cb
@@ -1892,6 +1900,12 @@
                                    pairing_cb.fail_reason);
       btif_dm_remove_bond(bd_addr);
       break;
+
+    case BTA_DM_LE_ADDR_ASSOC_EVT:
+      invoke_le_address_associate_cb(p_data->proc_id_addr.pairing_bda,
+                                     p_data->proc_id_addr.id_addr);
+      break;
+
     default:
       BTIF_TRACE_WARNING("%s: unhandled event (%d)", __func__, event);
       break;
@@ -2605,11 +2619,13 @@
 }
 
 static bool waiting_on_oob_advertiser_start = false;
-static uint8_t oob_advertiser_id = 0;
+static std::unique_ptr<uint8_t> oob_advertiser_id_;
 static void stop_oob_advertiser() {
+  // For chasing an advertising bug b/237023051
+  LOG_DEBUG("oob_advertiser_id: %s", oob_advertiser_id_.get());
   auto advertiser = get_ble_advertiser_instance();
-  advertiser->Unregister(oob_advertiser_id);
-  oob_advertiser_id = 0;
+  advertiser->Unregister(*oob_advertiser_id_);
+  oob_advertiser_id_ = nullptr;
 }
 
 /*******************************************************************************
@@ -2630,7 +2646,9 @@
     // the state machine lifecycle.  Rather, lets create the data, then start
     // advertising then request the address.
     if (!waiting_on_oob_advertiser_start) {
-      if (oob_advertiser_id != 0) {
+      // For chasing an advertising bug b/237023051
+      LOG_DEBUG("oob_advertiser_id: %s", oob_advertiser_id_.get());
+      if (oob_advertiser_id_ != nullptr) {
         stop_oob_advertiser();
       }
       waiting_on_oob_advertiser_start = true;
@@ -2659,7 +2677,7 @@
     invoke_oob_data_request_cb(transport, false, c, r, RawAddress{}, 0x00);
     SMP_ClearLocScOobData();
     waiting_on_oob_advertiser_start = false;
-    oob_advertiser_id = 0;
+    oob_advertiser_id_ = nullptr;
     return;
   }
   LOG_DEBUG("OOB advertiser with id %hhd", id);
@@ -2675,7 +2693,7 @@
   advertiser->Unregister(id);
   SMP_ClearLocScOobData();
   waiting_on_oob_advertiser_start = false;
-  oob_advertiser_id = 0;
+  oob_advertiser_id_ = nullptr;
 }
 
 // Step Two: CallBack from Step One, advertise and get address
@@ -2687,11 +2705,12 @@
     invoke_oob_data_request_cb(transport, false, c, r, RawAddress{}, 0x00);
     SMP_ClearLocScOobData();
     waiting_on_oob_advertiser_start = false;
-    oob_advertiser_id = 0;
+    oob_advertiser_id_ = nullptr;
     return;
   }
 
-  oob_advertiser_id = id;
+  oob_advertiser_id_ = std::make_unique<uint8_t>(id);
+  LOG_ERROR("oob_advertiser_id: %s", oob_advertiser_id_.get());
 
   auto advertiser = get_ble_advertiser_instance();
   AdvertiseParameters parameters;
@@ -2711,7 +2730,7 @@
   advertiser->StartAdvertising(
       id,
       base::Bind(&start_advertising_callback, id, transport, is_valid, c, r),
-      parameters, advertisement, scan_data, 3600 /* timeout_s */,
+      parameters, advertisement, scan_data, 120 /* timeout_s */,
       base::Bind(&timeout_cb, id));
 }
 
@@ -2947,6 +2966,7 @@
     bond_state_changed(status, bd_addr, BT_BOND_STATE_BONDING);
   }
   bond_state_changed(status, bd_addr, state);
+  // TODO(240451061): Calling `stop_oob_advertiser();` gets command disallowed...
 }
 
 void btif_dm_load_ble_local_keys(void) {
diff --git a/system/btif/src/btif_le_audio_broadcaster.cc b/system/btif/src/btif_le_audio_broadcaster.cc
index f4825cd..7b867a1 100644
--- a/system/btif/src/btif_le_audio_broadcaster.cc
+++ b/system/btif/src/btif_le_audio_broadcaster.cc
@@ -28,7 +28,6 @@
 
 using base::Bind;
 using base::Unretained;
-using bluetooth::le_audio::BroadcastAudioProfile;
 using bluetooth::le_audio::BroadcastId;
 using bluetooth::le_audio::BroadcastState;
 using bluetooth::le_audio::LeAudioBroadcasterCallbacks;
@@ -52,15 +51,12 @@
   }
 
   void CreateBroadcast(
-      std::vector<uint8_t> metadata, BroadcastAudioProfile profile,
+      std::vector<uint8_t> metadata,
       std::optional<std::array<uint8_t, 16>> broadcast_code) override {
     DVLOG(2) << __func__;
-    do_in_main_thread(
-        FROM_HERE,
-        Bind(&LeAudioBroadcaster::CreateAudioBroadcast,
-             Unretained(LeAudioBroadcaster::Get()), std::move(metadata),
-             static_cast<LeAudioBroadcaster::AudioProfile>(profile),
-             broadcast_code));
+    do_in_main_thread(FROM_HERE, Bind(&LeAudioBroadcaster::CreateAudioBroadcast,
+                                      Unretained(LeAudioBroadcaster::Get()),
+                                      std::move(metadata), broadcast_code));
   }
 
   void UpdateMetadata(uint32_t broadcast_id,
diff --git a/system/btif/src/btif_storage.cc b/system/btif/src/btif_storage.cc
index 41fa4df..b3653c1 100644
--- a/system/btif/src/btif_storage.cc
+++ b/system/btif/src/btif_storage.cc
@@ -943,15 +943,17 @@
 
 /*******************************************************************************
  *
- * Function         btif_storage_load_consolidate_devices
+ * Function         btif_storage_load_le_devices
  *
- * Description      BTIF storage API - Load the consolidate devices from NVRAM
- *                  Additionally, this API also invokes the adaper_properties_cb
- *                  and invoke_address_consolidate_cb for each of the
- *                  consolidate devices.
+ * Description      BTIF storage API - Loads all LE-only and Dual Mode devices
+ *                  from NVRAM. This API invokes the adaper_properties_cb.
+ *                  It also invokes invoke_address_consolidate_cb
+ *                  to consolidate each Dual Mode device and
+ *                  invoke_le_address_associate_cb to associate each LE-only
+ *                  device between its RPA and identity address.
  *
  ******************************************************************************/
-void btif_storage_load_consolidate_devices(void) {
+void btif_storage_load_le_devices(void) {
   btif_bonded_devices_t bonded_devices;
   btif_in_fetch_bonded_devices(&bonded_devices, 1);
   std::unordered_set<RawAddress> bonded_addresses;
@@ -966,10 +968,8 @@
     if (btif_storage_get_ble_bonding_key(
             bonded_devices.devices[i], BTM_LE_KEY_PID, (uint8_t*)&key,
             sizeof(tBTM_LE_PID_KEYS)) == BT_STATUS_SUCCESS) {
-      if (bonded_devices.devices[i] != key.pid_key.identity_addr &&
-          bonded_addresses.find(key.pid_key.identity_addr) !=
-              bonded_addresses.end()) {
-        LOG_INFO("found consolidated device %s %s",
+      if (bonded_devices.devices[i] != key.pid_key.identity_addr) {
+        LOG_INFO("found device with a known identity address %s %s",
                  bonded_devices.devices[i].ToString().c_str(),
                  key.pid_key.identity_addr.ToString().c_str());
 
@@ -1001,7 +1001,13 @@
   }
 
   for (const auto& device : consolidated_devices) {
-    invoke_address_consolidate_cb(device.first, device.second);
+    if (bonded_addresses.find(device.second) != bonded_addresses.end()) {
+      // Invokes address consolidation for DuMo devices
+      invoke_address_consolidate_cb(device.first, device.second);
+    } else {
+      // Associates RPA & identity address for LE-only devices
+      invoke_le_address_associate_cb(device.first, device.second);
+    }
   }
 }
 
diff --git a/system/btif/test/btif_core_test.cc b/system/btif/test/btif_core_test.cc
index 67356ab..82f86dc 100644
--- a/system/btif/test/btif_core_test.cc
+++ b/system/btif/test/btif_core_test.cc
@@ -65,6 +65,8 @@
                                  bt_bond_state_t state, int fail_reason) {}
 void address_consolidate_callback(RawAddress* main_bd_addr,
                                   RawAddress* secondary_bd_addr) {}
+void le_address_associate_callback(RawAddress* main_bd_addr,
+                                   RawAddress* secondary_bd_addr) {}
 void acl_state_changed_callback(bt_status_t status, RawAddress* remote_bd_addr,
                                 bt_acl_state_t state, int transport_link_type,
                                 bt_hci_error_code_t hci_reason) {}
@@ -94,6 +96,7 @@
     .ssp_request_cb = ssp_request_callback,
     .bond_state_changed_cb = bond_state_changed_callback,
     .address_consolidate_cb = address_consolidate_callback,
+    .le_address_associate_cb = le_address_associate_callback,
     .acl_state_changed_cb = acl_state_changed_callback,
     .thread_evt_cb = callback_thread_event,
     .dut_mode_recv_cb = dut_mode_recv_callback,
diff --git a/system/conf/bt_stack.conf b/system/conf/bt_stack.conf
index d1e7756..942dfdc 100644
--- a/system/conf/bt_stack.conf
+++ b/system/conf/bt_stack.conf
@@ -44,6 +44,18 @@
 # Disable LE Connection updates
 #PTS_DisableConnUpdates=true
 
+# Use EATT for the notifications
+#PTS_ForceEattForNotifications=true
+
+# Start EATT without validation Server Supported Features
+#PTS_ConnectEattUncondictionally=true
+
+# Start EATT on unecrypted link
+#PTS_ConnectEattUnencrypted=true
+
+# Force EATT implementation to connect EATT as a peripheral for collision test case
+#PTS_EattPeripheralCollionSupport=true
+
 # Disable BR/EDR discovery after LE pairing to avoid cross key derivation errors
 #PTS_DisableSDPOnLEPair=true
 
@@ -53,6 +65,9 @@
 # PTS AVRCP Test mode
 #PTS_AvrcpTest=true
 
+# Start broadcast with unecryption mode
+#PTS_BroadcastUnencrypted=true
+
 # SMP Certification Failure Cases
 # Set any of the following SMP error values (from smp_api_types.h)
 # to induce pairing failues for various PTS SMP test cases.
@@ -63,6 +78,7 @@
 #  SMP_PAIR_AUTH_FAIL = 3
 #  SMP_CONFIRM_VALUE_ERR = 4
 #  SMP_PAIR_NOT_SUPPORT = 5
+#  SMP_ENC_KEY_SIZE = 6
 #  SMP_PAIR_FAIL_UNKNOWN = 8
 #  SMP_REPEATED_ATTEMPTS = 9
 #  SMP_NUMERIC_COMPAR_FAIL = 12
diff --git a/system/gd/cert/run b/system/gd/cert/run
index 4482a2d..28aab89 100755
--- a/system/gd/cert/run
+++ b/system/gd/cert/run
@@ -268,13 +268,13 @@
 
 function soong_build {
     if [ "$CLEAN_VENV" == true ] ; then
-        $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"modules-in-a-dir" --dir="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system" dist $BUILD_TARGET -j20
+        $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"modules-in-a-dir" --dir="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system" dist $BUILD_TARGET -j $(nproc)
         if [[ $? -ne 0 ]] ; then
             echo -e "${RED}Failed to build ${BUILD_TARGET}${NOCOLOR}"
             exit 1
         fi
     else
-        $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"all-modules" --dir="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system" $BUILD_TARGET -j20
+        $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --build-mode --"all-modules" --dir="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system" dist $BUILD_TARGET -j $(nproc)
         if [[ $? -ne 0 ]] ; then
             echo -e "${RED}Failed to build ${BUILD_TARGET}${NOCOLOR}"
             exit 1
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..5df7c4a 100644
--- a/system/gd/hci/le_scanning_manager.cc
+++ b/system/gd/hci/le_scanning_manager.cc
@@ -28,6 +28,7 @@
 #include "module.h"
 #include "os/handler.h"
 #include "os/log.h"
+#include "storage/storage_module.h"
 
 namespace bluetooth {
 namespace hci {
@@ -235,12 +236,14 @@
       hci::HciLayer* hci_layer,
       hci::Controller* controller,
       hci::AclManager* acl_manager,
-      hci::VendorSpecificEventManager* vendor_specific_event_manager) {
+      hci::VendorSpecificEventManager* vendor_specific_event_manager,
+      storage::StorageModule* storage_module) {
     module_handler_ = handler;
     hci_layer_ = hci_layer;
     controller_ = controller;
     acl_manager_ = acl_manager;
     vendor_specific_event_manager_ = vendor_specific_event_manager;
+    storage_module_ = storage_module;
     le_address_manager_ = acl_manager->GetLeAddressManager();
     le_scanning_interface_ = hci_layer_->GetLeScanningInterface(
         module_handler_->BindOn(this, &LeScanningManager::impl::handle_scan_results));
@@ -501,6 +504,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,
@@ -701,6 +714,17 @@
         module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
   }
 
+  bool is_bonded(Address target_address) {
+    for (auto device : storage_module_->GetBondedDevices()) {
+      if (device.GetAddress() == target_address) {
+        LOG_DEBUG("Addresses match!");
+        return true;
+      }
+    }
+    LOG_DEBUG("Addresse DON'Ts match!");
+    return false;
+  }
+
   void scan_filter_parameter_setup(
       ApcfAction action, uint8_t filter_index, AdvertisingFilterParameter advertising_filter_parameter) {
     if (!is_filter_support_) {
@@ -708,6 +732,7 @@
       return;
     }
 
+    auto entry = remove_me_later_map_.find(filter_index);
     switch (action) {
       case ApcfAction::ADD:
         le_scanning_interface_->EnqueueCommand(
@@ -730,11 +755,33 @@
         le_scanning_interface_->EnqueueCommand(
             LeAdvFilterDeleteFilteringParametersBuilder::Create(filter_index),
             module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
+
+        // IRK Scanning
+        if (entry != remove_me_later_map_.end()) {
+          // Don't want to remove for a bonded device
+          if (!is_bonded(entry->second.GetAddress())) {
+            le_address_manager_->RemoveDeviceFromResolvingList(
+                static_cast<PeerAddressType>(entry->second.GetAddressType()), entry->second.GetAddress());
+          }
+          remove_me_later_map_.erase(filter_index);
+        }
+
         break;
       case ApcfAction::CLEAR:
         le_scanning_interface_->EnqueueCommand(
             LeAdvFilterClearFilteringParametersBuilder::Create(),
             module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
+
+        // IRK Scanning
+        if (entry != remove_me_later_map_.end()) {
+          // Don't want to remove for a bonded device
+          if (!is_bonded(entry->second.GetAddress())) {
+            le_address_manager_->RemoveDeviceFromResolvingList(
+                static_cast<PeerAddressType>(entry->second.GetAddressType()), entry->second.GetAddress());
+          }
+          remove_me_later_map_.erase(filter_index);
+        }
+
         break;
       default:
         LOG_ERROR("Unknown action type: %d", (uint16_t)action);
@@ -786,6 +833,8 @@
     }
   }
 
+  std::unordered_map<uint8_t, AddressWithType> remove_me_later_map_;
+
   void update_address_filter(
       ApcfAction action,
       uint8_t filter_index,
@@ -813,14 +862,35 @@
               action, filter_index, address, ApcfApplicationAddressType::NOT_APPLICABLE),
           module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
       if (!is_empty_128bit(irk)) {
+        // If an entry exists for this filter index, replace data because the filter has been
+        // updated.
+        auto entry = remove_me_later_map_.find(filter_index);
+        // IRK Scanning
+        if (entry != remove_me_later_map_.end()) {
+          // Don't want to remove for a bonded device
+          if (!is_bonded(entry->second.GetAddress())) {
+            le_address_manager_->RemoveDeviceFromResolvingList(
+                static_cast<PeerAddressType>(entry->second.GetAddressType()), entry->second.GetAddress());
+          }
+          remove_me_later_map_.erase(filter_index);
+        }
+
+        // Now replace it with a new one
         std::array<uint8_t, 16> empty_irk;
         le_address_manager_->AddDeviceToResolvingList(
             static_cast<PeerAddressType>(address_type), address, irk, empty_irk);
+        remove_me_later_map_.emplace(filter_index, AddressWithType(address, static_cast<AddressType>(address_type)));
       }
     } else {
       le_scanning_interface_->EnqueueCommand(
           LeAdvFilterClearBroadcasterAddressBuilder::Create(filter_index),
           module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
+      auto entry = remove_me_later_map_.find(filter_index);
+      if (entry != remove_me_later_map_.end()) {
+        // TODO(optedoblivion): If not bonded
+        le_address_manager_->RemoveDeviceFromResolvingList(static_cast<PeerAddressType>(address_type), address);
+        remove_me_later_map_.erase(filter_index);
+      }
     }
   }
 
@@ -1434,6 +1504,7 @@
   hci::Controller* controller_;
   hci::AclManager* acl_manager_;
   hci::VendorSpecificEventManager* vendor_specific_event_manager_;
+  storage::StorageModule* storage_module_;
   hci::LeScanningInterface* le_scanning_interface_;
   hci::LeAddressManager* le_address_manager_;
   bool address_manager_registered_ = false;
@@ -1492,6 +1563,7 @@
   list->add<hci::VendorSpecificEventManager>();
   list->add<hci::Controller>();
   list->add<hci::AclManager>();
+  list->add<storage::StorageModule>();
 }
 
 void LeScanningManager::Start() {
@@ -1500,7 +1572,8 @@
       GetDependency<hci::HciLayer>(),
       GetDependency<hci::Controller>(),
       GetDependency<AclManager>(),
-      GetDependency<VendorSpecificEventManager>());
+      GetDependency<VendorSpecificEventManager>(),
+      GetDependency<storage::StorageModule>());
 }
 
 void LeScanningManager::Stop() {
diff --git a/system/gd/rust/common/src/init_flags.rs b/system/gd/rust/common/src/init_flags.rs
index 52425a8..07e63f4 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 d1cbfbf..1a1cea1 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/gd/rust/topshim/src/btif.rs b/system/gd/rust/topshim/src/btif.rs
index 922024d..5e9b50e 100644
--- a/system/gd/rust/topshim/src/btif.rs
+++ b/system/gd/rust/topshim/src/btif.rs
@@ -702,6 +702,7 @@
     SspRequest(RawAddress, String, u32, BtSspVariant, u32),
     BondState(BtStatus, RawAddress, BtBondState, i32),
     AddressConsolidate(RawAddress, RawAddress),
+    LeAddressAssociate(RawAddress, RawAddress),
     AclState(BtStatus, RawAddress, BtAclState, BtTransport, BtHciErrorCode),
     // Unimplemented so far:
     // thread_evt_cb
@@ -757,6 +758,12 @@
     let _1 = unsafe { *(_1 as *const RawAddress) };
 });
 
+cb_variant!(BaseCb, le_address_associate_cb -> BaseCallbacks::LeAddressAssociate,
+*mut FfiAddress, *mut FfiAddress, {
+    let _0 = unsafe { *(_0 as *const RawAddress) };
+    let _1 = unsafe { *(_1 as *const RawAddress) };
+});
+
 cb_variant!(BaseCb, acl_state_cb -> BaseCallbacks::AclState,
 u32 -> BtStatus, *mut FfiAddress, bindings::bt_acl_state_t -> BtAclState, i32 -> BtTransport, bindings::bt_hci_error_code_t -> BtHciErrorCode, {
     let _1 = unsafe { *(_1 as *const RawAddress) };
@@ -871,6 +878,7 @@
             ssp_request_cb: Some(ssp_request_cb),
             bond_state_changed_cb: Some(bond_state_cb),
             address_consolidate_cb: Some(address_consolidate_cb),
+            le_address_associate_cb: Some(le_address_associate_cb),
             acl_state_changed_cb: Some(acl_state_cb),
             thread_evt_cb: None,
             dut_mode_recv_cb: None,
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index 96e425c..d8f80ab 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -458,6 +458,12 @@
 typedef void (*address_consolidate_callback)(RawAddress* main_bd_addr,
                                              RawAddress* secondary_bd_addr);
 
+/** Bluetooth LE Address association callback */
+/* Callback for the upper layer to associate the LE-only device's RPA to the
+ * identity address */
+typedef void (*le_address_associate_callback)(RawAddress* main_bd_addr,
+                                              RawAddress* secondary_bd_addr);
+
 /** Bluetooth ACL connection state changed callback */
 typedef void (*acl_state_changed_callback)(bt_status_t status,
                                            RawAddress* remote_bd_addr,
@@ -527,6 +533,7 @@
   ssp_request_callback ssp_request_cb;
   bond_state_changed_callback bond_state_changed_cb;
   address_consolidate_callback address_consolidate_cb;
+  le_address_associate_callback le_address_associate_cb;
   acl_state_changed_callback acl_state_changed_cb;
   callback_thread_event thread_evt_cb;
   dut_mode_recv_callback dut_mode_recv_cb;
diff --git a/system/include/hardware/bt_le_audio.h b/system/include/hardware/bt_le_audio.h
index 3188aa9..87766a3 100644
--- a/system/include/hardware/bt_le_audio.h
+++ b/system/include/hardware/bt_le_audio.h
@@ -163,12 +163,6 @@
   STREAMING,
 };
 
-/* A general hint for the codec configuration process. */
-enum class BroadcastAudioProfile {
-  SONIFICATION = 0,
-  MEDIA,
-};
-
 using BroadcastId = uint32_t;
 static constexpr BroadcastId kBroadcastIdInvalid = 0x00000000;
 using BroadcastCode = std::array<uint8_t, 16>;
@@ -259,7 +253,6 @@
   virtual void Cleanup(void) = 0;
   /* Create Broadcast instance */
   virtual void CreateBroadcast(std::vector<uint8_t> metadata,
-                               BroadcastAudioProfile profile,
                                std::optional<BroadcastCode> broadcast_code) = 0;
   /* Update the ongoing Broadcast metadata */
   virtual void UpdateMetadata(uint32_t broadcast_id,
diff --git a/system/internal_include/bt_target.h b/system/internal_include/bt_target.h
index 8ff2b37..98787a3 100644
--- a/system/internal_include/bt_target.h
+++ b/system/internal_include/bt_target.h
@@ -551,6 +551,27 @@
 #define GATT_CONFORMANCE_TESTING FALSE
 #endif
 
+/* Used only for GATT Multiple Variable Length Notifications PTS tests */
+#ifndef GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_NOTIF
+#define GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_NOTIF FALSE
+#endif
+
+/* Used only for GATT Multiple Variable Length READ PTS tests */
+#ifndef GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_READ
+#define GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_READ FALSE
+#endif
+
+/******************************************************************************
+ *
+ * CSIP
+ *
+ *****************************************************************************/
+
+/* Used to trigger invalid behaviour of CSIP test case PTS */
+#ifndef CSIP_UPPER_TESTER_FORCE_TO_SEND_LOCK
+#define CSIP_UPPER_TESTER_FORCE_TO_SEND_LOCK FALSE
+#endif
+
 /******************************************************************************
  *
  * SMP
diff --git a/system/internal_include/stack_config.h b/system/internal_include/stack_config.h
index 19968ca..53dd186 100644
--- a/system/internal_include/stack_config.h
+++ b/system/internal_include/stack_config.h
@@ -33,6 +33,11 @@
   bool (*get_pts_crosskey_sdp_disable)(void);
   const std::string* (*get_pts_smp_options)(void);
   int (*get_pts_smp_failure_case)(void);
+  bool (*get_pts_force_eatt_for_notifications)(void);
+  bool (*get_pts_connect_eatt_unconditionally)(void);
+  bool (*get_pts_connect_eatt_before_encryption)(void);
+  bool (*get_pts_unencrypt_broadcast)(void);
+  bool (*get_pts_eatt_peripheral_collision_support)(void);
   config_t* (*get_all)(void);
 } stack_config_t;
 
diff --git a/system/main/stack_config.cc b/system/main/stack_config.cc
index 29387a4..fdfe7d6 100644
--- a/system/main/stack_config.cc
+++ b/system/main/stack_config.cc
@@ -33,6 +33,13 @@
 const char* PTS_DISABLE_SDP_LE_PAIR = "PTS_DisableSDPOnLEPair";
 const char* PTS_SMP_PAIRING_OPTIONS_KEY = "PTS_SmpOptions";
 const char* PTS_SMP_FAILURE_CASE_KEY = "PTS_SmpFailureCase";
+const char* PTS_FORCE_EATT_FOR_NOTIFICATIONS = "PTS_ForceEattForNotifications";
+const char* PTS_CONNECT_EATT_UNCONDITIONALLY =
+    "PTS_ConnectEattUncondictionally";
+const char* PTS_CONNECT_EATT_UNENCRYPTED = "PTS_ConnectEattUnencrypted";
+const char* PTS_BROADCAST_UNENCRYPTED = "PTS_BroadcastUnencrypted";
+const char* PTS_EATT_PERIPHERAL_COLLISION_SUPPORT =
+    "PTS_EattPeripheralCollionSupport";
 
 static std::unique_ptr<config_t> config;
 }  // namespace
@@ -110,12 +117,45 @@
                         PTS_SMP_FAILURE_CASE_KEY, 0);
 }
 
+static bool get_pts_force_eatt_for_notifications(void) {
+  return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+                         PTS_FORCE_EATT_FOR_NOTIFICATIONS, false);
+}
+
+static bool get_pts_connect_eatt_unconditionally(void) {
+  return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+                         PTS_CONNECT_EATT_UNCONDITIONALLY, false);
+}
+
+static bool get_pts_connect_eatt_before_encryption(void) {
+  return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+                         PTS_CONNECT_EATT_UNENCRYPTED, false);
+}
+
+static bool get_pts_unencrypt_broadcast(void) {
+  return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+                         PTS_BROADCAST_UNENCRYPTED, false);
+}
+
+static bool get_pts_eatt_peripheral_collision_support(void) {
+  return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+                         PTS_EATT_PERIPHERAL_COLLISION_SUPPORT, false);
+}
+
 static config_t* get_all(void) { return config.get(); }
 
-const stack_config_t interface = {
-    get_trace_config_enabled,     get_pts_avrcp_test,
-    get_pts_secure_only_mode,     get_pts_conn_updates_disabled,
-    get_pts_crosskey_sdp_disable, get_pts_smp_options,
-    get_pts_smp_failure_case,     get_all};
+const stack_config_t interface = {get_trace_config_enabled,
+                                  get_pts_avrcp_test,
+                                  get_pts_secure_only_mode,
+                                  get_pts_conn_updates_disabled,
+                                  get_pts_crosskey_sdp_disable,
+                                  get_pts_smp_options,
+                                  get_pts_smp_failure_case,
+                                  get_pts_force_eatt_for_notifications,
+                                  get_pts_connect_eatt_unconditionally,
+                                  get_pts_connect_eatt_before_encryption,
+                                  get_pts_unencrypt_broadcast,
+                                  get_pts_eatt_peripheral_collision_support,
+                                  get_all};
 
 const stack_config_t* stack_config_get_interface(void) { return &interface; }
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_device_fuzz/avrcp_device_fuzz.cc b/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
index bc3dd6f..441bf5c 100644
--- a/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
+++ b/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
@@ -54,9 +54,13 @@
 
 bool get_pts_avrcp_test(void) { return false; }
 
-const stack_config_t interface = {
-    nullptr, get_pts_avrcp_test, nullptr, nullptr, nullptr, nullptr, nullptr,
-    nullptr};
+const stack_config_t interface = {nullptr, get_pts_avrcp_test,
+                                  nullptr, nullptr,
+                                  nullptr, nullptr,
+                                  nullptr, nullptr,
+                                  nullptr, nullptr,
+                                  nullptr, nullptr,
+                                  nullptr};
 
 void Callback(uint8_t, bool, std::unique_ptr<::bluetooth::PacketBuilder>) {}
 
diff --git a/system/profile/avrcp/tests/avrcp_device_test.cc b/system/profile/avrcp/tests/avrcp_device_test.cc
index 7aea88b..2bbc39a 100644
--- a/system/profile/avrcp/tests/avrcp_device_test.cc
+++ b/system/profile/avrcp/tests/avrcp_device_test.cc
@@ -50,9 +50,13 @@
 
 bool get_pts_avrcp_test(void) { return false; }
 
-const stack_config_t interface = {
-    nullptr, get_pts_avrcp_test, nullptr, nullptr, nullptr, nullptr, nullptr,
-    nullptr};
+const stack_config_t interface = {nullptr, get_pts_avrcp_test,
+                                  nullptr, nullptr,
+                                  nullptr, nullptr,
+                                  nullptr, nullptr,
+                                  nullptr, nullptr,
+                                  nullptr, nullptr,
+                                  nullptr};
 
 // TODO (apanicke): All the tests below are just basic positive unit tests.
 // Add more tests to increase code coverage.
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/service/hal/bluetooth_interface.cc b/system/service/hal/bluetooth_interface.cc
index f959d61..8f01058 100644
--- a/system/service/hal/bluetooth_interface.cc
+++ b/system/service/hal/bluetooth_interface.cc
@@ -156,6 +156,11 @@
   // Do nothing
 }
 
+void LeAddressAssociateCallback(RawAddress* main_bd_addr,
+                                RawAddress* secondary_bd_addr) {
+  // Do nothing
+}
+
 void AclStateChangedCallback(bt_status_t status, RawAddress* remote_bd_addr,
                              bt_acl_state_t state, int transport_link_type,
                              bt_hci_error_code_t hci_reason) {
@@ -250,6 +255,7 @@
     SSPRequestCallback,
     BondStateChangedCallback,
     AddressConsolidateCallback,
+    LeAddressAssociateCallback,
     AclStateChangedCallback,
     ThreadEventCallback,
     nullptr, /* dut_mode_recv_cb */
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 85095ae..8d1b8e5 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -668,6 +668,7 @@
         "packages/modules/Bluetooth/system/utils/include",
     ],
     srcs: crypto_toolbox_srcs + [
+        ":TestCommonMainHandler",
         ":TestMockStackBtm",
         "gatt/gatt_db.cc",
         "gatt/gatt_sr_hash.cc",
@@ -719,6 +720,7 @@
         "test/common/mock_controller.cc",
         "test/common/mock_gatt_layer.cc",
         "test/common/mock_hcic_layer.cc",
+        ":TestCommonStackConfig"
     ],
     static_libs: [
         "libbt-common",
@@ -762,6 +764,7 @@
     include_dirs:[
         "packages/modules/Bluetooth/system",
         "packages/modules/Bluetooth/system/gd",
+        "packages/modules/Bluetooth/system/internal_include"
     ],
     srcs: [
         "eatt/eatt.cc",
@@ -772,6 +775,8 @@
         "test/common/mock_l2cap_layer.cc",
         "test/gatt/mock_gatt_utils_ref.cc",
         "test/eatt/eatt_test.cc",
+        ":TestCommonMainHandler",
+        ":TestCommonStackConfig",
     ],
     shared_libs: [
         "libcutils",
@@ -862,6 +867,7 @@
         "metrics/stack_metrics_logging.cc",
         "test/btm/stack_btm_test.cc",
         "test/btm/peer_packet_types_test.cc",
+        "test/common/mock_eatt.cc",
     ],
     static_libs: [
         "libbt-common",
@@ -1073,6 +1079,8 @@
     ],
     srcs: [
         ":OsiCompatSources",
+        ":TestCommonMainHandler",
+        ":TestCommonStackConfig",
         ":TestMockBta",
         ":TestMockBtif",
         ":TestMockHci",
@@ -1265,3 +1273,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/avct/avct_l2c.cc b/system/stack/avct/avct_l2c.cc
index 57bc46e..7634a65 100644
--- a/system/stack/avct/avct_l2c.cc
+++ b/system/stack/avct/avct_l2c.cc
@@ -61,6 +61,7 @@
     NULL,
     NULL,
     NULL,
+    NULL,
 };
 
 /*******************************************************************************
diff --git a/system/stack/avct/avct_l2c_br.cc b/system/stack/avct/avct_l2c_br.cc
index a3eef3c..57bc501 100644
--- a/system/stack/avct/avct_l2c_br.cc
+++ b/system/stack/avct/avct_l2c_br.cc
@@ -61,6 +61,7 @@
                                            avct_br_on_l2cap_error,
                                            NULL,
                                            NULL,
+                                           NULL,
                                            NULL};
 
 /*******************************************************************************
diff --git a/system/stack/avdt/avdt_l2c.cc b/system/stack/avdt/avdt_l2c.cc
index 2607cc1..3e93d37 100644
--- a/system/stack/avdt/avdt_l2c.cc
+++ b/system/stack/avdt/avdt_l2c.cc
@@ -62,6 +62,7 @@
                                         avdt_on_l2cap_error,
                                         NULL,
                                         NULL,
+                                        NULL,
                                         NULL};
 
 /*******************************************************************************
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/btm/btm_ble.cc b/system/stack/btm/btm_ble.cc
index 3a18f25..57edb61 100644
--- a/system/stack/btm/btm_ble.cc
+++ b/system/stack/btm/btm_ble.cc
@@ -1880,6 +1880,15 @@
         }
         break;
 
+      case SMP_LE_ADDR_ASSOC_EVT:
+        if (btm_cb.api.p_le_callback) {
+          BTM_TRACE_DEBUG("btm_cb.api.p_le_callback=0x%x",
+                          btm_cb.api.p_le_callback);
+          (*btm_cb.api.p_le_callback)(event, bd_addr,
+                                      (tBTM_LE_EVT_DATA*)p_data);
+        }
+        break;
+
       default:
         BTM_TRACE_DEBUG("unknown event = %d", event);
         break;
diff --git a/system/stack/btm/btm_inq.cc b/system/stack/btm/btm_inq.cc
index b0c8be4..f42b160 100644
--- a/system/stack/btm/btm_inq.cc
+++ b/system/stack/btm/btm_inq.cc
@@ -1411,7 +1411,10 @@
   uint16_t temp_evt_len;
 
   if (bda) {
+    rem_name.bd_addr = *bda;
     VLOG(2) << "BDA " << *bda;
+  } else {
+    rem_name.bd_addr = RawAddress::kEmpty;
   }
 
   VLOG(2) << "Inquire BDA " << p_inq->remname_bda;
diff --git a/system/stack/btm/btm_iso_impl.h b/system/stack/btm/btm_iso_impl.h
index 661b5ca..245a4ef 100644
--- a/system/stack/btm/btm_iso_impl.h
+++ b/system/stack/btm/btm_iso_impl.h
@@ -29,6 +29,7 @@
 #include "common/time_util.h"
 #include "device/include/controller.h"
 #include "hci/include/hci_layer.h"
+#include "internal_include/stack_config.h"
 #include "osi/include/allocator.h"
 #include "osi/include/log.h"
 #include "stack/include/bt_hdr.h"
@@ -657,6 +658,12 @@
     LOG_ASSERT(!IsBigKnown(big_id))
         << "Invalid big - already exists: " << +big_id;
 
+    if (stack_config_get_interface()->get_pts_unencrypt_broadcast()) {
+      LOG_INFO("Force create broadcst without encryption for PTS test");
+      big_params.enc = 0;
+      big_params.enc_code = {0};
+    }
+
     last_big_create_req_sdu_itv_ = big_params.sdu_itv;
     btsnd_hcic_create_big(
         big_id, big_params.adv_handle, big_params.num_bis, big_params.sdu_itv,
diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc
index 4a83cde..513a3ad 100644
--- a/system/stack/btm/btm_sec.cc
+++ b/system/stack/btm/btm_sec.cc
@@ -46,6 +46,7 @@
 #include "osi/include/osi.h"
 #include "stack/btm/btm_dev.h"
 #include "stack/btm/security_device_record.h"
+#include "stack/eatt/eatt.h"
 #include "stack/include/acl_api.h"
 #include "stack/include/acl_hci_link_interface.h"
 #include "stack/include/btm_status.h"
@@ -4734,6 +4735,11 @@
   }
 
   btm_sec_check_pending_reqs();
+
+  if (btm_status == BTM_SUCCESS && is_le_transport) {
+    /* Link is encrypted, start EATT */
+    bluetooth::eatt::EattExtension::GetInstance()->Connect(p_dev_rec->bd_addr);
+  }
 }
 
 void btm_sec_cr_loc_oob_data_cback_event(const RawAddress& address,
diff --git a/system/stack/eatt/eatt.cc b/system/stack/eatt/eatt.cc
index e01126b..a023026 100644
--- a/system/stack/eatt/eatt.cc
+++ b/system/stack/eatt/eatt.cc
@@ -45,6 +45,7 @@
     reg_info_.pL2CA_DisconnectInd_Cb = eatt_disconnect_ind;
     reg_info_.pL2CA_Error_Cb = eatt_error_cb;
     reg_info_.pL2CA_DataInd_Cb = eatt_data_ind;
+    reg_info_.pL2CA_CreditBasedCollisionInd_Cb = eatt_collision_ind;
 
     if (L2CA_RegisterLECoc(BT_PSM_EATT, reg_info_, BTM_SEC_NONE, {}) == 0) {
       LOG(ERROR) << __func__ << " cannot register EATT";
@@ -94,6 +95,11 @@
                                                  p_cfg);
   }
 
+  static void eatt_collision_ind(const RawAddress& bd_addr) {
+    auto p_eatt_impl = GetImplInstance();
+    if (p_eatt_impl) p_eatt_impl->eatt_l2cap_collision_ind(bd_addr);
+  }
+
   static void eatt_error_cb(uint16_t lcid, uint16_t reason) {
     auto p_eatt_impl = GetImplInstance();
     if (p_eatt_impl) p_eatt_impl->eatt_l2cap_error_cb(lcid, reason);
@@ -129,8 +135,8 @@
   pimpl_->eatt_impl_->connect(bd_addr);
 }
 
-void EattExtension::Disconnect(const RawAddress& bd_addr) {
-  pimpl_->eatt_impl_->disconnect(bd_addr);
+void EattExtension::Disconnect(const RawAddress& bd_addr, uint16_t cid) {
+  pimpl_->eatt_impl_->disconnect(bd_addr, cid);
 }
 
 void EattExtension::Reconfigure(const RawAddress& bd_addr, uint16_t cid,
diff --git a/system/stack/eatt/eatt.h b/system/stack/eatt/eatt.h
index 2eac631..828d17a 100644
--- a/system/stack/eatt/eatt.h
+++ b/system/stack/eatt/eatt.h
@@ -24,6 +24,7 @@
 
 #define EATT_MIN_MTU_MPS (64)
 #define EATT_DEFAULT_MTU (256)
+#define EATT_ALL_CIDS (0xFFFF)
 
 namespace bluetooth {
 namespace eatt {
@@ -129,8 +130,10 @@
    * Disconnect all EATT channels to peer device.
    *
    * @param bd_addr peer device address
+   * @param cid remote channel id (EATT_ALL_CIDS for all)
    */
-  virtual void Disconnect(const RawAddress& bd_addr);
+  virtual void Disconnect(const RawAddress& bd_addr,
+                          uint16_t cid = EATT_ALL_CIDS);
 
   /**
    * Reconfigure EATT channel for give CID
diff --git a/system/stack/eatt/eatt_impl.h b/system/stack/eatt/eatt_impl.h
index 0324808..0a2c2af 100644
--- a/system/stack/eatt/eatt_impl.h
+++ b/system/stack/eatt/eatt_impl.h
@@ -24,12 +24,16 @@
 #include "bind_helpers.h"
 #include "device/include/controller.h"
 #include "eatt.h"
+#include "gd/common/init_flags.h"
+#include "gd/common/strings.h"
+#include "internal_include/stack_config.h"
 #include "l2c_api.h"
 #include "osi/include/alarm.h"
 #include "osi/include/allocator.h"
 #include "stack/btm/btm_sec.h"
 #include "stack/gatt/gatt_int.h"
 #include "stack/include/bt_hdr.h"
+#include "stack/include/btu.h"  // do_in_main_thread
 #include "stack/l2cap/l2c_int.h"
 #include "types/raw_address.h"
 
@@ -47,9 +51,9 @@
   tGATT_TCB* eatt_tcb_;
 
   std::map<uint16_t, std::shared_ptr<EattChannel>> eatt_channels;
-
+  bool collision;
   eatt_device(const RawAddress& bd_addr, uint16_t mtu, uint16_t mps)
-      : rx_mtu_(mtu), rx_mps_(mps), eatt_tcb_(nullptr) {
+      : rx_mtu_(mtu), rx_mps_(mps), eatt_tcb_(nullptr), collision(false) {
     bda_ = bd_addr;
   }
 };
@@ -113,15 +117,33 @@
   void eatt_l2cap_connect_ind(const RawAddress& bda,
                               std::vector<uint16_t>& lcids, uint16_t psm,
                               uint16_t peer_mtu, uint8_t identifier) {
+    if (!stack_config_get_interface()
+             ->get_pts_connect_eatt_before_encryption() &&
+        !BTM_IsEncrypted(bda, BT_TRANSPORT_LE)) {
+      /* If Link is not encrypted, we shall not accept EATT channel creation. */
+      std::vector<uint16_t> empty;
+      uint16_t result = L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION;
+      if (BTM_IsLinkKeyKnown(bda, BT_TRANSPORT_LE)) {
+        result = L2CAP_LE_RESULT_INSUFFICIENT_ENCRYP;
+      }
+      LOG_ERROR("ACL to device %s is unencrypted.", bda.ToString().c_str());
+      L2CA_ConnectCreditBasedRsp(bda, identifier, empty, result, nullptr);
+      return;
+    }
+
     /* The assumption is that L2CAP layer already check parameters etc.
      * Get our capabilities and accept all the channels.
      */
     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();
@@ -154,6 +176,49 @@
 
       LOG(INFO) << __func__ << " Channel connected CID " << loghex(cid);
     }
+
+    /* Android let Central to create EATT (PTS initiates EATT). Some PTS test
+     * cases wants Android to do it anyway (Android initiates EATT).
+     */
+    if (stack_config_get_interface()
+            ->get_pts_eatt_peripheral_collision_support()) {
+      connect_eatt_wrap(eatt_dev);
+    }
+  }
+
+  void eatt_retry_after_collision_if_needed(eatt_device* eatt_dev) {
+    if (!eatt_dev->collision) {
+      LOG_DEBUG("No collision.");
+      return;
+    }
+    /* We are here, because remote device wanted to create channels when
+     * Android proceed its own EATT creation. How to handle it is described
+     * here: BT Core 5.3, Volume 3, Part G, 5.4
+     */
+    LOG_INFO(
+        "EATT collision detected. If we are Central we will retry right "
+        "away");
+
+    eatt_dev->collision = false;
+    uint8_t role = L2CA_GetBleConnRole(eatt_dev->bda_);
+    if (role == HCI_ROLE_CENTRAL) {
+      LOG_INFO("Retrying EATT setup due to previous collision for device %s",
+               eatt_dev->bda_.ToString().c_str());
+      connect_eatt_wrap(eatt_dev);
+    } else if (stack_config_get_interface()
+                   ->get_pts_eatt_peripheral_collision_support()) {
+      /* This is only for the PTS. Android does not setup EATT when is a
+       * peripheral.
+       */
+      bt_status_t status = do_in_main_thread_delayed(
+          FROM_HERE,
+          base::BindOnce(&eatt_impl::connect_eatt_wrap, base::Unretained(this),
+                         std::move(eatt_dev)),
+          base::TimeDelta::FromMilliseconds(500));
+
+      LOG_INFO("Scheduled peripheral connect eatt for device with status: %d",
+               (int)status);
+    }
   }
 
   void eatt_l2cap_connect_cfm(const RawAddress& bda, uint16_t lcid,
@@ -188,6 +253,7 @@
     eatt_dev->eatt_tcb_->eatt++;
 
     LOG(INFO) << __func__ << " Channel connected CID " << loghex(lcid);
+    eatt_retry_after_collision_if_needed(eatt_dev);
   }
 
   void eatt_l2cap_reconfig_completed(const RawAddress& bda, uint16_t lcid,
@@ -216,6 +282,17 @@
     channel->EattChannelSetState(EattChannelState::EATT_CHANNEL_OPENED);
   }
 
+  void eatt_l2cap_collision_ind(const RawAddress& bda) {
+    eatt_device* eatt_dev = find_device_by_address(bda);
+    if (!eatt_dev) {
+      LOG_ERROR("Device %s not available anymore:", bda.ToString().c_str());
+      return;
+    }
+    /* Remote wanted to setup channels as well. Let's retry remote's request
+     * when we are done with ours.*/
+    eatt_dev->collision = true;
+  }
+
   void eatt_l2cap_error_cb(uint16_t lcid, uint16_t reason) {
     LOG(INFO) << __func__ << " cid: " << loghex(lcid) << " reason "
               << loghex(reason);
@@ -245,6 +322,8 @@
                    << static_cast<uint8_t>(channel->state_);
         break;
     }
+
+    eatt_retry_after_collision_if_needed(eatt_dev);
   }
 
   void eatt_l2cap_disconnect_ind(uint16_t lcid, bool please_confirm) {
@@ -296,7 +375,22 @@
     return eatt_dev;
   }
 
-  void connect_eatt(eatt_device* eatt_dev) {
+  void connect_eatt_wrap(eatt_device* eatt_dev) {
+    if (stack_config_get_interface()
+            ->get_pts_eatt_peripheral_collision_support()) {
+      /* For PTS case, lets assume we support only 5 channels */
+      LOG_INFO("Number of existing channels %d",
+               (int)eatt_dev->eatt_channels.size());
+      connect_eatt(eatt_dev, L2CAP_CREDIT_BASED_MAX_CIDS -
+                                 (int)eatt_dev->eatt_channels.size());
+      return;
+    }
+
+    connect_eatt(eatt_dev);
+  }
+
+  void connect_eatt(eatt_device* eatt_dev,
+                    uint8_t num_of_channels = L2CAP_CREDIT_BASED_MAX_CIDS) {
     /* Let us use maximum possible mps */
     if (eatt_dev->rx_mps_ == EATT_MIN_MTU_MPS)
       eatt_dev->rx_mps_ = controller_get_interface()->get_acl_data_size_ble();
@@ -305,8 +399,12 @@
         .mtu = eatt_dev->rx_mtu_,
         .mps = eatt_dev->rx_mps_,
         .credits = L2CAP_LE_CREDIT_DEFAULT,
+        .number_of_channels = num_of_channels,
     };
 
+    LOG_INFO("Connecting device %s, cnt count %d",
+             eatt_dev->bda_.ToString().c_str(), num_of_channels);
+
     /* Warning! CIDs in Android are unique across the ACL connections */
     std::vector<uint16_t> connecting_cids =
         L2CA_ConnectCreditBasedReq(psm_, eatt_dev->bda_, &local_coc_cfg);
@@ -593,13 +691,13 @@
       return;
     }
 
-    connect_eatt(eatt_dev);
+    connect_eatt_wrap(eatt_dev);
   }
 
   void disconnect_channel(uint16_t cid) { L2CA_DisconnectReq(cid); }
 
-  void disconnect(const RawAddress& bd_addr) {
-    LOG(INFO) << __func__ << " " << bd_addr;
+  void disconnect(const RawAddress& bd_addr, uint16_t cid) {
+    LOG_INFO(" Device: %s, cid: 0x%04x", bd_addr.ToString().c_str(), cid);
 
     eatt_device* eatt_dev = find_device_by_address(bd_addr);
     if (!eatt_dev) {
@@ -613,6 +711,19 @@
       return;
     }
 
+    if (cid != EATT_ALL_CIDS) {
+      auto chan = find_channel_by_cid(cid);
+      if (!chan) {
+        LOG_WARN("Cid %d not found for device %s", cid,
+                 bd_addr.ToString().c_str());
+        return;
+      }
+      LOG_INFO("Disconnecting cid %d", cid);
+      disconnect_channel(cid);
+      remove_channel_by_cid(cid);
+      return;
+    }
+
     auto iter = eatt_dev->eatt_channels.begin();
     while (iter != eatt_dev->eatt_channels.end()) {
       uint16_t cid = iter->first;
@@ -624,6 +735,7 @@
     }
     eatt_dev->eatt_tcb_->eatt = 0;
     eatt_dev->eatt_tcb_ = nullptr;
+    eatt_dev->collision = false;
   }
 
   void connect(const RawAddress& bd_addr) {
@@ -635,8 +747,8 @@
       return;
     }
 
-    LOG(INFO) << __func__ << " device " << bd_addr << " role"
-              << (role == HCI_ROLE_CENTRAL ? "central" : "peripheral");
+    LOG_INFO("Device %s, role %s", bd_addr.ToString().c_str(),
+             (role == HCI_ROLE_CENTRAL ? "central" : "peripheral"));
 
     if (eatt_dev) {
       /* We are reconnecting device we know that support EATT.
@@ -650,16 +762,30 @@
         return;
       }
 
-      connect_eatt(eatt_dev);
+      connect_eatt_wrap(eatt_dev);
       return;
     }
 
-    /* For new device, first read GATT server supported features. */
+    /* This is needed for L2CAP test cases */
+    if (stack_config_get_interface()->get_pts_connect_eatt_unconditionally()) {
+      /* For PTS just start connecting EATT right away */
+      eatt_device* eatt_dev = add_eatt_device(bd_addr);
+      connect_eatt_wrap(eatt_dev);
+      return;
+    }
+
+    if (gatt_profile_get_eatt_support(bd_addr)) {
+      LOG_DEBUG("Eatt is supported for device %s", bd_addr.ToString().c_str());
+      supported_features_cb(role, bd_addr, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK);
+      return;
+    }
+
+    /* If we don't know yet, read GATT server supported features. */
     if (gatt_cl_read_sr_supp_feat_req(
             bd_addr, base::BindOnce(&eatt_impl::supported_features_cb,
                                     base::Unretained(this), role)) == false) {
-      LOG(INFO) << __func__ << "Eatt is not supported. Checked for device "
-                << bd_addr;
+      LOG_INFO("Read server supported features failed for device %s",
+               bd_addr.ToString().c_str());
     }
   }
 
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..3158f74 100644
--- a/system/stack/gatt/gatt_api.cc
+++ b/system/stack/gatt/gatt_api.cc
@@ -32,6 +32,7 @@
 #include "bt_target.h"
 #include "device/include/controller.h"
 #include "gatt_int.h"
+#include "internal_include/stack_config.h"
 #include "l2c_api.h"
 #include "main/shim/dumpsys.h"
 #include "osi/include/allocator.h"
@@ -468,8 +469,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);
@@ -480,6 +483,39 @@
   return cmd_status;
 }
 
+#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_NOTIF == TRUE)
+static tGATT_STATUS GATTS_HandleMultileValueNotification(
+    tGATT_TCB* p_tcb, std::vector<tGATT_VALUE> gatt_notif_vector) {
+  LOG(INFO) << __func__;
+
+  uint16_t cid = gatt_tcb_get_att_cid(*p_tcb, true /* eatt support */);
+  uint16_t payload_size = gatt_tcb_get_payload_size_tx(*p_tcb, cid);
+
+  /* TODO Handle too big packet size here. Not needed now for testing. */
+  /* Just build the message. */
+  BT_HDR* p_buf =
+      (BT_HDR*)osi_malloc(sizeof(BT_HDR) + payload_size + L2CAP_MIN_OFFSET);
+
+  uint8_t* p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
+  UINT8_TO_STREAM(p, GATT_HANDLE_MULTI_VALUE_NOTIF);
+  p_buf->offset = L2CAP_MIN_OFFSET;
+  p_buf->len = 1;
+  for (auto notif : gatt_notif_vector) {
+    LOG(INFO) << __func__ << "Adding handle: " << loghex(notif.handle)
+              << "val len: " << +notif.len;
+    UINT16_TO_STREAM(p, notif.handle);
+    p_buf->len += 2;
+    UINT16_TO_STREAM(p, notif.len);
+    p_buf->len += 2;
+    ARRAY_TO_STREAM(p, notif.value, notif.len);
+    p_buf->len += notif.len;
+  }
+
+  LOG(INFO) << __func__ << "Total len: " << +p_buf->len;
+
+  return attp_send_sr_msg(*p_tcb, cid, p_buf);
+}
+#endif
 /*******************************************************************************
  *
  * Function         GATTS_HandleValueNotification
@@ -503,6 +539,11 @@
   uint8_t tcb_idx = GATT_GET_TCB_IDX(conn_id);
   tGATT_REG* p_reg = gatt_get_regcb(gatt_if);
   tGATT_TCB* p_tcb = gatt_get_tcb_by_idx(tcb_idx);
+#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_NOTIF == TRUE)
+  static uint8_t cached_tcb_idx = 0xFF;
+  static std::vector<tGATT_VALUE> gatt_notif_vector(2);
+  tGATT_VALUE* p_gatt_notif;
+#endif
 
   VLOG(1) << __func__;
 
@@ -515,6 +556,43 @@
     return GATT_ILLEGAL_PARAMETER;
   }
 
+#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_NOTIF == TRUE)
+  /* Upper tester for Multiple Value length notifications */
+  if (stack_config_get_interface()->get_pts_force_eatt_for_notifications() &&
+      gatt_sr_is_cl_multi_variable_len_notif_supported(*p_tcb)) {
+    if (cached_tcb_idx == 0xFF) {
+      LOG(INFO) << __func__ << " Storing first notification";
+      p_gatt_notif = &gatt_notif_vector[0];
+
+      p_gatt_notif->handle = attr_handle;
+      p_gatt_notif->len = val_len;
+      std::copy(p_val, p_val + val_len, p_gatt_notif->value);
+
+      notif.auth_req = GATT_AUTH_REQ_NONE;
+
+      cached_tcb_idx = tcb_idx;
+      return GATT_SUCCESS;
+    }
+
+    if (cached_tcb_idx == tcb_idx) {
+      LOG(INFO) << __func__ << " Storing second notification";
+      cached_tcb_idx = 0xFF;
+      p_gatt_notif = &gatt_notif_vector[1];
+
+      p_gatt_notif->handle = attr_handle;
+      p_gatt_notif->len = val_len;
+      std::copy(p_val, p_val + val_len, p_gatt_notif->value);
+
+      notif.auth_req = GATT_AUTH_REQ_NONE;
+
+      return GATTS_HandleMultileValueNotification(p_tcb, gatt_notif_vector);
+    }
+
+    LOG(ERROR) << __func__ << "PTS Mode: Invalid tcb_idx: " << tcb_idx
+               << " cached_tcb_idx: " << cached_tcb_idx;
+  }
+#endif
+
   memset(&notif, 0, sizeof(notif));
   notif.handle = attr_handle;
   notif.len = val_len;
@@ -526,13 +604,15 @@
   gatt_sr_msg.attr_value = notif;
 
   uint16_t cid = gatt_tcb_get_att_cid(*p_tcb, p_reg->eatt_support);
+  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);
 
-  BT_HDR* p_buf =
-      attp_build_sr_msg(*p_tcb, GATT_HANDLE_VALUE_NOTIF, &gatt_sr_msg);
   if (p_buf != NULL) {
     cmd_sent = attp_send_sr_msg(*p_tcb, cid, p_buf);
-  } else
+  } else {
     cmd_sent = GATT_NO_RESOURCES;
+  }
   return cmd_sent;
 }
 
@@ -740,6 +820,10 @@
   uint8_t tcb_idx = GATT_GET_TCB_IDX(conn_id);
   tGATT_TCB* p_tcb = gatt_get_tcb_by_idx(tcb_idx);
   tGATT_REG* p_reg = gatt_get_regcb(gatt_if);
+#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_READ == TRUE)
+  static uint16_t cached_read_handle;
+  static int cached_tcb_idx = -1;
+#endif
 
   VLOG(1) << __func__ << ": conn_id=" << loghex(conn_id)
           << ", type=" << loghex(type);
@@ -783,6 +867,34 @@
       break;
     }
     case GATT_READ_BY_HANDLE:
+#if (GATT_UPPER_TESTER_MULT_VARIABLE_LENGTH_READ == TRUE)
+      LOG_INFO("Upper tester: Handle read 0x%04x", p_read->by_handle.handle);
+      /* This is upper tester for the  Multi Read stuff as this is mandatory for
+       * EATT, even Android is not making use of this operation :/ */
+      if (cached_tcb_idx < 0) {
+        cached_tcb_idx = tcb_idx;
+        LOG_INFO("Upper tester: Read multiple  - first read");
+        cached_read_handle = p_read->by_handle.handle;
+      } else if (cached_tcb_idx == tcb_idx) {
+        LOG_INFO("Upper tester: Read multiple  - second read");
+        cached_tcb_idx = -1;
+        tGATT_READ_MULTI* p_read_multi =
+            (tGATT_READ_MULTI*)osi_malloc(sizeof(tGATT_READ_MULTI));
+        p_read_multi->num_handles = 2;
+        p_read_multi->handles[0] = cached_read_handle;
+        p_read_multi->handles[1] = p_read->by_handle.handle;
+        p_read_multi->variable_len = true;
+
+        p_clcb->s_handle = 0;
+        p_clcb->op_subtype = GATT_READ_MULTIPLE_VAR_LEN;
+        p_clcb->p_attr_buf = (uint8_t*)p_read_multi;
+        p_clcb->cid = gatt_tcb_get_att_cid(*p_tcb, true /* eatt support */);
+
+        break;
+      }
+
+      FALLTHROUGH_INTENDED;
+#endif
     case GATT_READ_PARTIAL:
       p_clcb->uuid = Uuid::kEmpty;
       p_clcb->s_handle = p_read->by_handle.handle;
diff --git a/system/stack/gatt/gatt_attr.cc b/system/stack/gatt/gatt_attr.cc
index ccb9645..e67d890 100644
--- a/system/stack/gatt/gatt_attr.cc
+++ b/system/stack/gatt/gatt_attr.cc
@@ -81,6 +81,9 @@
 
 static bool gatt_sr_is_robust_caching_enabled();
 
+static bool read_sr_supported_feat_req(
+    uint16_t conn_id, base::OnceCallback<void(const RawAddress&, uint8_t)> cb);
+
 static tGATT_STATUS gatt_sr_read_db_hash(uint16_t conn_id,
                                          tGATT_VALUE* p_value);
 static tGATT_STATUS gatt_sr_read_cl_supp_feat(uint16_t conn_id,
@@ -567,10 +570,15 @@
           << " status: " << status
           << " conn id: " << loghex(static_cast<uint8_t>(conn_id));
 
-  if (op != GATTC_OPTYPE_READ) return;
+  if (op != GATTC_OPTYPE_READ && op != GATTC_OPTYPE_WRITE) {
+    LOG_DEBUG("Not interested in opcode %d", op);
+    return;
+  }
 
   if (iter == OngoingOps.end()) {
-    LOG(ERROR) << __func__ << " Unexpected read complete";
+    /* If OngoingOps is empty it means we are not interested in the result here.
+     */
+    LOG_DEBUG("Unexpected read complete");
     return;
   }
 
@@ -578,6 +586,20 @@
   uint16_t cl_op_uuid = operation_callback_data->op_uuid;
   operation_callback_data->op_uuid = 0;
 
+  if (op == GATTC_OPTYPE_WRITE) {
+    if (cl_op_uuid == GATT_UUID_GATT_SRV_CHGD) {
+      LOG_DEBUG("Write response from Service Changed CCC");
+      OngoingOps.erase(iter);
+      /* Read server supported features here supported */
+      read_sr_supported_feat_req(
+          conn_id, base::BindOnce([](const RawAddress& bdaddr,
+                                     uint8_t support) { return; }));
+    } else {
+      LOG_DEBUG("Not interested in that write response");
+    }
+    return;
+  }
+
   uint8_t* pp = p_data->att_value.value;
 
   VLOG(1) << __func__ << " cl_op_uuid " << loghex(cl_op_uuid);
@@ -666,6 +688,13 @@
       ccc_value.len = 2;
       ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION;
       GATTC_Write(p_clcb->conn_id, GATT_WRITE, &ccc_value);
+
+      gatt_op_cb_data cb_data;
+      cb_data.cb = base::BindOnce(
+          [](const RawAddress& bdaddr, uint8_t support) { return; });
+      cb_data.op_uuid = GATT_UUID_GATT_SRV_CHGD;
+      OngoingOps[p_clcb->conn_id] = std::move(cb_data);
+
       break;
     }
   }
@@ -723,6 +752,30 @@
     bluetooth::eatt::EattExtension::AddFromStorage(tcb.peer_bda);
 }
 
+static bool read_sr_supported_feat_req(
+    uint16_t conn_id, base::OnceCallback<void(const RawAddress&, uint8_t)> cb) {
+  tGATT_READ_PARAM param = {};
+
+  param.service.s_handle = 1;
+  param.service.e_handle = 0xFFFF;
+  param.service.auth_req = 0;
+
+  param.service.uuid = bluetooth::Uuid::From16Bit(GATT_UUID_SERVER_SUP_FEAT);
+
+  if (GATTC_Read(conn_id, GATT_READ_BY_TYPE, &param) != GATT_SUCCESS) {
+    LOG_ERROR("Read GATT Support features GATT_Read Failed");
+    return false;
+  }
+
+  gatt_op_cb_data cb_data;
+
+  cb_data.cb = std::move(cb);
+  cb_data.op_uuid = GATT_UUID_SERVER_SUP_FEAT;
+  OngoingOps[conn_id] = std::move(cb_data);
+
+  return true;
+}
+
 /*******************************************************************************
  *
  * Function         gatt_cl_read_sr_supp_feat_req
@@ -736,7 +789,6 @@
     const RawAddress& peer_bda,
     base::OnceCallback<void(const RawAddress&, uint8_t)> cb) {
   tGATT_PROFILE_CLCB* p_clcb;
-  tGATT_READ_PARAM param;
   uint16_t conn_id;
 
   if (!cb) return false;
@@ -765,25 +817,7 @@
     return false;
   }
 
-  memset(&param, 0, sizeof(tGATT_READ_PARAM));
-
-  param.service.s_handle = 1;
-  param.service.e_handle = 0xFFFF;
-  param.service.auth_req = 0;
-
-  param.service.uuid = bluetooth::Uuid::From16Bit(GATT_UUID_SERVER_SUP_FEAT);
-
-  if (GATTC_Read(conn_id, GATT_READ_BY_TYPE, &param) != GATT_SUCCESS) {
-    LOG(ERROR) << __func__ << " Read GATT Support features GATT_Read Failed";
-    return false;
-  }
-
-  gatt_op_cb_data cb_data;
-  cb_data.cb = std::move(cb);
-  cb_data.op_uuid = GATT_UUID_SERVER_SUP_FEAT;
-  OngoingOps[conn_id] = std::move(cb_data);
-
-  return true;
+  return read_sr_supported_feat_req(conn_id, std::move(cb));
 }
 
 /*******************************************************************************
@@ -822,7 +856,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();
 }
 
 /*******************************************************************************
@@ -842,6 +876,20 @@
 
 /*******************************************************************************
  *
+ * Function         gatt_sr_is_cl_multi_variable_len_notif_supported
+ *
+ * Description      Check if Multiple Variable Length Notifications
+ *                  supported for the connection
+ *
+ * Returns          true if enabled by client side, otherwise false
+ *
+ ******************************************************************************/
+bool gatt_sr_is_cl_multi_variable_len_notif_supported(tGATT_TCB& tcb) {
+  return (tcb.cl_supp_feat & BLE_GATT_CL_SUP_FEAT_MULTI_NOTIF_BITMASK);
+}
+
+/*******************************************************************************
+ *
  * Function         gatt_sr_is_cl_change_aware
  *
  * Description      Check if the connection is change-aware
diff --git a/system/stack/gatt/gatt_cl.cc b/system/stack/gatt/gatt_cl.cc
index 89218e4..c5e5c76 100644
--- a/system/stack/gatt/gatt_cl.cc
+++ b/system/stack/gatt/gatt_cl.cc
@@ -200,6 +200,11 @@
       memcpy(&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
       break;
 
+    case GATT_READ_MULTIPLE_VAR_LEN:
+      op_code = GATT_REQ_READ_MULTI_VAR;
+      memcpy(&msg.read_multi, p_clcb->p_attr_buf, sizeof(tGATT_READ_MULTI));
+      break;
+
     case GATT_READ_INC_SRV_UUID128:
       op_code = GATT_REQ_READ;
       msg.handle = p_clcb->s_handle;
diff --git a/system/stack/gatt/gatt_int.h b/system/stack/gatt/gatt_int.h
index 0641d4c..1e9e7e1 100644
--- a/system/stack/gatt/gatt_int.h
+++ b/system/stack/gatt/gatt_int.h
@@ -485,6 +485,7 @@
 extern bool gatt_cl_read_sr_supp_feat_req(
     const RawAddress& peer_bda,
     base::OnceCallback<void(const RawAddress&, uint8_t)> cb);
+extern bool gatt_sr_is_cl_multi_variable_len_notif_supported(tGATT_TCB& tcb);
 
 extern bool gatt_sr_is_cl_change_aware(tGATT_TCB& tcb);
 extern void gatt_sr_init_cl_status(tGATT_TCB& tcb);
@@ -495,7 +496,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_main.cc b/system/stack/gatt/gatt_main.cc
index df6a056..6fef443 100644
--- a/system/stack/gatt/gatt_main.cc
+++ b/system/stack/gatt/gatt_main.cc
@@ -27,6 +27,7 @@
 #include "btif/include/btif_storage.h"
 #include "connection_manager.h"
 #include "device/include/interop.h"
+#include "internal_include/stack_config.h"
 #include "l2c_api.h"
 #include "osi/include/allocator.h"
 #include "osi/include/osi.h"
@@ -82,6 +83,7 @@
                                           gatt_on_l2cap_error,
                                           NULL,
                                           NULL,
+                                          NULL,
                                           NULL};
 
 tGATT_CB gatt_cb;
@@ -510,7 +512,10 @@
     }
   }
 
-  EattExtension::GetInstance()->Connect(bd_addr);
+  if (stack_config_get_interface()->get_pts_connect_eatt_before_encryption()) {
+    LOG_INFO(" Start EATT before encryption ");
+    EattExtension::GetInstance()->Connect(bd_addr);
+  }
 }
 
 /** This function is called to process the congestion callback from lcb */
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 1311500..64b2f7a 100644
--- a/system/stack/gatt/gatt_utils.cc
+++ b/system/stack/gatt/gatt_utils.cc
@@ -664,8 +664,16 @@
     }
   }
 
-  LOG(WARNING) << __func__ << " disconnecting...";
-  gatt_disconnect(p_clcb->p_tcb);
+  auto eatt_channel = EattExtension::GetInstance()->FindEattChannelByCid(
+      p_clcb->p_tcb->peer_bda, p_clcb->cid);
+  if (eatt_channel) {
+    LOG_WARN("disconnecting EATT cid: %d", p_clcb->cid);
+    EattExtension::GetInstance()->Disconnect(p_clcb->p_tcb->peer_bda,
+                                             p_clcb->cid);
+  } else {
+    LOG_WARN("disconnecting GATT...");
+    gatt_disconnect(p_clcb->p_tcb);
+  }
 }
 
 extern void gatts_proc_srv_chg_ind_ack(tGATT_TCB tcb);
@@ -813,7 +821,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
diff --git a/system/stack/hid/hidd_conn.cc b/system/stack/hid/hidd_conn.cc
index 561c993..da78d40 100644
--- a/system/stack/hid/hidd_conn.cc
+++ b/system/stack/hid/hidd_conn.cc
@@ -58,6 +58,7 @@
                                               hidd_on_l2cap_error,
                                               NULL,
                                               NULL,
+                                              NULL,
                                               NULL};
 
 /*******************************************************************************
diff --git a/system/stack/hid/hidh_conn.cc b/system/stack/hid/hidh_conn.cc
index ef0cc3b..1730a8e 100644
--- a/system/stack/hid/hidh_conn.cc
+++ b/system/stack/hid/hidh_conn.cc
@@ -78,6 +78,7 @@
     .pL2CA_CreditBasedConnectInd_Cb = nullptr,
     .pL2CA_CreditBasedConnectCfm_Cb = nullptr,
     .pL2CA_CreditBasedReconfigCompleted_Cb = nullptr,
+    .pL2CA_CreditBasedCollisionInd_Cb = nullptr,
 };
 static void hidh_try_repage(uint8_t dhandle);
 
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/include/btm_api_types.h b/system/stack/include/btm_api_types.h
index 6fcfa6e..3e68f46 100644
--- a/system/stack/include/btm_api_types.h
+++ b/system/stack/include/btm_api_types.h
@@ -602,6 +602,8 @@
 /* KEY update event */
 #define BTM_LE_KEY_EVT (BTM_LE_LAST_FROM_SMP + 1)
 #define BTM_LE_CONSENT_REQ_EVT SMP_CONSENT_REQ_EVT
+/* Identity address associate event */
+#define BTM_LE_ADDR_ASSOC_EVT SMP_LE_ADDR_ASSOC_EVT
 typedef uint8_t tBTM_LE_EVT;
 
 enum : uint8_t {
diff --git a/system/stack/include/btm_ble_api_types.h b/system/stack/include/btm_ble_api_types.h
index afdb5e1..8de1dcc 100644
--- a/system/stack/include/btm_ble_api_types.h
+++ b/system/stack/include/btm_ble_api_types.h
@@ -565,6 +565,7 @@
   tSMP_OOB_DATA_TYPE req_oob_type;
   tBTM_LE_KEY key;
   tSMP_LOC_OOB_DATA local_oob_data;
+  RawAddress id_addr;
 } tBTM_LE_EVT_DATA;
 
 /* Simple Pairing Events.  Called by the stack when Simple Pairing related
diff --git a/system/stack/include/gatt_api.h b/system/stack/include/gatt_api.h
index b38e943..1bb3d07 100644
--- a/system/stack/include/gatt_api.h
+++ b/system/stack/include/gatt_api.h
@@ -518,6 +518,7 @@
   GATT_READ_BY_TYPE = 1,
   GATT_READ_BY_HANDLE,
   GATT_READ_MULTIPLE,
+  GATT_READ_MULTIPLE_VAR_LEN,
   GATT_READ_CHAR_VALUE,
   GATT_READ_PARTIAL,
   GATT_READ_MAX
diff --git a/system/stack/include/l2c_api.h b/system/stack/include/l2c_api.h
index eb5ff83..92cf17f 100644
--- a/system/stack/include/l2c_api.h
+++ b/system/stack/include/l2c_api.h
@@ -181,6 +181,9 @@
 static_assert(L2CAP_LE_CREDIT_THRESHOLD < L2CAP_LE_CREDIT_DEFAULT,
               "Threshold must be smaller than default credits");
 
+// Max number of CIDs in the L2CAP CREDIT BASED CONNECTION REQUEST
+constexpr uint16_t L2CAP_CREDIT_BASED_MAX_CIDS = 5;
+
 /* Define a structure to hold the configuration parameter for LE L2CAP
  * connection oriented channels.
  */
@@ -189,6 +192,7 @@
   uint16_t mtu = 100;
   uint16_t mps = 100;
   uint16_t credits = L2CAP_LE_CREDIT_DEFAULT;
+  uint8_t number_of_channels = L2CAP_CREDIT_BASED_MAX_CIDS;
 };
 
 /*********************************
@@ -283,6 +287,14 @@
                                                 uint16_t psm, uint16_t peer_mtu,
                                                 uint8_t identifier);
 
+/* Collision Indication callback prototype. Used to notify upper layer that
+ * remote devices sent Credit Based Connection Request but it was rejected due
+ * to ongoing local request. Upper layer might want to sent another request when
+ * local request is completed. Parameters are:
+ *              BD Address of remote
+ */
+typedef void(tL2CA_CREDIT_BASED_COLLISION_IND_CB)(const RawAddress& bdaddr);
+
 /* Credit based connection confirmation callback prototype. Parameters are
  *              BD Address of remote
  *              Connected Local CIDs
@@ -324,6 +336,7 @@
   tL2CA_CREDIT_BASED_CONNECT_CFM_CB* pL2CA_CreditBasedConnectCfm_Cb;
   tL2CA_CREDIT_BASED_RECONFIG_COMPLETED_CB*
       pL2CA_CreditBasedReconfigCompleted_Cb;
+  tL2CA_CREDIT_BASED_COLLISION_IND_CB* pL2CA_CreditBasedCollisionInd_Cb;
 } tL2CAP_APPL_INFO;
 
 /* Define the structure that applications use to create or accept
diff --git a/system/stack/include/smp_api_types.h b/system/stack/include/smp_api_types.h
index 42b61be..47e4078 100644
--- a/system/stack/include/smp_api_types.h
+++ b/system/stack/include/smp_api_types.h
@@ -98,7 +98,8 @@
   SMP_UNUSED11 = 11,
   SMP_BR_KEYS_REQ_EVT = 12, /* SMP over BR keys request event */
   SMP_UNUSED13 = 13,
-  SMP_CONSENT_REQ_EVT = 14, /* Consent request event */
+  SMP_CONSENT_REQ_EVT = 14,   /* Consent request event */
+  SMP_LE_ADDR_ASSOC_EVT = 15, /* Identity address association event */
 } tSMP_EVT;
 
 /* pairing failure reason code */
@@ -297,6 +298,7 @@
   tSMP_CMPL cmplt;
   tSMP_OOB_DATA_TYPE req_oob_type;
   tSMP_LOC_OOB_DATA loc_oob_data;
+  RawAddress id_addr;
 } tSMP_EVT_DATA;
 
 /* AES Encryption output */
diff --git a/system/stack/l2cap/l2c_api.cc b/system/stack/l2cap/l2c_api.cc
index 48af5d1..6eedcbb 100644
--- a/system/stack/l2cap/l2c_api.cc
+++ b/system/stack/l2cap/l2c_api.cc
@@ -782,7 +782,13 @@
 
   tL2C_CCB* p_ccb_primary;
 
-  for (int i = 0; i < 5; i++) {
+  /* Make sure user set proper value for number of cids */
+  if (p_cfg->number_of_channels > L2CAP_CREDIT_BASED_MAX_CIDS ||
+      p_cfg->number_of_channels == 0) {
+    p_cfg->number_of_channels = L2CAP_CREDIT_BASED_MAX_CIDS;
+  }
+
+  for (int i = 0; i < p_cfg->number_of_channels; i++) {
     /* Allocate a channel control block */
     tL2C_CCB* p_ccb = l2cu_allocate_ccb(p_lcb, 0);
     if (p_ccb == NULL) {
diff --git a/system/stack/l2cap/l2c_ble.cc b/system/stack/l2cap/l2c_ble.cc
index 5ab208c..67ccd01 100755
--- a/system/stack/l2cap/l2c_ble.cc
+++ b/system/stack/l2cap/l2c_ble.cc
@@ -541,15 +541,6 @@
           "num_of_channels = %d",
           mtu, mps, initial_credit, num_of_channels);
 
-      if (p_lcb->pending_ecoc_conn_cnt > 0) {
-        LOG_WARN("L2CAP - L2CAP_CMD_CREDIT_BASED_CONN_REQ collision:");
-        l2cu_reject_credit_based_conn_req(p_lcb, id, num_of_channels,
-                                          L2CAP_LE_RESULT_NO_RESOURCES);
-        return;
-      }
-
-      p_lcb->pending_ecoc_conn_cnt = num_of_channels;
-
       /* Check PSM Support */
       p_rcb = l2cu_find_ble_rcb_by_psm(con_info.psm);
       if (p_rcb == NULL) {
@@ -559,6 +550,19 @@
         return;
       }
 
+      if (p_lcb->pending_ecoc_conn_cnt > 0) {
+        LOG_WARN("L2CAP - L2CAP_CMD_CREDIT_BASED_CONN_REQ collision:");
+        if (p_rcb->api.pL2CA_CreditBasedCollisionInd_Cb &&
+            con_info.psm == BT_PSM_EATT) {
+          (*p_rcb->api.pL2CA_CreditBasedCollisionInd_Cb)(p_lcb->remote_bd_addr);
+        }
+        l2cu_reject_credit_based_conn_req(p_lcb, id, num_of_channels,
+                                          L2CAP_LE_RESULT_NO_RESOURCES);
+        return;
+      }
+
+      p_lcb->pending_ecoc_conn_cnt = num_of_channels;
+
       if (!p_rcb->api.pL2CA_CreditBasedConnectInd_Cb) {
         LOG_WARN("L2CAP - rcvd conn req for outgoing-only connection PSM: %d",
                  con_info.psm);
@@ -669,6 +673,7 @@
        * all the channels has been rejected
        */
       if (con_info.l2cap_result == L2CAP_LE_RESULT_NO_PSM ||
+          con_info.l2cap_result == L2CAP_LE_RESULT_NO_RESOURCES ||
           con_info.l2cap_result ==
               L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION ||
           con_info.l2cap_result == L2CAP_LE_RESULT_INSUFFICIENT_ENCRYP ||
@@ -715,22 +720,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/l2cap/l2c_csm.cc b/system/stack/l2cap/l2c_csm.cc
index 3646964..79a35f6 100755
--- a/system/stack/l2cap/l2c_csm.cc
+++ b/system/stack/l2cap/l2c_csm.cc
@@ -801,6 +801,7 @@
 static void l2c_csm_w4_l2ca_connect_rsp(tL2C_CCB* p_ccb, tL2CEVT event,
                                         void* p_data) {
   tL2C_CONN_INFO* p_ci;
+  tL2C_LCB* p_lcb = p_ccb->p_lcb;
   tL2CA_DISCONNECT_IND_CB* disconnect_ind =
       p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
   uint16_t local_cid = p_ccb->local_cid;
@@ -818,7 +819,7 @@
 
     case L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP:
       p_ci = (tL2C_CONN_INFO*)p_data;
-      if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE) {
+      if ((p_lcb == nullptr) || (p_lcb && p_lcb->transport != BT_TRANSPORT_LE)) {
         LOG_WARN("LE link doesn't exist");
         return;
       }
@@ -826,14 +827,14 @@
                                            p_ci->l2cap_result);
       alarm_cancel(p_ccb->l2c_ccb_timer);
 
-      for (int i = 0; i < p_ccb->p_lcb->pending_ecoc_conn_cnt; i++) {
-        uint16_t cid = p_ccb->p_lcb->pending_ecoc_connection_cids[i];
+      for (int i = 0; i < p_lcb->pending_ecoc_conn_cnt; i++) {
+        uint16_t cid = p_lcb->pending_ecoc_connection_cids[i];
         if (cid == 0) {
             LOG_WARN("pending_ecoc_connection_cids[%d] is %d", i, cid);
             continue;
         }
 
-        tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_ccb->p_lcb, cid);
+        tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
         if (temp_p_ccb) {
           auto it = std::find(p_ci->lcids.begin(), p_ci->lcids.end(), cid);
           if (it != p_ci->lcids.end()) {
@@ -846,8 +847,8 @@
             LOG_WARN("temp_p_ccb is NULL, pending_ecoc_connection_cids[%d] is %d", i, cid);
         }
       }
-      p_ccb->p_lcb->pending_ecoc_conn_cnt = 0;
-      memset(p_ccb->p_lcb->pending_ecoc_connection_cids, 0,
+      p_lcb->pending_ecoc_conn_cnt = 0;
+      memset(p_lcb->pending_ecoc_connection_cids, 0,
              L2CAP_CREDIT_BASED_MAX_CIDS);
 
       break;
@@ -887,19 +888,19 @@
     case L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP_NEG:
       p_ci = (tL2C_CONN_INFO*)p_data;
       alarm_cancel(p_ccb->l2c_ccb_timer);
-      if (p_ccb->p_lcb != nullptr) {
-        if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE) {
+      if (p_lcb != nullptr) {
+        if (p_lcb->transport == BT_TRANSPORT_LE) {
           l2cu_send_peer_credit_based_conn_res(p_ccb, p_ci->lcids,
                                                p_ci->l2cap_result);
         }
-        for (int i = 0; i < p_ccb->p_lcb->pending_ecoc_conn_cnt; i++) {
-          uint16_t cid = p_ccb->p_lcb->pending_ecoc_connection_cids[i];
-          tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_ccb->p_lcb, cid);
+        for (int i = 0; i < p_lcb->pending_ecoc_conn_cnt; i++) {
+          uint16_t cid = p_lcb->pending_ecoc_connection_cids[i];
+          tL2C_CCB* temp_p_ccb = l2cu_find_ccb_by_cid(p_lcb, cid);
           l2cu_release_ccb(temp_p_ccb);
         }
 
-        p_ccb->p_lcb->pending_ecoc_conn_cnt = 0;
-        memset(p_ccb->p_lcb->pending_ecoc_connection_cids, 0,
+        p_lcb->pending_ecoc_conn_cnt = 0;
+        memset(p_lcb->pending_ecoc_connection_cids, 0,
                L2CAP_CREDIT_BASED_MAX_CIDS);
       }
       break;
@@ -1619,6 +1620,10 @@
     case L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP: /* Upper layer credit based
                                                   connect response */
       return ("SEND_CREDIT_BASED_CONNECT_RSP");
+    case L2CEVT_L2CA_CREDIT_BASED_CONNECT_RSP_NEG: /* Upper layer credit based
+                                                      connect response
+                                                      (failed)*/
+      return ("SEND_CREDIT_BASED_CONNECT_RSP_NEG");
     case L2CEVT_L2CA_CREDIT_BASED_RECONFIG_REQ: /* Upper layer credit based
                                                    reconfig request */
       return ("SEND_CREDIT_BASED_RECONFIG_REQ");
diff --git a/system/stack/l2cap/l2c_fcr.cc b/system/stack/l2cap/l2c_fcr.cc
index 1688e9b..4960858 100644
--- a/system/stack/l2cap/l2c_fcr.cc
+++ b/system/stack/l2cap/l2c_fcr.cc
@@ -681,8 +681,12 @@
 
   /* Buffer length should not exceed local mps */
   if (p_buf->len > p_ccb->local_conn_cfg.mps) {
-    /* Discard the buffer */
+    LOG_ERROR("buffer length=%d exceeds local mps=%d. Drop and disconnect.",
+              p_buf->len, p_ccb->local_conn_cfg.mps);
+
+    /* Discard the buffer and disconnect*/
     osi_free(p_buf);
+    l2cu_disconnect_chnl(p_ccb);
     return;
   }
 
@@ -699,8 +703,11 @@
 
     /* Check the SDU Length with local MTU size */
     if (sdu_length > p_ccb->local_conn_cfg.mtu) {
-      /* Discard the buffer */
+      LOG_ERROR("sdu length=%d exceeds local mtu=%d. Drop and disconnect.",
+                sdu_length, p_ccb->local_conn_cfg.mtu);
+      /* Discard the buffer and disconnect*/
       osi_free(p_buf);
+      l2cu_disconnect_chnl(p_ccb);
       return;
     }
 
diff --git a/system/stack/l2cap/l2c_int.h b/system/stack/l2cap/l2c_int.h
index 12b9992..39ba1ec 100644
--- a/system/stack/l2cap/l2c_int.h
+++ b/system/stack/l2cap/l2c_int.h
@@ -397,8 +397,6 @@
 #define L2CAP_GET_PRIORITY_QUOTA(pri) \
   ((L2CAP_NUM_CHNL_PRIORITY - (pri)) * L2CAP_CHNL_PRIORITY_WEIGHT)
 
-#define L2CAP_CREDIT_BASED_MAX_CIDS 5
-
 /* CCBs within the same LCB are served in round robin with priority It will make
  * sure that low priority channel (for example, HF signaling on RFCOMM) can be
  * sent to the headset even if higher priority channel (for example, AV media
diff --git a/system/stack/sdp/sdp_server.cc b/system/stack/sdp/sdp_server.cc
index 969d7c2..4842ecc 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
@@ -410,12 +411,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]);
 
@@ -564,7 +575,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;
@@ -662,24 +672,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/smp/smp_act.cc b/system/stack/smp/smp_act.cc
index 6d8f007..9aa64cf 100644
--- a/system/stack/smp/smp_act.cc
+++ b/system/stack/smp/smp_act.cc
@@ -138,6 +138,10 @@
         cb_data.io_req.resp_keys = SMP_BR_SEC_DEFAULT_KEY;
         break;
 
+      case SMP_LE_ADDR_ASSOC_EVT:
+        cb_data.id_addr = p_cb->id_addr;
+        break;
+
       default:
         LOG_ERROR("Unexpected event:%hhu", p_cb->cb_evt);
         break;
@@ -212,6 +216,7 @@
 
         // Expected, but nothing to do
         case SMP_SC_LOC_OOB_DATA_UP_EVT:
+        case SMP_LE_ADDR_ASSOC_EVT:
           break;
 
         default:
@@ -544,6 +549,14 @@
   STREAM_TO_UINT8(p_cb->peer_i_key, p);
   STREAM_TO_UINT8(p_cb->peer_r_key, p);
 
+  tSMP_STATUS reason = p_cb->cert_failure;
+  if (reason == SMP_ENC_KEY_SIZE) {
+    tSMP_INT_DATA smp_int_data;
+    smp_int_data.status = reason;
+    smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data);
+    return;
+  }
+
   if (smp_command_has_invalid_parameters(p_cb)) {
     tSMP_INT_DATA smp_int_data;
     smp_int_data.status = SMP_INVALID_PARAMETERS;
@@ -1050,8 +1063,11 @@
 
   /* store the ID key from peer device */
   if ((p_cb->peer_auth_req & SMP_AUTH_BOND) &&
-      (p_cb->loc_auth_req & SMP_AUTH_BOND))
+      (p_cb->loc_auth_req & SMP_AUTH_BOND)) {
     btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PID, &pid_key, true);
+    p_cb->cb_evt = SMP_LE_ADDR_ASSOC_EVT;
+    smp_send_app_cback(p_cb, NULL);
+  }
   smp_key_distribution_by_transport(p_cb, NULL);
 }
 
diff --git a/system/stack/test/btm/stack_btm_test.cc b/system/stack/test/btm/stack_btm_test.cc
index 256b474..e278e44 100644
--- a/system/stack/test/btm/stack_btm_test.cc
+++ b/system/stack/test/btm/stack_btm_test.cc
@@ -70,6 +70,11 @@
 bool get_pts_crosskey_sdp_disable(void) { return false; }
 const std::string* get_pts_smp_options(void) { return &kSmpOptions; }
 int get_pts_smp_failure_case(void) { return 123; }
+bool get_pts_force_eatt_for_notifications(void) { return false; }
+bool get_pts_connect_eatt_unconditionally(void) { return false; }
+bool get_pts_connect_eatt_before_encryption(void) { return false; }
+bool get_pts_unencrypt_broadcast(void) { return false; }
+bool get_pts_eatt_peripheral_collision_support(void) { return false; }
 config_t* get_all(void) { return nullptr; }
 const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; }
 
@@ -81,6 +86,15 @@
     .get_pts_crosskey_sdp_disable = get_pts_crosskey_sdp_disable,
     .get_pts_smp_options = get_pts_smp_options,
     .get_pts_smp_failure_case = get_pts_smp_failure_case,
+    .get_pts_force_eatt_for_notifications =
+        get_pts_force_eatt_for_notifications,
+    .get_pts_connect_eatt_unconditionally =
+        get_pts_connect_eatt_unconditionally,
+    .get_pts_connect_eatt_before_encryption =
+        get_pts_connect_eatt_before_encryption,
+    .get_pts_unencrypt_broadcast = get_pts_unencrypt_broadcast,
+    .get_pts_eatt_peripheral_collision_support =
+        get_pts_eatt_peripheral_collision_support,
     .get_all = get_all,
 };
 const stack_config_t* stack_config_get_interface(void) {
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/common/mock_btm_api_layer.cc b/system/stack/test/common/mock_btm_api_layer.cc
index 0b5c76f..c1779c5 100644
--- a/system/stack/test/common/mock_btm_api_layer.cc
+++ b/system/stack/test/common/mock_btm_api_layer.cc
@@ -31,3 +31,13 @@
                                              sec_level, psm, mx_proto_id,
                                              mx_chan_id);
 }
+
+bool BTM_IsEncrypted(const RawAddress& remote_bd_addr,
+                     tBT_TRANSPORT transport) {
+  return btm_api_interface->IsEncrypted(remote_bd_addr, transport);
+}
+
+bool BTM_IsLinkKeyKnown(const RawAddress& remote_bd_addr,
+                        tBT_TRANSPORT transport) {
+  return btm_api_interface->IsLinkKeyKnown(remote_bd_addr, transport);
+}
diff --git a/system/stack/test/common/mock_btm_api_layer.h b/system/stack/test/common/mock_btm_api_layer.h
index 052babb..8c92fb3 100644
--- a/system/stack/test/common/mock_btm_api_layer.h
+++ b/system/stack/test/common/mock_btm_api_layer.h
@@ -32,6 +32,10 @@
                                 uint32_t mx_chan_id) = 0;
   virtual uint8_t acl_link_role(const RawAddress& remote_bd_addr,
                                 tBT_TRANSPORT transport) = 0;
+  virtual bool IsEncrypted(const RawAddress& remote_bd_addr,
+                           tBT_TRANSPORT transport) = 0;
+  virtual bool IsLinkKeyKnown(const RawAddress& remote_bd_addr,
+                              tBT_TRANSPORT transport) = 0;
   virtual ~BtmApiInterface() = default;
 };
 
@@ -43,6 +47,10 @@
                     uint32_t mx_chan_id));
   MOCK_METHOD2(acl_link_role, uint8_t(const RawAddress& remote_bd_addr,
                                       tBT_TRANSPORT transport));
+  MOCK_METHOD2(IsEncrypted,
+               bool(const RawAddress& remote_bd_addr, tBT_TRANSPORT transport));
+  MOCK_METHOD2(IsLinkKeyKnown,
+               bool(const RawAddress& remote_bd_addr, tBT_TRANSPORT transport));
 };
 
 /**
diff --git a/system/stack/test/common/mock_eatt.cc b/system/stack/test/common/mock_eatt.cc
index 560531a..6e07bf5 100644
--- a/system/stack/test/common/mock_eatt.cc
+++ b/system/stack/test/common/mock_eatt.cc
@@ -46,8 +46,8 @@
   pimpl_->Connect(bd_addr);
 }
 
-void EattExtension::Disconnect(const RawAddress& bd_addr) {
-  pimpl_->Disconnect(bd_addr);
+void EattExtension::Disconnect(const RawAddress& bd_addr, uint16_t cid) {
+  pimpl_->Disconnect(bd_addr, cid);
 }
 
 void EattExtension::Reconfigure(const RawAddress& bd_addr, uint16_t cid,
diff --git a/system/stack/test/common/mock_eatt.h b/system/stack/test/common/mock_eatt.h
index a7b6c5f..6d1682b 100644
--- a/system/stack/test/common/mock_eatt.h
+++ b/system/stack/test/common/mock_eatt.h
@@ -35,7 +35,7 @@
   static MockEattExtension* GetInstance();
 
   MOCK_METHOD((void), Connect, (const RawAddress& bd_addr));
-  MOCK_METHOD((void), Disconnect, (const RawAddress& bd_addr));
+  MOCK_METHOD((void), Disconnect, (const RawAddress& bd_addr, uint16_t cid));
   MOCK_METHOD((void), Reconfigure,
               (const RawAddress& bd_addr, uint16_t cid, uint16_t mtu));
   MOCK_METHOD((void), ReconfigureAll,
diff --git a/system/stack/test/eatt/eatt_test.cc b/system/stack/test/eatt/eatt_test.cc
index 3eda92c..fbd060f 100644
--- a/system/stack/test/eatt/eatt_test.cc
+++ b/system/stack/test/eatt/eatt_test.cc
@@ -62,7 +62,8 @@
 
 class EattTest : public testing::Test {
  protected:
-  void ConnectDeviceEattSupported(int num_of_accepted_connections) {
+  void ConnectDeviceEattSupported(int num_of_accepted_connections,
+                                  bool collision = false) {
     ON_CALL(gatt_interface_, ClientReadSupportedFeatures)
         .WillByDefault(
             [](const RawAddress& addr,
@@ -80,6 +81,14 @@
 
     eatt_instance_->Connect(test_address);
 
+    if (collision) {
+      EXPECT_CALL(l2cap_interface_,
+                  ConnectCreditBasedReq(BT_PSM_EATT, test_address, _))
+          .Times(1);
+
+      l2cap_app_info_.pL2CA_CreditBasedCollisionInd_Cb(test_address);
+    }
+
     int i = 0;
     for (uint16_t cid : test_local_cids) {
       EattChannel* channel =
@@ -185,17 +194,25 @@
 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, _))
+  ON_CALL(btm_api_interface_, IsEncrypted)
+      .WillByDefault(
+          [](const RawAddress& addr, tBT_TRANSPORT transport) { return true; });
+  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) {
   hci_role_ = HCI_ROLE_PERIPHERAL;
+  ON_CALL(btm_api_interface_, IsEncrypted)
+      .WillByDefault(
+          [](const RawAddress& addr, tBT_TRANSPORT transport) { return true; });
   ON_CALL(gatt_interface_, ClientReadSupportedFeatures)
       .WillByDefault(
           [](const RawAddress& addr,
@@ -222,11 +239,69 @@
   hci_role_ = HCI_ROLE_CENTRAL;
 }
 
+TEST_F(EattTest, IncomingEattConnectionByKnownDeviceEncryptionOff) {
+  hci_role_ = HCI_ROLE_PERIPHERAL;
+  ON_CALL(btm_api_interface_, IsEncrypted)
+      .WillByDefault([](const RawAddress& addr, tBT_TRANSPORT transport) {
+        return false;
+      });
+  ON_CALL(btm_api_interface_, IsLinkKeyKnown)
+      .WillByDefault(
+          [](const RawAddress& addr, tBT_TRANSPORT transport) { return true; });
+  ON_CALL(gatt_interface_, ClientReadSupportedFeatures)
+      .WillByDefault(
+          [](const RawAddress& addr,
+             base::OnceCallback<void(const RawAddress&, uint8_t)> cb) {
+            std::move(cb).Run(addr, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK);
+            return true;
+          });
+  ON_CALL(gatt_interface_, GetEattSupport)
+      .WillByDefault([](const RawAddress& addr) { return true; });
+
+  eatt_instance_->Connect(test_address);
+  std::vector<uint16_t> incoming_cids{71, 72, 73, 74, 75};
+
+  EXPECT_CALL(l2cap_interface_,
+              ConnectCreditBasedRsp(test_address, 1, _,
+                                    L2CAP_LE_RESULT_INSUFFICIENT_ENCRYP, _))
+      .WillOnce(Return(true));
+
+  l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(
+      test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1);
+
+  hci_role_ = HCI_ROLE_CENTRAL;
+}
+
+TEST_F(EattTest, IncomingEattConnectionByUnknownDeviceEncryptionOff) {
+  std::vector<uint16_t> incoming_cids{71, 72, 73, 74, 75};
+
+  ON_CALL(btm_api_interface_, IsEncrypted)
+      .WillByDefault([](const RawAddress& addr, tBT_TRANSPORT transport) {
+        return false;
+      });
+  ON_CALL(btm_api_interface_, IsLinkKeyKnown)
+      .WillByDefault([](const RawAddress& addr, tBT_TRANSPORT transport) {
+        return false;
+      });
+  EXPECT_CALL(
+      l2cap_interface_,
+      ConnectCreditBasedRsp(test_address, 1, _,
+                            L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION, _))
+      .WillOnce(Return(true));
+
+  l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(
+      test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1);
+}
+
 TEST_F(EattTest, ReconnectInitiatedByRemoteSucceed) {
   ConnectDeviceEattSupported(1);
   DisconnectEattDevice(connected_cids_);
   std::vector<uint16_t> incoming_cids{71, 72, 73, 74, 75};
 
+  ON_CALL(btm_api_interface_, IsEncrypted)
+      .WillByDefault(
+          [](const RawAddress& addr, tBT_TRANSPORT transport) { return true; });
+
   EXPECT_CALL(
       l2cap_interface_,
       ConnectCreditBasedRsp(test_address, 1, incoming_cids, L2CAP_CONN_OK, _))
@@ -443,4 +518,10 @@
   /* Force second disconnect */
   eatt_instance_->Disconnect(test_address);
 }
+
+TEST_F(EattTest, TestCollisionHandling) {
+  ConnectDeviceEattSupported(0, true /* collision*/);
+  ConnectDeviceEattSupported(5, true /* collision*/);
+}
+
 }  // namespace
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..d416e98 100644
--- a/system/stack/test/gatt/mock_gatt_utils_ref.cc
+++ b/system/stack/test/gatt/mock_gatt_utils_ref.cc
@@ -21,9 +21,6 @@
 #include "types/raw_address.h"
 #include "utils/include/bt_utils.h"
 
-/** stack/btu/btu_task.cc, indirect reference, gatt_utils.cc -> libosi */
-bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; }
-
 /** stack/gatt/connection_manager.cc */
 namespace connection_manager {
 bool background_connect_remove(uint8_t app_id, const RawAddress& address) {
@@ -35,8 +32,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/gatt/stack_gatt_test.cc b/system/stack/test/gatt/stack_gatt_test.cc
index 7e97771..38d6cc6 100644
--- a/system/stack/test/gatt/stack_gatt_test.cc
+++ b/system/stack/test/gatt/stack_gatt_test.cc
@@ -33,8 +33,6 @@
 
 void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
 
-bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; }
-
 class StackGattTest : public ::testing::Test {};
 
 namespace {
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/stack/test/stack_smp_test.cc b/system/stack/test/stack_smp_test.cc
index c20914e..abf10b9 100644
--- a/system/stack/test/stack_smp_test.cc
+++ b/system/stack/test/stack_smp_test.cc
@@ -45,6 +45,11 @@
 bool get_pts_crosskey_sdp_disable(void) { return false; }
 const std::string* get_pts_smp_options(void) { return &kSmpOptions; }
 int get_pts_smp_failure_case(void) { return 123; }
+bool get_pts_force_eatt_for_notifications(void) { return false; }
+bool get_pts_connect_eatt_unconditionally(void) { return false; }
+bool get_pts_connect_eatt_before_encryption(void) { return false; }
+bool get_pts_unencrypt_broadcast(void) { return false; }
+bool get_pts_eatt_peripheral_collision_support(void) { return false; }
 config_t* get_all(void) { return nullptr; }
 const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; }
 
@@ -56,6 +61,15 @@
     .get_pts_crosskey_sdp_disable = get_pts_crosskey_sdp_disable,
     .get_pts_smp_options = get_pts_smp_options,
     .get_pts_smp_failure_case = get_pts_smp_failure_case,
+    .get_pts_force_eatt_for_notifications =
+        get_pts_force_eatt_for_notifications,
+    .get_pts_connect_eatt_unconditionally =
+        get_pts_connect_eatt_unconditionally,
+    .get_pts_connect_eatt_before_encryption =
+        get_pts_connect_eatt_before_encryption,
+    .get_pts_unencrypt_broadcast = get_pts_unencrypt_broadcast,
+    .get_pts_eatt_peripheral_collision_support =
+        get_pts_eatt_peripheral_collision_support,
     .get_all = get_all,
 };
 const stack_config_t* stack_config_get_interface(void) {
diff --git a/system/test/common/stack_config.cc b/system/test/common/stack_config.cc
index db34351..becbabd 100644
--- a/system/test/common/stack_config.cc
+++ b/system/test/common/stack_config.cc
@@ -30,6 +30,11 @@
 bool get_pts_crosskey_sdp_disable(void) { return false; }
 const std::string* get_pts_smp_options(void) { return &kSmpOptions; }
 int get_pts_smp_failure_case(void) { return 123; }
+bool get_pts_force_eatt_for_notifications(void) { return false; }
+bool get_pts_connect_eatt_unconditionally(void) { return false; }
+bool get_pts_connect_eatt_before_encryption(void) { return false; }
+bool get_pts_unencrypt_broadcast(void) { return false; }
+bool get_pts_eatt_peripheral_collision_support(void) { return false; }
 struct config_t;
 config_t* get_all(void) { return nullptr; }
 struct packet_fragmenter_t;
@@ -43,6 +48,15 @@
     .get_pts_crosskey_sdp_disable = get_pts_crosskey_sdp_disable,
     .get_pts_smp_options = get_pts_smp_options,
     .get_pts_smp_failure_case = get_pts_smp_failure_case,
+    .get_pts_force_eatt_for_notifications =
+        get_pts_force_eatt_for_notifications,
+    .get_pts_connect_eatt_unconditionally =
+        get_pts_connect_eatt_unconditionally,
+    .get_pts_connect_eatt_before_encryption =
+        get_pts_connect_eatt_before_encryption,
+    .get_pts_unencrypt_broadcast = get_pts_unencrypt_broadcast,
+    .get_pts_eatt_peripheral_collision_support =
+        get_pts_eatt_peripheral_collision_support,
     .get_all = get_all,
 };
 
diff --git a/system/test/headless/headless.cc b/system/test/headless/headless.cc
index 23884c1..f3ff188 100644
--- a/system/test/headless/headless.cc
+++ b/system/test/headless/headless.cc
@@ -109,6 +109,11 @@
   LOG_INFO("%s", __func__);
 }
 
+void le_address_associate(RawAddress* main_bd_addr,
+                          RawAddress* secondary_bd_addr) {
+  LOG_INFO("%s", __func__);
+}
+
 /** Bluetooth ACL connection state changed callback */
 void acl_state_changed(bt_status_t status, RawAddress* remote_bd_addr,
                        bt_acl_state_t state, int transport_link_type,
@@ -171,6 +176,7 @@
     .ssp_request_cb = ssp_request,
     .bond_state_changed_cb = bond_state_changed,
     .address_consolidate_cb = address_consolidate,
+    .le_address_associate_cb = le_address_associate,
     .acl_state_changed_cb = acl_state_changed,
     .thread_evt_cb = thread_event,
     .dut_mode_recv_cb = dut_mode_recv,
diff --git a/system/test/mock/mock_bluetooth_interface.cc b/system/test/mock/mock_bluetooth_interface.cc
index 4312828..a245451 100644
--- a/system/test/mock/mock_bluetooth_interface.cc
+++ b/system/test/mock/mock_bluetooth_interface.cc
@@ -230,6 +230,9 @@
 void invoke_address_consolidate_cb(RawAddress main_bd_addr,
                                    RawAddress secondary_bd_addr) {}
 
+void invoke_le_address_associate_cb(RawAddress main_bd_addr,
+                                    RawAddress secondary_bd_addr) {}
+
 void invoke_acl_state_changed_cb(bt_status_t status, RawAddress bd_addr,
                                  bt_acl_state_t state, int transport_link_type,
                                  bt_hci_error_code_t hci_reason) {}
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;