Snap for 10768428 from 6f42f5e3e847fdeb407b8543ce593658db9986fd to mainline-conscrypt-release

Change-Id: If290bbc31373f54605f3548d139bc7828640aa8b
diff --git a/OWNERS b/OWNERS
index 1c906b8..6511a48 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 27441
+
 # Project owners
 girardier@google.com #{LAST_RESORT_SUGGESTION}
 muhammadfalam@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/OWNERS_automotive b/OWNERS_automotive
index 7ecd876..267d177 100644
--- a/OWNERS_automotive
+++ b/OWNERS_automotive
@@ -1,3 +1,2 @@
 # Project owners
-pirozzoj@google.com
 salsavage@google.com
diff --git a/OWNERS_chromeos b/OWNERS_chromeos
index 2bc73f2..5ee1216 100644
--- a/OWNERS_chromeos
+++ b/OWNERS_chromeos
@@ -5,6 +5,7 @@
 # Audio
 enshuo@google.com
 hychao@google.com
+jrwu@google.com
 
 # General review
 michaelfsun@google.com
diff --git a/OWNERS_leaudio b/OWNERS_leaudio
new file mode 100644
index 0000000..30db38d
--- /dev/null
+++ b/OWNERS_leaudio
@@ -0,0 +1,3 @@
+siyuanh@google.com
+jpawlowski@google.com
+rongxuan@google.com
diff --git a/TEST_MAPPING b/TEST_MAPPING
index dbba17b..efb8850 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -69,6 +69,70 @@
     {
       "name": "net_test_stack_ad_parser"
     },
+    {
+      "name": "net_test_audio_a2dp_hw",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_audio_hearing_aid_hw",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_bluetooth",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_bta",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_bta_security",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btif",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btif_hf_client_service",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btif_profile_queue",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btif_avrcp_audio_track",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_device",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_device_iot_config",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_gatt_conn_multiplexing",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_hci",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_a2dp_codecs_native",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_ad_parser",
+      "keywords": ["cf"]
+    },
     // go/a-unit-tests tests (unit_test: true)
     // Thoses test run on the host in the CI automatically.
     // Run the one that are available on the device on the
@@ -204,6 +268,182 @@
     },
     {
       "name": "net_test_types"
+    },
+    {
+      "name": "bluetooth_csis_test",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_flatbuffer_tests",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_groups_test",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_has_test",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_hh_test",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_le_audio_client_test",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_le_audio_test",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_packet_parser_test",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_test_broadcaster",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_test_broadcaster_state_machine",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_test_common",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_test_gd_unit",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_test_sdp",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bluetooth_vc_test",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "bt_host_test_bta",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "libaptx_enc_tests",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "libaptxhd_enc_tests",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_avrcp",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btcore",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btif_config_cache",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btif_hh",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btif_rc",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btif_stack",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btm_iso",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_btpackets",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_eatt",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_main_shim",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_osi",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_performance",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_a2dp_native",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_acl",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_avdtp",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_btm",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_btu",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_gatt",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_gatt_native",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_gatt_sr_hash_native",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_hci",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_hid",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_l2cap",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_rfcomm",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_sdp",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_stack_smp",
+      "keywords": ["cf"]
+    },
+    {
+      "name": "net_test_types",
+      "keywords": ["cf"]
     }
   ],
   "hwasan-presubmit": [
diff --git a/android/BluetoothLegacyMigration/OWNERS b/android/BluetoothLegacyMigration/OWNERS
index 8f5c903..fad553d 100644
--- a/android/BluetoothLegacyMigration/OWNERS
+++ b/android/BluetoothLegacyMigration/OWNERS
@@ -1,8 +1,6 @@
 # Reviewers for /android/BluetoothLegacyMigration
 
 eruffieux@google.com
-rahulsabnis@google.com
 sattiraju@google.com
 siyuanh@google.com
 wescande@google.com
-zachoverflow@google.com
diff --git a/android/apishim/OWNERS b/android/apishim/OWNERS
index c0cdca9..a3f9cc0 100644
--- a/android/apishim/OWNERS
+++ b/android/apishim/OWNERS
@@ -7,7 +7,6 @@
 mylesgw@google.com
 optedoblivion@google.com
 qasimj@google.com
-rahulsabnis@google.com
 sattiraju@google.com
 siyuanh@google.com
 wescande@google.com
\ No newline at end of file
diff --git a/android/app/Android.bp b/android/app/Android.bp
index bb6ec74..e217a32 100644
--- a/android/app/Android.bp
+++ b/android/app/Android.bp
@@ -110,6 +110,7 @@
         "libbt-sbc-encoder",
         "libbt-stack",
         "libbt-stack-core",
+        "libbt_shim_bridge",
         "libbtcore",
         "libbtdevice",
         "libbte",
diff --git a/android/app/OWNERS b/android/app/OWNERS
index 2a03157..792aab3 100644
--- a/android/app/OWNERS
+++ b/android/app/OWNERS
@@ -6,7 +6,6 @@
 eruffieux@google.com
 jpawlowski@google.com
 mylesgw@google.com
-rahulsabnis@google.com
 sattiraju@google.com
 siyuanh@google.com
 wescande@google.com
diff --git a/android/app/jni/com_android_bluetooth.h b/android/app/jni/com_android_bluetooth.h
index 2c77182..502e695 100644
--- a/android/app/jni/com_android_bluetooth.h
+++ b/android/app/jni/com_android_bluetooth.h
@@ -158,8 +158,6 @@
 
 int register_com_android_bluetooth_btservice_BluetoothKeystore(JNIEnv* env);
 
-int register_com_android_bluetooth_btservice_activity_attribution(JNIEnv* env);
-
 int register_com_android_bluetooth_le_audio(JNIEnv* env);
 
 int register_com_android_bluetooth_vc(JNIEnv* env);
@@ -168,6 +166,23 @@
 
 int register_com_android_bluetooth_btservice_BluetoothQualityReport(
     JNIEnv* env);
+
+struct JNIJavaMethod {
+    const char* name;
+    const char* signature;
+    jmethodID* id;
+    bool is_static{false};
+};
+
+void jniGetMethodsOrDie(JNIEnv* env, const char* className,
+                        const JNIJavaMethod* methods, int nMethods);
+
+#define REGISTER_NATIVE_METHODS(env, classname, methodsArray) \
+    jniRegisterNativeMethods(env, classname, methodsArray, NELEM(methodsArray))
+
+#define GET_JAVA_METHODS(env, classname, methodsArray) \
+    jniGetMethodsOrDie(env, classname, methodsArray, NELEM(methodsArray))
+
 }  // namespace android
 
 #endif /* COM_ANDROID_BLUETOOTH_H */
diff --git a/android/app/jni/com_android_bluetooth_BluetoothQualityReport.cpp b/android/app/jni/com_android_bluetooth_BluetoothQualityReport.cpp
index eb9653f..13e49ea 100644
--- a/android/app/jni/com_android_bluetooth_BluetoothQualityReport.cpp
+++ b/android/app/jni/com_android_bluetooth_BluetoothQualityReport.cpp
@@ -82,12 +82,6 @@
 
 static BluetoothQualityReportCallbacksImpl sBluetoothQualityReportCallbacks;
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_bqrDeliver = env->GetMethodID(clazz, "bqrDeliver", "([BIII[B)V");
-
-  LOG(INFO) << __func__ << ": succeeds";
-}
-
 static void initNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
@@ -151,17 +145,28 @@
   }
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()V", (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-};
-
 int register_com_android_bluetooth_btservice_BluetoothQualityReport(
     JNIEnv* env) {
-  return jniRegisterNativeMethods(env,
-                                  "com/android/bluetooth/btservice/"
-                                  "BluetoothQualityReportNativeInterface",
-                                  sMethods, NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initNative", "()V", (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env,
+      "com/android/bluetooth/btservice/BluetoothQualityReportNativeInterface",
+      methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"bqrDeliver", "([BIII[B)V", &method_bqrDeliver},
+  };
+  GET_JAVA_METHODS(
+      env,
+      "com/android/bluetooth/btservice/BluetoothQualityReportNativeInterface",
+      javaMethods);
+
+  return 0;
 }
 }  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_a2dp.cpp b/android/app/jni/com_android_bluetooth_a2dp.cpp
index 991bc1b..b1c9613 100644
--- a/android/app/jni/com_android_bluetooth_a2dp.cpp
+++ b/android/app/jni/com_android_bluetooth_a2dp.cpp
@@ -191,48 +191,6 @@
     bta2dp_mandatory_codec_preferred_callback,
 };
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  jclass jniBluetoothCodecConfigClass =
-      env->FindClass("android/bluetooth/BluetoothCodecConfig");
-  android_bluetooth_BluetoothCodecConfig.constructor =
-      env->GetMethodID(jniBluetoothCodecConfigClass, "<init>", "(IIIIIJJJJ)V");
-  android_bluetooth_BluetoothCodecConfig.getCodecType =
-      env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecType", "()I");
-  android_bluetooth_BluetoothCodecConfig.getCodecPriority =
-      env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecPriority", "()I");
-  android_bluetooth_BluetoothCodecConfig.getSampleRate =
-      env->GetMethodID(jniBluetoothCodecConfigClass, "getSampleRate", "()I");
-  android_bluetooth_BluetoothCodecConfig.getBitsPerSample =
-      env->GetMethodID(jniBluetoothCodecConfigClass, "getBitsPerSample", "()I");
-  android_bluetooth_BluetoothCodecConfig.getChannelMode =
-      env->GetMethodID(jniBluetoothCodecConfigClass, "getChannelMode", "()I");
-  android_bluetooth_BluetoothCodecConfig.getCodecSpecific1 = env->GetMethodID(
-      jniBluetoothCodecConfigClass, "getCodecSpecific1", "()J");
-  android_bluetooth_BluetoothCodecConfig.getCodecSpecific2 = env->GetMethodID(
-      jniBluetoothCodecConfigClass, "getCodecSpecific2", "()J");
-  android_bluetooth_BluetoothCodecConfig.getCodecSpecific3 = env->GetMethodID(
-      jniBluetoothCodecConfigClass, "getCodecSpecific3", "()J");
-  android_bluetooth_BluetoothCodecConfig.getCodecSpecific4 = env->GetMethodID(
-      jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J");
-
-  method_onConnectionStateChanged =
-      env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V");
-
-  method_onAudioStateChanged =
-      env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V");
-
-  method_onCodecConfigChanged =
-      env->GetMethodID(clazz, "onCodecConfigChanged",
-                       "([BLandroid/bluetooth/BluetoothCodecConfig;"
-                       "[Landroid/bluetooth/BluetoothCodecConfig;"
-                       "[Landroid/bluetooth/BluetoothCodecConfig;)V");
-
-  method_isMandatoryCodecPreferred =
-      env->GetMethodID(clazz, "isMandatoryCodecPreferred", "([B)Z");
-
-  ALOGI("%s: succeeds", __func__);
-}
-
 static std::vector<btav_a2dp_codec_config_t> prepareCodecPreferences(
     JNIEnv* env, jobject object, jobjectArray codecConfigArray) {
   std::vector<btav_a2dp_codec_config_t> codec_preferences;
@@ -501,24 +459,65 @@
   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative",
-     "(I[Landroid/bluetooth/BluetoothCodecConfig;[Landroid/bluetooth/BluetoothCodecConfig;)V",
-     (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
-    {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
-    {"setSilenceDeviceNative", "([BZ)Z", (void*)setSilenceDeviceNative},
-    {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
-    {"setCodecConfigPreferenceNative",
-     "([B[Landroid/bluetooth/BluetoothCodecConfig;)Z",
-     (void*)setCodecConfigPreferenceNative},
-};
-
 int register_com_android_bluetooth_a2dp(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env, "com/android/bluetooth/a2dp/A2dpNativeInterface", sMethods,
-      NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initNative",
+       "(I[Landroid/bluetooth/BluetoothCodecConfig;"
+       "[Landroid/bluetooth/BluetoothCodecConfig;)V",
+       (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
+      {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
+      {"setSilenceDeviceNative", "([BZ)Z", (void*)setSilenceDeviceNative},
+      {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
+      {"setCodecConfigPreferenceNative",
+       "([B[Landroid/bluetooth/BluetoothCodecConfig;)Z",
+       (void*)setCodecConfigPreferenceNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/a2dp/A2dpNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onConnectionStateChanged", "([BI)V", &method_onConnectionStateChanged},
+      {"onAudioStateChanged", "([BI)V", &method_onAudioStateChanged},
+      {"onCodecConfigChanged",
+       "([BLandroid/bluetooth/BluetoothCodecConfig;"
+       "[Landroid/bluetooth/BluetoothCodecConfig;"
+       "[Landroid/bluetooth/BluetoothCodecConfig;)V",
+       &method_onCodecConfigChanged},
+      {"isMandatoryCodecPreferred", "([B)Z", &method_isMandatoryCodecPreferred},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/a2dp/A2dpNativeInterface",
+                   javaMethods);
+
+  const JNIJavaMethod codecConfigCallbacksMethods[] = {
+      {"<init>", "(IIIIIJJJJ)V",
+       &android_bluetooth_BluetoothCodecConfig.constructor},
+      {"getCodecType", "()I",
+       &android_bluetooth_BluetoothCodecConfig.getCodecType},
+      {"getCodecPriority", "()I",
+       &android_bluetooth_BluetoothCodecConfig.getCodecPriority},
+      {"getSampleRate", "()I",
+       &android_bluetooth_BluetoothCodecConfig.getSampleRate},
+      {"getBitsPerSample", "()I",
+       &android_bluetooth_BluetoothCodecConfig.getBitsPerSample},
+      {"getChannelMode", "()I",
+       &android_bluetooth_BluetoothCodecConfig.getChannelMode},
+      {"getCodecSpecific1", "()J",
+       &android_bluetooth_BluetoothCodecConfig.getCodecSpecific1},
+      {"getCodecSpecific2", "()J",
+       &android_bluetooth_BluetoothCodecConfig.getCodecSpecific2},
+      {"getCodecSpecific3", "()J",
+       &android_bluetooth_BluetoothCodecConfig.getCodecSpecific3},
+      {"getCodecSpecific4", "()J",
+       &android_bluetooth_BluetoothCodecConfig.getCodecSpecific4},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/BluetoothCodecConfig",
+                   codecConfigCallbacksMethods);
+
+  return 0;
 }
 }
diff --git a/android/app/jni/com_android_bluetooth_a2dp_sink.cpp b/android/app/jni/com_android_bluetooth_a2dp_sink.cpp
index 2419e4e..47fe153 100644
--- a/android/app/jni/com_android_bluetooth_a2dp_sink.cpp
+++ b/android/app/jni/com_android_bluetooth_a2dp_sink.cpp
@@ -110,19 +110,6 @@
     a2dp_sink_audio_config_callback,
 };
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_onConnectionStateChanged =
-      env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V");
-
-  method_onAudioStateChanged =
-      env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V");
-
-  method_onAudioConfigChanged =
-      env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V");
-
-  ALOGI("%s: succeeds", __func__);
-}
-
 static void initNative(JNIEnv* env, jobject object,
                        jint maxConnectedAudioDevices) {
   std::unique_lock<std::shared_timed_mutex> lock(callbacks_mutex);
@@ -260,20 +247,32 @@
   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "(I)V", (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
-    {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
-    {"informAudioFocusStateNative", "(I)V", (void*)informAudioFocusStateNative},
-    {"informAudioTrackGainNative", "(F)V", (void*)informAudioTrackGainNative},
-    {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
-};
-
 int register_com_android_bluetooth_a2dp_sink(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env, "com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface", sMethods,
-      NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initNative", "(I)V", (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
+      {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
+      {"informAudioFocusStateNative", "(I)V",
+       (void*)informAudioFocusStateNative},
+      {"informAudioTrackGainNative", "(F)V", (void*)informAudioTrackGainNative},
+      {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onConnectionStateChanged", "([BI)V", &method_onConnectionStateChanged},
+      {"onAudioStateChanged", "([BI)V", &method_onAudioStateChanged},
+      {"onAudioConfigChanged", "([BII)V", &method_onAudioConfigChanged},
+  };
+  GET_JAVA_METHODS(env,
+                   "com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface",
+                   javaMethods);
+
+  return 0;
 }
 }
diff --git a/android/app/jni/com_android_bluetooth_avrcp_controller.cpp b/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
index 316e1d0..c5cceac 100644
--- a/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
+++ b/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
@@ -24,10 +24,7 @@
 #include <shared_mutex>
 
 namespace android {
-static jmethodID method_handlePassthroughRsp;
 static jmethodID method_onConnectionStateChanged;
-static jmethodID method_getRcFeatures;
-static jmethodID method_setplayerappsettingrsp;
 static jmethodID method_handleplayerappsetting;
 static jmethodID method_handleplayerappsettingchanged;
 static jmethodID method_handleSetAbsVolume;
@@ -37,7 +34,6 @@
 static jmethodID method_handleplaystatuschanged;
 static jmethodID method_handleGetFolderItemsRsp;
 static jmethodID method_handleGetPlayerItemsRsp;
-static jmethodID method_handleGroupNavigationRsp;
 static jmethodID method_createFromNativeMediaItem;
 static jmethodID method_createFromNativeFolderItem;
 static jmethodID method_createFromNativePlayerItem;
@@ -58,40 +54,11 @@
 
 static void btavrcp_passthrough_response_callback(const RawAddress& bd_addr,
                                                   int id, int pressed) {
-  ALOGI("%s: id: %d, pressed: %d", __func__, id, pressed);
-  std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex);
-  CallbackEnv sCallbackEnv(__func__);
-  if (!sCallbackEnv.valid()) return;
-  if (!sCallbacksObj) {
-    ALOGE("%s: sCallbacksObj is null", __func__);
-    return;
-  }
-
-  ScopedLocalRef<jbyteArray> addr(
-      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
-  if (!addr.get()) {
-    ALOGE("%s: Failed to allocate a new byte array", __func__);
-    return;
-  }
-
-  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)&bd_addr.address);
-  sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handlePassthroughRsp,
-                               (jint)id, (jint)pressed, addr.get());
+  ALOGV("%s: id: %d, pressed: %d --- Not implemented", __func__, id, pressed);
 }
 
 static void btavrcp_groupnavigation_response_callback(int id, int pressed) {
-  ALOGV("%s", __func__);
-  std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex);
-  CallbackEnv sCallbackEnv(__func__);
-  if (!sCallbackEnv.valid()) return;
-  if (!sCallbacksObj) {
-    ALOGE("%s: sCallbacksObj is null", __func__);
-    return;
-  }
-
-  sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGroupNavigationRsp,
-                               (jint)id, (jint)pressed);
+  ALOGV("%s: id: %d, pressed: %d --- Not implemented", __func__, id, pressed);
 }
 
 static void btavrcp_connection_state_callback(bool rc_connect, bool br_connect,
@@ -121,50 +88,11 @@
 
 static void btavrcp_get_rcfeatures_callback(const RawAddress& bd_addr,
                                             int features) {
-  ALOGV("%s", __func__);
-  std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex);
-  CallbackEnv sCallbackEnv(__func__);
-  if (!sCallbackEnv.valid()) return;
-  if (!sCallbacksObj) {
-    ALOGE("%s: sCallbacksObj is null", __func__);
-    return;
-  }
-
-  ScopedLocalRef<jbyteArray> addr(
-      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
-  if (!addr.get()) {
-    ALOGE("%s: Failed to allocate a new byte array", __func__);
-    return;
-  }
-
-  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)&bd_addr.address);
-  sCallbackEnv->CallVoidMethod(sCallbacksObj, method_getRcFeatures, addr.get(),
-                               (jint)features);
+  ALOGV("%s --- Not implemented", __func__);
 }
-
 static void btavrcp_setplayerapplicationsetting_rsp_callback(
     const RawAddress& bd_addr, uint8_t accepted) {
-  ALOGV("%s", __func__);
-  std::shared_lock<std::shared_timed_mutex> lock(sCallbacks_mutex);
-  CallbackEnv sCallbackEnv(__func__);
-  if (!sCallbackEnv.valid()) return;
-  if (!sCallbacksObj) {
-    ALOGE("%s: sCallbacksObj is null", __func__);
-    return;
-  }
-
-  ScopedLocalRef<jbyteArray> addr(
-      sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
-  if (!addr.get()) {
-    ALOGE("%s: Failed to allocate a new byte array", __func__);
-    return;
-  }
-
-  sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
-                                   (jbyte*)&bd_addr.address);
-  sCallbackEnv->CallVoidMethod(sCallbacksObj, method_setplayerappsettingrsp,
-                               addr.get(), (jint)accepted);
+  ALOGV("%s --- Not implemented", __func__);
 }
 
 static void btavrcp_playerapplicationsetting_callback(
@@ -788,81 +716,8 @@
     btavrcp_addressed_player_changed_callback,
     btavrcp_now_playing_content_changed_callback,
     btavrcp_available_player_changed_callback,
-    btavrcp_get_rcpsm_callback};
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_handlePassthroughRsp =
-      env->GetMethodID(clazz, "handlePassthroughRsp", "(II[B)V");
-
-  method_handleGroupNavigationRsp =
-      env->GetMethodID(clazz, "handleGroupNavigationRsp", "(II)V");
-
-  method_onConnectionStateChanged =
-      env->GetMethodID(clazz, "onConnectionStateChanged", "(ZZ[B)V");
-
-  method_getRcFeatures = env->GetMethodID(clazz, "getRcFeatures", "([BI)V");
-
-  method_getRcPsm = env->GetMethodID(clazz, "getRcPsm", "([BI)V");
-
-  method_setplayerappsettingrsp =
-      env->GetMethodID(clazz, "setPlayerAppSettingRsp", "([BB)V");
-
-  method_handleplayerappsetting =
-      env->GetMethodID(clazz, "handlePlayerAppSetting", "([B[BI)V");
-
-  method_handleplayerappsettingchanged =
-      env->GetMethodID(clazz, "onPlayerAppSettingChanged", "([B[BI)V");
-
-  method_handleSetAbsVolume =
-      env->GetMethodID(clazz, "handleSetAbsVolume", "([BBB)V");
-
-  method_handleRegisterNotificationAbsVol =
-      env->GetMethodID(clazz, "handleRegisterNotificationAbsVol", "([BB)V");
-
-  method_handletrackchanged =
-      env->GetMethodID(clazz, "onTrackChanged", "([BB[I[Ljava/lang/String;)V");
-
-  method_handleplaypositionchanged =
-      env->GetMethodID(clazz, "onPlayPositionChanged", "([BII)V");
-
-  method_handleplaystatuschanged =
-      env->GetMethodID(clazz, "onPlayStatusChanged", "([BB)V");
-
-  method_handleGetFolderItemsRsp =
-      env->GetMethodID(clazz, "handleGetFolderItemsRsp",
-                       "([BI[Lcom/android/bluetooth/avrcpcontroller/"
-                       "AvrcpItem;)V");
-  method_handleGetPlayerItemsRsp = env->GetMethodID(
-      clazz, "handleGetPlayerItemsRsp",
-      "([B[Lcom/android/bluetooth/avrcpcontroller/AvrcpPlayer;)V");
-
-  method_createFromNativeMediaItem =
-      env->GetMethodID(clazz, "createFromNativeMediaItem",
-                       "([BJILjava/lang/String;[I[Ljava/lang/String;)Lcom/"
-                       "android/bluetooth/avrcpcontroller/AvrcpItem;");
-  method_createFromNativeFolderItem = env->GetMethodID(
-      clazz, "createFromNativeFolderItem",
-      "([BJILjava/lang/String;I)Lcom/android/bluetooth/avrcpcontroller/"
-      "AvrcpItem;");
-  method_createFromNativePlayerItem =
-      env->GetMethodID(clazz, "createFromNativePlayerItem",
-                       "([BILjava/lang/String;[BII)Lcom/android/bluetooth/"
-                       "avrcpcontroller/AvrcpPlayer;");
-  method_handleChangeFolderRsp =
-      env->GetMethodID(clazz, "handleChangeFolderRsp", "([BI)V");
-  method_handleSetBrowsedPlayerRsp =
-      env->GetMethodID(clazz, "handleSetBrowsedPlayerRsp", "([BII)V");
-  method_handleSetAddressedPlayerRsp =
-      env->GetMethodID(clazz, "handleSetAddressedPlayerRsp", "([BI)V");
-  method_handleAddressedPlayerChanged =
-      env->GetMethodID(clazz, "handleAddressedPlayerChanged", "([BI)V");
-  method_handleNowPlayingContentChanged =
-      env->GetMethodID(clazz, "handleNowPlayingContentChanged", "([B)V");
-  method_onAvailablePlayerChanged =
-      env->GetMethodID(clazz, "onAvailablePlayerChanged", "([B)V");
-
-  ALOGI("%s: succeeds", __func__);
-}
+    btavrcp_get_rcpsm_callback,
+};
 
 static void initNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_timed_mutex> lock(sCallbacks_mutex);
@@ -1282,33 +1137,84 @@
   env->ReleaseByteArrayElements(address, addr, 0);
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()V", (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"sendPassThroughCommandNative", "([BII)Z",
-     (void*)sendPassThroughCommandNative},
-    {"sendGroupNavigationCommandNative", "([BII)Z",
-     (void*)sendGroupNavigationCommandNative},
-    {"setPlayerApplicationSettingValuesNative", "([BB[B[B)V",
-     (void*)setPlayerApplicationSettingValuesNative},
-    {"sendAbsVolRspNative", "([BII)V", (void*)sendAbsVolRspNative},
-    {"sendRegisterAbsVolRspNative", "([BBII)V",
-     (void*)sendRegisterAbsVolRspNative},
-    {"getCurrentMetadataNative", "([B)V", (void*)getCurrentMetadataNative},
-    {"getPlaybackStateNative", "([B)V", (void*)getPlaybackStateNative},
-    {"getNowPlayingListNative", "([BII)V", (void*)getNowPlayingListNative},
-    {"getFolderListNative", "([BII)V", (void*)getFolderListNative},
-    {"getPlayerListNative", "([BII)V", (void*)getPlayerListNative},
-    {"changeFolderPathNative", "([BBJ)V", (void*)changeFolderPathNative},
-    {"playItemNative", "([BBJI)V", (void*)playItemNative},
-    {"setBrowsedPlayerNative", "([BI)V", (void*)setBrowsedPlayerNative},
-    {"setAddressedPlayerNative", "([BI)V", (void*)setAddressedPlayerNative},
-};
-
 int register_com_android_bluetooth_avrcp_controller(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env, "com/android/bluetooth/avrcpcontroller/AvrcpControllerService",
-      sMethods, NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initNative", "()V", (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"sendPassThroughCommandNative", "([BII)Z",
+       (void*)sendPassThroughCommandNative},
+      {"sendGroupNavigationCommandNative", "([BII)Z",
+       (void*)sendGroupNavigationCommandNative},
+      {"setPlayerApplicationSettingValuesNative", "([BB[B[B)V",
+       (void*)setPlayerApplicationSettingValuesNative},
+      {"sendAbsVolRspNative", "([BII)V", (void*)sendAbsVolRspNative},
+      {"sendRegisterAbsVolRspNative", "([BBII)V",
+       (void*)sendRegisterAbsVolRspNative},
+      {"getCurrentMetadataNative", "([B)V", (void*)getCurrentMetadataNative},
+      {"getPlaybackStateNative", "([B)V", (void*)getPlaybackStateNative},
+      {"getNowPlayingListNative", "([BII)V", (void*)getNowPlayingListNative},
+      {"getFolderListNative", "([BII)V", (void*)getFolderListNative},
+      {"getPlayerListNative", "([BII)V", (void*)getPlayerListNative},
+      {"changeFolderPathNative", "([BBJ)V", (void*)changeFolderPathNative},
+      {"playItemNative", "([BBJI)V", (void*)playItemNative},
+      {"setBrowsedPlayerNative", "([BI)V", (void*)setBrowsedPlayerNative},
+      {"setAddressedPlayerNative", "([BI)V", (void*)setAddressedPlayerNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env,
+      "com/android/bluetooth/avrcpcontroller/AvrcpControllerNativeInterface",
+      methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onConnectionStateChanged", "(ZZ[B)V", &method_onConnectionStateChanged},
+      {"getRcPsm", "([BI)V", &method_getRcPsm},
+      {"handlePlayerAppSetting", "([B[BI)V", &method_handleplayerappsetting},
+      {"onPlayerAppSettingChanged", "([B[BI)V",
+       &method_handleplayerappsettingchanged},
+      {"handleSetAbsVolume", "([BBB)V", &method_handleSetAbsVolume},
+      {"handleRegisterNotificationAbsVol", "([BB)V",
+       &method_handleRegisterNotificationAbsVol},
+      {"onTrackChanged", "([BB[I[Ljava/lang/String;)V",
+       &method_handletrackchanged},
+      {"onPlayPositionChanged", "([BII)V", &method_handleplaypositionchanged},
+      {"onPlayStatusChanged", "([BB)V", &method_handleplaystatuschanged},
+      {"handleGetFolderItemsRsp",
+       "([BI[Lcom/android/bluetooth/avrcpcontroller/AvrcpItem;)V",
+       &method_handleGetFolderItemsRsp},
+      {"handleGetPlayerItemsRsp",
+       "([B[Lcom/android/bluetooth/avrcpcontroller/AvrcpPlayer;)V",
+       &method_handleGetPlayerItemsRsp},
+      {"handleChangeFolderRsp", "([BI)V", &method_handleChangeFolderRsp},
+      {"handleSetBrowsedPlayerRsp", "([BII)V",
+       &method_handleSetBrowsedPlayerRsp},
+      {"handleSetAddressedPlayerRsp", "([BI)V",
+       &method_handleSetAddressedPlayerRsp},
+      {"handleAddressedPlayerChanged", "([BI)V",
+       &method_handleAddressedPlayerChanged},
+      {"handleNowPlayingContentChanged", "([B)V",
+       &method_handleNowPlayingContentChanged},
+      {"onAvailablePlayerChanged", "([B)V", &method_onAvailablePlayerChanged},
+      // Fetch static method
+      {"createFromNativeMediaItem",
+       "([BJILjava/lang/String;[I[Ljava/lang/String;)"
+       "Lcom/android/bluetooth/avrcpcontroller/AvrcpItem;",
+       &method_createFromNativeMediaItem, true},
+      {"createFromNativeFolderItem",
+       "([BJILjava/lang/String;I)"
+       "Lcom/android/bluetooth/avrcpcontroller/AvrcpItem;",
+       &method_createFromNativeFolderItem, true},
+      {"createFromNativePlayerItem",
+       "([BILjava/lang/String;[BII)"
+       "Lcom/android/bluetooth/avrcpcontroller/AvrcpPlayer;",
+       &method_createFromNativePlayerItem, true},
+  };
+  GET_JAVA_METHODS(
+      env,
+      "com/android/bluetooth/avrcpcontroller/AvrcpControllerNativeInterface",
+      javaMethods);
+  return 0;
 }
-}
+}  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_avrcp_target.cpp b/android/app/jni/com_android_bluetooth_avrcp_target.cpp
index edfb684..2a37df8 100644
--- a/android/app/jni/com_android_bluetooth_avrcp_target.cpp
+++ b/android/app/jni/com_android_bluetooth_avrcp_target.cpp
@@ -232,63 +232,6 @@
 static jmethodID method_getPlayerSettings;
 static jmethodID method_setPlayerSettings;
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_getCurrentSongInfo = env->GetMethodID(
-      clazz, "getCurrentSongInfo", "()Lcom/android/bluetooth/audio_util/Metadata;");
-
-  method_getPlaybackStatus = env->GetMethodID(
-      clazz, "getPlayStatus", "()Lcom/android/bluetooth/audio_util/PlayStatus;");
-
-  method_sendMediaKeyEvent =
-      env->GetMethodID(clazz, "sendMediaKeyEvent", "(IZ)V");
-
-  method_getCurrentMediaId =
-      env->GetMethodID(clazz, "getCurrentMediaId", "()Ljava/lang/String;");
-
-  method_getNowPlayingList =
-      env->GetMethodID(clazz, "getNowPlayingList", "()Ljava/util/List;");
-
-  method_getCurrentPlayerId =
-      env->GetMethodID(clazz, "getCurrentPlayerId", "()I");
-
-  method_getMediaPlayerList =
-      env->GetMethodID(clazz, "getMediaPlayerList", "()Ljava/util/List;");
-
-  method_setBrowsedPlayer = env->GetMethodID(clazz, "setBrowsedPlayer", "(I)V");
-
-  method_getFolderItemsRequest = env->GetMethodID(
-      clazz, "getFolderItemsRequest", "(ILjava/lang/String;)V");
-
-  method_playItem =
-      env->GetMethodID(clazz, "playItem", "(IZLjava/lang/String;)V");
-
-  method_setActiveDevice =
-      env->GetMethodID(clazz, "setActiveDevice", "(Ljava/lang/String;)V");
-
-  // Volume Management functions
-  method_volumeDeviceConnected =
-      env->GetMethodID(clazz, "deviceConnected", "(Ljava/lang/String;Z)V");
-
-  method_volumeDeviceDisconnected =
-      env->GetMethodID(clazz, "deviceDisconnected", "(Ljava/lang/String;)V");
-
-  method_setVolume = env->GetMethodID(clazz, "setVolume", "(I)V");
-
-  method_listPlayerSettings =
-      env->GetMethodID(clazz, "listPlayerSettingsRequest", "()V");
-
-  method_listPlayerSettingValues =
-      env->GetMethodID(clazz, "listPlayerSettingValuesRequest", "(B)V");
-
-  method_getPlayerSettings =
-      env->GetMethodID(clazz, "getCurrentPlayerSettingValuesRequest", "([B)V");
-
-  method_setPlayerSettings =
-      env->GetMethodID(clazz, "setPlayerSettingsRequest", "([B[B)V");
-
-  ALOGI("%s: AvrcpTargetJni initialized!", __func__);
-}
-
 static void initNative(JNIEnv* env, jobject object) {
   ALOGD("%s", __func__);
   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
@@ -1096,41 +1039,73 @@
                                                values_vector);
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()V", (void*)initNative},
-    {"registerBipServerNative", "(I)V", (void*)registerBipServerNative},
-    {"unregisterBipServerNative", "()V", (void*)unregisterBipServerNative},
-    {"sendMediaUpdateNative", "(ZZZ)V", (void*)sendMediaUpdateNative},
-    {"sendFolderUpdateNative", "(ZZZ)V", (void*)sendFolderUpdateNative},
-    {"setBrowsedPlayerResponseNative", "(IZLjava/lang/String;I)V",
-     (void*)setBrowsedPlayerResponseNative},
-    {"getFolderItemsResponseNative", "(Ljava/lang/String;Ljava/util/List;)V",
-     (void*)getFolderItemsResponseNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectDeviceNative", "(Ljava/lang/String;)Z",
-     (void*)connectDeviceNative},
-    {"disconnectDeviceNative", "(Ljava/lang/String;)Z",
-     (void*)disconnectDeviceNative},
-    {"sendVolumeChangedNative", "(Ljava/lang/String;I)V",
-     (void*)sendVolumeChangedNative},
-    {"setBipClientStatusNative", "(Ljava/lang/String;Z)V",
-     (void*)setBipClientStatusNative},
-    {"listPlayerSettingsResponseNative", "([B)V",
-     (void*)listPlayerSettingsResponseNative},
-    {"listPlayerSettingValuesResponseNative", "(B[B)V",
-     (void*)listPlayerSettingValuesResponseNative},
-    {"getPlayerSettingsResponseNative", "([B[B)V",
-     (void*)getPlayerSettingsResponseNative},
-    {"setPlayerSettingsResponseNative", "(Z)V",
-     (void*)setPlayerSettingsResponseNative},
-    {"sendPlayerSettingsNative", "([B[B)V", (void*)sendPlayerSettingsNative},
-};
-
 int register_com_android_bluetooth_avrcp_target(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env, "com/android/bluetooth/avrcp/AvrcpNativeInterface", sMethods,
-      NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initNative", "()V", (void*)initNative},
+      {"registerBipServerNative", "(I)V", (void*)registerBipServerNative},
+      {"unregisterBipServerNative", "()V", (void*)unregisterBipServerNative},
+      {"sendMediaUpdateNative", "(ZZZ)V", (void*)sendMediaUpdateNative},
+      {"sendFolderUpdateNative", "(ZZZ)V", (void*)sendFolderUpdateNative},
+      {"setBrowsedPlayerResponseNative", "(IZLjava/lang/String;I)V",
+       (void*)setBrowsedPlayerResponseNative},
+      {"getFolderItemsResponseNative", "(Ljava/lang/String;Ljava/util/List;)V",
+       (void*)getFolderItemsResponseNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectDeviceNative", "(Ljava/lang/String;)Z",
+       (void*)connectDeviceNative},
+      {"disconnectDeviceNative", "(Ljava/lang/String;)Z",
+       (void*)disconnectDeviceNative},
+      {"sendVolumeChangedNative", "(Ljava/lang/String;I)V",
+       (void*)sendVolumeChangedNative},
+      {"setBipClientStatusNative", "(Ljava/lang/String;Z)V",
+       (void*)setBipClientStatusNative},
+      {"listPlayerSettingsResponseNative", "([B)V",
+       (void*)listPlayerSettingsResponseNative},
+      {"listPlayerSettingValuesResponseNative", "(B[B)V",
+       (void*)listPlayerSettingValuesResponseNative},
+      {"getPlayerSettingsResponseNative", "([B[B)V",
+       (void*)getPlayerSettingsResponseNative},
+      {"setPlayerSettingsResponseNative", "(Z)V",
+       (void*)setPlayerSettingsResponseNative},
+      {"sendPlayerSettingsNative", "([B[B)V", (void*)sendPlayerSettingsNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/avrcp/AvrcpNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"getCurrentSongInfo", "()Lcom/android/bluetooth/audio_util/Metadata;",
+       &method_getCurrentSongInfo},
+      {"getPlayStatus", "()Lcom/android/bluetooth/audio_util/PlayStatus;",
+       &method_getPlaybackStatus},
+      {"sendMediaKeyEvent", "(IZ)V", &method_sendMediaKeyEvent},
+      {"getCurrentMediaId", "()Ljava/lang/String;", &method_getCurrentMediaId},
+      {"getNowPlayingList", "()Ljava/util/List;", &method_getNowPlayingList},
+      {"getCurrentPlayerId", "()I", &method_getCurrentPlayerId},
+      {"getMediaPlayerList", "()Ljava/util/List;", &method_getMediaPlayerList},
+      {"setBrowsedPlayer", "(I)V", &method_setBrowsedPlayer},
+      {"getFolderItemsRequest", "(ILjava/lang/String;)V",
+       &method_getFolderItemsRequest},
+      {"playItem", "(IZLjava/lang/String;)V", &method_playItem},
+      {"setActiveDevice", "(Ljava/lang/String;)V", &method_setActiveDevice},
+      {"deviceConnected", "(Ljava/lang/String;Z)V",
+       &method_volumeDeviceConnected},
+      {"deviceDisconnected", "(Ljava/lang/String;)V",
+       &method_volumeDeviceDisconnected},
+      {"setVolume", "(I)V", &method_setVolume},
+      {"listPlayerSettingsRequest", "()V", &method_listPlayerSettings},
+      {"listPlayerSettingValuesRequest", "(B)V",
+       &method_listPlayerSettingValues},
+      {"getCurrentPlayerSettingValuesRequest", "([B)V",
+       &method_getPlayerSettings},
+      {"setPlayerSettingsRequest", "([B[B)V", &method_setPlayerSettings},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/avrcp/AvrcpNativeInterface",
+                   javaMethods);
+
+  return 0;
 }
 
 }  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp b/android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp
deleted file mode 100644
index 2790b00..0000000
--- a/android/app/jni/com_android_bluetooth_btservice_ActivityAttribution.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#define LOG_TAG "BluetoothActivityAttributionJni"
-
-#include <string.h>
-
-#include <shared_mutex>
-
-#include "base/logging.h"
-#include "com_android_bluetooth.h"
-#include "hardware/bt_activity_attribution.h"
-
-using bluetooth::activity_attribution::ActivityAttributionInterface;
-
-namespace android {
-static ActivityAttributionInterface* sActivityAttributionInterface = nullptr;
-
-static void notifyActivityAttributionInfoNative(JNIEnv* env, jobject object,
-                                                jint uid, jstring packageName,
-                                                jstring deviceAddress) {
-  const bt_interface_t* btInf = getBluetoothInterface();
-  if (btInf == nullptr) {
-    LOG(ERROR) << "Bluetooth module is not loaded";
-    return;
-  }
-  sActivityAttributionInterface =
-      (ActivityAttributionInterface*)btInf->get_profile_interface(
-          BT_ACTIVITY_ATTRIBUTION_ID);
-  if (sActivityAttributionInterface == nullptr) {
-    LOG(ERROR) << "Failed to get ActivityAttribution Interface";
-    return;
-  }
-
-  if (packageName == nullptr || deviceAddress == nullptr) {
-    LOG(ERROR) << "Failed to get package name or device address";
-    return;
-  }
-  const char* nativeName = env->GetStringUTFChars(packageName, nullptr);
-  const char* nativeAddress = env->GetStringUTFChars(deviceAddress, nullptr);
-  sActivityAttributionInterface->NotifyActivityAttributionInfo(uid, nativeName,
-                                                               nativeAddress);
-  env->ReleaseStringUTFChars(packageName, nativeName);
-  env->ReleaseStringUTFChars(deviceAddress, nativeAddress);
-}
-
-static JNINativeMethod sMethods[] = {
-    {"notifyActivityAttributionInfoNative",
-     "(ILjava/lang/String;Ljava/lang/String;)V",
-     (void*)notifyActivityAttributionInfoNative},
-};
-
-int register_com_android_bluetooth_btservice_activity_attribution(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env,
-      "com/android/bluetooth/btservice/activityattribution/"
-      "ActivityAttributionNativeInterface",
-      sMethods, NELEM(sMethods));
-}
-
-}  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index fdb8e3c..3225476 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -66,7 +66,6 @@
 static jmethodID method_linkQualityReportCallback;
 static jmethodID method_switchBufferSizeCallback;
 static jmethodID method_switchCodecCallback;
-static jmethodID method_setWakeAlarm;
 static jmethodID method_acquireWakeLock;
 static jmethodID method_releaseWakeLock;
 static jmethodID method_energyInfo;
@@ -532,8 +531,21 @@
 
 static jobject createClassicOobDataObject(JNIEnv* env, bt_oob_data_t oob_data) {
   ALOGV("%s", __func__);
-  jclass classicBuilderClass =
-      env->FindClass("android/bluetooth/OobData$ClassicBuilder");
+  jmethodID classicBuilderConstructor;
+  jmethodID setRMethod;
+  jmethodID setNameMethod;
+  jmethodID buildMethod;
+
+  const JNIJavaMethod javaMethods[] = {
+      {"<init>", "([B[B[B)V", &classicBuilderConstructor},
+      {"setRandomizerHash", "([B)Landroid/bluetooth/OobData$ClassicBuilder;",
+       &setRMethod},
+      {"setDeviceName", "([B)Landroid/bluetooth/OobData$ClassicBuilder;",
+       &setNameMethod},
+      {"build", "()Landroid/bluetooth/OobData;", &buildMethod},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/OobData$ClassicBuilder",
+                   javaMethods);
 
   jbyteArray confirmationHash = env->NewByteArray(OOB_C_SIZE);
   env->SetByteArrayRegion(confirmationHash, 0, OOB_C_SIZE,
@@ -547,16 +559,15 @@
   env->SetByteArrayRegion(address, 0, OOB_ADDRESS_SIZE,
                           reinterpret_cast<jbyte*>(oob_data.address));
 
-  jmethodID classicBuilderConstructor =
-      env->GetMethodID(classicBuilderClass, "<init>", "([B[B[B)V");
+  jclass classicBuilderClass =
+      env->FindClass("android/bluetooth/OobData$ClassicBuilder");
 
   jobject oobDataClassicBuilder =
       env->NewObject(classicBuilderClass, classicBuilderConstructor,
                      confirmationHash, oobDataLength, address);
 
-  jmethodID setRMethod =
-      env->GetMethodID(classicBuilderClass, "setRandomizerHash",
-                       "([B)Landroid/bluetooth/OobData$ClassicBuilder;");
+  env->DeleteLocalRef(classicBuilderClass);
+
   jbyteArray randomizerHash = env->NewByteArray(OOB_R_SIZE);
   env->SetByteArrayRegion(randomizerHash, 0, OOB_R_SIZE,
                           reinterpret_cast<jbyte*>(oob_data.r));
@@ -564,10 +575,6 @@
   oobDataClassicBuilder =
       env->CallObjectMethod(oobDataClassicBuilder, setRMethod, randomizerHash);
 
-  jmethodID setNameMethod =
-      env->GetMethodID(classicBuilderClass, "setDeviceName",
-                       "([B)Landroid/bluetooth/OobData$ClassicBuilder;");
-
   int name_char_count = 0;
   for (int i = 0; i < OOB_NAME_MAX_SIZE; i++) {
     if (oob_data.device_name[i] == 0) {
@@ -583,16 +590,26 @@
   oobDataClassicBuilder =
       env->CallObjectMethod(oobDataClassicBuilder, setNameMethod, deviceName);
 
-  jmethodID buildMethod = env->GetMethodID(classicBuilderClass, "build",
-                                           "()Landroid/bluetooth/OobData;");
-
   return env->CallObjectMethod(oobDataClassicBuilder, buildMethod);
 }
 
 static jobject createLeOobDataObject(JNIEnv* env, bt_oob_data_t oob_data) {
   ALOGV("%s", __func__);
 
-  jclass leBuilderClass = env->FindClass("android/bluetooth/OobData$LeBuilder");
+  jmethodID leBuilderConstructor;
+  jmethodID setRMethod;
+  jmethodID setNameMethod;
+  jmethodID buildMethod;
+
+  const JNIJavaMethod javaMethods[] = {
+      {"<init>", "([B[BI)V", &leBuilderConstructor},
+      {"setRandomizerHash", "([B)Landroid/bluetooth/OobData$LeBuilder;",
+       &setRMethod},
+      {"setDeviceName", "([B)Landroid/bluetooth/OobData$LeBuilder;",
+       &setNameMethod},
+      {"build", "()Landroid/bluetooth/OobData;", &buildMethod},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/OobData$LeBuilder", javaMethods);
 
   jbyteArray confirmationHash = env->NewByteArray(OOB_C_SIZE);
   env->SetByteArrayRegion(confirmationHash, 0, OOB_C_SIZE,
@@ -604,15 +621,13 @@
 
   jint le_role = (jint)oob_data.le_device_role;
 
-  jmethodID leBuilderConstructor =
-      env->GetMethodID(leBuilderClass, "<init>", "([B[BI)V");
+  jclass leBuilderClass = env->FindClass("android/bluetooth/OobData$LeBuilder");
 
   jobject oobDataLeBuilder = env->NewObject(
       leBuilderClass, leBuilderConstructor, confirmationHash, address, le_role);
 
-  jmethodID setRMethod =
-      env->GetMethodID(leBuilderClass, "setRandomizerHash",
-                       "([B)Landroid/bluetooth/OobData$LeBuilder;");
+  env->DeleteLocalRef(leBuilderClass);
+
   jbyteArray randomizerHash = env->NewByteArray(OOB_R_SIZE);
   env->SetByteArrayRegion(randomizerHash, 0, OOB_R_SIZE,
                           reinterpret_cast<jbyte*>(oob_data.r));
@@ -620,10 +635,6 @@
   oobDataLeBuilder =
       env->CallObjectMethod(oobDataLeBuilder, setRMethod, randomizerHash);
 
-  jmethodID setNameMethod =
-      env->GetMethodID(leBuilderClass, "setDeviceName",
-                       "([B)Landroid/bluetooth/OobData$LeBuilder;");
-
   int name_char_count = 0;
   for (int i = 0; i < OOB_NAME_MAX_SIZE; i++) {
     if (oob_data.device_name[i] == 0) {
@@ -639,9 +650,6 @@
   oobDataLeBuilder =
       env->CallObjectMethod(oobDataLeBuilder, setNameMethod, deviceName);
 
-  jmethodID buildMethod = env->GetMethodID(leBuilderClass, "build",
-                                           "()Landroid/bluetooth/OobData;");
-
   return env->CallObjectMethod(oobDataLeBuilder, buildMethod);
 }
 
@@ -806,7 +814,7 @@
   }
 
   sCallbackEnv->CallVoidMethod(
-      sJniAdapterServiceObj, method_energyInfo, p_energy_info->status,
+      sJniCallbacksObj, method_energyInfo, p_energy_info->status,
       p_energy_info->ctrl_state, p_energy_info->tx_time, p_energy_info->rx_time,
       p_energy_info->idle_time, p_energy_info->energy_used, array.get());
 }
@@ -833,12 +841,6 @@
                                              switch_codec_callback,
                                              le_rand_callback};
 
-// The callback to call when the wake alarm fires.
-static alarm_cb sAlarmCallback;
-
-// The data to pass to the wake alarm callback.
-static void* sAlarmCallbackData;
-
 class JNIThreadAttacher {
  public:
   JNIThreadAttacher(JavaVM* vm) : vm_(vm), env_(nullptr) {
@@ -885,37 +887,6 @@
   jint status_;
 };
 
-static bool set_wake_alarm_callout(uint64_t delay_millis, bool should_wake,
-                                   alarm_cb cb, void* data) {
-  std::shared_lock<std::shared_timed_mutex> lock(jniObjMutex);
-  if (!sJniAdapterServiceObj) {
-    ALOGE("%s, JNI obj is null. Failed to call JNI callback", __func__);
-    return false;
-  }
-
-  JNIThreadAttacher attacher(vm);
-  JNIEnv* env = attacher.getEnv();
-
-  if (env == nullptr) {
-    ALOGE("%s: Unable to get JNI Env", __func__);
-    return false;
-  }
-
-  sAlarmCallback = cb;
-  sAlarmCallbackData = data;
-
-  jboolean jshould_wake = should_wake ? JNI_TRUE : JNI_FALSE;
-  jboolean ret =
-      env->CallBooleanMethod(sJniAdapterServiceObj, method_setWakeAlarm,
-                             (jlong)delay_millis, jshould_wake);
-  if (!ret) {
-    sAlarmCallback = NULL;
-    sAlarmCallbackData = NULL;
-  }
-
-  return (ret == JNI_TRUE);
-}
-
 static int acquire_wake_lock_callout(const char* lock_name) {
   std::shared_lock<std::shared_timed_mutex> lock(jniObjMutex);
   if (!sJniAdapterServiceObj) {
@@ -936,7 +907,7 @@
     ScopedLocalRef<jstring> lock_name_jni(env, env->NewStringUTF(lock_name));
     if (lock_name_jni.get()) {
       bool acquired = env->CallBooleanMethod(
-          sJniAdapterServiceObj, method_acquireWakeLock, lock_name_jni.get());
+          sJniCallbacksObj, method_acquireWakeLock, lock_name_jni.get());
       if (!acquired) ret = BT_STATUS_WAKELOCK_ERROR;
     } else {
       ALOGE("%s unable to allocate string: %s", __func__, lock_name);
@@ -967,7 +938,7 @@
     ScopedLocalRef<jstring> lock_name_jni(env, env->NewStringUTF(lock_name));
     if (lock_name_jni.get()) {
       bool released = env->CallBooleanMethod(
-          sJniAdapterServiceObj, method_releaseWakeLock, lock_name_jni.get());
+          sJniCallbacksObj, method_releaseWakeLock, lock_name_jni.get());
       if (!released) ret = BT_STATUS_WAKELOCK_ERROR;
     } else {
       ALOGE("%s unable to allocate string: %s", __func__, lock_name);
@@ -978,19 +949,10 @@
   return ret;
 }
 
-// Called by Java code when alarm is fired. A wake lock is held by the caller
-// over the duration of this callback.
-static void alarmFiredNative(JNIEnv* env, jobject obj) {
-  if (sAlarmCallback) {
-    sAlarmCallback(sAlarmCallbackData);
-  } else {
-    ALOGE("%s() - Alarm fired with callback not set!", __func__);
-  }
-}
-
 static bt_os_callouts_t sBluetoothOsCallouts = {
-    sizeof(sBluetoothOsCallouts), set_wake_alarm_callout,
-    acquire_wake_lock_callout, release_wake_lock_callout,
+    sizeof(sBluetoothOsCallouts),
+    acquire_wake_lock_callout,
+    release_wake_lock_callout,
 };
 
 int hal_util_load_bt_library(const bt_interface_t** interface) {
@@ -1030,75 +992,6 @@
 #endif
 }
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  jclass jniUidTrafficClass = env->FindClass("android/bluetooth/UidTraffic");
-  android_bluetooth_UidTraffic.constructor =
-      env->GetMethodID(jniUidTrafficClass, "<init>", "(IJJ)V");
-
-  jclass jniCallbackClass =
-      env->FindClass("com/android/bluetooth/btservice/JniCallbacks");
-  sJniCallbacksField = env->GetFieldID(
-      clazz, "mJniCallbacks", "Lcom/android/bluetooth/btservice/JniCallbacks;");
-
-  method_oobDataReceivedCallback =
-      env->GetMethodID(jniCallbackClass, "oobDataReceivedCallback",
-                       "(ILandroid/bluetooth/OobData;)V");
-
-  method_stateChangeCallback =
-      env->GetMethodID(jniCallbackClass, "stateChangeCallback", "(I)V");
-
-  method_adapterPropertyChangedCallback = env->GetMethodID(
-      jniCallbackClass, "adapterPropertyChangedCallback", "([I[[B)V");
-  method_discoveryStateChangeCallback = env->GetMethodID(
-      jniCallbackClass, "discoveryStateChangeCallback", "(I)V");
-
-  method_devicePropertyChangedCallback = env->GetMethodID(
-      jniCallbackClass, "devicePropertyChangedCallback", "([B[I[[B)V");
-  method_deviceFoundCallback =
-      env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
-  method_pinRequestCallback =
-      env->GetMethodID(jniCallbackClass, "pinRequestCallback", "([B[BIZ)V");
-  method_sspRequestCallback =
-      env->GetMethodID(jniCallbackClass, "sspRequestCallback", "([B[BIII)V");
-
-  method_bondStateChangeCallback =
-      env->GetMethodID(jniCallbackClass, "bondStateChangeCallback", "(I[BII)V");
-
-  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[BIIII)V");
-
-  method_linkQualityReportCallback = env->GetMethodID(
-      jniCallbackClass, "linkQualityReportCallback", "(JIIIIII)V");
-
-  method_switchBufferSizeCallback =
-      env->GetMethodID(jniCallbackClass, "switchBufferSizeCallback", "(Z)V");
-
-  method_switchCodecCallback =
-      env->GetMethodID(jniCallbackClass, "switchCodecCallback", "(Z)V");
-
-  method_setWakeAlarm = env->GetMethodID(clazz, "setWakeAlarm", "(JZ)Z");
-  method_acquireWakeLock =
-      env->GetMethodID(clazz, "acquireWakeLock", "(Ljava/lang/String;)Z");
-  method_releaseWakeLock =
-      env->GetMethodID(clazz, "releaseWakeLock", "(Ljava/lang/String;)Z");
-  method_energyInfo = env->GetMethodID(
-      clazz, "energyInfoCallback", "(IIJJJJ[Landroid/bluetooth/UidTraffic;)V");
-
-  if (env->GetJavaVM(&vm) != JNI_OK) {
-    ALOGE("Could not get JavaVM");
-  }
-
-  if (hal_util_load_bt_library((bt_interface_t const**)&sBluetoothInterface)) {
-    ALOGE("No Bluetooth Library found");
-  }
-}
-
 static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest,
                        jboolean isCommonCriteriaMode, int configCompareResult,
                        jobjectArray initFlags, jboolean isAtvDevice,
@@ -1271,6 +1164,7 @@
                                       const char* methodName) {
   jclass myClass = env->FindClass(className);
   jmethodID myMethod = env->GetMethodID(myClass, methodName, "()[B");
+  env->DeleteLocalRef(myClass);
   return (jbyteArray)env->CallObjectMethod(object, myMethod);
 }
 
@@ -1278,6 +1172,7 @@
                           const char* methodName) {
   jclass myClass = env->FindClass(className);
   jmethodID myMethod = env->GetMethodID(myClass, methodName, "()I");
+  env->DeleteLocalRef(myClass);
   return env->CallIntMethod(object, myMethod);
 }
 
@@ -1741,7 +1636,7 @@
   return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
 }
 
-static int readEnergyInfo() {
+static int readEnergyInfoNative() {
   ALOGV("%s", __func__);
 
   if (!sBluetoothInterface) return JNI_FALSE;
@@ -1970,7 +1865,7 @@
   return;
 }
 
-static jboolean isLogRedactionEnabled(JNIEnv* env, jobject obj) {
+static jboolean isLogRedactionEnabledNative(JNIEnv* env, jobject obj) {
   ALOGV("%s", __func__);
   return bluetooth::os::should_log_be_redacted();
 }
@@ -2191,72 +2086,126 @@
              : JNI_FALSE;
 }
 
-static JNINativeMethod sMethods[] = {
-    /* name, signature, funcPtr */
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "(ZZI[Ljava/lang/String;ZLjava/lang/String;)Z",
-     (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"enableNative", "()Z", (void*)enableNative},
-    {"disableNative", "()Z", (void*)disableNative},
-    {"setAdapterPropertyNative", "(I[B)Z", (void*)setAdapterPropertyNative},
-    {"getAdapterPropertiesNative", "()Z", (void*)getAdapterPropertiesNative},
-    {"getAdapterPropertyNative", "(I)Z", (void*)getAdapterPropertyNative},
-    {"getDevicePropertyNative", "([BI)Z", (void*)getDevicePropertyNative},
-    {"setDevicePropertyNative", "([BI[B)Z", (void*)setDevicePropertyNative},
-    {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
-    {"cancelDiscoveryNative", "()Z", (void*)cancelDiscoveryNative},
-    {"createBondNative", "([BII)Z", (void*)createBondNative},
-    {"createBondOutOfBandNative",
-     "([BILandroid/bluetooth/OobData;Landroid/bluetooth/OobData;)Z",
-     (void*)createBondOutOfBandNative},
-    {"removeBondNative", "([B)Z", (void*)removeBondNative},
-    {"cancelBondNative", "([B)Z", (void*)cancelBondNative},
-    {"generateLocalOobDataNative", "(I)V", (void*)generateLocalOobDataNative},
-    {"getConnectionStateNative", "([B)I", (void*)getConnectionStateNative},
-    {"pinReplyNative", "([BZI[B)Z", (void*)pinReplyNative},
-    {"sspReplyNative", "([BIZI)Z", (void*)sspReplyNative},
-    {"getRemoteServicesNative", "([BI)Z", (void*)getRemoteServicesNative},
-    {"alarmFiredNative", "()V", (void*)alarmFiredNative},
-    {"readEnergyInfo", "()I", (void*)readEnergyInfo},
-    {"dumpNative", "(Ljava/io/FileDescriptor;[Ljava/lang/String;)V",
-     (void*)dumpNative},
-    {"dumpMetricsNative", "()[B", (void*)dumpMetricsNative},
-    {"factoryResetNative", "()Z", (void*)factoryResetNative},
-    {"obfuscateAddressNative", "([B)[B", (void*)obfuscateAddressNative},
-    {"setBufferLengthMillisNative", "(II)Z",
-     (void*)setBufferLengthMillisNative},
-    {"getMetricIdNative", "([B)I", (void*)getMetricIdNative},
-    {"connectSocketNative", "([BI[BIII)I", (void*)connectSocketNative},
-    {"createSocketChannelNative", "(ILjava/lang/String;[BIII)I",
-     (void*)createSocketChannelNative},
-    {"requestMaximumTxDataLengthNative", "([B)V",
-     (void*)requestMaximumTxDataLengthNative},
-    {"allowLowLatencyAudioNative", "(Z[B)Z", (void*)allowLowLatencyAudioNative},
-    {"metadataChangedNative", "([BI[B)V", (void*)metadataChangedNative},
-    {"isLogRedactionEnabled", "()Z", (void*)isLogRedactionEnabled},
-    {"interopMatchAddrNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
-     (void*)interopMatchAddrNative},
-    {"interopMatchNameNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
-     (void*)interopMatchNameNative},
-    {"interopMatchAddrOrNameNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
-     (void*)interopMatchAddrOrNameNative},
-    {"interopDatabaseAddRemoveAddrNative",
-     "(ZLjava/lang/String;Ljava/lang/String;I)V",
-     (void*)interopDatabaseAddRemoveAddrNative},
-    {"interopDatabaseAddRemoveNameNative",
-     "(ZLjava/lang/String;Ljava/lang/String;)V",
-     (void*)interopDatabaseAddRemoveNameNative},
-    {"getRemotePbapPceVersionNative", "(Ljava/lang/String;)I",
-     (void*)getRemotePbapPceVersionNative},
-    {"pbapPseDynamicVersionUpgradeIsEnabledNative", "()Z",
-     (void*)pbapPseDynamicVersionUpgradeIsEnabledNative},
-};
-
 int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env, "com/android/bluetooth/btservice/AdapterService", sMethods,
-      NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initNative", "(ZZI[Ljava/lang/String;ZLjava/lang/String;)Z",
+       (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"enableNative", "()Z", (void*)enableNative},
+      {"disableNative", "()Z", (void*)disableNative},
+      {"setAdapterPropertyNative", "(I[B)Z", (void*)setAdapterPropertyNative},
+      {"getAdapterPropertiesNative", "()Z", (void*)getAdapterPropertiesNative},
+      {"getAdapterPropertyNative", "(I)Z", (void*)getAdapterPropertyNative},
+      {"getDevicePropertyNative", "([BI)Z", (void*)getDevicePropertyNative},
+      {"setDevicePropertyNative", "([BI[B)Z", (void*)setDevicePropertyNative},
+      {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
+      {"cancelDiscoveryNative", "()Z", (void*)cancelDiscoveryNative},
+      {"createBondNative", "([BII)Z", (void*)createBondNative},
+      {"createBondOutOfBandNative",
+       "([BILandroid/bluetooth/OobData;Landroid/bluetooth/OobData;)Z",
+       (void*)createBondOutOfBandNative},
+      {"removeBondNative", "([B)Z", (void*)removeBondNative},
+      {"cancelBondNative", "([B)Z", (void*)cancelBondNative},
+      {"generateLocalOobDataNative", "(I)V", (void*)generateLocalOobDataNative},
+      {"getConnectionStateNative", "([B)I", (void*)getConnectionStateNative},
+      {"pinReplyNative", "([BZI[B)Z", (void*)pinReplyNative},
+      {"sspReplyNative", "([BIZI)Z", (void*)sspReplyNative},
+      {"getRemoteServicesNative", "([BI)Z", (void*)getRemoteServicesNative},
+      {"readEnergyInfoNative", "()I", (void*)readEnergyInfoNative},
+      {"dumpNative", "(Ljava/io/FileDescriptor;[Ljava/lang/String;)V",
+       (void*)dumpNative},
+      {"dumpMetricsNative", "()[B", (void*)dumpMetricsNative},
+      {"factoryResetNative", "()Z", (void*)factoryResetNative},
+      {"obfuscateAddressNative", "([B)[B", (void*)obfuscateAddressNative},
+      {"setBufferLengthMillisNative", "(II)Z",
+       (void*)setBufferLengthMillisNative},
+      {"getMetricIdNative", "([B)I", (void*)getMetricIdNative},
+      {"connectSocketNative", "([BI[BIII)I", (void*)connectSocketNative},
+      {"createSocketChannelNative", "(ILjava/lang/String;[BIII)I",
+       (void*)createSocketChannelNative},
+      {"requestMaximumTxDataLengthNative", "([B)V",
+       (void*)requestMaximumTxDataLengthNative},
+      {"allowLowLatencyAudioNative", "(Z[B)Z",
+       (void*)allowLowLatencyAudioNative},
+      {"metadataChangedNative", "([BI[B)V", (void*)metadataChangedNative},
+      {"isLogRedactionEnabledNative", "()Z",
+       (void*)isLogRedactionEnabledNative},
+      {"interopMatchAddrNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
+       (void*)interopMatchAddrNative},
+      {"interopMatchNameNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
+       (void*)interopMatchNameNative},
+      {"interopMatchAddrOrNameNative",
+       "(Ljava/lang/String;Ljava/lang/String;)Z",
+       (void*)interopMatchAddrOrNameNative},
+      {"interopDatabaseAddRemoveAddrNative",
+       "(ZLjava/lang/String;Ljava/lang/String;I)V",
+       (void*)interopDatabaseAddRemoveAddrNative},
+      {"interopDatabaseAddRemoveNameNative",
+       "(ZLjava/lang/String;Ljava/lang/String;)V",
+       (void*)interopDatabaseAddRemoveNameNative},
+      {"getRemotePbapPceVersionNative", "(Ljava/lang/String;)I",
+       (void*)getRemotePbapPceVersionNative},
+      {"pbapPseDynamicVersionUpgradeIsEnabledNative", "()Z",
+       (void*)pbapPseDynamicVersionUpgradeIsEnabledNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/btservice/AdapterNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  jclass jniAdapterNativeInterfaceClass =
+      env->FindClass("com/android/bluetooth/btservice/AdapterNativeInterface");
+  sJniCallbacksField =
+      env->GetFieldID(jniAdapterNativeInterfaceClass, "mJniCallbacks",
+                      "Lcom/android/bluetooth/btservice/JniCallbacks;");
+  env->DeleteLocalRef(jniAdapterNativeInterfaceClass);
+
+  const JNIJavaMethod javaMethods[] = {
+      {"oobDataReceivedCallback", "(ILandroid/bluetooth/OobData;)V",
+       &method_oobDataReceivedCallback},
+      {"stateChangeCallback", "(I)V", &method_stateChangeCallback},
+      {"adapterPropertyChangedCallback", "([I[[B)V",
+       &method_adapterPropertyChangedCallback},
+      {"discoveryStateChangeCallback", "(I)V",
+       &method_discoveryStateChangeCallback},
+      {"devicePropertyChangedCallback", "([B[I[[B)V",
+       &method_devicePropertyChangedCallback},
+      {"deviceFoundCallback", "([B)V", &method_deviceFoundCallback},
+      {"pinRequestCallback", "([B[BIZ)V", &method_pinRequestCallback},
+      {"sspRequestCallback", "([B[BIII)V", &method_sspRequestCallback},
+      {"bondStateChangeCallback", "(I[BII)V", &method_bondStateChangeCallback},
+      {"addressConsolidateCallback", "([B[B)V",
+       &method_addressConsolidateCallback},
+      {"leAddressAssociateCallback", "([B[B)V",
+       &method_leAddressAssociateCallback},
+      {"aclStateChangeCallback", "(I[BIIII)V", &method_aclStateChangeCallback},
+      {"linkQualityReportCallback", "(JIIIIII)V",
+       &method_linkQualityReportCallback},
+      {"switchBufferSizeCallback", "(Z)V", &method_switchBufferSizeCallback},
+      {"switchCodecCallback", "(Z)V", &method_switchCodecCallback},
+      {"acquireWakeLock", "(Ljava/lang/String;)Z", &method_acquireWakeLock},
+      {"releaseWakeLock", "(Ljava/lang/String;)Z", &method_releaseWakeLock},
+      {"energyInfoCallback", "(IIJJJJ[Landroid/bluetooth/UidTraffic;)V",
+       &method_energyInfo},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/btservice/JniCallbacks",
+                   javaMethods);
+
+  const JNIJavaMethod javaUuidTrafficMethods[] = {
+      {"<init>", "(IJJ)V", &android_bluetooth_UidTraffic.constructor},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/UidTraffic", javaUuidTrafficMethods);
+
+  if (env->GetJavaVM(&vm) != JNI_OK) {
+    ALOGE("Could not get JavaVM");
+  }
+
+  if (hal_util_load_bt_library((bt_interface_t const**)&sBluetoothInterface)) {
+    ALOGE("No Bluetooth Library found");
+  }
+
+  return 0;
 }
 
 } /* namespace android */
@@ -2283,13 +2232,6 @@
   }
 
   status =
-      android::register_com_android_bluetooth_btservice_activity_attribution(e);
-  if (status < 0) {
-    ALOGE("jni activity attribution registration failure: %d", status);
-    return JNI_ERR;
-  }
-
-  status =
       android::register_com_android_bluetooth_btservice_BluetoothKeystore(e);
   if (status < 0) {
     ALOGE("jni BluetoothKeyStore registration failure: %d", status);
@@ -2402,3 +2344,32 @@
 
   return JNI_VERSION_1_6;
 }
+
+namespace android {
+
+/** Load the java methods or die*/
+void jniGetMethodsOrDie(JNIEnv* env, const char* className,
+                        const JNIJavaMethod* methods, int nMethods) {
+  jclass clazz = env->FindClass(className);
+  if (clazz == nullptr) {
+    LOG(FATAL) << "Native registration unable to find class '" << className
+               << "'; aborting...";
+  }
+
+  for (int i = 0; i < nMethods; i++) {
+    const JNIJavaMethod& method = methods[i];
+    if (method.is_static) {
+      *method.id = env->GetStaticMethodID(clazz, method.name, method.signature);
+    } else {
+      *method.id = env->GetMethodID(clazz, method.name, method.signature);
+    }
+    if (method.id == nullptr) {
+      LOG(FATAL) << "In class " << className << ": Unable to find '"
+                 << method.name << "' with signature=" << method.signature
+                 << " is_static=" << method.is_static;
+    }
+  }
+
+  env->DeleteLocalRef(clazz);
+}
+}  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_btservice_BluetoothKeystore.cpp b/android/app/jni/com_android_bluetooth_btservice_BluetoothKeystore.cpp
index bceea1d..fc7d886 100644
--- a/android/app/jni/com_android_bluetooth_btservice_BluetoothKeystore.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_BluetoothKeystore.cpp
@@ -86,17 +86,6 @@
 
 static BluetoothKeystoreCallbacksImpl sBluetoothKeystoreCallbacks;
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_setEncryptKeyOrRemoveKeyCallback =
-      env->GetMethodID(clazz, "setEncryptKeyOrRemoveKeyCallback",
-                       "(Ljava/lang/String;Ljava/lang/String;)V");
-
-  method_getKeyCallback = env->GetMethodID(
-      clazz, "getKeyCallback", "(Ljava/lang/String;)Ljava/lang/String;");
-
-  LOG(INFO) << __func__ << ": succeeds";
-}
-
 static void initNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
@@ -155,17 +144,32 @@
   }
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()V", (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-};
-
 int register_com_android_bluetooth_btservice_BluetoothKeystore(JNIEnv* env) {
-  return jniRegisterNativeMethods(
+  const JNINativeMethod methods[] = {
+      {"initNative", "()V", (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
       env,
       "com/android/bluetooth/btservice/bluetoothkeystore/"
       "BluetoothKeystoreNativeInterface",
-      sMethods, NELEM(sMethods));
+      methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"setEncryptKeyOrRemoveKeyCallback",
+       "(Ljava/lang/String;Ljava/lang/String;)V",
+       &method_setEncryptKeyOrRemoveKeyCallback},
+      {"getKeyCallback", "(Ljava/lang/String;)Ljava/lang/String;",
+       &method_getKeyCallback},
+  };
+  GET_JAVA_METHODS(env,
+                   "com/android/bluetooth/btservice/bluetoothkeystore/"
+                   "BluetoothKeystoreNativeInterface",
+                   javaMethods);
+
+  return 0;
 }
 }  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_csip_set_coordinator.cpp b/android/app/jni/com_android_bluetooth_csip_set_coordinator.cpp
index dd786d4..27b71f6 100644
--- a/android/app/jni/com_android_bluetooth_csip_set_coordinator.cpp
+++ b/android/app/jni/com_android_bluetooth_csip_set_coordinator.cpp
@@ -153,22 +153,6 @@
 
 static CsisClientCallbacksImpl sCsisClientCallbacks;
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_onConnectionStateChanged =
-      env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V");
-
-  method_onDeviceAvailable =
-      env->GetMethodID(clazz, "onDeviceAvailable", "([BIIIJJ)V");
-
-  method_onSetMemberAvailable =
-      env->GetMethodID(clazz, "onSetMemberAvailable", "([BI)V");
-
-  method_onGroupLockChanged =
-      env->GetMethodID(clazz, "onGroupLockChanged", "(IZI)V");
-
-  LOG(INFO) << __func__ << ": succeeds";
-}
-
 static void initNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
@@ -280,18 +264,31 @@
   sCsisClientInterface->LockGroup(group_id, lock);
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()V", (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectNative", "([B)Z", (void*)connectNative},
-    {"disconnectNative", "([B)Z", (void*)disconnectNative},
-    {"groupLockSetNative", "(IZ)V", (void*)groupLockSetNative},
-};
-
 int register_com_android_bluetooth_csip_set_coordinator(JNIEnv* env) {
-  return jniRegisterNativeMethods(
+  const JNINativeMethod methods[] = {
+      {"initNative", "()V", (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectNative", "([B)Z", (void*)connectNative},
+      {"disconnectNative", "([B)Z", (void*)disconnectNative},
+      {"groupLockSetNative", "(IZ)V", (void*)groupLockSetNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
       env, "com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface",
-      sMethods, NELEM(sMethods));
+      methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[]{
+      {"onConnectionStateChanged", "([BI)V", &method_onConnectionStateChanged},
+      {"onDeviceAvailable", "([BIIIJJ)V", &method_onDeviceAvailable},
+      {"onSetMemberAvailable", "([BI)V", &method_onSetMemberAvailable},
+      {"onGroupLockChanged", "(IZI)V", &method_onGroupLockChanged},
+  };
+  GET_JAVA_METHODS(
+      env, "com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface",
+      javaMethods);
+
+  return 0;
 }
 }  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_gatt.cpp b/android/app/jni/com_android_bluetooth_gatt.cpp
index 1c329d6..3aca789 100644
--- a/android/app/jni/com_android_bluetooth_gatt.cpp
+++ b/android/app/jni/com_android_bluetooth_gatt.cpp
@@ -490,14 +490,19 @@
   jmethodID gattDbElementConstructor =
       env->GetMethodID(gattDbElementClazz.get(), "<init>", "()V");
 
-  ScopedLocalRef<jclass> arrayListclazz(env,
-                                        env->FindClass("java/util/ArrayList"));
-  jmethodID arrayAdd =
-      env->GetMethodID(arrayListclazz.get(), "add", "(Ljava/lang/Object;)Z");
+  jmethodID arrayAdd;
 
-  ScopedLocalRef<jclass> uuidClazz(env, env->FindClass("java/util/UUID"));
-  jmethodID uuidConstructor =
-      env->GetMethodID(uuidClazz.get(), "<init>", "(JJ)V");
+  const JNIJavaMethod javaMethods[] = {
+      {"add", "(Ljava/lang/Object;)Z", &arrayAdd},
+  };
+  GET_JAVA_METHODS(env, "java/util/ArrayList", javaMethods);
+
+  jmethodID uuidConstructor;
+
+  const JNIJavaMethod javaUuidMethods[] = {
+      {"<init>", "(JJ)V", &uuidConstructor},
+  };
+  GET_JAVA_METHODS(env, "java/util/UUID", javaUuidMethods);
 
   for (int i = 0; i < count; i++) {
     const btgatt_db_element_t& curr = db[i];
@@ -512,6 +517,7 @@
     fid = env->GetFieldID(gattDbElementClazz.get(), "attributeHandle", "I");
     env->SetIntField(element.get(), fid, curr.attribute_handle);
 
+    ScopedLocalRef<jclass> uuidClazz(env, env->FindClass("java/util/UUID"));
     ScopedLocalRef<jobject> uuid(
         env, env->NewObject(uuidClazz.get(), uuidConstructor,
                             uuid_msb(curr.uuid), uuid_lsb(curr.uuid)));
@@ -1232,121 +1238,6 @@
 /**
  * Native function definitions
  */
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  // Client callbacks
-
-  method_onClientRegistered =
-      env->GetMethodID(clazz, "onClientRegistered", "(IIJJ)V");
-  method_onScannerRegistered =
-      env->GetMethodID(clazz, "onScannerRegistered", "(IIJJ)V");
-  method_onScanResult =
-      env->GetMethodID(clazz, "onScanResult",
-                       "(IILjava/lang/String;IIIIII[BLjava/lang/String;)V");
-  method_onConnected =
-      env->GetMethodID(clazz, "onConnected", "(IIILjava/lang/String;)V");
-  method_onDisconnected =
-      env->GetMethodID(clazz, "onDisconnected", "(IIILjava/lang/String;)V");
-  method_onReadCharacteristic =
-      env->GetMethodID(clazz, "onReadCharacteristic", "(III[B)V");
-  method_onWriteCharacteristic =
-      env->GetMethodID(clazz, "onWriteCharacteristic", "(III[B)V");
-  method_onExecuteCompleted =
-      env->GetMethodID(clazz, "onExecuteCompleted", "(II)V");
-  method_onSearchCompleted =
-      env->GetMethodID(clazz, "onSearchCompleted", "(II)V");
-  method_onReadDescriptor =
-      env->GetMethodID(clazz, "onReadDescriptor", "(III[B)V");
-  method_onWriteDescriptor =
-      env->GetMethodID(clazz, "onWriteDescriptor", "(III[B)V");
-  method_onNotify =
-      env->GetMethodID(clazz, "onNotify", "(ILjava/lang/String;IZ[B)V");
-  method_onRegisterForNotifications =
-      env->GetMethodID(clazz, "onRegisterForNotifications", "(IIII)V");
-  method_onReadRemoteRssi =
-      env->GetMethodID(clazz, "onReadRemoteRssi", "(ILjava/lang/String;II)V");
-  method_onConfigureMTU = env->GetMethodID(clazz, "onConfigureMTU", "(III)V");
-  method_onScanFilterConfig =
-      env->GetMethodID(clazz, "onScanFilterConfig", "(IIIII)V");
-  method_onScanFilterParamsConfigured =
-      env->GetMethodID(clazz, "onScanFilterParamsConfigured", "(IIII)V");
-  method_onScanFilterEnableDisabled =
-      env->GetMethodID(clazz, "onScanFilterEnableDisabled", "(III)V");
-  method_onClientCongestion =
-      env->GetMethodID(clazz, "onClientCongestion", "(IZ)V");
-  method_onBatchScanStorageConfigured =
-      env->GetMethodID(clazz, "onBatchScanStorageConfigured", "(II)V");
-  method_onBatchScanStartStopped =
-      env->GetMethodID(clazz, "onBatchScanStartStopped", "(III)V");
-  method_onBatchScanReports =
-      env->GetMethodID(clazz, "onBatchScanReports", "(IIII[B)V");
-  method_onBatchScanThresholdCrossed =
-      env->GetMethodID(clazz, "onBatchScanThresholdCrossed", "(I)V");
-  method_createOnTrackAdvFoundLostObject =
-      env->GetMethodID(clazz, "createOnTrackAdvFoundLostObject",
-                       "(II[BI[BIIILjava/lang/String;IIII)Lcom/android/"
-                       "bluetooth/gatt/AdvtFilterOnFoundOnLostInfo;");
-  method_onTrackAdvFoundLost = env->GetMethodID(
-      clazz, "onTrackAdvFoundLost",
-      "(Lcom/android/bluetooth/gatt/AdvtFilterOnFoundOnLostInfo;)V");
-  method_onScanParamSetupCompleted =
-      env->GetMethodID(clazz, "onScanParamSetupCompleted", "(II)V");
-  method_getSampleGattDbElement =
-      env->GetMethodID(clazz, "getSampleGattDbElement",
-                       "()Lcom/android/bluetooth/gatt/GattDbElement;");
-  method_onGetGattDb =
-      env->GetMethodID(clazz, "onGetGattDb", "(ILjava/util/ArrayList;)V");
-  method_onClientPhyRead =
-      env->GetMethodID(clazz, "onClientPhyRead", "(ILjava/lang/String;III)V");
-  method_onClientPhyUpdate =
-      env->GetMethodID(clazz, "onClientPhyUpdate", "(IIII)V");
-  method_onClientConnUpdate =
-      env->GetMethodID(clazz, "onClientConnUpdate", "(IIIII)V");
-  method_onServiceChanged =
-      env->GetMethodID(clazz, "onServiceChanged", "(I)V");
-  method_onClientSubrateChange =
-      env->GetMethodID(clazz, "onClientSubrateChange", "(IIIIII)V");
-
-  // Server callbacks
-
-  method_onServerRegistered =
-      env->GetMethodID(clazz, "onServerRegistered", "(IIJJ)V");
-  method_onClientConnected =
-      env->GetMethodID(clazz, "onClientConnected", "(Ljava/lang/String;ZII)V");
-  method_onServiceAdded =
-      env->GetMethodID(clazz, "onServiceAdded", "(IILjava/util/List;)V");
-  method_onServiceStopped =
-      env->GetMethodID(clazz, "onServiceStopped", "(III)V");
-  method_onServiceDeleted =
-      env->GetMethodID(clazz, "onServiceDeleted", "(III)V");
-  method_onResponseSendCompleted =
-      env->GetMethodID(clazz, "onResponseSendCompleted", "(II)V");
-  method_onServerReadCharacteristic = env->GetMethodID(
-      clazz, "onServerReadCharacteristic", "(Ljava/lang/String;IIIIZ)V");
-  method_onServerReadDescriptor = env->GetMethodID(
-      clazz, "onServerReadDescriptor", "(Ljava/lang/String;IIIIZ)V");
-  method_onServerWriteCharacteristic = env->GetMethodID(
-      clazz, "onServerWriteCharacteristic", "(Ljava/lang/String;IIIIIZZ[B)V");
-  method_onServerWriteDescriptor = env->GetMethodID(
-      clazz, "onServerWriteDescriptor", "(Ljava/lang/String;IIIIIZZ[B)V");
-  method_onExecuteWrite =
-      env->GetMethodID(clazz, "onExecuteWrite", "(Ljava/lang/String;III)V");
-  method_onNotificationSent =
-      env->GetMethodID(clazz, "onNotificationSent", "(II)V");
-  method_onServerCongestion =
-      env->GetMethodID(clazz, "onServerCongestion", "(IZ)V");
-  method_onServerMtuChanged = env->GetMethodID(clazz, "onMtuChanged", "(II)V");
-  method_onServerPhyRead =
-      env->GetMethodID(clazz, "onServerPhyRead", "(ILjava/lang/String;III)V");
-  method_onServerPhyUpdate =
-      env->GetMethodID(clazz, "onServerPhyUpdate", "(IIII)V");
-  method_onServerConnUpdate =
-      env->GetMethodID(clazz, "onServerConnUpdate", "(IIIII)V");
-  method_onServerSubrateChange =
-      env->GetMethodID(clazz, "onServerSubrateChange", "(IIIIII)V");
-
-  info("classInitNative: Success!");
-}
-
 static const bt_interface_t* btIf;
 
 static void initializeNative(JNIEnv* env, jobject object) {
@@ -1751,11 +1642,14 @@
                                           jint filter_index) {
   if (!sGattIf) return;
 
-  jclass uuidClazz = env->FindClass("java/util/UUID");
-  jmethodID uuidGetMsb =
-      env->GetMethodID(uuidClazz, "getMostSignificantBits", "()J");
-  jmethodID uuidGetLsb =
-      env->GetMethodID(uuidClazz, "getLeastSignificantBits", "()J");
+  jmethodID uuidGetMsb;
+  jmethodID uuidGetLsb;
+
+  const JNIJavaMethod javaMethods[] = {
+      {"getMostSignificantBits", "()J", &uuidGetMsb},
+      {"getLeastSignificantBits", "()J", &uuidGetLsb},
+  };
+  GET_JAVA_METHODS(env, "java/util/UUID", javaMethods);
 
   std::vector<ApcfCommand> native_filters;
 
@@ -2074,19 +1968,26 @@
                                        jobject gatt_db_elements) {
   if (!sGattIf) return;
 
-  jclass arrayListclazz = env->FindClass("java/util/List");
-  jmethodID arrayGet =
-      env->GetMethodID(arrayListclazz, "get", "(I)Ljava/lang/Object;");
-  jmethodID arraySize = env->GetMethodID(arrayListclazz, "size", "()I");
+  jmethodID arrayGet;
+  jmethodID arraySize;
+
+  const JNIJavaMethod javaListMethods[] = {
+      {"get", "(I)Ljava/lang/Object;", &arrayGet},
+      {"size", "()I", &arraySize},
+  };
+  GET_JAVA_METHODS(env, "java/util/List", javaListMethods);
 
   int count = env->CallIntMethod(gatt_db_elements, arraySize);
   std::vector<btgatt_db_element_t> db;
 
-  jclass uuidClazz = env->FindClass("java/util/UUID");
-  jmethodID uuidGetMsb =
-      env->GetMethodID(uuidClazz, "getMostSignificantBits", "()J");
-  jmethodID uuidGetLsb =
-      env->GetMethodID(uuidClazz, "getLeastSignificantBits", "()J");
+  jmethodID uuidGetMsb;
+  jmethodID uuidGetLsb;
+
+  const JNIJavaMethod javaUuidMethods[] = {
+      {"getMostSignificantBits", "()J", &uuidGetMsb},
+      {"getLeastSignificantBits", "()J", &uuidGetLsb},
+  };
+  GET_JAVA_METHODS(env, "java/util/UUID", javaUuidMethods);
 
   jobject objectForClass =
       env->CallObjectMethod(mCallbacksObj, method_getSampleGattDbElement);
@@ -2219,27 +2120,6 @@
   }
 }
 
-static void advertiseClassInitNative(JNIEnv* env, jclass clazz) {
-  method_onAdvertisingSetStarted =
-      env->GetMethodID(clazz, "onAdvertisingSetStarted", "(IIII)V");
-  method_onOwnAddressRead =
-      env->GetMethodID(clazz, "onOwnAddressRead", "(IILjava/lang/String;)V");
-  method_onAdvertisingEnabled =
-      env->GetMethodID(clazz, "onAdvertisingEnabled", "(IZI)V");
-  method_onAdvertisingDataSet =
-      env->GetMethodID(clazz, "onAdvertisingDataSet", "(II)V");
-  method_onScanResponseDataSet =
-      env->GetMethodID(clazz, "onScanResponseDataSet", "(II)V");
-  method_onAdvertisingParametersUpdated =
-      env->GetMethodID(clazz, "onAdvertisingParametersUpdated", "(III)V");
-  method_onPeriodicAdvertisingParametersUpdated = env->GetMethodID(
-      clazz, "onPeriodicAdvertisingParametersUpdated", "(II)V");
-  method_onPeriodicAdvertisingDataSet =
-      env->GetMethodID(clazz, "onPeriodicAdvertisingDataSet", "(II)V");
-  method_onPeriodicAdvertisingEnabled =
-      env->GetMethodID(clazz, "onPeriodicAdvertisingEnabled", "(IZI)V");
-}
-
 static void advertiseInitializeNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_mutex> lock(callbacks_mutex);
   if (mAdvertiseCallbacksObj != NULL) {
@@ -2549,16 +2429,6 @@
       base::Bind(&enablePeriodicSetCb, advertiser_id, enable));
 }
 
-static void periodicScanClassInitNative(JNIEnv* env, jclass clazz) {
-  method_onSyncStarted =
-      env->GetMethodID(clazz, "onSyncStarted", "(IIIILjava/lang/String;III)V");
-  method_onSyncReport = env->GetMethodID(clazz, "onSyncReport", "(IIII[B)V");
-  method_onSyncLost = env->GetMethodID(clazz, "onSyncLost", "(I)V");
-  method_onSyncTransferredCallback = env->GetMethodID(
-      clazz, "onSyncTransferredCallback", "(IILjava/lang/String;)V");
-  method_onBigInfoReport = env->GetMethodID(clazz, "onBigInfoReport", "(IZ)V");
-}
-
 static void periodicScanInitializeNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_mutex> lock(callbacks_mutex);
   if (mPeriodicScanCallbacksObj != NULL) {
@@ -2633,17 +2503,6 @@
   sGattIf->client->test_command(command, params);
 }
 
-static void distanceMeasurementClassInitNative(JNIEnv* env, jclass clazz) {
-  method_onDistanceMeasurementStarted = env->GetMethodID(
-      clazz, "onDistanceMeasurementStarted", "(Ljava/lang/String;I)V");
-  method_onDistanceMeasurementStartFail = env->GetMethodID(
-      clazz, "onDistanceMeasurementStartFail", "(Ljava/lang/String;II)V");
-  method_onDistanceMeasurementStopped = env->GetMethodID(
-      clazz, "onDistanceMeasurementStopped", "(Ljava/lang/String;II)V");
-  method_onDistanceMeasurementResult = env->GetMethodID(
-      clazz, "onDistanceMeasurementResult", "(Ljava/lang/String;IIIIIII)V");
-}
-
 static void distanceMeasurementInitializeNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_mutex> lock(callbacks_mutex);
   if (mDistanceMeasurementCallbacksObj != NULL) {
@@ -2683,182 +2542,344 @@
  */
 
 // JNI functions defined in AdvertiseManagerNativeInterface class.
-static JNINativeMethod sAdvertiseMethods[] = {
-    {"classInitNative", "()V", (void*)advertiseClassInitNative},
-    {"initializeNative", "()V", (void*)advertiseInitializeNative},
-    {"cleanupNative", "()V", (void*)advertiseCleanupNative},
-    {"startAdvertisingSetNative",
-     "(Landroid/bluetooth/le/AdvertisingSetParameters;[B[BLandroid/bluetooth/"
-     "le/PeriodicAdvertisingParameters;[BIIII)V",
-     (void*)startAdvertisingSetNative},
-    {"stopAdvertisingSetNative", "(I)V", (void*)stopAdvertisingSetNative},
-    {"getOwnAddressNative", "(I)V", (void*)getOwnAddressNative},
-    {"enableAdvertisingSetNative", "(IZII)V",
-     (void*)enableAdvertisingSetNative},
-    {"setAdvertisingDataNative", "(I[B)V", (void*)setAdvertisingDataNative},
-    {"setScanResponseDataNative", "(I[B)V", (void*)setScanResponseDataNative},
-    {"setAdvertisingParametersNative",
-     "(ILandroid/bluetooth/le/AdvertisingSetParameters;)V",
-     (void*)setAdvertisingParametersNative},
-    {"setPeriodicAdvertisingParametersNative",
-     "(ILandroid/bluetooth/le/PeriodicAdvertisingParameters;)V",
-     (void*)setPeriodicAdvertisingParametersNative},
-    {"setPeriodicAdvertisingDataNative", "(I[B)V",
-     (void*)setPeriodicAdvertisingDataNative},
-    {"setPeriodicAdvertisingEnableNative", "(IZ)V",
-     (void*)setPeriodicAdvertisingEnableNative},
-};
 
 // JNI functions defined in PeriodicScanManager class.
-static JNINativeMethod sPeriodicScanMethods[] = {
-    {"classInitNative", "()V", (void*)periodicScanClassInitNative},
-    {"initializeNative", "()V", (void*)periodicScanInitializeNative},
-    {"cleanupNative", "()V", (void*)periodicScanCleanupNative},
-    {"startSyncNative", "(ILjava/lang/String;III)V", (void*)startSyncNative},
-    {"stopSyncNative", "(I)V", (void*)stopSyncNative},
-    {"cancelSyncNative", "(ILjava/lang/String;)V", (void*)cancelSyncNative},
-    {"syncTransferNative", "(ILjava/lang/String;II)V",
-     (void*)syncTransferNative},
-    {"transferSetInfoNative", "(ILjava/lang/String;II)V",
-     (void*)transferSetInfoNative},
-};
-
-// JNI functions defined in ScanNativeInterface class.
-static JNINativeMethod sScanMethods[] = {
-    {"registerScannerNative", "(JJ)V", (void*)registerScannerNative},
-    {"unregisterScannerNative", "(I)V", (void*)unregisterScannerNative},
-    {"gattClientScanNative", "(Z)V", (void*)gattClientScanNative},
-    // Batch scan JNI functions.
-    {"gattClientConfigBatchScanStorageNative", "(IIII)V",
-     (void*)gattClientConfigBatchScanStorageNative},
-    {"gattClientStartBatchScanNative", "(IIIIII)V",
-     (void*)gattClientStartBatchScanNative},
-    {"gattClientStopBatchScanNative", "(I)V",
-     (void*)gattClientStopBatchScanNative},
-    {"gattClientReadScanReportsNative", "(II)V",
-     (void*)gattClientReadScanReportsNative},
-    // Scan filter JNI functions.
-    {"gattClientScanFilterParamAddNative",
-     "(Lcom/android/bluetooth/gatt/FilterParams;)V",
-     (void*)gattClientScanFilterParamAddNative},
-    {"gattClientScanFilterParamDeleteNative", "(II)V",
-     (void*)gattClientScanFilterParamDeleteNative},
-    {"gattClientScanFilterParamClearAllNative", "(I)V",
-     (void*)gattClientScanFilterParamClearAllNative},
-    {"gattClientScanFilterAddNative",
-     "(I[Lcom/android/bluetooth/gatt/ScanFilterQueue$Entry;I)V",
-     (void*)gattClientScanFilterAddNative},
-    {"gattClientScanFilterClearNative", "(II)V",
-     (void*)gattClientScanFilterClearNative},
-    {"gattClientScanFilterEnableNative", "(IZ)V",
-     (void*)gattClientScanFilterEnableNative},
-    {"gattSetScanParametersNative", "(III)V",
-     (void*)gattSetScanParametersNative},
-};
-
 // JNI functions defined in DistanceMeasurementManager class.
-static JNINativeMethod sDistanceMeasurementMethods[] = {
-    {"classInitNative", "()V", (void*)distanceMeasurementClassInitNative},
-    {"initializeNative", "()V", (void*)distanceMeasurementInitializeNative},
-    {"cleanupNative", "()V", (void*)distanceMeasurementCleanupNative},
-    {"startDistanceMeasurementNative", "(Ljava/lang/String;II)V",
-     (void*)startDistanceMeasurementNative},
-    {"stopDistanceMeasurementNative", "(Ljava/lang/String;I)V",
-     (void*)stopDistanceMeasurementNative},
-};
 
 // JNI functions defined in GattNativeInterface class.
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initializeNative", "()V", (void*)initializeNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"gattClientGetDeviceTypeNative", "(Ljava/lang/String;)I",
-     (void*)gattClientGetDeviceTypeNative},
-    {"gattClientRegisterAppNative", "(JJZ)V",
-     (void*)gattClientRegisterAppNative},
-    {"gattClientUnregisterAppNative", "(I)V",
-     (void*)gattClientUnregisterAppNative},
-    {"gattClientConnectNative", "(ILjava/lang/String;IZIZI)V",
-     (void*)gattClientConnectNative},
-    {"gattClientDisconnectNative", "(ILjava/lang/String;I)V",
-     (void*)gattClientDisconnectNative},
-    {"gattClientSetPreferredPhyNative", "(ILjava/lang/String;III)V",
-     (void*)gattClientSetPreferredPhyNative},
-    {"gattClientReadPhyNative", "(ILjava/lang/String;)V",
-     (void*)gattClientReadPhyNative},
-    {"gattClientRefreshNative", "(ILjava/lang/String;)V",
-     (void*)gattClientRefreshNative},
-    {"gattClientSearchServiceNative", "(IZJJ)V",
-     (void*)gattClientSearchServiceNative},
-    {"gattClientDiscoverServiceByUuidNative", "(IJJ)V",
-     (void*)gattClientDiscoverServiceByUuidNative},
-    {"gattClientGetGattDbNative", "(I)V", (void*)gattClientGetGattDbNative},
-    {"gattClientReadCharacteristicNative", "(III)V",
-     (void*)gattClientReadCharacteristicNative},
-    {"gattClientReadUsingCharacteristicUuidNative", "(IJJIII)V",
-     (void*)gattClientReadUsingCharacteristicUuidNative},
-    {"gattClientReadDescriptorNative", "(III)V",
-     (void*)gattClientReadDescriptorNative},
-    {"gattClientWriteCharacteristicNative", "(IIII[B)V",
-     (void*)gattClientWriteCharacteristicNative},
-    {"gattClientWriteDescriptorNative", "(III[B)V",
-     (void*)gattClientWriteDescriptorNative},
-    {"gattClientExecuteWriteNative", "(IZ)V",
-     (void*)gattClientExecuteWriteNative},
-    {"gattClientRegisterForNotificationsNative", "(ILjava/lang/String;IZ)V",
-     (void*)gattClientRegisterForNotificationsNative},
-    {"gattClientReadRemoteRssiNative", "(ILjava/lang/String;)V",
-     (void*)gattClientReadRemoteRssiNative},
-    {"gattClientConfigureMTUNative", "(II)V",
-     (void*)gattClientConfigureMTUNative},
-    {"gattConnectionParameterUpdateNative", "(ILjava/lang/String;IIIIII)V",
-     (void*)gattConnectionParameterUpdateNative},
-    {"gattServerRegisterAppNative", "(JJZ)V",
-     (void*)gattServerRegisterAppNative},
-    {"gattServerUnregisterAppNative", "(I)V",
-     (void*)gattServerUnregisterAppNative},
-    {"gattServerConnectNative", "(ILjava/lang/String;ZI)V",
-     (void*)gattServerConnectNative},
-    {"gattServerDisconnectNative", "(ILjava/lang/String;I)V",
-     (void*)gattServerDisconnectNative},
-    {"gattServerSetPreferredPhyNative", "(ILjava/lang/String;III)V",
-     (void*)gattServerSetPreferredPhyNative},
-    {"gattServerReadPhyNative", "(ILjava/lang/String;)V",
-     (void*)gattServerReadPhyNative},
-    {"gattServerAddServiceNative", "(ILjava/util/List;)V",
-     (void*)gattServerAddServiceNative},
-    {"gattServerStopServiceNative", "(II)V",
-     (void*)gattServerStopServiceNative},
-    {"gattServerDeleteServiceNative", "(II)V",
-     (void*)gattServerDeleteServiceNative},
-    {"gattServerSendIndicationNative", "(III[B)V",
-     (void*)gattServerSendIndicationNative},
-    {"gattServerSendNotificationNative", "(III[B)V",
-     (void*)gattServerSendNotificationNative},
-    {"gattServerSendResponseNative", "(IIIIII[BI)V",
-     (void*)gattServerSendResponseNative},
-    {"gattSubrateRequestNative", "(ILjava/lang/String;IIIII)V",
-     (void*)gattSubrateRequestNative},
+static int register_com_android_bluetooth_gatt_scan(JNIEnv* env) {
+  const JNINativeMethod methods[] = {
+      {"registerScannerNative", "(JJ)V", (void*)registerScannerNative},
+      {"unregisterScannerNative", "(I)V", (void*)unregisterScannerNative},
+      {"gattClientScanNative", "(Z)V", (void*)gattClientScanNative},
+      // Batch scan JNI functions.
+      {"gattClientConfigBatchScanStorageNative", "(IIII)V",
+       (void*)gattClientConfigBatchScanStorageNative},
+      {"gattClientStartBatchScanNative", "(IIIIII)V",
+       (void*)gattClientStartBatchScanNative},
+      {"gattClientStopBatchScanNative", "(I)V",
+       (void*)gattClientStopBatchScanNative},
+      {"gattClientReadScanReportsNative", "(II)V",
+       (void*)gattClientReadScanReportsNative},
+      // Scan filter JNI functions.
+      {"gattClientScanFilterParamAddNative",
+       "(Lcom/android/bluetooth/gatt/FilterParams;)V",
+       (void*)gattClientScanFilterParamAddNative},
+      {"gattClientScanFilterParamDeleteNative", "(II)V",
+       (void*)gattClientScanFilterParamDeleteNative},
+      {"gattClientScanFilterParamClearAllNative", "(I)V",
+       (void*)gattClientScanFilterParamClearAllNative},
+      {"gattClientScanFilterAddNative",
+       "(I[Lcom/android/bluetooth/gatt/ScanFilterQueue$Entry;I)V",
+       (void*)gattClientScanFilterAddNative},
+      {"gattClientScanFilterClearNative", "(II)V",
+       (void*)gattClientScanFilterClearNative},
+      {"gattClientScanFilterEnableNative", "(IZ)V",
+       (void*)gattClientScanFilterEnableNative},
+      {"gattSetScanParametersNative", "(III)V",
+       (void*)gattSetScanParametersNative},
+  };
+  return REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/gatt/ScanNativeInterface", methods);
+}
 
-    {"gattTestNative", "(IJJLjava/lang/String;IIIII)V", (void*)gattTestNative},
-};
+static int register_com_android_bluetooth_gatt_advertise_manager(JNIEnv* env) {
+  const JNINativeMethod methods[] = {
+      {"initializeNative", "()V", (void*)advertiseInitializeNative},
+      {"cleanupNative", "()V", (void*)advertiseCleanupNative},
+      {"startAdvertisingSetNative",
+       "(Landroid/bluetooth/le/AdvertisingSetParameters;"
+       "[B[BLandroid/bluetooth/le/PeriodicAdvertisingParameters;[BIIII)V",
+       (void*)startAdvertisingSetNative},
+      {"stopAdvertisingSetNative", "(I)V", (void*)stopAdvertisingSetNative},
+      {"getOwnAddressNative", "(I)V", (void*)getOwnAddressNative},
+      {"enableAdvertisingSetNative", "(IZII)V",
+       (void*)enableAdvertisingSetNative},
+      {"setAdvertisingDataNative", "(I[B)V", (void*)setAdvertisingDataNative},
+      {"setScanResponseDataNative", "(I[B)V", (void*)setScanResponseDataNative},
+      {"setAdvertisingParametersNative",
+       "(ILandroid/bluetooth/le/AdvertisingSetParameters;)V",
+       (void*)setAdvertisingParametersNative},
+      {"setPeriodicAdvertisingParametersNative",
+       "(ILandroid/bluetooth/le/PeriodicAdvertisingParameters;)V",
+       (void*)setPeriodicAdvertisingParametersNative},
+      {"setPeriodicAdvertisingDataNative", "(I[B)V",
+       (void*)setPeriodicAdvertisingDataNative},
+      {"setPeriodicAdvertisingEnableNative", "(IZ)V",
+       (void*)setPeriodicAdvertisingEnableNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/gatt/AdvertiseManagerNativeInterface",
+      methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onAdvertisingSetStarted", "(IIII)V", &method_onAdvertisingSetStarted},
+      {"onOwnAddressRead", "(IILjava/lang/String;)V", &method_onOwnAddressRead},
+      {"onAdvertisingEnabled", "(IZI)V", &method_onAdvertisingEnabled},
+      {"onAdvertisingDataSet", "(II)V", &method_onAdvertisingDataSet},
+      {"onScanResponseDataSet", "(II)V", &method_onScanResponseDataSet},
+      {"onAdvertisingParametersUpdated", "(III)V",
+       &method_onAdvertisingParametersUpdated},
+      {"onPeriodicAdvertisingParametersUpdated", "(II)V",
+       &method_onPeriodicAdvertisingParametersUpdated},
+      {"onPeriodicAdvertisingDataSet", "(II)V",
+       &method_onPeriodicAdvertisingDataSet},
+      {"onPeriodicAdvertisingEnabled", "(IZI)V",
+       &method_onPeriodicAdvertisingEnabled},
+  };
+  GET_JAVA_METHODS(env,
+                   "com/android/bluetooth/gatt/AdvertiseManagerNativeInterface",
+                   javaMethods);
+
+  return 0;
+}
+
+static int register_com_android_bluetooth_gatt_periodic_scan(JNIEnv* env) {
+  const JNINativeMethod methods[] = {
+      {"initializeNative", "()V", (void*)periodicScanInitializeNative},
+      {"cleanupNative", "()V", (void*)periodicScanCleanupNative},
+      {"startSyncNative", "(ILjava/lang/String;III)V", (void*)startSyncNative},
+      {"stopSyncNative", "(I)V", (void*)stopSyncNative},
+      {"cancelSyncNative", "(ILjava/lang/String;)V", (void*)cancelSyncNative},
+      {"syncTransferNative", "(ILjava/lang/String;II)V",
+       (void*)syncTransferNative},
+      {"transferSetInfoNative", "(ILjava/lang/String;II)V",
+       (void*)transferSetInfoNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/gatt/PeriodicScanNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onSyncStarted", "(IIIILjava/lang/String;III)V", &method_onSyncStarted},
+      {"onSyncReport", "(IIII[B)V", &method_onSyncReport},
+      {"onSyncLost", "(I)V", &method_onSyncLost},
+      {"onSyncTransferredCallback", "(IILjava/lang/String;)V",
+       &method_onSyncTransferredCallback},
+      {"onBigInfoReport", "(IZ)V", &method_onBigInfoReport},
+  };
+  GET_JAVA_METHODS(env,
+                   "com/android/bluetooth/gatt/PeriodicScanNativeInterface",
+                   javaMethods);
+
+  return 0;
+}
+
+static int register_com_android_bluetooth_gatt_distance_measurement(
+    JNIEnv* env) {
+  const JNINativeMethod methods[] = {
+      {"initializeNative", "()V", (void*)distanceMeasurementInitializeNative},
+      {"cleanupNative", "()V", (void*)distanceMeasurementCleanupNative},
+      {"startDistanceMeasurementNative", "(Ljava/lang/String;II)V",
+       (void*)startDistanceMeasurementNative},
+      {"stopDistanceMeasurementNative", "(Ljava/lang/String;I)V",
+       (void*)stopDistanceMeasurementNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/gatt/DistanceMeasurementNativeInterface",
+      methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onDistanceMeasurementStarted", "(Ljava/lang/String;I)V",
+       &method_onDistanceMeasurementStarted},
+      {"onDistanceMeasurementStartFail", "(Ljava/lang/String;II)V",
+       &method_onDistanceMeasurementStartFail},
+      {"onDistanceMeasurementStopped", "(Ljava/lang/String;II)V",
+       &method_onDistanceMeasurementStopped},
+      {"onDistanceMeasurementResult", "(Ljava/lang/String;IIIIIII)V",
+       &method_onDistanceMeasurementResult},
+  };
+  GET_JAVA_METHODS(
+      env, "com/android/bluetooth/gatt/DistanceMeasurementNativeInterface",
+      javaMethods);
+
+  return 0;
+}
+
+static int register_com_android_bluetooth_gatt_(JNIEnv* env) {
+  const JNINativeMethod methods[] = {
+      {"initializeNative", "()V", (void*)initializeNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"gattClientGetDeviceTypeNative", "(Ljava/lang/String;)I",
+       (void*)gattClientGetDeviceTypeNative},
+      {"gattClientRegisterAppNative", "(JJZ)V",
+       (void*)gattClientRegisterAppNative},
+      {"gattClientUnregisterAppNative", "(I)V",
+       (void*)gattClientUnregisterAppNative},
+      {"gattClientConnectNative", "(ILjava/lang/String;IZIZI)V",
+       (void*)gattClientConnectNative},
+      {"gattClientDisconnectNative", "(ILjava/lang/String;I)V",
+       (void*)gattClientDisconnectNative},
+      {"gattClientSetPreferredPhyNative", "(ILjava/lang/String;III)V",
+       (void*)gattClientSetPreferredPhyNative},
+      {"gattClientReadPhyNative", "(ILjava/lang/String;)V",
+       (void*)gattClientReadPhyNative},
+      {"gattClientRefreshNative", "(ILjava/lang/String;)V",
+       (void*)gattClientRefreshNative},
+      {"gattClientSearchServiceNative", "(IZJJ)V",
+       (void*)gattClientSearchServiceNative},
+      {"gattClientDiscoverServiceByUuidNative", "(IJJ)V",
+       (void*)gattClientDiscoverServiceByUuidNative},
+      {"gattClientGetGattDbNative", "(I)V", (void*)gattClientGetGattDbNative},
+      {"gattClientReadCharacteristicNative", "(III)V",
+       (void*)gattClientReadCharacteristicNative},
+      {"gattClientReadUsingCharacteristicUuidNative", "(IJJIII)V",
+       (void*)gattClientReadUsingCharacteristicUuidNative},
+      {"gattClientReadDescriptorNative", "(III)V",
+       (void*)gattClientReadDescriptorNative},
+      {"gattClientWriteCharacteristicNative", "(IIII[B)V",
+       (void*)gattClientWriteCharacteristicNative},
+      {"gattClientWriteDescriptorNative", "(III[B)V",
+       (void*)gattClientWriteDescriptorNative},
+      {"gattClientExecuteWriteNative", "(IZ)V",
+       (void*)gattClientExecuteWriteNative},
+      {"gattClientRegisterForNotificationsNative", "(ILjava/lang/String;IZ)V",
+       (void*)gattClientRegisterForNotificationsNative},
+      {"gattClientReadRemoteRssiNative", "(ILjava/lang/String;)V",
+       (void*)gattClientReadRemoteRssiNative},
+      {"gattClientConfigureMTUNative", "(II)V",
+       (void*)gattClientConfigureMTUNative},
+      {"gattConnectionParameterUpdateNative", "(ILjava/lang/String;IIIIII)V",
+       (void*)gattConnectionParameterUpdateNative},
+      {"gattServerRegisterAppNative", "(JJZ)V",
+       (void*)gattServerRegisterAppNative},
+      {"gattServerUnregisterAppNative", "(I)V",
+       (void*)gattServerUnregisterAppNative},
+      {"gattServerConnectNative", "(ILjava/lang/String;ZI)V",
+       (void*)gattServerConnectNative},
+      {"gattServerDisconnectNative", "(ILjava/lang/String;I)V",
+       (void*)gattServerDisconnectNative},
+      {"gattServerSetPreferredPhyNative", "(ILjava/lang/String;III)V",
+       (void*)gattServerSetPreferredPhyNative},
+      {"gattServerReadPhyNative", "(ILjava/lang/String;)V",
+       (void*)gattServerReadPhyNative},
+      {"gattServerAddServiceNative", "(ILjava/util/List;)V",
+       (void*)gattServerAddServiceNative},
+      {"gattServerStopServiceNative", "(II)V",
+       (void*)gattServerStopServiceNative},
+      {"gattServerDeleteServiceNative", "(II)V",
+       (void*)gattServerDeleteServiceNative},
+      {"gattServerSendIndicationNative", "(III[B)V",
+       (void*)gattServerSendIndicationNative},
+      {"gattServerSendNotificationNative", "(III[B)V",
+       (void*)gattServerSendNotificationNative},
+      {"gattServerSendResponseNative", "(IIIIII[BI)V",
+       (void*)gattServerSendResponseNative},
+      {"gattSubrateRequestNative", "(ILjava/lang/String;IIIII)V",
+       (void*)gattSubrateRequestNative},
+
+      {"gattTestNative", "(IJJLjava/lang/String;IIIII)V",
+       (void*)gattTestNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/gatt/GattNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      // Client callbacks
+      {"onClientRegistered", "(IIJJ)V", &method_onClientRegistered},
+      {"onScannerRegistered", "(IIJJ)V", &method_onScannerRegistered},
+      {"onScanResult", "(IILjava/lang/String;IIIIII[BLjava/lang/String;)V",
+       &method_onScanResult},
+      {"onConnected", "(IIILjava/lang/String;)V", &method_onConnected},
+      {"onDisconnected", "(IIILjava/lang/String;)V", &method_onDisconnected},
+      {"onReadCharacteristic", "(III[B)V", &method_onReadCharacteristic},
+      {"onWriteCharacteristic", "(III[B)V", &method_onWriteCharacteristic},
+      {"onExecuteCompleted", "(II)V", &method_onExecuteCompleted},
+      {"onSearchCompleted", "(II)V", &method_onSearchCompleted},
+      {"onReadDescriptor", "(III[B)V", &method_onReadDescriptor},
+      {"onWriteDescriptor", "(III[B)V", &method_onWriteDescriptor},
+      {"onNotify", "(ILjava/lang/String;IZ[B)V", &method_onNotify},
+      {"onRegisterForNotifications", "(IIII)V",
+       &method_onRegisterForNotifications},
+      {"onReadRemoteRssi", "(ILjava/lang/String;II)V",
+       &method_onReadRemoteRssi},
+      {"onConfigureMTU", "(III)V", &method_onConfigureMTU},
+      {"onScanFilterConfig", "(IIIII)V", &method_onScanFilterConfig},
+      {"onScanFilterParamsConfigured", "(IIII)V",
+       &method_onScanFilterParamsConfigured},
+      {"onScanFilterEnableDisabled", "(III)V",
+       &method_onScanFilterEnableDisabled},
+      {"onClientCongestion", "(IZ)V", &method_onClientCongestion},
+      {"onBatchScanStorageConfigured", "(II)V",
+       &method_onBatchScanStorageConfigured},
+      {"onBatchScanStartStopped", "(III)V", &method_onBatchScanStartStopped},
+      {"onBatchScanReports", "(IIII[B)V", &method_onBatchScanReports},
+      {"onBatchScanThresholdCrossed", "(I)V",
+       &method_onBatchScanThresholdCrossed},
+      {"createOnTrackAdvFoundLostObject",
+       "(II[BI[BIIILjava/lang/String;IIII)"
+       "Lcom/android/bluetooth/gatt/AdvtFilterOnFoundOnLostInfo;",
+       &method_createOnTrackAdvFoundLostObject},
+      {"onTrackAdvFoundLost",
+       "(Lcom/android/bluetooth/gatt/AdvtFilterOnFoundOnLostInfo;)V",
+       &method_onTrackAdvFoundLost},
+      {"onScanParamSetupCompleted", "(II)V", &method_onScanParamSetupCompleted},
+      {"getSampleGattDbElement", "()Lcom/android/bluetooth/gatt/GattDbElement;",
+       &method_getSampleGattDbElement},
+      {"onGetGattDb", "(ILjava/util/ArrayList;)V", &method_onGetGattDb},
+      {"onClientPhyRead", "(ILjava/lang/String;III)V", &method_onClientPhyRead},
+      {"onClientPhyUpdate", "(IIII)V", &method_onClientPhyUpdate},
+      {"onClientConnUpdate", "(IIIII)V", &method_onClientConnUpdate},
+      {"onServiceChanged", "(I)V", &method_onServiceChanged},
+      {"onClientSubrateChange", "(IIIIII)V", &method_onClientSubrateChange},
+
+      // Server callbacks
+      {"onServerRegistered", "(IIJJ)V", &method_onServerRegistered},
+      {"onClientConnected", "(Ljava/lang/String;ZII)V",
+       &method_onClientConnected},
+      {"onServiceAdded", "(IILjava/util/List;)V", &method_onServiceAdded},
+      {"onServiceStopped", "(III)V", &method_onServiceStopped},
+      {"onServiceDeleted", "(III)V", &method_onServiceDeleted},
+      {"onResponseSendCompleted", "(II)V", &method_onResponseSendCompleted},
+      {"onServerReadCharacteristic", "(Ljava/lang/String;IIIIZ)V",
+       &method_onServerReadCharacteristic},
+      {"onServerReadDescriptor", "(Ljava/lang/String;IIIIZ)V",
+       &method_onServerReadDescriptor},
+      {"onServerWriteCharacteristic", "(Ljava/lang/String;IIIIIZZ[B)V",
+       &method_onServerWriteCharacteristic},
+      {"onServerWriteDescriptor", "(Ljava/lang/String;IIIIIZZ[B)V",
+       &method_onServerWriteDescriptor},
+      {"onExecuteWrite", "(Ljava/lang/String;III)V", &method_onExecuteWrite},
+      {"onNotificationSent", "(II)V", &method_onNotificationSent},
+      {"onServerCongestion", "(IZ)V", &method_onServerCongestion},
+      {"onMtuChanged", "(II)V", &method_onServerMtuChanged},
+      {"onServerPhyRead", "(ILjava/lang/String;III)V", &method_onServerPhyRead},
+      {"onServerPhyUpdate", "(IIII)V", &method_onServerPhyUpdate},
+      {"onServerConnUpdate", "(IIIII)V", &method_onServerConnUpdate},
+      {"onServerSubrateChange", "(IIIIII)V", &method_onServerSubrateChange},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/gatt/GattNativeInterface",
+                   javaMethods);
+
+  return 0;
+}
 
 int register_com_android_bluetooth_gatt(JNIEnv* env) {
-  int register_success = jniRegisterNativeMethods(
-      env, "com/android/bluetooth/gatt/ScanNativeInterface", sScanMethods,
-      NELEM(sScanMethods));
-  register_success &= jniRegisterNativeMethods(
-      env, "com/android/bluetooth/gatt/AdvertiseManagerNativeInterface",
-      sAdvertiseMethods, NELEM(sAdvertiseMethods));
-  register_success &= jniRegisterNativeMethods(
-      env, "com/android/bluetooth/gatt/PeriodicScanManager",
-      sPeriodicScanMethods, NELEM(sPeriodicScanMethods));
-  register_success &= jniRegisterNativeMethods(
-      env, "com/android/bluetooth/gatt/DistanceMeasurementNativeInterface",
-      sDistanceMeasurementMethods, NELEM(sDistanceMeasurementMethods));
-  return register_success &
-         jniRegisterNativeMethods(
-             env, "com/android/bluetooth/gatt/GattNativeInterface", sMethods,
-             NELEM(sMethods));
+  const std::array<std::function<int(JNIEnv*)>, 5> register_fns = {
+      register_com_android_bluetooth_gatt_scan,
+      register_com_android_bluetooth_gatt_advertise_manager,
+      register_com_android_bluetooth_gatt_periodic_scan,
+      register_com_android_bluetooth_gatt_distance_measurement,
+      register_com_android_bluetooth_gatt_,
+  };
+
+  for (const auto& fn : register_fns) {
+    const int result = fn(env);
+    if (result != 0) {
+      return result;
+    }
+  }
+  return 0;
 }
 }  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_hap_client.cpp b/android/app/jni/com_android_bluetooth_hap_client.cpp
index 1d77a27..b64b776 100644
--- a/android/app/jni/com_android_bluetooth_hap_client.cpp
+++ b/android/app/jni/com_android_bluetooth_hap_client.cpp
@@ -301,59 +301,6 @@
 
 static HasClientCallbacksImpl sHasClientCallbacks;
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  jclass jniBluetoothBluetoothHapPresetInfoClass =
-      env->FindClass("android/bluetooth/BluetoothHapPresetInfo");
-  CHECK(jniBluetoothBluetoothHapPresetInfoClass != NULL);
-
-  android_bluetooth_BluetoothHapPresetInfo.constructor =
-      env->GetMethodID(jniBluetoothBluetoothHapPresetInfoClass, "<init>",
-                       "(ILjava/lang/String;ZZ)V");
-
-  method_onConnectionStateChanged =
-      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
-
-  method_onDeviceAvailable =
-      env->GetMethodID(clazz, "onDeviceAvailable", "([BI)V");
-
-  method_onFeaturesUpdate =
-      env->GetMethodID(clazz, "onFeaturesUpdate", "([BI)V");
-
-  method_onActivePresetSelected =
-      env->GetMethodID(clazz, "onActivePresetSelected", "([BI)V");
-
-  method_onGroupActivePresetSelected =
-      env->GetMethodID(clazz, "onActivePresetGroupSelected", "(II)V");
-
-  method_onActivePresetSelectError =
-      env->GetMethodID(clazz, "onActivePresetSelectError", "([BI)V");
-
-  method_onGroupActivePresetSelectError =
-      env->GetMethodID(clazz, "onActivePresetGroupSelectError", "(II)V");
-
-  method_onPresetInfo =
-      env->GetMethodID(clazz, "onPresetInfo",
-                       "([BI[Landroid/bluetooth/BluetoothHapPresetInfo;)V");
-
-  method_onGroupPresetInfo =
-      env->GetMethodID(clazz, "onGroupPresetInfo",
-                       "(II[Landroid/bluetooth/BluetoothHapPresetInfo;)V");
-
-  method_onPresetNameSetError =
-      env->GetMethodID(clazz, "onPresetNameSetError", "([BII)V");
-
-  method_onGroupPresetNameSetError =
-      env->GetMethodID(clazz, "onGroupPresetNameSetError", "(III)V");
-
-  method_onPresetInfoError =
-      env->GetMethodID(clazz, "onPresetInfoError", "([BII)V");
-
-  method_onGroupPresetInfoError =
-      env->GetMethodID(clazz, "onGroupPresetInfoError", "(III)V");
-
-  LOG(INFO) << __func__ << ": succeeds";
-}
-
 static void initNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
@@ -616,30 +563,65 @@
                                      std::move(name_str));
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()V", (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectHapClientNative", "([B)Z", (void*)connectHapClientNative},
-    {"disconnectHapClientNative", "([B)Z", (void*)disconnectHapClientNative},
-    {"selectActivePresetNative", "([BI)V", (void*)selectActivePresetNative},
-    {"groupSelectActivePresetNative", "(II)V",
-     (void*)groupSelectActivePresetNative},
-    {"nextActivePresetNative", "([B)V", (void*)nextActivePresetNative},
-    {"groupNextActivePresetNative", "(I)V", (void*)groupNextActivePresetNative},
-    {"previousActivePresetNative", "([B)V", (void*)previousActivePresetNative},
-    {"groupPreviousActivePresetNative", "(I)V",
-     (void*)groupPreviousActivePresetNative},
-    {"getPresetInfoNative", "([BI)V", (void*)getPresetInfoNative},
-    {"setPresetNameNative", "([BILjava/lang/String;)V",
-     (void*)setPresetNameNative},
-    {"groupSetPresetNameNative", "(IILjava/lang/String;)V",
-     (void*)groupSetPresetNameNative},
-};
-
 int register_com_android_bluetooth_hap_client(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env, "com/android/bluetooth/hap/HapClientNativeInterface", sMethods,
-      NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initNative", "()V", (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectHapClientNative", "([B)Z", (void*)connectHapClientNative},
+      {"disconnectHapClientNative", "([B)Z", (void*)disconnectHapClientNative},
+      {"selectActivePresetNative", "([BI)V", (void*)selectActivePresetNative},
+      {"groupSelectActivePresetNative", "(II)V",
+       (void*)groupSelectActivePresetNative},
+      {"nextActivePresetNative", "([B)V", (void*)nextActivePresetNative},
+      {"groupNextActivePresetNative", "(I)V",
+       (void*)groupNextActivePresetNative},
+      {"previousActivePresetNative", "([B)V",
+       (void*)previousActivePresetNative},
+      {"groupPreviousActivePresetNative", "(I)V",
+       (void*)groupPreviousActivePresetNative},
+      {"getPresetInfoNative", "([BI)V", (void*)getPresetInfoNative},
+      {"setPresetNameNative", "([BILjava/lang/String;)V",
+       (void*)setPresetNameNative},
+      {"groupSetPresetNameNative", "(IILjava/lang/String;)V",
+       (void*)groupSetPresetNameNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/hap/HapClientNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
+      {"onDeviceAvailable", "([BI)V", &method_onDeviceAvailable},
+      {"onFeaturesUpdate", "([BI)V", &method_onFeaturesUpdate},
+      {"onActivePresetSelected", "([BI)V", &method_onActivePresetSelected},
+      {"onActivePresetGroupSelected", "(II)V",
+       &method_onGroupActivePresetSelected},
+      {"onActivePresetSelectError", "([BI)V",
+       &method_onActivePresetSelectError},
+      {"onActivePresetGroupSelectError", "(II)V",
+       &method_onGroupActivePresetSelectError},
+      {"onPresetInfo", "([BI[Landroid/bluetooth/BluetoothHapPresetInfo;)V",
+       &method_onPresetInfo},
+      {"onGroupPresetInfo", "(II[Landroid/bluetooth/BluetoothHapPresetInfo;)V",
+       &method_onGroupPresetInfo},
+      {"onPresetNameSetError", "([BII)V", &method_onPresetNameSetError},
+      {"onGroupPresetNameSetError", "(III)V",
+       &method_onGroupPresetNameSetError},
+      {"onPresetInfoError", "([BII)V", &method_onPresetInfoError},
+      {"onGroupPresetInfoError", "(III)V", &method_onGroupPresetInfoError},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/hap/HapClientNativeInterface",
+                   javaMethods);
+
+  const JNIJavaMethod javaHapPresetMethods[] = {
+      {"<init>", "(ILjava/lang/String;ZZ)V",
+       &android_bluetooth_BluetoothHapPresetInfo.constructor},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/BluetoothHapPresetInfo",
+                   javaHapPresetMethods);
+
+  return 0;
 }
 }  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_hearing_aid.cpp b/android/app/jni/com_android_bluetooth_hearing_aid.cpp
index 5e2f88e..6ec9f16 100644
--- a/android/app/jni/com_android_bluetooth_hearing_aid.cpp
+++ b/android/app/jni/com_android_bluetooth_hearing_aid.cpp
@@ -87,16 +87,6 @@
 
 static HearingAidCallbacksImpl sHearingAidCallbacks;
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_onConnectionStateChanged =
-      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
-
-  method_onDeviceAvailable =
-      env->GetMethodID(clazz, "onDeviceAvailable", "(BJ[B)V");
-
-  LOG(INFO) << __func__ << ": succeeds";
-}
-
 static void initNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
@@ -216,19 +206,31 @@
   sHearingAidInterface->SetVolume(volume);
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()V", (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectHearingAidNative", "([B)Z", (void*)connectHearingAidNative},
-    {"disconnectHearingAidNative", "([B)Z", (void*)disconnectHearingAidNative},
-    {"addToAcceptlistNative", "([B)Z", (void*)addToAcceptlistNative},
-    {"setVolumeNative", "(I)V", (void*)setVolumeNative},
-};
-
 int register_com_android_bluetooth_hearing_aid(JNIEnv* env) {
-  return jniRegisterNativeMethods(
+  const JNINativeMethod methods[] = {
+      {"initNative", "()V", (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectHearingAidNative", "([B)Z", (void*)connectHearingAidNative},
+      {"disconnectHearingAidNative", "([B)Z",
+       (void*)disconnectHearingAidNative},
+      {"addToAcceptlistNative", "([B)Z", (void*)addToAcceptlistNative},
+      {"setVolumeNative", "(I)V", (void*)setVolumeNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
       env, "com/android/bluetooth/hearingaid/HearingAidNativeInterface",
-      sMethods, NELEM(sMethods));
+      methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
+      {"onDeviceAvailable", "(BJ[B)V", &method_onDeviceAvailable},
+  };
+  GET_JAVA_METHODS(env,
+                   "com/android/bluetooth/hearingaid/HearingAidNativeInterface",
+                   javaMethods);
+
+  return 0;
 }
 }  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_hfp.cpp b/android/app/jni/com_android_bluetooth_hfp.cpp
index d6c39f2..feca3f6 100644
--- a/android/app/jni/com_android_bluetooth_hfp.cpp
+++ b/android/app/jni/com_android_bluetooth_hfp.cpp
@@ -419,40 +419,6 @@
   }
 };
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_onConnectionStateChanged =
-      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
-  method_onAudioStateChanged =
-      env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
-  method_onVrStateChanged =
-      env->GetMethodID(clazz, "onVrStateChanged", "(I[B)V");
-  method_onAnswerCall = env->GetMethodID(clazz, "onAnswerCall", "([B)V");
-  method_onHangupCall = env->GetMethodID(clazz, "onHangupCall", "([B)V");
-  method_onVolumeChanged =
-      env->GetMethodID(clazz, "onVolumeChanged", "(II[B)V");
-  method_onDialCall =
-      env->GetMethodID(clazz, "onDialCall", "(Ljava/lang/String;[B)V");
-  method_onSendDtmf = env->GetMethodID(clazz, "onSendDtmf", "(I[B)V");
-  method_onNoiseReductionEnable =
-      env->GetMethodID(clazz, "onNoiseReductionEnable", "(Z[B)V");
-  method_onWBS = env->GetMethodID(clazz, "onWBS", "(I[B)V");
-  method_onSWB = env->GetMethodID(clazz, "onSWB", "(I[B)V");
-  method_onAtChld = env->GetMethodID(clazz, "onAtChld", "(I[B)V");
-  method_onAtCnum = env->GetMethodID(clazz, "onAtCnum", "([B)V");
-  method_onAtCind = env->GetMethodID(clazz, "onAtCind", "([B)V");
-  method_onAtCops = env->GetMethodID(clazz, "onAtCops", "([B)V");
-  method_onAtClcc = env->GetMethodID(clazz, "onAtClcc", "([B)V");
-  method_onUnknownAt =
-      env->GetMethodID(clazz, "onUnknownAt", "(Ljava/lang/String;[B)V");
-  method_onKeyPressed = env->GetMethodID(clazz, "onKeyPressed", "([B)V");
-  method_onAtBind =
-      env->GetMethodID(clazz, "onATBind", "(Ljava/lang/String;[B)V");
-  method_onAtBiev = env->GetMethodID(clazz, "onATBiev", "(II[B)V");
-  method_onAtBia = env->GetMethodID(clazz, "onAtBia", "(ZZZZ[B)V");
-
-  ALOGI("%s: succeeds", __func__);
-}
-
 static void initializeNative(JNIEnv* env, jobject object, jint max_hf_clients,
                              jboolean inband_ringing_enabled) {
   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
@@ -977,42 +943,73 @@
   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initializeNative", "(IZ)V", (void*)initializeNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectHfpNative", "([B)Z", (void*)connectHfpNative},
-    {"disconnectHfpNative", "([B)Z", (void*)disconnectHfpNative},
-    {"connectAudioNative", "([B)Z", (void*)connectAudioNative},
-    {"disconnectAudioNative", "([B)Z", (void*)disconnectAudioNative},
-    {"isNoiseReductionSupportedNative", "([B)Z",
-     (void*)isNoiseReductionSupportedNative},
-    {"isVoiceRecognitionSupportedNative", "([B)Z",
-     (void*)isVoiceRecognitionSupportedNative},
-    {"startVoiceRecognitionNative", "([B)Z",
-     (void*)startVoiceRecognitionNative},
-    {"stopVoiceRecognitionNative", "([B)Z", (void*)stopVoiceRecognitionNative},
-    {"setVolumeNative", "(II[B)Z", (void*)setVolumeNative},
-    {"notifyDeviceStatusNative", "(IIII[B)Z", (void*)notifyDeviceStatusNative},
-    {"copsResponseNative", "(Ljava/lang/String;[B)Z",
-     (void*)copsResponseNative},
-    {"cindResponseNative", "(IIIIIII[B)Z", (void*)cindResponseNative},
-    {"atResponseStringNative", "(Ljava/lang/String;[B)Z",
-     (void*)atResponseStringNative},
-    {"atResponseCodeNative", "(II[B)Z", (void*)atResponseCodeNative},
-    {"clccResponseNative", "(IIIIZLjava/lang/String;I[B)Z",
-     (void*)clccResponseNative},
-    {"phoneStateChangeNative", "(IIILjava/lang/String;ILjava/lang/String;[B)Z",
-     (void*)phoneStateChangeNative},
-    {"setScoAllowedNative", "(Z)Z", (void*)setScoAllowedNative},
-    {"sendBsirNative", "(Z[B)Z", (void*)sendBsirNative},
-    {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
-};
-
 int register_com_android_bluetooth_hfp(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env, "com/android/bluetooth/hfp/HeadsetNativeInterface", sMethods,
-      NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initializeNative", "(IZ)V", (void*)initializeNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectHfpNative", "([B)Z", (void*)connectHfpNative},
+      {"disconnectHfpNative", "([B)Z", (void*)disconnectHfpNative},
+      {"connectAudioNative", "([B)Z", (void*)connectAudioNative},
+      {"disconnectAudioNative", "([B)Z", (void*)disconnectAudioNative},
+      {"isNoiseReductionSupportedNative", "([B)Z",
+       (void*)isNoiseReductionSupportedNative},
+      {"isVoiceRecognitionSupportedNative", "([B)Z",
+       (void*)isVoiceRecognitionSupportedNative},
+      {"startVoiceRecognitionNative", "([B)Z",
+       (void*)startVoiceRecognitionNative},
+      {"stopVoiceRecognitionNative", "([B)Z",
+       (void*)stopVoiceRecognitionNative},
+      {"setVolumeNative", "(II[B)Z", (void*)setVolumeNative},
+      {"notifyDeviceStatusNative", "(IIII[B)Z",
+       (void*)notifyDeviceStatusNative},
+      {"copsResponseNative", "(Ljava/lang/String;[B)Z",
+       (void*)copsResponseNative},
+      {"cindResponseNative", "(IIIIIII[B)Z", (void*)cindResponseNative},
+      {"atResponseStringNative", "(Ljava/lang/String;[B)Z",
+       (void*)atResponseStringNative},
+      {"atResponseCodeNative", "(II[B)Z", (void*)atResponseCodeNative},
+      {"clccResponseNative", "(IIIIZLjava/lang/String;I[B)Z",
+       (void*)clccResponseNative},
+      {"phoneStateChangeNative",
+       "(IIILjava/lang/String;ILjava/lang/String;[B)Z",
+       (void*)phoneStateChangeNative},
+      {"setScoAllowedNative", "(Z)Z", (void*)setScoAllowedNative},
+      {"sendBsirNative", "(Z[B)Z", (void*)sendBsirNative},
+      {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/hfp/HeadsetNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
+      {"onAudioStateChanged", "(I[B)V", &method_onAudioStateChanged},
+      {"onVrStateChanged", "(I[B)V", &method_onVrStateChanged},
+      {"onAnswerCall", "([B)V", &method_onAnswerCall},
+      {"onHangupCall", "([B)V", &method_onHangupCall},
+      {"onVolumeChanged", "(II[B)V", &method_onVolumeChanged},
+      {"onDialCall", "(Ljava/lang/String;[B)V", &method_onDialCall},
+      {"onSendDtmf", "(I[B)V", &method_onSendDtmf},
+      {"onNoiseReductionEnable", "(Z[B)V", &method_onNoiseReductionEnable},
+      {"onWBS", "(I[B)V", &method_onWBS},
+      {"onSWB", "(I[B)V", &method_onSWB},
+      {"onAtChld", "(I[B)V", &method_onAtChld},
+      {"onAtCnum", "([B)V", &method_onAtCnum},
+      {"onAtCind", "([B)V", &method_onAtCind},
+      {"onAtCops", "([B)V", &method_onAtCops},
+      {"onAtClcc", "([B)V", &method_onAtClcc},
+      {"onUnknownAt", "(Ljava/lang/String;[B)V", &method_onUnknownAt},
+      {"onKeyPressed", "([B)V", &method_onKeyPressed},
+      {"onATBind", "(Ljava/lang/String;[B)V", &method_onAtBind},
+      {"onATBiev", "(II[B)V", &method_onAtBiev},
+      {"onAtBia", "(ZZZZ[B)V", &method_onAtBia},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/hfp/HeadsetNativeInterface",
+                   javaMethods);
+
+  return 0;
 }
 
 } /* namespace android */
diff --git a/android/app/jni/com_android_bluetooth_hfpclient.cpp b/android/app/jni/com_android_bluetooth_hfpclient.cpp
index d575684..1176132 100644
--- a/android/app/jni/com_android_bluetooth_hfpclient.cpp
+++ b/android/app/jni/com_android_bluetooth_hfpclient.cpp
@@ -429,42 +429,6 @@
     unknown_event_cb,
 };
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_onConnectionStateChanged =
-      env->GetMethodID(clazz, "onConnectionStateChanged", "(III[B)V");
-  method_onAudioStateChanged =
-      env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
-  method_onVrStateChanged =
-      env->GetMethodID(clazz, "onVrStateChanged", "(I[B)V");
-  method_onNetworkState = env->GetMethodID(clazz, "onNetworkState", "(I[B)V");
-  method_onNetworkRoaming = env->GetMethodID(clazz, "onNetworkRoaming", "(I[B)V");
-  method_onNetworkSignal = env->GetMethodID(clazz, "onNetworkSignal", "(I[B)V");
-  method_onBatteryLevel = env->GetMethodID(clazz, "onBatteryLevel", "(I[B)V");
-  method_onCurrentOperator =
-      env->GetMethodID(clazz, "onCurrentOperator", "(Ljava/lang/String;[B)V");
-  method_onCall = env->GetMethodID(clazz, "onCall", "(I[B)V");
-  method_onCallSetup = env->GetMethodID(clazz, "onCallSetup", "(I[B)V");
-  method_onCallHeld = env->GetMethodID(clazz, "onCallHeld", "(I[B)V");
-  method_onRespAndHold = env->GetMethodID(clazz, "onRespAndHold", "(I[B)V");
-  method_onClip = env->GetMethodID(clazz, "onClip", "(Ljava/lang/String;[B)V");
-  method_onCallWaiting =
-      env->GetMethodID(clazz, "onCallWaiting", "(Ljava/lang/String;[B)V");
-  method_onCurrentCalls =
-      env->GetMethodID(clazz, "onCurrentCalls", "(IIIILjava/lang/String;[B)V");
-  method_onVolumeChange = env->GetMethodID(clazz, "onVolumeChange", "(II[B)V");
-  method_onCmdResult = env->GetMethodID(clazz, "onCmdResult", "(II[B)V");
-  method_onSubscriberInfo =
-      env->GetMethodID(clazz, "onSubscriberInfo", "(Ljava/lang/String;I[B)V");
-  method_onInBandRing = env->GetMethodID(clazz, "onInBandRing", "(I[B)V");
-  method_onLastVoiceTagNumber =
-      env->GetMethodID(clazz, "onLastVoiceTagNumber", "(Ljava/lang/String;[B)V");
-  method_onRingIndication = env->GetMethodID(clazz, "onRingIndication", "([B)V");
-  method_onUnknownEvent =
-      env->GetMethodID(clazz, "onUnknownEvent", "(Ljava/lang/String;[B)V");
-
-  ALOGI("%s succeeds", __func__);
-}
-
 static void initializeNative(JNIEnv* env, jobject object) {
   ALOGD("%s: HfpClient", __func__);
   std::unique_lock<std::shared_mutex> interface_lock(interface_mutex);
@@ -911,38 +875,72 @@
   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initializeNative", "()V", (void*)initializeNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectNative", "([B)Z", (void*)connectNative},
-    {"disconnectNative", "([B)Z", (void*)disconnectNative},
-    {"connectAudioNative", "([B)Z", (void*)connectAudioNative},
-    {"disconnectAudioNative", "([B)Z", (void*)disconnectAudioNative},
-    {"startVoiceRecognitionNative", "([B)Z",
-     (void*)startVoiceRecognitionNative},
-    {"stopVoiceRecognitionNative", "([B)Z", (void*)stopVoiceRecognitionNative},
-    {"setVolumeNative", "([BII)Z", (void*)setVolumeNative},
-    {"dialNative", "([BLjava/lang/String;)Z", (void*)dialNative},
-    {"dialMemoryNative", "([BI)Z", (void*)dialMemoryNative},
-    {"handleCallActionNative", "([BII)Z", (void*)handleCallActionNative},
-    {"queryCurrentCallsNative", "([B)Z", (void*)queryCurrentCallsNative},
-    {"queryCurrentOperatorNameNative", "([B)Z",
-     (void*)queryCurrentOperatorNameNative},
-    {"retrieveSubscriberInfoNative", "([B)Z",
-     (void*)retrieveSubscriberInfoNative},
-    {"sendDtmfNative", "([BB)Z", (void*)sendDtmfNative},
-    {"requestLastVoiceTagNumberNative", "([B)Z",
-     (void*)requestLastVoiceTagNumberNative},
-    {"sendATCmdNative", "([BIIILjava/lang/String;)Z", (void*)sendATCmdNative},
-    {"sendAndroidAtNative", "([BLjava/lang/String;)Z",
-     (void*)sendAndroidAtNative},
-};
-
 int register_com_android_bluetooth_hfpclient(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env, "com/android/bluetooth/hfpclient/NativeInterface",
-      sMethods, NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initializeNative", "()V", (void*)initializeNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectNative", "([B)Z", (void*)connectNative},
+      {"disconnectNative", "([B)Z", (void*)disconnectNative},
+      {"connectAudioNative", "([B)Z", (void*)connectAudioNative},
+      {"disconnectAudioNative", "([B)Z", (void*)disconnectAudioNative},
+      {"startVoiceRecognitionNative", "([B)Z",
+       (void*)startVoiceRecognitionNative},
+      {"stopVoiceRecognitionNative", "([B)Z",
+       (void*)stopVoiceRecognitionNative},
+      {"setVolumeNative", "([BII)Z", (void*)setVolumeNative},
+      {"dialNative", "([BLjava/lang/String;)Z", (void*)dialNative},
+      {"dialMemoryNative", "([BI)Z", (void*)dialMemoryNative},
+      {"handleCallActionNative", "([BII)Z", (void*)handleCallActionNative},
+      {"queryCurrentCallsNative", "([B)Z", (void*)queryCurrentCallsNative},
+      {"queryCurrentOperatorNameNative", "([B)Z",
+       (void*)queryCurrentOperatorNameNative},
+      {"retrieveSubscriberInfoNative", "([B)Z",
+       (void*)retrieveSubscriberInfoNative},
+      {"sendDtmfNative", "([BB)Z", (void*)sendDtmfNative},
+      {"requestLastVoiceTagNumberNative", "([B)Z",
+       (void*)requestLastVoiceTagNumberNative},
+      {"sendATCmdNative", "([BIIILjava/lang/String;)Z", (void*)sendATCmdNative},
+      {"sendAndroidAtNative", "([BLjava/lang/String;)Z",
+       (void*)sendAndroidAtNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/hfpclient/NativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onConnectionStateChanged", "(III[B)V",
+       &method_onConnectionStateChanged},
+      {"onAudioStateChanged", "(I[B)V", &method_onAudioStateChanged},
+      {"onVrStateChanged", "(I[B)V", &method_onVrStateChanged},
+      {"onNetworkState", "(I[B)V", &method_onNetworkState},
+      {"onNetworkRoaming", "(I[B)V", &method_onNetworkRoaming},
+      {"onNetworkSignal", "(I[B)V", &method_onNetworkSignal},
+      {"onBatteryLevel", "(I[B)V", &method_onBatteryLevel},
+      {"onCurrentOperator", "(Ljava/lang/String;[B)V",
+       &method_onCurrentOperator},
+      {"onCall", "(I[B)V", &method_onCall},
+      {"onCallSetup", "(I[B)V", &method_onCallSetup},
+      {"onCallHeld", "(I[B)V", &method_onCallHeld},
+      {"onRespAndHold", "(I[B)V", &method_onRespAndHold},
+      {"onClip", "(Ljava/lang/String;[B)V", &method_onClip},
+      {"onCallWaiting", "(Ljava/lang/String;[B)V", &method_onCallWaiting},
+      {"onCurrentCalls", "(IIIILjava/lang/String;[B)V", &method_onCurrentCalls},
+      {"onVolumeChange", "(II[B)V", &method_onVolumeChange},
+      {"onCmdResult", "(II[B)V", &method_onCmdResult},
+      {"onSubscriberInfo", "(Ljava/lang/String;I[B)V",
+       &method_onSubscriberInfo},
+      {"onInBandRing", "(I[B)V", &method_onInBandRing},
+      {"onLastVoiceTagNumber", "(Ljava/lang/String;[B)V",
+       &method_onLastVoiceTagNumber},
+      {"onRingIndication", "([B)V", &method_onRingIndication},
+      {"onUnknownEvent", "(Ljava/lang/String;[B)V", &method_onUnknownEvent},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/hfpclient/NativeInterface",
+                   javaMethods);
+
+  return 0;
 }
 
 } /* namespace android */
diff --git a/android/app/jni/com_android_bluetooth_hid_device.cpp b/android/app/jni/com_android_bluetooth_hid_device.cpp
index 2146222..b56218c 100644
--- a/android/app/jni/com_android_bluetooth_hid_device.cpp
+++ b/android/app/jni/com_android_bluetooth_hid_device.cpp
@@ -150,21 +150,6 @@
     vc_unplug_callback,
 };
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  ALOGV("%s: done", __FUNCTION__);
-
-  method_onApplicationStateChanged =
-      env->GetMethodID(clazz, "onApplicationStateChanged", "([BZ)V");
-  method_onConnectStateChanged =
-      env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V");
-  method_onGetReport = env->GetMethodID(clazz, "onGetReport", "(BBS)V");
-  method_onSetReport = env->GetMethodID(clazz, "onSetReport", "(BB[B)V");
-  method_onSetProtocol = env->GetMethodID(clazz, "onSetProtocol", "(B)V");
-  method_onInterruptData = env->GetMethodID(clazz, "onInterruptData", "(B[B)V");
-  method_onVirtualCableUnplug =
-      env->GetMethodID(clazz, "onVirtualCableUnplug", "()V");
-}
-
 static void initNative(JNIEnv* env, jobject object) {
   const bt_interface_t* btif;
   bt_status_t status;
@@ -495,25 +480,40 @@
   return result;
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()V", (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"registerAppNative",
-     "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;B[B[I[I)Z",
-     (void*)registerAppNative},
-    {"unregisterAppNative", "()Z", (void*)unregisterAppNative},
-    {"sendReportNative", "(I[B)Z", (void*)sendReportNative},
-    {"replyReportNative", "(BB[B)Z", (void*)replyReportNative},
-    {"reportErrorNative", "(B)Z", (void*)reportErrorNative},
-    {"unplugNative", "()Z", (void*)unplugNative},
-    {"connectNative", "([B)Z", (void*)connectNative},
-    {"disconnectNative", "()Z", (void*)disconnectNative},
-};
-
 int register_com_android_bluetooth_hid_device(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env, "com/android/bluetooth/hid/HidDeviceNativeInterface", sMethods,
-      NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initNative", "()V", (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"registerAppNative",
+       "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;B[B[I[I)Z",
+       (void*)registerAppNative},
+      {"unregisterAppNative", "()Z", (void*)unregisterAppNative},
+      {"sendReportNative", "(I[B)Z", (void*)sendReportNative},
+      {"replyReportNative", "(BB[B)Z", (void*)replyReportNative},
+      {"reportErrorNative", "(B)Z", (void*)reportErrorNative},
+      {"unplugNative", "()Z", (void*)unplugNative},
+      {"connectNative", "([B)Z", (void*)connectNative},
+      {"disconnectNative", "()Z", (void*)disconnectNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/hid/HidDeviceNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onApplicationStateChanged", "([BZ)V",
+       &method_onApplicationStateChanged},
+      {"onConnectStateChanged", "([BI)V", &method_onConnectStateChanged},
+      {"onGetReport", "(BBS)V", &method_onGetReport},
+      {"onSetReport", "(BB[B)V", &method_onSetReport},
+      {"onSetProtocol", "(B)V", &method_onSetProtocol},
+      {"onInterruptData", "(B[B)V", &method_onInterruptData},
+      {"onVirtualCableUnplug", "()V", &method_onVirtualCableUnplug},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/hid/HidDeviceNativeInterface",
+                   javaMethods);
+
+  return 0;
 }
 }  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_hid_host.cpp b/android/app/jni/com_android_bluetooth_hid_host.cpp
index 7a16423..637a25d 100644
--- a/android/app/jni/com_android_bluetooth_hid_host.cpp
+++ b/android/app/jni/com_android_bluetooth_hid_host.cpp
@@ -187,20 +187,6 @@
     handshake_callback};
 
 // Define native functions
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_onConnectStateChanged =
-      env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V");
-  method_onGetProtocolMode =
-      env->GetMethodID(clazz, "onGetProtocolMode", "([BI)V");
-  method_onGetReport = env->GetMethodID(clazz, "onGetReport", "([B[BI)V");
-  method_onHandshake = env->GetMethodID(clazz, "onHandshake", "([BI)V");
-  method_onVirtualUnplug = env->GetMethodID(clazz, "onVirtualUnplug", "([BI)V");
-  method_onGetIdleTime = env->GetMethodID(clazz, "onGetIdleTime", "([BI)V");
-
-  ALOGI("%s: succeeds", __func__);
-}
-
 static void initializeNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_timed_mutex> lock(mCallbacks_mutex);
   const bt_interface_t* btInf = getBluetoothInterface();
@@ -504,25 +490,39 @@
   return status == BT_STATUS_SUCCESS ? JNI_TRUE : JNI_FALSE;
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initializeNative", "()V", (void*)initializeNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectHidNative", "([B)Z", (void*)connectHidNative},
-    {"disconnectHidNative", "([B)Z", (void*)disconnectHidNative},
-    {"getProtocolModeNative", "([B)Z", (void*)getProtocolModeNative},
-    {"virtualUnPlugNative", "([B)Z", (void*)virtualUnPlugNative},
-    {"setProtocolModeNative", "([BB)Z", (void*)setProtocolModeNative},
-    {"getReportNative", "([BBBI)Z", (void*)getReportNative},
-    {"setReportNative", "([BBLjava/lang/String;)Z", (void*)setReportNative},
-    {"sendDataNative", "([BLjava/lang/String;)Z", (void*)sendDataNative},
-    {"getIdleTimeNative", "([B)Z", (void*)getIdleTimeNative},
-    {"setIdleTimeNative", "([BB)Z", (void*)setIdleTimeNative},
-};
-
 int register_com_android_bluetooth_hid_host(JNIEnv* env) {
-  return jniRegisterNativeMethods(env,
-                                  "com/android/bluetooth/hid/HidHostService",
-                                  sMethods, NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initializeNative", "()V", (void*)initializeNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectHidNative", "([B)Z", (void*)connectHidNative},
+      {"disconnectHidNative", "([B)Z", (void*)disconnectHidNative},
+      {"getProtocolModeNative", "([B)Z", (void*)getProtocolModeNative},
+      {"virtualUnPlugNative", "([B)Z", (void*)virtualUnPlugNative},
+      {"setProtocolModeNative", "([BB)Z", (void*)setProtocolModeNative},
+      {"getReportNative", "([BBBI)Z", (void*)getReportNative},
+      {"setReportNative", "([BBLjava/lang/String;)Z", (void*)setReportNative},
+      {"sendDataNative", "([BLjava/lang/String;)Z", (void*)sendDataNative},
+      {"getIdleTimeNative", "([B)Z", (void*)getIdleTimeNative},
+      {"setIdleTimeNative", "([BB)Z", (void*)setIdleTimeNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/hid/HidHostNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onConnectStateChanged", "([BI)V", &method_onConnectStateChanged},
+      {"onGetProtocolMode", "([BI)V", &method_onGetProtocolMode},
+      {"onGetReport", "([B[BI)V", &method_onGetReport},
+      {"onHandshake", "([BI)V", &method_onHandshake},
+      {"onVirtualUnplug", "([BI)V", &method_onVirtualUnplug},
+      {"onGetIdleTime", "([BI)V", &method_onGetIdleTime},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/hid/HidHostNativeInterface",
+                   javaMethods);
+
+  return 0;
 }
+
 }  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_le_audio.cpp b/android/app/jni/com_android_bluetooth_le_audio.cpp
index 08f859c..9451fca 100644
--- a/android/app/jni/com_android_bluetooth_le_audio.cpp
+++ b/android/app/jni/com_android_bluetooth_le_audio.cpp
@@ -314,39 +314,6 @@
 
 static LeAudioClientCallbacksImpl sLeAudioClientCallbacks;
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  jclass jniBluetoothLeAudioCodecConfigClass =
-      env->FindClass("android/bluetooth/BluetoothLeAudioCodecConfig");
-  android_bluetooth_BluetoothLeAudioCodecConfig.constructor = env->GetMethodID(
-      jniBluetoothLeAudioCodecConfigClass, "<init>", "(IIIIIIIII)V");
-  android_bluetooth_BluetoothLeAudioCodecConfig.getCodecType = env->GetMethodID(
-      jniBluetoothLeAudioCodecConfigClass, "getCodecType", "()I");
-
-  method_onGroupStatus = env->GetMethodID(clazz, "onGroupStatus", "(II)V");
-  method_onGroupNodeStatus =
-      env->GetMethodID(clazz, "onGroupNodeStatus", "([BII)V");
-  method_onAudioConf = env->GetMethodID(clazz, "onAudioConf", "(IIIII)V");
-  method_onSinkAudioLocationAvailable =
-      env->GetMethodID(clazz, "onSinkAudioLocationAvailable", "([BI)V");
-  method_onInitialized = env->GetMethodID(clazz, "onInitialized", "()V");
-  method_onConnectionStateChanged =
-      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
-  method_onAudioLocalCodecCapabilities =
-      env->GetMethodID(clazz, "onAudioLocalCodecCapabilities",
-                       "([Landroid/bluetooth/BluetoothLeAudioCodecConfig;"
-                       "[Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V");
-  method_onAudioGroupCodecConf =
-      env->GetMethodID(clazz, "onAudioGroupCodecConf",
-                       "(ILandroid/bluetooth/BluetoothLeAudioCodecConfig;"
-                       "Landroid/bluetooth/BluetoothLeAudioCodecConfig;"
-                       "[Landroid/bluetooth/BluetoothLeAudioCodecConfig;"
-                       "[Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V");
-  method_onHealthBasedRecommendationAction =
-      env->GetMethodID(clazz, "onHealthBasedRecommendationAction", "([BI)V");
-  method_onHealthBasedGroupRecommendationAction = env->GetMethodID(
-      clazz, "onHealthBasedGroupRecommendationAction", "(II)V");
-}
-
 std::vector<btle_audio_codec_config_t> prepareCodecPreferences(
     JNIEnv* env, jobject object, jobjectArray codecConfigArray) {
   std::vector<btle_audio_codec_config_t> codec_preferences;
@@ -620,27 +587,6 @@
       groupId, isOutputPreferenceLeAudio, isDuplexPreferenceLeAudio);
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "([Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V",
-     (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectLeAudioNative", "([B)Z", (void*)connectLeAudioNative},
-    {"disconnectLeAudioNative", "([B)Z", (void*)disconnectLeAudioNative},
-    {"setEnableStateNative", "([BZ)Z", (void*)setEnableStateNative},
-    {"groupAddNodeNative", "(I[B)Z", (void*)groupAddNodeNative},
-    {"groupRemoveNodeNative", "(I[B)Z", (void*)groupRemoveNodeNative},
-    {"groupSetActiveNative", "(I)V", (void*)groupSetActiveNative},
-    {"setCodecConfigPreferenceNative",
-     "(ILandroid/bluetooth/BluetoothLeAudioCodecConfig;Landroid/bluetooth/"
-     "BluetoothLeAudioCodecConfig;)V",
-     (void*)setCodecConfigPreferenceNative},
-    {"setCcidInformationNative", "(II)V", (void*)setCcidInformationNative},
-    {"setInCallNative", "(Z)V", (void*)setInCallNative},
-    {"sendAudioProfilePreferencesNative", "(IZZ)V",
-     (void*)sendAudioProfilePreferencesNative},
-};
-
 /* Le Audio Broadcaster */
 static jmethodID method_onBroadcastCreated;
 static jmethodID method_onBroadcastDestroyed;
@@ -1099,63 +1045,6 @@
 
 static LeAudioBroadcasterCallbacksImpl sLeAudioBroadcasterCallbacks;
 
-static void BroadcasterClassInitNative(JNIEnv* env, jclass clazz) {
-  method_onBroadcastCreated =
-      env->GetMethodID(clazz, "onBroadcastCreated", "(IZ)V");
-  method_onBroadcastDestroyed =
-      env->GetMethodID(clazz, "onBroadcastDestroyed", "(I)V");
-  method_onBroadcastStateChanged =
-      env->GetMethodID(clazz, "onBroadcastStateChanged", "(II)V");
-  method_onBroadcastMetadataChanged =
-      env->GetMethodID(clazz, "onBroadcastMetadataChanged",
-                       "(ILandroid/bluetooth/BluetoothLeBroadcastMetadata;)V");
-
-  jclass jniArrayListClass = env->FindClass("java/util/ArrayList");
-  java_util_ArrayList.constructor =
-      env->GetMethodID(jniArrayListClass, "<init>", "()V");
-  java_util_ArrayList.add =
-      env->GetMethodID(jniArrayListClass, "add", "(Ljava/lang/Object;)Z");
-
-  jclass jniBluetoothLeAudioCodecConfigMetadataClass =
-      env->FindClass("android/bluetooth/BluetoothLeAudioCodecConfigMetadata");
-  android_bluetooth_BluetoothLeAudioCodecConfigMetadata.constructor =
-      env->GetMethodID(jniBluetoothLeAudioCodecConfigMetadataClass, "<init>",
-                       "(JIII[B)V");
-
-  jclass jniBluetoothLeAudioContentMetadataClass =
-      env->FindClass("android/bluetooth/BluetoothLeAudioContentMetadata");
-  android_bluetooth_BluetoothLeAudioContentMetadata.constructor =
-      env->GetMethodID(jniBluetoothLeAudioContentMetadataClass, "<init>",
-                       "(Ljava/lang/String;Ljava/lang/String;[B)V");
-
-  jclass jniBluetoothLeBroadcastChannelClass =
-      env->FindClass("android/bluetooth/BluetoothLeBroadcastChannel");
-  android_bluetooth_BluetoothLeBroadcastChannel.constructor = env->GetMethodID(
-      jniBluetoothLeBroadcastChannelClass, "<init>",
-      "(ZILandroid/bluetooth/BluetoothLeAudioCodecConfigMetadata;)V");
-
-  jclass jniBluetoothLeBroadcastSubgroupClass =
-      env->FindClass("android/bluetooth/BluetoothLeBroadcastSubgroup");
-  android_bluetooth_BluetoothLeBroadcastSubgroup.constructor = env->GetMethodID(
-      jniBluetoothLeBroadcastSubgroupClass, "<init>",
-      "(JLandroid/bluetooth/BluetoothLeAudioCodecConfigMetadata;"
-      "Landroid/bluetooth/BluetoothLeAudioContentMetadata;"
-      "Ljava/util/List;)V");
-
-  jclass jniBluetoothDeviceClass =
-      env->FindClass("android/bluetooth/BluetoothDevice");
-  android_bluetooth_BluetoothDevice.constructor = env->GetMethodID(
-      jniBluetoothDeviceClass, "<init>", "(Ljava/lang/String;I)V");
-
-  jclass jniBluetoothLeBroadcastMetadataClass =
-      env->FindClass("android/bluetooth/BluetoothLeBroadcastMetadata");
-  android_bluetooth_BluetoothLeBroadcastMetadata.constructor = env->GetMethodID(
-      jniBluetoothLeBroadcastMetadataClass, "<init>",
-      "(ILandroid/bluetooth/BluetoothDevice;IIIZZLjava/lang/String;"
-      "[BIILandroid/bluetooth/BluetoothLeAudioContentMetadata;"
-      "Ljava/util/List;)V");
-}
-
 static void BroadcasterInitNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_timed_mutex> interface_lock(
       sBroadcasterInterfaceMutex);
@@ -1446,30 +1335,160 @@
   sLeAudioBroadcasterInterface->GetBroadcastMetadata(broadcast_id);
 }
 
-static JNINativeMethod sBroadcasterMethods[] = {
-    {"classInitNative", "()V", (void*)BroadcasterClassInitNative},
-    {"initNative", "()V", (void*)BroadcasterInitNative},
-    {"stopNative", "()V", (void*)BroadcasterStopNative},
-    {"cleanupNative", "()V", (void*)BroadcasterCleanupNative},
-    {"createBroadcastNative", "(ZLjava/lang/String;[B[B[I[[B)V",
-     (void*)CreateBroadcastNative},
-    {"updateMetadataNative", "(ILjava/lang/String;[B[[B)V",
-     (void*)UpdateMetadataNative},
-    {"startBroadcastNative", "(I)V", (void*)StartBroadcastNative},
-    {"stopBroadcastNative", "(I)V", (void*)StopBroadcastNative},
-    {"pauseBroadcastNative", "(I)V", (void*)PauseBroadcastNative},
-    {"destroyBroadcastNative", "(I)V", (void*)DestroyBroadcastNative},
-    {"getBroadcastMetadataNative", "(I)V", (void*)getBroadcastMetadataNative},
-};
+static int register_com_android_bluetooth_le_audio_broadcaster(JNIEnv* env) {
+  const JNINativeMethod methods[] = {
+      {"initNative", "()V", (void*)BroadcasterInitNative},
+      {"stopNative", "()V", (void*)BroadcasterStopNative},
+      {"cleanupNative", "()V", (void*)BroadcasterCleanupNative},
+      {"createBroadcastNative", "(ZLjava/lang/String;[B[B[I[[B)V",
+       (void*)CreateBroadcastNative},
+      {"updateMetadataNative", "(ILjava/lang/String;[B[[B)V",
+       (void*)UpdateMetadataNative},
+      {"startBroadcastNative", "(I)V", (void*)StartBroadcastNative},
+      {"stopBroadcastNative", "(I)V", (void*)StopBroadcastNative},
+      {"pauseBroadcastNative", "(I)V", (void*)PauseBroadcastNative},
+      {"destroyBroadcastNative", "(I)V", (void*)DestroyBroadcastNative},
+      {"getBroadcastMetadataNative", "(I)V", (void*)getBroadcastMetadataNative},
+  };
+
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface",
+      methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onBroadcastCreated", "(IZ)V", &method_onBroadcastCreated},
+      {"onBroadcastDestroyed", "(I)V", &method_onBroadcastDestroyed},
+      {"onBroadcastStateChanged", "(II)V", &method_onBroadcastStateChanged},
+      {"onBroadcastMetadataChanged",
+       "(ILandroid/bluetooth/BluetoothLeBroadcastMetadata;)V",
+       &method_onBroadcastMetadataChanged},
+  };
+  GET_JAVA_METHODS(
+      env, "com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface",
+      javaMethods);
+
+  const JNIJavaMethod javaArrayListMethods[] = {
+      {"<init>", "()V", &java_util_ArrayList.constructor},
+      {"add", "(Ljava/lang/Object;)Z", &java_util_ArrayList.add},
+  };
+  GET_JAVA_METHODS(env, "java/util/ArrayList", javaArrayListMethods);
+
+  const JNIJavaMethod javaLeAudioCodecMethods[] = {
+      {"<init>", "(JIII[B)V",
+       &android_bluetooth_BluetoothLeAudioCodecConfigMetadata.constructor},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/BluetoothLeAudioCodecConfigMetadata",
+                   javaLeAudioCodecMethods);
+
+  const JNIJavaMethod javaLeAudioContentMethods[] = {
+      {"<init>", "(Ljava/lang/String;Ljava/lang/String;[B)V",
+       &android_bluetooth_BluetoothLeAudioContentMetadata.constructor},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/BluetoothLeAudioContentMetadata",
+                   javaLeAudioContentMethods);
+
+  const JNIJavaMethod javaLeBroadcastChannelMethods[] = {
+      {"<init>", "(ZILandroid/bluetooth/BluetoothLeAudioCodecConfigMetadata;)V",
+       &android_bluetooth_BluetoothLeBroadcastChannel.constructor},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/BluetoothLeBroadcastChannel",
+                   javaLeBroadcastChannelMethods);
+
+  const JNIJavaMethod javaLeBroadcastSubgroupMethods[] = {
+      {"<init>",
+       "(JLandroid/bluetooth/BluetoothLeAudioCodecConfigMetadata;"
+       "Landroid/bluetooth/BluetoothLeAudioContentMetadata;"
+       "Ljava/util/List;)V",
+       &android_bluetooth_BluetoothLeBroadcastSubgroup.constructor},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/BluetoothLeBroadcastSubgroup",
+                   javaLeBroadcastSubgroupMethods);
+
+  const JNIJavaMethod javaBluetoothDevieceMethods[] = {
+      {"<init>", "(Ljava/lang/String;I)V",
+       &android_bluetooth_BluetoothDevice.constructor},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/BluetoothDevice",
+                   javaBluetoothDevieceMethods);
+
+  const JNIJavaMethod javaLeBroadcastMetadataMethods[] = {
+      {"<init>",
+       "(ILandroid/bluetooth/BluetoothDevice;IIIZZLjava/lang/String;"
+       "[BIILandroid/bluetooth/BluetoothLeAudioContentMetadata;"
+       "Ljava/util/List;)V",
+       &android_bluetooth_BluetoothLeBroadcastMetadata.constructor},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/BluetoothLeBroadcastMetadata",
+                   javaLeBroadcastMetadataMethods);
+
+  return 0;
+}
 
 int register_com_android_bluetooth_le_audio(JNIEnv* env) {
-  int register_success = jniRegisterNativeMethods(
-      env, "com/android/bluetooth/le_audio/LeAudioNativeInterface", sMethods,
-      NELEM(sMethods));
-  return register_success &
-         jniRegisterNativeMethods(
-             env,
-             "com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface",
-             sBroadcasterMethods, NELEM(sBroadcasterMethods));
+  const JNINativeMethod methods[] = {
+      {"initNative", "([Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V",
+       (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectLeAudioNative", "([B)Z", (void*)connectLeAudioNative},
+      {"disconnectLeAudioNative", "([B)Z", (void*)disconnectLeAudioNative},
+      {"setEnableStateNative", "([BZ)Z", (void*)setEnableStateNative},
+      {"groupAddNodeNative", "(I[B)Z", (void*)groupAddNodeNative},
+      {"groupRemoveNodeNative", "(I[B)Z", (void*)groupRemoveNodeNative},
+      {"groupSetActiveNative", "(I)V", (void*)groupSetActiveNative},
+      {"setCodecConfigPreferenceNative",
+       "(ILandroid/bluetooth/BluetoothLeAudioCodecConfig;"
+       "Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V",
+       (void*)setCodecConfigPreferenceNative},
+      {"setCcidInformationNative", "(II)V", (void*)setCcidInformationNative},
+      {"setInCallNative", "(Z)V", (void*)setInCallNative},
+      {"sendAudioProfilePreferencesNative", "(IZZ)V",
+       (void*)sendAudioProfilePreferencesNative},
+  };
+
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/le_audio/LeAudioNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onGroupStatus", "(II)V", &method_onGroupStatus},
+      {"onGroupNodeStatus", "([BII)V", &method_onGroupNodeStatus},
+      {"onAudioConf", "(IIIII)V", &method_onAudioConf},
+      {"onSinkAudioLocationAvailable", "([BI)V",
+       &method_onSinkAudioLocationAvailable},
+      {"onInitialized", "()V", &method_onInitialized},
+      {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
+      {"onAudioLocalCodecCapabilities",
+       "([Landroid/bluetooth/BluetoothLeAudioCodecConfig;"
+       "[Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V",
+       &method_onAudioLocalCodecCapabilities},
+      {"onAudioGroupCodecConf",
+       "(ILandroid/bluetooth/BluetoothLeAudioCodecConfig;"
+       "Landroid/bluetooth/BluetoothLeAudioCodecConfig;"
+       "[Landroid/bluetooth/BluetoothLeAudioCodecConfig;"
+       "[Landroid/bluetooth/BluetoothLeAudioCodecConfig;)V",
+       &method_onAudioGroupCodecConf},
+      {"onHealthBasedRecommendationAction", "([BI)V",
+       &method_onHealthBasedRecommendationAction},
+      {"onHealthBasedGroupRecommendationAction", "(II)V",
+       &method_onHealthBasedGroupRecommendationAction},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/le_audio/LeAudioNativeInterface",
+                   javaMethods);
+
+  const JNIJavaMethod javaLeAudioCodecMethods[] = {
+      {"<init>", "(IIIIIIIII)V",
+       &android_bluetooth_BluetoothLeAudioCodecConfig.constructor},
+      {"getCodecType", "()I",
+       &android_bluetooth_BluetoothLeAudioCodecConfig.getCodecType},
+  };
+  GET_JAVA_METHODS(env, "android/bluetooth/BluetoothLeAudioCodecConfig",
+                   javaLeAudioCodecMethods);
+
+  return register_com_android_bluetooth_le_audio_broadcaster(env);
 }
 }  // namespace android
diff --git a/android/app/jni/com_android_bluetooth_pan.cpp b/android/app/jni/com_android_bluetooth_pan.cpp
index 84d7b1c..a0645a0 100644
--- a/android/app/jni/com_android_bluetooth_pan.cpp
+++ b/android/app/jni/com_android_bluetooth_pan.cpp
@@ -98,19 +98,10 @@
     connection_state_callback};
 
 // Define native functions
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_onConnectStateChanged =
-      env->GetMethodID(clazz, "onConnectStateChanged", "([BIIII)V");
-  method_onControlStateChanged = env->GetMethodID(
-      clazz, "onControlStateChanged", "(IIILjava/lang/String;)V");
-
-  info("succeeds");
-}
 static const bt_interface_t* btIf;
 
 static void initializeNative(JNIEnv* env, jobject object) {
-  debug("pan");
+  debug("Initialize pan");
   if (btIf) return;
 
   btIf = getBluetoothInterface();
@@ -153,6 +144,7 @@
 }
 
 static void cleanupNative(JNIEnv* env, jobject object) {
+  debug("Cleanup pan");
   if (!btIf) return;
 
   if (sPanIf != NULL) {
@@ -172,7 +164,7 @@
 static jboolean connectPanNative(JNIEnv* env, jobject object,
                                  jbyteArray address, jint src_role,
                                  jint dest_role) {
-  debug("in");
+  debug("Connect pan");
   if (!sPanIf) return JNI_FALSE;
 
   jbyte* addr = env->GetByteArrayElements(address, NULL);
@@ -194,6 +186,7 @@
 
 static jboolean disconnectPanNative(JNIEnv* env, jobject object,
                                     jbyteArray address) {
+  debug("Disconnects pan");
   if (!sPanIf) return JNI_FALSE;
 
   jbyte* addr = env->GetByteArrayElements(address, NULL);
@@ -213,17 +206,27 @@
   return ret;
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initializeNative", "()V", (void*)initializeNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectPanNative", "([BII)Z", (void*)connectPanNative},
-    {"disconnectPanNative", "([B)Z", (void*)disconnectPanNative},
-    // TBD cleanup
-};
-
 int register_com_android_bluetooth_pan(JNIEnv* env) {
-  return jniRegisterNativeMethods(env, "com/android/bluetooth/pan/PanService",
-                                  sMethods, NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initializeNative", "()V", (void*)initializeNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectPanNative", "([BII)Z", (void*)connectPanNative},
+      {"disconnectPanNative", "([B)Z", (void*)disconnectPanNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/pan/PanNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[]{
+      {"onConnectStateChanged", "([BIIII)V", &method_onConnectStateChanged},
+      {"onControlStateChanged", "(IIILjava/lang/String;)V",
+       &method_onControlStateChanged},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/pan/PanNativeInterface",
+                   javaMethods);
+
+  return 0;
 }
 }
diff --git a/android/app/jni/com_android_bluetooth_sdp.cpp b/android/app/jni/com_android_bluetooth_sdp.cpp
index ded546a..c4022a1 100644
--- a/android/app/jni/com_android_bluetooth_sdp.cpp
+++ b/android/app/jni/com_android_bluetooth_sdp.cpp
@@ -75,32 +75,6 @@
   sCallbacksObj = env->NewGlobalRef(object);
 }
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  /* generic SDP record (raw data)*/
-  method_sdpRecordFoundCallback =
-      env->GetMethodID(clazz, "sdpRecordFoundCallback", "(I[B[BI[B)V");
-
-  /* MAS SDP record*/
-  method_sdpMasRecordFoundCallback = env->GetMethodID(
-      clazz, "sdpMasRecordFoundCallback", "(I[B[BIIIIIILjava/lang/String;Z)V");
-  /* MNS SDP record*/
-  method_sdpMnsRecordFoundCallback = env->GetMethodID(
-      clazz, "sdpMnsRecordFoundCallback", "(I[B[BIIIILjava/lang/String;Z)V");
-  /* PBAP PSE record */
-  method_sdpPseRecordFoundCallback = env->GetMethodID(
-      clazz, "sdpPseRecordFoundCallback", "(I[B[BIIIIILjava/lang/String;Z)V");
-  /* OPP Server record */
-  method_sdpOppOpsRecordFoundCallback =
-      env->GetMethodID(clazz, "sdpOppOpsRecordFoundCallback",
-                       "(I[B[BIIILjava/lang/String;[BZ)V");
-  /* SAP Server record */
-  method_sdpSapsRecordFoundCallback = env->GetMethodID(
-      clazz, "sdpSapsRecordFoundCallback", "(I[B[BIILjava/lang/String;Z)V");
-  /* DIP record */
-  method_sdpDipRecordFoundCallback = env->GetMethodID(
-      clazz, "sdpDipRecordFoundCallback", "(I[B[BIIIIIZZ)V");
-}
-
 static jboolean sdpSearchNative(JNIEnv* env, jobject obj, jbyteArray address,
                                 jbyteArray uuidObj) {
   ALOGD("%s", __func__);
@@ -508,28 +482,49 @@
   }
 }
 
-static JNINativeMethod sMethods[] = {
-    /* name, signature, funcPtr */
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initializeNative", "()V", (void*)initializeNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"sdpSearchNative", "([B[B)Z", (void*)sdpSearchNative},
-    {"sdpCreateMapMasRecordNative", "(Ljava/lang/String;IIIIII)I",
-     (void*)sdpCreateMapMasRecordNative},
-    {"sdpCreateMapMnsRecordNative", "(Ljava/lang/String;IIII)I",
-     (void*)sdpCreateMapMnsRecordNative},
-    {"sdpCreatePbapPceRecordNative", "(Ljava/lang/String;I)I",
-     (void*)sdpCreatePbapPceRecordNative},
-    {"sdpCreatePbapPseRecordNative", "(Ljava/lang/String;IIIII)I",
-     (void*)sdpCreatePbapPseRecordNative},
-    {"sdpCreateOppOpsRecordNative", "(Ljava/lang/String;III[B)I",
-     (void*)sdpCreateOppOpsRecordNative},
-    {"sdpCreateSapsRecordNative", "(Ljava/lang/String;II)I",
-     (void*)sdpCreateSapsRecordNative},
-    {"sdpRemoveSdpRecordNative", "(I)Z", (void*)sdpRemoveSdpRecordNative}};
-
 int register_com_android_bluetooth_sdp(JNIEnv* env) {
-  return jniRegisterNativeMethods(env, "com/android/bluetooth/sdp/SdpManager",
-                                  sMethods, NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initializeNative", "()V", (void*)initializeNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"sdpSearchNative", "([B[B)Z", (void*)sdpSearchNative},
+      {"sdpCreateMapMasRecordNative", "(Ljava/lang/String;IIIIII)I",
+       (void*)sdpCreateMapMasRecordNative},
+      {"sdpCreateMapMnsRecordNative", "(Ljava/lang/String;IIII)I",
+       (void*)sdpCreateMapMnsRecordNative},
+      {"sdpCreatePbapPceRecordNative", "(Ljava/lang/String;I)I",
+       (void*)sdpCreatePbapPceRecordNative},
+      {"sdpCreatePbapPseRecordNative", "(Ljava/lang/String;IIIII)I",
+       (void*)sdpCreatePbapPseRecordNative},
+      {"sdpCreateOppOpsRecordNative", "(Ljava/lang/String;III[B)I",
+       (void*)sdpCreateOppOpsRecordNative},
+      {"sdpCreateSapsRecordNative", "(Ljava/lang/String;II)I",
+       (void*)sdpCreateSapsRecordNative},
+      {"sdpRemoveSdpRecordNative", "(I)Z", (void*)sdpRemoveSdpRecordNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/sdp/SdpManagerNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"sdpRecordFoundCallback", "(I[B[BI[B)V", &method_sdpRecordFoundCallback},
+      {"sdpMasRecordFoundCallback", "(I[B[BIIIIIILjava/lang/String;Z)V",
+       &method_sdpMasRecordFoundCallback},
+      {"sdpMnsRecordFoundCallback", "(I[B[BIIIILjava/lang/String;Z)V",
+       &method_sdpMnsRecordFoundCallback},
+      {"sdpPseRecordFoundCallback", "(I[B[BIIIIILjava/lang/String;Z)V",
+       &method_sdpPseRecordFoundCallback},
+      {"sdpOppOpsRecordFoundCallback", "(I[B[BIIILjava/lang/String;[BZ)V",
+       &method_sdpOppOpsRecordFoundCallback},
+      {"sdpSapsRecordFoundCallback", "(I[B[BIILjava/lang/String;Z)V",
+       &method_sdpSapsRecordFoundCallback},
+      {"sdpDipRecordFoundCallback", "(I[B[BIIIIIZZ)V",
+       &method_sdpDipRecordFoundCallback},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/sdp/SdpManagerNativeInterface",
+                   javaMethods);
+
+  return 0;
 }
 }
diff --git a/android/app/jni/com_android_bluetooth_vc.cpp b/android/app/jni/com_android_bluetooth_vc.cpp
index 4f070e7..0826872 100644
--- a/android/app/jni/com_android_bluetooth_vc.cpp
+++ b/android/app/jni/com_android_bluetooth_vc.cpp
@@ -199,31 +199,6 @@
 
 static VolumeControlCallbacksImpl sVolumeControlCallbacks;
 
-static void classInitNative(JNIEnv* env, jclass clazz) {
-  method_onConnectionStateChanged =
-      env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
-
-  method_onVolumeStateChanged =
-      env->GetMethodID(clazz, "onVolumeStateChanged", "(IZ[BZ)V");
-
-  method_onGroupVolumeStateChanged =
-      env->GetMethodID(clazz, "onGroupVolumeStateChanged", "(IZIZ)V");
-
-  method_onDeviceAvailable =
-      env->GetMethodID(clazz, "onDeviceAvailable", "(I[B)V");
-
-  method_onExtAudioOutVolumeOffsetChanged =
-      env->GetMethodID(clazz, "onExtAudioOutVolumeOffsetChanged", "(II[B)V");
-
-  method_onExtAudioOutLocationChanged =
-      env->GetMethodID(clazz, "onExtAudioOutLocationChanged", "(II[B)V");
-
-  method_onExtAudioOutDescriptionChanged = env->GetMethodID(
-      clazz, "onExtAudioOutDescriptionChanged", "(ILjava/lang/String;[B)V");
-
-  LOG(INFO) << __func__ << ": succeeds";
-}
-
 static void initNative(JNIEnv* env, jobject object) {
   std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
   std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
@@ -540,36 +515,55 @@
   return JNI_TRUE;
 }
 
-static JNINativeMethod sMethods[] = {
-    {"classInitNative", "()V", (void*)classInitNative},
-    {"initNative", "()V", (void*)initNative},
-    {"cleanupNative", "()V", (void*)cleanupNative},
-    {"connectVolumeControlNative", "([B)Z", (void*)connectVolumeControlNative},
-    {"disconnectVolumeControlNative", "([B)Z",
-     (void*)disconnectVolumeControlNative},
-    {"setVolumeNative", "([BI)V", (void*)setVolumeNative},
-    {"setGroupVolumeNative", "(II)V", (void*)setGroupVolumeNative},
-    {"muteNative", "([B)V", (void*)muteNative},
-    {"muteGroupNative", "(I)V", (void*)muteGroupNative},
-    {"unmuteNative", "([B)V", (void*)unmuteNative},
-    {"unmuteGroupNative", "(I)V", (void*)unmuteGroupNative},
-    {"getExtAudioOutVolumeOffsetNative", "([BI)Z",
-     (void*)getExtAudioOutVolumeOffsetNative},
-    {"setExtAudioOutVolumeOffsetNative", "([BII)Z",
-     (void*)setExtAudioOutVolumeOffsetNative},
-    {"getExtAudioOutLocationNative", "([BI)Z",
-     (void*)getExtAudioOutLocationNative},
-    {"setExtAudioOutLocationNative", "([BII)Z",
-     (void*)setExtAudioOutLocationNative},
-    {"getExtAudioOutDescriptionNative", "([BI)Z",
-     (void*)getExtAudioOutDescriptionNative},
-    {"setExtAudioOutDescriptionNative", "([BILjava/lang/String;)Z",
-     (void*)setExtAudioOutDescriptionNative},
-};
-
 int register_com_android_bluetooth_vc(JNIEnv* env) {
-  return jniRegisterNativeMethods(
-      env, "com/android/bluetooth/vc/VolumeControlNativeInterface", sMethods,
-      NELEM(sMethods));
+  const JNINativeMethod methods[] = {
+      {"initNative", "()V", (void*)initNative},
+      {"cleanupNative", "()V", (void*)cleanupNative},
+      {"connectVolumeControlNative", "([B)Z",
+       (void*)connectVolumeControlNative},
+      {"disconnectVolumeControlNative", "([B)Z",
+       (void*)disconnectVolumeControlNative},
+      {"setVolumeNative", "([BI)V", (void*)setVolumeNative},
+      {"setGroupVolumeNative", "(II)V", (void*)setGroupVolumeNative},
+      {"muteNative", "([B)V", (void*)muteNative},
+      {"muteGroupNative", "(I)V", (void*)muteGroupNative},
+      {"unmuteNative", "([B)V", (void*)unmuteNative},
+      {"unmuteGroupNative", "(I)V", (void*)unmuteGroupNative},
+      {"getExtAudioOutVolumeOffsetNative", "([BI)Z",
+       (void*)getExtAudioOutVolumeOffsetNative},
+      {"setExtAudioOutVolumeOffsetNative", "([BII)Z",
+       (void*)setExtAudioOutVolumeOffsetNative},
+      {"getExtAudioOutLocationNative", "([BI)Z",
+       (void*)getExtAudioOutLocationNative},
+      {"setExtAudioOutLocationNative", "([BII)Z",
+       (void*)setExtAudioOutLocationNative},
+      {"getExtAudioOutDescriptionNative", "([BI)Z",
+       (void*)getExtAudioOutDescriptionNative},
+      {"setExtAudioOutDescriptionNative", "([BILjava/lang/String;)Z",
+       (void*)setExtAudioOutDescriptionNative},
+  };
+  const int result = REGISTER_NATIVE_METHODS(
+      env, "com/android/bluetooth/vc/VolumeControlNativeInterface", methods);
+  if (result != 0) {
+    return result;
+  }
+
+  const JNIJavaMethod javaMethods[] = {
+      {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
+      {"onVolumeStateChanged", "(IZ[BZ)V", &method_onVolumeStateChanged},
+      {"onGroupVolumeStateChanged", "(IZIZ)V",
+       &method_onGroupVolumeStateChanged},
+      {"onDeviceAvailable", "(I[B)V", &method_onDeviceAvailable},
+      {"onExtAudioOutVolumeOffsetChanged", "(II[B)V",
+       &method_onExtAudioOutVolumeOffsetChanged},
+      {"onExtAudioOutLocationChanged", "(II[B)V",
+       &method_onExtAudioOutLocationChanged},
+      {"onExtAudioOutDescriptionChanged", "(ILjava/lang/String;[B)V",
+       &method_onExtAudioOutDescriptionChanged},
+  };
+  GET_JAVA_METHODS(env, "com/android/bluetooth/vc/VolumeControlNativeInterface",
+                   javaMethods);
+
+  return 0;
 }
 }  // namespace android
diff --git a/android/app/res/values-zh-rCN/strings.xml b/android/app/res/values-zh-rCN/strings.xml
index 802a913..e04dede 100644
--- a/android/app/res/values-zh-rCN/strings.xml
+++ b/android/app/res/values-zh-rCN/strings.xml
@@ -120,11 +120,11 @@
     <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>
-    <string name="bluetooth_map_settings_no_account_slots_left" msgid="755024228476065757">"无法选择帐号,目前没有任何空档"</string>
+    <string name="bluetooth_map_settings_no_account_slots_left" msgid="755024228476065757">"无法选择账号,目前没有任何空档"</string>
     <string name="bluetooth_connected" msgid="5687474377090799447">"蓝牙音频已连接"</string>
     <string name="bluetooth_disconnected" msgid="6841396291728343534">"蓝牙音频已断开连接"</string>
     <string name="a2dp_sink_mbs_label" msgid="6035366346569127155">"蓝牙音频"</string>
diff --git a/android/app/src/com/android/bluetooth/Utils.java b/android/app/src/com/android/bluetooth/Utils.java
index 9372cb4..a3d149d 100644
--- a/android/app/src/com/android/bluetooth/Utils.java
+++ b/android/app/src/com/android/bluetooth/Utils.java
@@ -63,7 +63,6 @@
 import android.provider.Telephony;
 import android.util.Log;
 
-import androidx.annotation.RequiresApi;
 
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
@@ -1235,4 +1234,47 @@
         return pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
                 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
     }
+
+    /**
+     * Returns the longest prefix of a string for which the UTF-8 encoding fits into the given
+     * number of bytes, with the additional guarantee that the string is not truncated in the middle
+     * of a valid surrogate pair.
+     *
+     * <p>Unpaired surrogates are counted as taking 3 bytes of storage. However, a subsequent
+     * attempt to actually encode a string containing unpaired surrogates is likely to be rejected
+     * by the UTF-8 implementation.
+     *
+     * <p>(copied from framework/base/core/java/android/text/TextUtils.java)
+     *
+     * @param str a string
+     * @param maxbytes the maximum number of UTF-8 encoded bytes
+     * @return the beginning of the string, so that it uses at most maxbytes bytes in UTF-8
+     * @throws IndexOutOfBoundsException if maxbytes is negative
+     */
+    public static String truncateStringForUtf8Storage(String str, int maxbytes) {
+        if (maxbytes < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        int bytes = 0;
+        for (int i = 0, len = str.length(); i < len; i++) {
+            char c = str.charAt(i);
+            if (c < 0x80) {
+                bytes += 1;
+            } else if (c < 0x800) {
+                bytes += 2;
+            } else if (c < Character.MIN_SURROGATE
+                    || c > Character.MAX_SURROGATE
+                    || str.codePointAt(i) < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
+                bytes += 3;
+            } else {
+                bytes += 4;
+                i += (bytes > maxbytes) ? 0 : 1;
+            }
+            if (bytes > maxbytes) {
+                return str.substring(0, i);
+            }
+        }
+        return str;
+    }
 }
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java b/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java
index a638539..c6156e6 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java
@@ -47,11 +47,8 @@
 
     @GuardedBy("INSTANCE_LOCK")
     private static A2dpNativeInterface sInstance;
-    private static final Object INSTANCE_LOCK = new Object();
 
-    static {
-        classInitNative();
-    }
+    private static final Object INSTANCE_LOCK = new Object();
 
     @VisibleForTesting
     private A2dpNativeInterface() {
@@ -75,22 +72,29 @@
         }
     }
 
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(A2dpNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
     /**
      * Initializes the native interface.
      *
      * @param maxConnectedAudioDevices maximum number of A2DP Sink devices that can be connected
-     * simultaneously
-     * @param codecConfigPriorities an array with the codec configuration
-     * priorities to configure.
+     *     simultaneously
+     * @param codecConfigPriorities an array with the codec configuration priorities to configure.
      */
-    public void init(int maxConnectedAudioDevices, BluetoothCodecConfig[] codecConfigPriorities,
+    public void init(
+            int maxConnectedAudioDevices,
+            BluetoothCodecConfig[] codecConfigPriorities,
             BluetoothCodecConfig[] codecConfigOffloading) {
         initNative(maxConnectedAudioDevices, codecConfigPriorities, codecConfigOffloading);
     }
 
-    /**
-     * Cleanup the native interface.
-     */
+    /** Cleanup the native interface. */
     public void cleanup() {
         cleanupNative();
     }
@@ -227,7 +231,6 @@
     }
 
     // Native methods that call into the JNI interface
-    private static native void classInitNative();
     private native void initNative(int maxConnectedAudioDevices,
                                    BluetoothCodecConfig[] codecConfigPriorities,
                                    BluetoothCodecConfig[] codecConfigOffloading);
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
index d7742df..8fd242d 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -23,6 +23,8 @@
 import static com.android.bluetooth.Utils.enforceCdmAssociation;
 import static com.android.bluetooth.Utils.hasBluetoothPrivilegedPermission;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.bluetooth.BluetoothA2dp;
@@ -47,7 +49,9 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Looper;
 import android.sysprop.BluetoothProperties;
 import android.util.Log;
 
@@ -72,11 +76,10 @@
 
 /**
  * Provides Bluetooth A2DP profile, as a service in the Bluetooth application.
- * @hide
  */
 public class A2dpService extends ProfileService {
+    private static final String TAG = A2dpService.class.getSimpleName();
     private static final boolean DBG = true;
-    private static final String TAG = "A2dpService";
 
     // TODO(b/240635097): remove in U
     private static final int SOURCE_CODEC_TYPE_OPUS = 6;
@@ -86,9 +89,9 @@
     private AdapterService mAdapterService;
     private DatabaseManager mDatabaseManager;
     private HandlerThread mStateMachinesThread;
+    private Handler mHandler = null;
 
-    @VisibleForTesting
-    A2dpNativeInterface mA2dpNativeInterface;
+    private final A2dpNativeInterface mNativeInterface;
     @VisibleForTesting
     ServiceFactory mFactory = new ServiceFactory();
     @VisibleForTesting
@@ -116,6 +119,17 @@
 
     private BroadcastReceiver mBondStateChangedReceiver;
 
+    A2dpService() {
+        mNativeInterface = requireNonNull(A2dpNativeInterface.getInstance());
+    }
+
+    @VisibleForTesting
+    A2dpService(Context ctx, A2dpNativeInterface nativeInterface) {
+        attachBaseContext(ctx);
+        mNativeInterface = requireNonNull(nativeInterface);
+        onCreate();
+    }
+
     public static boolean isEnabled() {
         return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false);
     }
@@ -137,35 +151,39 @@
             throw new IllegalStateException("start() called twice");
         }
 
-        // Step 1: Get AdapterService, A2dpNativeInterface, DatabaseManager, AudioManager.
+        // Step 1: Get AdapterService, DatabaseManager, AudioManager.
         // None of them can be null.
-        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
-                "AdapterService cannot be null when A2dpService starts");
-        mA2dpNativeInterface = Objects.requireNonNull(A2dpNativeInterface.getInstance(),
-                "A2dpNativeInterface cannot be null when A2dpService starts");
-        mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
-                "DatabaseManager cannot be null when A2dpService starts");
+        mAdapterService =
+                requireNonNull(
+                        AdapterService.getAdapterService(),
+                        "AdapterService cannot be null when A2dpService starts");
+        mDatabaseManager =
+                requireNonNull(
+                        mAdapterService.getDatabase(),
+                        "DatabaseManager cannot be null when A2dpService starts");
         mAudioManager = getSystemService(AudioManager.class);
         mCompanionDeviceManager = getSystemService(CompanionDeviceManager.class);
-        Objects.requireNonNull(mAudioManager,
-                               "AudioManager cannot be null when A2dpService starts");
+        requireNonNull(mAudioManager, "AudioManager cannot be null when A2dpService starts");
 
         // Step 2: Get maximum number of connected audio devices
         mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices();
         Log.i(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices);
 
         // Step 3: Start handler thread for state machines
+        // Setup Handler.
+        mHandler = new Handler(Looper.getMainLooper());
         mStateMachines.clear();
         mStateMachinesThread = new HandlerThread("A2dpService.StateMachines");
         mStateMachinesThread.start();
 
         // Step 4: Setup codec config
-        mA2dpCodecConfig = new A2dpCodecConfig(this, mA2dpNativeInterface);
+        mA2dpCodecConfig = new A2dpCodecConfig(this, mNativeInterface);
 
         // Step 5: Initialize native interface
-        mA2dpNativeInterface.init(mMaxConnectedAudioDevices,
-                                  mA2dpCodecConfig.codecConfigPriorities(),
-                                  mA2dpCodecConfig.codecConfigOffloading());
+        mNativeInterface.init(
+                mMaxConnectedAudioDevices,
+                mA2dpCodecConfig.codecConfigPriorities(),
+                mA2dpCodecConfig.codecConfigOffloading());
 
         // Step 6: Check if A2DP is in offload mode
         mA2dpOffloadEnabled = mAdapterService.isA2dpOffloadEnabled();
@@ -182,11 +200,6 @@
 
         // Step 8: Mark service as started
         setA2dpService(this);
-        BluetoothDevice activeDevice = getActiveDevice();
-        String deviceAddress = activeDevice != null ?
-                activeDevice.getAddress() :
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS;
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(), deviceAddress);
 
         // Step 9: Clear active device
         removeActiveDevice(false);
@@ -206,11 +219,6 @@
         removeActiveDevice(true);
 
         // Step 8: Mark service as stopped
-        BluetoothDevice activeDevice = getActiveDevice();
-        String deviceAddress = activeDevice != null ?
-                activeDevice.getAddress() :
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS;
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(), deviceAddress);
         setA2dpService(null);
 
         // Step 7: Unregister broadcast receivers
@@ -218,8 +226,7 @@
         mBondStateChangedReceiver = null;
 
         // Step 6: Cleanup native interface
-        mA2dpNativeInterface.cleanup();
-        mA2dpNativeInterface = null;
+        mNativeInterface.cleanup();
 
         // Step 5: Clear codec config
         mA2dpCodecConfig = null;
@@ -245,9 +252,8 @@
         // Step 2: Reset maximum number of connected audio devices
         mMaxConnectedAudioDevices = 1;
 
-        // Step 1: Clear AdapterService, A2dpNativeInterface, AudioManager
+        // Step 1: Clear AdapterService, AudioManager
         mAudioManager = null;
-        mA2dpNativeInterface = null;
         mAdapterService = null;
 
         return true;
@@ -514,7 +520,7 @@
 
             synchronized (mStateMachines) {
                 // Make sure the Active device in native layer is set to null and audio is off
-                if (!mA2dpNativeInterface.setActiveDevice(null)) {
+                if (!mNativeInterface.setActiveDevice(null)) {
                     Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native "
                             + "layer");
                     return false;
@@ -542,7 +548,7 @@
             // Set the device as the active device if currently no active device.
             setActiveDevice(device);
         }
-        if (!mA2dpNativeInterface.setSilenceDevice(device, silence)) {
+        if (!mNativeInterface.setSilenceDevice(device, silence)) {
             Log.e(TAG, "Cannot set " + device + " silence mode " + silence + " in native layer");
             return false;
         }
@@ -599,7 +605,7 @@
 
             BluetoothDevice newActiveDevice = null;
             synchronized (mStateMachines) {
-                if (!mA2dpNativeInterface.setActiveDevice(device)) {
+                if (!mNativeInterface.setActiveDevice(device)) {
                     Log.e(TAG, "setActiveDevice(" + device + "): Cannot set as active in native "
                             + "layer");
                     // Remove active device and stop playing audio.
@@ -945,8 +951,7 @@
 
     // Handle messages from native (JNI) to Java
     void messageFromNative(A2dpStackEvent stackEvent) {
-        Objects.requireNonNull(stackEvent.device,
-                               "Device should never be null, event: " + stackEvent);
+        requireNonNull(stackEvent.device, "Device should never be null, event: " + stackEvent);
         synchronized (mStateMachines) {
             BluetoothDevice device = stackEvent.device;
             A2dpStateMachine sm = mStateMachines.get(device);
@@ -1041,8 +1046,9 @@
             if (DBG) {
                 Log.d(TAG, "Creating a new state machine for " + device);
             }
-            sm = A2dpStateMachine.make(device, this, mA2dpNativeInterface,
-                                       mStateMachinesThread.getLooper());
+            sm =
+                    A2dpStateMachine.make(
+                            device, this, mNativeInterface, mStateMachinesThread.getLooper());
             mStateMachines.put(device, sm);
             return sm;
         }
@@ -1058,20 +1064,25 @@
 
         // Make sure volume has been store before device been remove from active.
         if (mFactory.getAvrcpTargetService() != null) {
-            mFactory.getAvrcpTargetService().volumeDeviceSwitched(device);
+            mFactory.getAvrcpTargetService().handleA2dpActiveDeviceChanged(device);
         }
         synchronized (mStateMachines) {
             mActiveDevice = device;
         }
 
+        mAdapterService
+                .getActiveDeviceManager()
+                .profileActiveDeviceChanged(BluetoothProfile.A2DP, device);
+        mAdapterService.getSilenceDeviceManager().a2dpActiveDeviceChanged(device);
+
         BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED,
                 BluetoothProfile.A2DP, mAdapterService.obfuscateAddress(device),
                 mAdapterService.getMetricId(device));
+
         Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-        mAdapterService.getActiveDeviceManager().a2dpActiveStateChanged(device);
         Utils.sendBroadcast(this, intent, BLUETOOTH_CONNECT,
                 Utils.getTempAllowlistBroadcastOptions());
     }
@@ -1098,7 +1109,7 @@
             int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
                                            BluetoothDevice.ERROR);
             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-            Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
+            requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
             bondStateChanged(device, state);
         }
     }
@@ -1237,7 +1248,16 @@
         }
     }
 
+    void handleConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+        mHandler.post(() -> connectionStateChanged(device, fromState, toState));
+    }
+
     void connectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+        if (!isAvailable()) {
+            Log.w(TAG, "connectionStateChanged: service is not available");
+            return;
+        }
+
         if ((device == null) || (fromState == toState)) {
             return;
         }
@@ -1259,22 +1279,18 @@
                 removeStateMachine(device);
             }
         }
+        if (mFactory.getAvrcpTargetService() != null) {
+            mFactory.getAvrcpTargetService().handleA2dpConnectionStateChanged(device, toState);
+        }
         mAdapterService
                 .getActiveDeviceManager()
+                .profileConnectionStateChanged(BluetoothProfile.A2DP, device, fromState, toState);
+        mAdapterService
+                .getSilenceDeviceManager()
                 .a2dpConnectionStateChanged(device, fromState, toState);
     }
 
     /**
-     * Retrieves the most recently connected device in the A2DP connected devices list.
-     */
-    public BluetoothDevice getFallbackDevice() {
-        DatabaseManager dbManager = mAdapterService.getDatabase();
-        return dbManager != null ? dbManager
-            .getMostRecentlyConnectedDevicesInList(getConnectedDevices())
-            : null;
-    }
-
-    /**
      * Binder object: must be a static class or memory leak may occur.
      */
     @VisibleForTesting
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
index 7a69e21..1a58e20 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -735,7 +735,7 @@
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
-        mA2dpService.connectionStateChanged(mDevice, prevState, newState);
+        mA2dpService.handleConnectionStateChanged(mDevice, prevState, newState);
         Utils.sendBroadcast(mA2dpService, intent, BLUETOOTH_CONNECT,
                 Utils.getTempAllowlistBroadcastOptions());
     }
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface.java
index 0f2b3b0..e61d8da 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface.java
@@ -22,6 +22,7 @@
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Objects;
 
@@ -35,11 +36,8 @@
 
     @GuardedBy("INSTANCE_LOCK")
     private static A2dpSinkNativeInterface sInstance;
-    private static final Object INSTANCE_LOCK = new Object();
 
-    static {
-        classInitNative();
-    }
+    private static final Object INSTANCE_LOCK = new Object();
 
     private A2dpSinkNativeInterface() {
         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
@@ -58,6 +56,14 @@
         }
     }
 
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(A2dpSinkNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
     /**
      * Initializes the native interface and sets the max number of connected devices
      *
@@ -190,7 +196,6 @@
     }
 
     // Native methods that call into the JNI interface
-    private static native void classInitNative();
     private native void initNative(int maxConnectedAudioDevices);
     private native void cleanupNative();
     private native boolean connectA2dpNative(byte[] address);
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index 02e8a5a..9f27399 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -15,6 +15,8 @@
  */
 package com.android.bluetooth.a2dpsink;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.RequiresPermission;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothAudioConfig;
@@ -22,7 +24,9 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.IBluetoothA2dpSink;
 import android.content.AttributionSource;
+import android.content.Context;
 import android.media.AudioManager;
+import android.os.Looper;
 import android.sysprop.BluetoothProperties;
 import android.util.Log;
 
@@ -30,6 +34,7 @@
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.SynchronousResultReceiver;
 
@@ -37,32 +42,50 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Provides Bluetooth A2DP Sink profile, as a service in the Bluetooth application.
- * @hide
  */
 public class A2dpSinkService extends ProfileService {
-    private static final String TAG = "A2dpSinkService";
+    private static final String TAG = A2dpSinkService.class.getSimpleName();
     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final Map<BluetoothDevice, A2dpSinkStateMachine> mDeviceStateMap =
+            new ConcurrentHashMap<>(1);
+
+    private final A2dpSinkNativeInterface mNativeInterface;
+    private final Looper mLooper;
+
+    private final Object mActiveDeviceLock = new Object();
+
+    @GuardedBy("mActiveDeviceLock")
+    private BluetoothDevice mActiveDevice = null;
+
+    private final Object mStreamHandlerLock = new Object();
+
+    @GuardedBy("mStreamHandlerLock")
+    private A2dpSinkStreamHandler mA2dpSinkStreamHandler;
+
+    private static A2dpSinkService sService;
+
     private int mMaxConnectedAudioDevices;
 
     private AdapterService mAdapterService;
     private DatabaseManager mDatabaseManager;
-    private Map<BluetoothDevice, A2dpSinkStateMachine> mDeviceStateMap =
-            new ConcurrentHashMap<>(1);
 
-    private final Object mStreamHandlerLock = new Object();
+    A2dpSinkService() {
+        mNativeInterface = requireNonNull(A2dpSinkNativeInterface.getInstance());
+        mLooper = Looper.getMainLooper();
+    }
 
-    private final Object mActiveDeviceLock = new Object();
-    private BluetoothDevice mActiveDevice = null;
-
-    private A2dpSinkStreamHandler mA2dpSinkStreamHandler;
-    private static A2dpSinkService sService;
-
-    A2dpSinkNativeInterface mNativeInterface;
+    @VisibleForTesting
+    A2dpSinkService(Context ctx, A2dpSinkNativeInterface nativeInterface, Looper looper) {
+        attachBaseContext(ctx);
+        mNativeInterface = requireNonNull(nativeInterface);
+        mLooper = looper;
+        onCreate();
+    }
 
     public static boolean isEnabled() {
         return BluetoothProperties.isProfileA2dpSinkEnabled().orElse(false);
@@ -70,11 +93,14 @@
 
     @Override
     protected boolean start() {
-        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
-                "AdapterService cannot be null when A2dpSinkService starts");
-        mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(),
-                "DatabaseManager cannot be null when A2dpSinkService starts");
-        mNativeInterface = A2dpSinkNativeInterface.getInstance();
+        mAdapterService =
+                requireNonNull(
+                        AdapterService.getAdapterService(),
+                        "AdapterService cannot be null when A2dpSinkService starts");
+        mDatabaseManager =
+                requireNonNull(
+                        AdapterService.getAdapterService().getDatabase(),
+                        "DatabaseManager cannot be null when A2dpSinkService starts");
 
         mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices();
         mNativeInterface.init(mMaxConnectedAudioDevices);
@@ -84,21 +110,11 @@
         }
 
         setA2dpSinkService(this);
-        BluetoothDevice activeDevice = getActiveDevice();
-        String deviceAddress = activeDevice != null ?
-                activeDevice.getAddress() :
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS;
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(), deviceAddress);
         return true;
     }
 
     @Override
     protected boolean stop() {
-        BluetoothDevice activeDevice = getActiveDevice();
-        String deviceAddress = activeDevice != null ?
-                activeDevice.getAddress() :
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS;
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(), deviceAddress);
         setA2dpSinkService(null);
         mNativeInterface.cleanup();
         for (A2dpSinkStateMachine stateMachine : mDeviceStateMap.values()) {
@@ -120,16 +136,12 @@
 
     /**
      * Testing API to inject a mockA2dpSinkService.
-     * @hide
      */
     @VisibleForTesting
     public static synchronized void setA2dpSinkService(A2dpSinkService service) {
         sService = service;
     }
 
-
-    public A2dpSinkService() {}
-
     /**
      * Set the device that should be allowed to actively stream
      */
@@ -428,13 +440,16 @@
     /**
      * Remove a device's state machine.
      *
-     * Called by the state machines when they disconnect.
+     * <p>Called by the state machines when they disconnect.
      *
-     * Visible for testing so it can be mocked and verified on.
+     * <p>Visible for testing so it can be mocked and verified on.
      */
-    @VisibleForTesting
-    public void removeStateMachine(A2dpSinkStateMachine stateMachine) {
+    void removeStateMachine(A2dpSinkStateMachine stateMachine) {
+        if (stateMachine == null) {
+            return;
+        }
         mDeviceStateMap.remove(stateMachine.getDevice());
+        stateMachine.quitNow();
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
@@ -443,11 +458,12 @@
 
     protected A2dpSinkStateMachine getOrCreateStateMachine(BluetoothDevice device) {
         A2dpSinkStateMachine newStateMachine =
-                new A2dpSinkStateMachine(device, this, mNativeInterface);
+                new A2dpSinkStateMachine(mLooper, device, this, mNativeInterface);
         A2dpSinkStateMachine existingStateMachine =
                 mDeviceStateMap.putIfAbsent(device, newStateMachine);
         // Given null is not a valid value in our map, ConcurrentHashMap will return null if the
-        // key was absent and our new value was added. We should then start and return it.
+        // key was absent and our new value was added. We should then start and return it. Else
+        // we quit the new one so we don't leak a thread
         if (existingStateMachine == null) {
             newStateMachine.start();
             return newStateMachine;
@@ -594,7 +610,7 @@
             return;
         }
         A2dpSinkStateMachine stateMachine = getOrCreateStateMachine(device);
-        stateMachine.sendMessage(A2dpSinkStateMachine.STACK_EVENT, event);
+        stateMachine.onStackEvent(event);
     }
 
     private void onAudioStateChanged(StackEvent event) {
@@ -621,7 +637,7 @@
         if (device == null) {
             return;
         }
-        A2dpSinkStateMachine stateMachine = getOrCreateStateMachine(device);
-        stateMachine.sendMessage(A2dpSinkStateMachine.STACK_EVENT, event);
+        A2dpSinkStateMachine stateMachine = getStateMachineForDevice(device);
+        stateMachine.onStackEvent(event);
     }
 }
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
index 2c699cc..56f42c7 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
@@ -24,6 +24,7 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Intent;
 import android.media.AudioFormat;
+import android.os.Looper;
 import android.os.Message;
 import android.util.Log;
 
@@ -31,24 +32,24 @@
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.MetricsLogger;
 import com.android.bluetooth.btservice.ProfileService;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 
-
-public class A2dpSinkStateMachine extends StateMachine {
-    static final String TAG = "A2DPSinkStateMachine";
+class A2dpSinkStateMachine extends StateMachine {
+    private static final String TAG = A2dpSinkStateMachine.class.getSimpleName();
     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
-    //0->99 Events from Outside
-    public static final int CONNECT = 1;
-    public static final int DISCONNECT = 2;
+    // 0->99 Events from Outside
+    @VisibleForTesting static final int CONNECT = 1;
+    @VisibleForTesting static final int DISCONNECT = 2;
 
-    //100->199 Internal Events
-    protected static final int CLEANUP = 100;
-    private static final int CONNECT_TIMEOUT = 101;
+    // 100->199 Internal Events
+    @VisibleForTesting static final int CLEANUP = 100;
+    @VisibleForTesting static final int CONNECT_TIMEOUT = 101;
 
-    //200->299 Events from Native
-    static final int STACK_EVENT = 200;
+    // 200->299 Events from Native
+    @VisibleForTesting static final int STACK_EVENT = 200;
 
     static final int CONNECT_TIMEOUT_MS = 10000;
 
@@ -64,9 +65,12 @@
     protected int mMostRecentState = BluetoothProfile.STATE_DISCONNECTED;
     protected BluetoothAudioConfig mAudioConfig = null;
 
-    A2dpSinkStateMachine(BluetoothDevice device, A2dpSinkService service,
+    A2dpSinkStateMachine(
+            Looper looper,
+            BluetoothDevice device,
+            A2dpSinkService service,
             A2dpSinkNativeInterface nativeInterface) {
-        super(TAG);
+        super(TAG, looper);
         mDevice = device;
         mDeviceAddress = Utils.getByteAddress(mDevice);
         mService = service;
@@ -111,20 +115,21 @@
         return mDevice;
     }
 
-    /**
-     * send the Connect command asynchronously
-     */
-    public final void connect() {
+    /** send the Connect command asynchronously */
+    final void connect() {
         sendMessage(CONNECT);
     }
 
-    /**
-     * send the Disconnect command asynchronously
-     */
-    public final void disconnect() {
+    /** send the Disconnect command asynchronously */
+    final void disconnect() {
         sendMessage(DISCONNECT);
     }
 
+    /** send the stack event asynchronously */
+    final void onStackEvent(StackEvent event) {
+        sendMessage(STACK_EVENT, event);
+    }
+
     /**
      * Dump the current State Machine to the string builder.
      * @param sb output string
@@ -218,6 +223,10 @@
                 case CONNECT_TIMEOUT:
                     transitionTo(mDisconnected);
                     return true;
+                case DISCONNECT:
+                    Log.d(TAG, "Received disconnect message while connecting. deferred");
+                    deferMessage(message);
+                    return true;
             }
             return false;
         }
diff --git a/android/app/src/com/android/bluetooth/audio_util/BrowsablePlayerConnector.java b/android/app/src/com/android/bluetooth/audio_util/BrowsablePlayerConnector.java
index aa4fc8d..cb9ca44 100644
--- a/android/app/src/com/android/bluetooth/audio_util/BrowsablePlayerConnector.java
+++ b/android/app/src/com/android/bluetooth/audio_util/BrowsablePlayerConnector.java
@@ -41,7 +41,7 @@
  * using the MediaBrowserService. This way we do not have to do the same checks
  * when constructing BrowsedPlayerWrappers by hand.
  */
-public class BrowsablePlayerConnector {
+public class BrowsablePlayerConnector extends Handler {
     private static final String TAG = "AvrcpBrowsablePlayerConnector";
     private static final boolean DEBUG = true;
     private static final long CONNECT_TIMEOUT_MS = 10000; // Time in ms to wait for a connection
@@ -51,7 +51,6 @@
     private static final int MSG_TIMEOUT = 2;
 
     private static BrowsablePlayerConnector sInjectConnector;
-    private Handler mHandler;
     private PlayerListCallback mCallback;
 
     private List<BrowsedPlayerWrapper> mResults = new ArrayList<BrowsedPlayerWrapper>();
@@ -83,7 +82,7 @@
             return null;
         }
 
-        BrowsablePlayerConnector newWrapper = new BrowsablePlayerConnector(looper, cb);
+        BrowsablePlayerConnector newConnector = new BrowsablePlayerConnector(looper, cb);
 
         // Try to start connecting all the browsed player wrappers
         for (ResolveInfo info : players) {
@@ -92,7 +91,7 @@
                             looper,
                             info.serviceInfo.packageName,
                             info.serviceInfo.name);
-            newWrapper.mPendingPlayers.add(player);
+            newConnector.mPendingPlayers.add(player);
             player.connect((int status, BrowsedPlayerWrapper wrapper) -> {
                 // Use the handler to avoid concurrency issues
                 if (DEBUG) {
@@ -100,86 +99,17 @@
                             + info.serviceInfo.packageName
                             + " : status=" + status);
                 }
-                Message msg = newWrapper.mHandler.obtainMessage(MSG_CONNECT_CB);
-                msg.arg1 = status;
-                msg.obj = wrapper;
-                newWrapper.mHandler.sendMessage(msg);
+                newConnector.obtainMessage(MSG_CONNECT_CB, status, 0, wrapper).sendToTarget();
             });
         }
 
-        Message msg = newWrapper.mHandler.obtainMessage(MSG_TIMEOUT);
-        newWrapper.mHandler.sendMessageDelayed(msg, CONNECT_TIMEOUT_MS);
-        return newWrapper;
+        newConnector.sendEmptyMessageDelayed(MSG_TIMEOUT, CONNECT_TIMEOUT_MS);
+        return newConnector;
     }
 
     private BrowsablePlayerConnector(Looper looper, PlayerListCallback cb) {
+        super(looper);
         mCallback = cb;
-        mHandler = new Handler(looper) {
-            public void handleMessage(Message msg) {
-                if (DEBUG) Log.d(TAG, "Received a message: msg.what=" + msg.what);
-                switch(msg.what) {
-                    case MSG_GET_FOLDER_ITEMS_CB: {
-                        int status = msg.arg1;
-                        int results_size = msg.arg2;
-                        BrowsedPlayerWrapper wrapper = (BrowsedPlayerWrapper) msg.obj;
-
-                        // If we failed to remove the wrapper from the pending set, that
-                        // means a timeout occurred and the callback was triggered afterwards
-                        if (!mPendingPlayers.remove(wrapper)) {
-                            return;
-                        }
-
-                        if (status == BrowsedPlayerWrapper.STATUS_SUCCESS && results_size != 0) {
-                            Log.i(TAG, "Successfully added package to results: "
-                                    + wrapper.getPackageName());
-                            mResults.add(wrapper);
-                        }
-                    } break;
-
-                    case MSG_CONNECT_CB: {
-                        BrowsedPlayerWrapper wrapper = (BrowsedPlayerWrapper) msg.obj;
-
-                        if (msg.arg1 != BrowsedPlayerWrapper.STATUS_SUCCESS) {
-                            Log.i(TAG, wrapper.getPackageName() + " is not browsable");
-                            // If we failed to remove the wrapper from the pending set, that
-                            // means a timeout occurred and the callback was triggered afterwards
-                            if (!mPendingPlayers.remove(wrapper)) {
-                                return;
-                            }
-                            break;
-                        }
-
-                        // Check to see if the root folder has any items
-                        if (DEBUG) {
-                            Log.i(TAG, "Checking root contents for " + wrapper.getPackageName());
-                        }
-                        wrapper.getFolderItems(wrapper.getRootId(),
-                                (int status, String mediaId, List<ListItem> results) -> {
-                                    // Send the response as a message so that it is properly
-                                    // synchronized
-                                    Message cb =
-                                            mHandler.obtainMessage(MSG_GET_FOLDER_ITEMS_CB);
-                                    cb.arg1 = status;
-                                    cb.arg2 = results.size();
-                                    cb.obj = wrapper;
-                                    mHandler.sendMessage(cb);
-                                });
-                    } break;
-
-                    case MSG_TIMEOUT: {
-                        Log.v(TAG, "Timed out waiting for players");
-                        removePendingPlayers();
-                    } break;
-                }
-
-                if (mPendingPlayers.size() == 0) {
-                    Log.i(TAG, "Successfully connected to "
-                            + mResults.size() + " browsable players.");
-                    removeMessages(MSG_TIMEOUT);
-                    mCallback.run(mResults);
-                }
-            }
-        };
     }
 
     private void removePendingPlayers() {
@@ -193,9 +123,74 @@
     void cleanup() {
         if (mPendingPlayers.size() != 0) {
             Log.i(TAG, "Bluetooth turn off with " + mPendingPlayers.size() + " pending player(s)");
-            mHandler.removeMessages(MSG_TIMEOUT);
             removePendingPlayers();
-            mHandler = null;
+            removeCallbacksAndMessages(null);
+        }
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        if (DEBUG) Log.d(TAG, "Received a message: msg.what=" + msg.what);
+        switch(msg.what) {
+            case MSG_GET_FOLDER_ITEMS_CB: {
+                int status = msg.arg1;
+                int results_size = msg.arg2;
+                BrowsedPlayerWrapper wrapper = (BrowsedPlayerWrapper) msg.obj;
+
+                // If we failed to remove the wrapper from the pending set, that
+                // means a timeout occurred and the callback was triggered afterwards
+                // or the connector was cleaned up.
+                if (!mPendingPlayers.remove(wrapper)) {
+                    return;
+                }
+
+                if (status == BrowsedPlayerWrapper.STATUS_SUCCESS && results_size != 0) {
+                    Log.i(TAG, "Successfully added package to results: "
+                            + wrapper.getPackageName());
+                    mResults.add(wrapper);
+                }
+                break;
+            }
+
+            case MSG_CONNECT_CB: {
+                BrowsedPlayerWrapper wrapper = (BrowsedPlayerWrapper) msg.obj;
+
+                if (msg.arg1 != BrowsedPlayerWrapper.STATUS_SUCCESS) {
+                    Log.i(TAG, wrapper.getPackageName() + " is not browsable");
+                    // If we failed to remove the wrapper from the pending set, that
+                    // means a timeout occurred and the callback was triggered afterwards
+                    if (!mPendingPlayers.remove(wrapper)) {
+                        return;
+                    }
+                    break;
+                }
+
+                // Check to see if the root folder has any items
+                if (DEBUG) {
+                    Log.i(TAG, "Checking root contents for " + wrapper.getPackageName());
+                }
+                wrapper.getFolderItems(wrapper.getRootId(),
+                        (int status, String mediaId, List<ListItem> results) -> {
+                            // Send the response as a message so that it is properly
+                            // synchronized
+                            obtainMessage(MSG_GET_FOLDER_ITEMS_CB, status, results.size(), wrapper)
+                                    .sendToTarget();
+                        });
+                break;
+            }
+
+            case MSG_TIMEOUT: {
+                Log.v(TAG, "Timed out waiting for players");
+                removePendingPlayers();
+                break;
+            }
+        }
+
+        if (mPendingPlayers.size() == 0) {
+            Log.i(TAG, "Successfully connected to "
+                    + mResults.size() + " browsable players.");
+            removeMessages(MSG_TIMEOUT);
+            mCallback.run(mResults);
         }
     }
 }
diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java
index 3225244..256326d 100644
--- a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java
+++ b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerWrapper.java
@@ -461,8 +461,11 @@
         @Override
         public void onMetadataChanged(@Nullable MediaMetadata mediaMetadata) {
             if (!isMetadataReady()) {
-                Log.v(TAG, "onMetadataChanged(): " + mPackageName
-                        + " tried to update with no queue");
+                Log.v(
+                        TAG,
+                        "onMetadataChanged(): "
+                                + mPackageName
+                                + " tried to update with no metadata");
                 return;
             }
 
@@ -495,13 +498,16 @@
         @Override
         public void onPlaybackStateChanged(@Nullable PlaybackState state) {
             if (!isPlaybackStateReady()) {
-                Log.v(TAG, "onPlaybackStateChanged(): " + mPackageName
-                        + " tried to update with no queue");
+                Log.v(
+                        TAG,
+                        "onPlaybackStateChanged(): "
+                                + mPackageName
+                                + " tried to update with no state");
                 return;
             }
 
-            mPlaybackStateChangeEventLogger.logv(TAG, "onPlaybackStateChanged(): "
-                    + mPackageName + " : " + state.toString());
+            mPlaybackStateChangeEventLogger.logv(
+                    TAG, "onPlaybackStateChanged(): " + mPackageName + " : " + state);
 
             if (!playstateEquals(state, getPlaybackState())) {
                 e("The callback playback state doesn't match the current state");
@@ -513,8 +519,8 @@
                 return;
             }
 
-            // If there is no playstate, ignore the update.
-            if (state.getState() == PlaybackState.STATE_NONE) {
+            // If state isn't null and there is no playstate, ignore the update.
+            if (state != null && state.getState() == PlaybackState.STATE_NONE) {
                 Log.v(TAG, "Waiting to send update as controller has no playback state");
                 return;
             }
diff --git a/android/app/src/com/android/bluetooth/audio_util/PlayerSettingsManager.java b/android/app/src/com/android/bluetooth/audio_util/PlayerSettingsManager.java
index da8d2b9..cbbcf74 100644
--- a/android/app/src/com/android/bluetooth/audio_util/PlayerSettingsManager.java
+++ b/android/app/src/com/android/bluetooth/audio_util/PlayerSettingsManager.java
@@ -19,6 +19,7 @@
 import android.support.v4.media.session.MediaControllerCompat;
 import android.support.v4.media.session.MediaSessionCompat;
 import android.support.v4.media.session.PlaybackStateCompat;
+import android.util.Log;
 
 import com.android.bluetooth.avrcp.AvrcpTargetService;
 
@@ -50,7 +51,9 @@
         if (wrapper != null) {
             mActivePlayerController = new MediaControllerCompat(mService,
                     MediaSessionCompat.Token.fromToken(wrapper.getSessionToken()));
-            mActivePlayerController.registerCallback(mControllerCallback);
+            if (!registerMediaControllerCallback(mActivePlayerController, mControllerCallback)) {
+                mActivePlayerController = null;
+            }
         } else {
             mActivePlayerController = null;
         }
@@ -62,7 +65,7 @@
     public void cleanup() {
         updateRemoteDevice();
         if (mActivePlayerController != null) {
-            mActivePlayerController.unregisterCallback(mControllerCallback);
+            unregisterMediaControllerCallback(mActivePlayerController, mControllerCallback);
         }
     }
 
@@ -71,12 +74,15 @@
      */
     private void activePlayerChanged(MediaPlayerWrapper mediaPlayerWrapper) {
         if (mActivePlayerController != null) {
-            mActivePlayerController.unregisterCallback(mControllerCallback);
+            unregisterMediaControllerCallback(mActivePlayerController, mControllerCallback);
         }
         if (mediaPlayerWrapper != null) {
             mActivePlayerController = new MediaControllerCompat(mService,
                     MediaSessionCompat.Token.fromToken(mediaPlayerWrapper.getSessionToken()));
-            mActivePlayerController.registerCallback(mControllerCallback);
+            if (!registerMediaControllerCallback(mActivePlayerController, mControllerCallback)) {
+                mActivePlayerController = null;
+                updateRemoteDevice();
+            }
         } else {
             mActivePlayerController = null;
             updateRemoteDevice();
@@ -94,7 +100,15 @@
      * - The repeat / shuffle player state changed
      */
     private void updateRemoteDevice() {
-        mService.sendPlayerSettings(getPlayerRepeatMode(), getPlayerShuffleMode());
+        int repeatMode = getPlayerRepeatMode();
+        int shuffleMode = getPlayerShuffleMode();
+        Log.i(
+                TAG,
+                "updateRemoteDevice: "
+                        + getRepeatModeStringValue(repeatMode)
+                        + ", "
+                        + getShuffleModeStringValue(shuffleMode));
+        mService.sendPlayerSettings(repeatMode, shuffleMode);
     }
 
     /**
@@ -104,8 +118,13 @@
         if (mActivePlayerController == null) {
             return false;
         }
-        MediaControllerCompat.TransportControls controls =
-                mActivePlayerController.getTransportControls();
+        MediaControllerCompat.TransportControls controls;
+        try {
+            controls = mActivePlayerController.getTransportControls();
+        } catch (SecurityException e) {
+            Log.e(TAG, e.toString());
+            return false;
+        }
         switch (repeatMode) {
             case PlayerSettingsValues.STATE_REPEAT_OFF:
                 controls.setRepeatMode(PlaybackStateCompat.REPEAT_MODE_NONE);
@@ -130,10 +149,16 @@
      */
     public boolean setPlayerShuffleMode(int shuffleMode) {
         if (mActivePlayerController == null) {
+            Log.i(TAG, "setPlayerShuffleMode: no active player");
             return false;
         }
-        MediaControllerCompat.TransportControls controls =
-                mActivePlayerController.getTransportControls();
+        MediaControllerCompat.TransportControls controls;
+        try {
+            controls = mActivePlayerController.getTransportControls();
+        } catch (SecurityException e) {
+            Log.e(TAG, e.toString());
+            return false;
+        }
         switch (shuffleMode) {
             case PlayerSettingsValues.STATE_SHUFFLE_OFF:
                 controls.setShuffleMode(PlaybackStateCompat.SHUFFLE_MODE_NONE);
@@ -155,9 +180,16 @@
      */
     public int getPlayerRepeatMode() {
         if (mActivePlayerController == null) {
+            Log.i(TAG, "getPlayerRepeatMode: no active player");
             return PlayerSettingsValues.STATE_REPEAT_OFF;
         }
-        int mediaFwkMode = mActivePlayerController.getRepeatMode();
+        int mediaFwkMode;
+        try {
+            mediaFwkMode = mActivePlayerController.getRepeatMode();
+        } catch (SecurityException e) {
+            Log.e(TAG, e.toString());
+            return PlayerSettingsValues.STATE_REPEAT_OFF;
+        }
         switch (mediaFwkMode) {
             case PlaybackStateCompat.REPEAT_MODE_NONE:
                 return PlayerSettingsValues.STATE_REPEAT_OFF;
@@ -179,9 +211,16 @@
      */
     public int getPlayerShuffleMode() {
         if (mActivePlayerController == null) {
+            Log.i(TAG, "getPlayerShuffleMode: no active player");
             return PlayerSettingsValues.STATE_SHUFFLE_OFF;
         }
-        int mediaFwkMode = mActivePlayerController.getShuffleMode();
+        int mediaFwkMode;
+        try {
+            mediaFwkMode = mActivePlayerController.getShuffleMode();
+        } catch (SecurityException e) {
+            Log.e(TAG, e.toString());
+            return PlayerSettingsValues.STATE_SHUFFLE_OFF;
+        }
         switch (mediaFwkMode) {
             case PlaybackStateCompat.SHUFFLE_MODE_NONE:
                 return PlayerSettingsValues.STATE_SHUFFLE_OFF;
@@ -196,6 +235,36 @@
         }
     }
 
+    /**
+     * The binder of some MediaControllers can fail to register the callback, this result on a crash
+     * on the media side that has to be handled here.
+     */
+    private static boolean registerMediaControllerCallback(
+            MediaControllerCompat controller, MediaControllerCallback callback) {
+        try {
+            controller.registerCallback(callback);
+        } catch (SecurityException e) {
+            Log.e(TAG, e.toString());
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * The binder of some MediaControllers can fail to unregister the callback, this result on a
+     * crash on the media side that has to be handled here.
+     */
+    private static boolean unregisterMediaControllerCallback(
+            MediaControllerCompat controller, MediaControllerCallback callback) {
+        try {
+            controller.unregisterCallback(callback);
+        } catch (SecurityException e) {
+            Log.e(TAG, e.toString());
+            return false;
+        }
+        return true;
+    }
+
     // Receives callbacks from the MediaControllerCompat.
     private class MediaControllerCallback extends MediaControllerCompat.Callback {
         @Override
@@ -268,4 +337,32 @@
          */
         public static final int STATE_DEFAULT_OFF = 1;
     }
+
+    private static String getRepeatModeStringValue(int repeatMode) {
+        switch (repeatMode) {
+            case PlayerSettingsValues.STATE_REPEAT_OFF:
+                return "STATE_REPEAT_OFF";
+            case PlayerSettingsValues.STATE_REPEAT_SINGLE_TRACK:
+                return "STATE_REPEAT_SINGLE_TRACK";
+            case PlayerSettingsValues.STATE_REPEAT_ALL_TRACK:
+                return "STATE_REPEAT_ALL_TRACK";
+            case PlayerSettingsValues.STATE_REPEAT_GROUP:
+                return "STATE_REPEAT_GROUP";
+            default:
+                return "STATE_DEFAULT_OFF";
+        }
+    }
+
+    private static String getShuffleModeStringValue(int shuffleMode) {
+        switch (shuffleMode) {
+            case PlayerSettingsValues.STATE_SHUFFLE_OFF:
+                return "STATE_SHUFFLE_OFF";
+            case PlayerSettingsValues.STATE_SHUFFLE_ALL_TRACK:
+                return "STATE_SHUFFLE_ALL_TRACK";
+            case PlayerSettingsValues.STATE_SHUFFLE_GROUP:
+                return "STATE_SHUFFLE_GROUP";
+            default:
+                return "STATE_DEFAULT_OFF";
+        }
+    }
 }
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java
index 332c41d..66e1fb6 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java
@@ -68,7 +68,7 @@
 
     public AvrcpCoverArtService(Context context) {
         mContext = context;
-        mNativeInterface = AvrcpNativeInterface.getInterface();
+        mNativeInterface = AvrcpNativeInterface.getInstance();
         mAcceptThread = new SocketAcceptor();
         mStorage = new AvrcpCoverArtStorage(COVER_ART_STORAGE_MAX_ITEMS);
     }
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java
index 5fb1091..3b22d7a 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java
@@ -26,6 +26,8 @@
 import com.android.bluetooth.audio_util.PlayerInfo;
 import com.android.bluetooth.audio_util.PlayerSettingsManager.PlayerSettingsValues;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.List;
 import java.util.Objects;
@@ -38,27 +40,37 @@
     private static final String TAG = "AvrcpNativeInterface";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    @GuardedBy("INSTANCE_LOCK")
     private static AvrcpNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
     private AvrcpTargetService mAvrcpService;
     private AdapterService mAdapterService;
 
-    static {
-        classInitNative();
-    }
-
     private AvrcpNativeInterface() {
         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
                 "AdapterService cannot be null when AvrcpNativeInterface init");
     }
 
-    static AvrcpNativeInterface getInterface() {
-        if (sInstance == null) {
-            sInstance = new AvrcpNativeInterface();
+    static AvrcpNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new AvrcpNativeInterface();
+            }
         }
 
         return sInstance;
     }
 
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(AvrcpNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
     void init(AvrcpTargetService service) {
         d("Init AvrcpNativeInterface");
         mAvrcpService = service;
@@ -345,8 +357,6 @@
         sendPlayerSettingsNative(settingsArray, valuesArray);
     }
 
-    private static native void classInitNative();
-
     private native void initNative();
 
     private native void registerBipServerNative(int l2capPsm);
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
index 8d1aefa..0dad9c9 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
@@ -17,7 +17,6 @@
 package com.android.bluetooth.avrcp;
 
 import android.annotation.NonNull;
-import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUtils;
@@ -45,7 +44,6 @@
 import com.android.bluetooth.audio_util.PlayStatus;
 import com.android.bluetooth.audio_util.PlayerInfo;
 import com.android.bluetooth.audio_util.PlayerSettingsManager;
-import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.MetricsLogger;
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.bluetooth.btservice.ServiceFactory;
@@ -140,25 +138,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (action.equals(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
-                if (mNativeInterface == null) return;
-
-                // Update all the playback status info for each connected device
-                mNativeInterface.sendMediaUpdate(false, true, false);
-            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
-                if (mNativeInterface == null) return;
-
-                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                if (device == null) return;
-
-                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-                if (state == BluetoothProfile.STATE_DISCONNECTED) {
-                    // If there is no connection, disconnectDevice() will do nothing
-                    if (mNativeInterface.disconnectDevice(device.getAddress())) {
-                        Log.d(TAG, "request to disconnect device " + device);
-                    }
-                }
-            } else if (action.equals(AudioManager.ACTION_VOLUME_CHANGED)) {
+            if (action.equals(AudioManager.ACTION_VOLUME_CHANGED)) {
                 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
                 if (streamType == AudioManager.STREAM_MUSIC) {
                     int volume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
@@ -224,7 +204,7 @@
 
         mPlayerSettingsManager = new PlayerSettingsManager(mMediaPlayerList, this);
 
-        mNativeInterface = AvrcpNativeInterface.getInterface();
+        mNativeInterface = AvrcpNativeInterface.getInstance();
         mNativeInterface.init(AvrcpTargetService.this);
 
         mAvrcpVersion = AvrcpVersion.getCurrentSystemPropertiesValue();
@@ -252,20 +232,11 @@
         mReceiver = new AvrcpBroadcastReceiver();
         IntentFilter filter = new IntentFilter();
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        filter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
-        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
         filter.addAction(AudioManager.ACTION_VOLUME_CHANGED);
         registerReceiver(mReceiver, filter);
 
         // Only allow the service to be used once it is initialized
         sInstance = this;
-        BluetoothDevice activeDevice = getA2dpActiveDevice();
-        String deviceAddress = activeDevice != null ?
-                activeDevice.getAddress() :
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS;
-        AdapterService.getAdapterService().notifyActivityAttributionInfo(
-                getAttributionSource(), deviceAddress);
-
         return true;
     }
 
@@ -282,12 +253,6 @@
             mAvrcpCoverArtService.stop();
         }
         mAvrcpCoverArtService = null;
-        BluetoothDevice activeDevice = getA2dpActiveDevice();
-        String deviceAddress = activeDevice != null ?
-                activeDevice.getAddress() :
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS;
-        AdapterService.getAdapterService().notifyActivityAttributionInfo(
-                getAttributionSource(), deviceAddress);
 
         sInstance = null;
         unregisterReceiver(mReceiver);
@@ -338,18 +303,6 @@
     }
 
     /**
-     * Signal to the service that the current audio out device has changed and to inform
-     * the audio service whether the new device supports absolute volume. If it does, also
-     * set the absolute volume level on the remote device.
-     */
-    public void volumeDeviceSwitched(BluetoothDevice device) {
-        if (DEBUG) {
-            Log.d(TAG, "volumeDeviceSwitched: device=" + device);
-        }
-        mVolumeManager.volumeDeviceSwitched(device);
-    }
-
-    /**
      * Remove the stored volume for a device.
      */
     public void removeStoredVolumeForDevice(BluetoothDevice device) {
@@ -368,6 +321,35 @@
         return mVolumeManager.getVolume(device, mVolumeManager.getNewDeviceVolume());
     }
 
+    /**
+     * Handle when A2DP connection state changes.
+     *
+     * <p>If the A2DP connection disconnects, we request AVRCP to disconnect device as well.
+     */
+    public void handleA2dpConnectionStateChanged(BluetoothDevice device, int newState) {
+        if (device == null || mNativeInterface == null) return;
+        if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+            // If there is no connection, disconnectDevice() will do nothing
+            if (mNativeInterface.disconnectDevice(device.getAddress())) {
+                Log.d(TAG, "request to disconnect device " + device);
+            }
+        }
+    }
+    /**
+     * Handle when Active Device changes in A2DP.
+     *
+     * <p>Signal to the service that the current audio out device has changed and to inform the
+     * audio service whether the new device supports absolute volume. If it does, also set the
+     * absolute volume level on the remote device.
+     */
+    public void handleA2dpActiveDeviceChanged(BluetoothDevice device) {
+        mVolumeManager.volumeDeviceSwitched(device);
+        if (mNativeInterface != null) {
+            // Update all the playback status info for each connected device
+            mNativeInterface.sendMediaUpdate(false, true, false);
+        }
+    }
+
     // TODO (apanicke): Add checks to rejectlist Absolute Volume devices if they behave poorly.
     void setVolume(int avrcpVolume) {
         BluetoothDevice activeDevice = getA2dpActiveDevice();
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClient.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClient.java
index c62a64f..99605c6 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClient.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClient.java
@@ -118,7 +118,9 @@
     }
 
     /**
-     * Creates a BIP image pull client and connects to a remote device's BIP image push server.
+     * Creates a BIP image pull client
+     *
+     * <p>{@link connectAsync()} must be called separately.
      */
     public AvrcpBipClient(BluetoothDevice remoteDevice, int psm, Callback callback) {
         if (remoteDevice == null) {
@@ -138,7 +140,6 @@
         Looper looper = mThread.getLooper();
 
         mHandler = new AvrcpBipClientHandler(looper, this);
-        mHandler.obtainMessage(CONNECT).sendToTarget();
     }
 
     /**
@@ -165,7 +166,7 @@
     public void shutdown() {
         debug("Shutdown client");
         try {
-            mHandler.obtainMessage(DISCONNECT).sendToTarget();
+            disconnectAsync();
         } catch (IllegalStateException e) {
             // Means we haven't been started or we're already stopped. Doing this makes this call
             // always safe no matter the state.
@@ -242,6 +243,11 @@
         }
     }
 
+    /** Connects asynchronously */
+    void connectAsync() {
+        mHandler.obtainMessage(CONNECT).sendToTarget();
+    }
+
     /**
      * Connects to the remote device's BIP Image Pull server
      */
@@ -316,10 +322,14 @@
         }
     }
 
+    /** Disconnects asynchronously */
+    void disconnectAsync() {
+        mHandler.obtainMessage(DISCONNECT).sendToTarget();
+    }
+
     /**
      * Permanently disconnects this client from the remote device's BIP server and notifies of the
      * new connection status.
-     *
      */
     private synchronized void disconnect() {
         if (mSession != null) {
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerNativeInterface.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerNativeInterface.java
new file mode 100644
index 0000000..0fb1ea7
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerNativeInterface.java
@@ -0,0 +1,529 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.avrcpcontroller;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.support.v4.media.session.PlaybackStateCompat;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+import java.util.UUID;
+
+/** Provides Bluetooth AVRCP Controller native interface for the AVRCP Controller service */
+public class AvrcpControllerNativeInterface {
+    static final String TAG = AvrcpControllerNativeInterface.class.getSimpleName();
+    static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+    static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private AvrcpControllerService mAvrcpController;
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static AvrcpControllerNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
+    static AvrcpControllerNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new AvrcpControllerNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(AvrcpControllerNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
+    void init(AvrcpControllerService controller) {
+        mAvrcpController = controller;
+        initNative();
+    }
+
+    void cleanup() {
+        cleanupNative();
+    }
+
+    boolean sendPassThroughCommand(byte[] address, int keyCode, int keyState) {
+        return sendPassThroughCommandNative(address, keyCode, keyState);
+    }
+
+    void setPlayerApplicationSettingValues(
+            byte[] address, byte numAttrib, byte[] attribIds, byte[] attribVal) {
+        setPlayerApplicationSettingValuesNative(address, numAttrib, attribIds, attribVal);
+    }
+
+    void sendAbsVolRsp(byte[] address, int absVol, int label) {
+        sendAbsVolRspNative(address, absVol, label);
+    }
+
+    void sendRegisterAbsVolRsp(byte[] address, byte rspType, int absVol, int label) {
+        sendRegisterAbsVolRspNative(address, rspType, absVol, label);
+    }
+
+    void getCurrentMetadata(byte[] address) {
+        getCurrentMetadataNative(address);
+    }
+
+    void getPlaybackState(byte[] address) {
+        getPlaybackStateNative(address);
+    }
+
+    void getNowPlayingList(byte[] address, int start, int end) {
+        getNowPlayingListNative(address, start, end);
+    }
+
+    void getFolderList(byte[] address, int start, int end) {
+        getFolderListNative(address, start, end);
+    }
+
+    void getPlayerList(byte[] address, int start, int end) {
+        getPlayerListNative(address, start, end);
+    }
+
+    void changeFolderPath(byte[] address, byte direction, long uid) {
+        changeFolderPathNative(address, direction, uid);
+    }
+
+    void playItem(byte[] address, byte scope, long uid, int uidCounter) {
+        playItemNative(address, scope, uid, uidCounter);
+    }
+
+    void setBrowsedPlayer(byte[] address, int playerId) {
+        setBrowsedPlayerNative(address, playerId);
+    }
+
+    /**********************************************************************************************/
+    /*********************************** callbacks from native ************************************/
+    /**********************************************************************************************/
+
+    // Called by JNI when a device has connected or disconnected.
+    void onConnectionStateChanged(
+            boolean remoteControlConnected, boolean browsingConnected, byte[] address) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(
+                    TAG,
+                    "onConnectionStateChanged: "
+                            + (" remoteControlConnected=" + remoteControlConnected)
+                            + (" browsingConnected=" + browsingConnected)
+                            + (" device=" + device));
+        }
+
+        mAvrcpController.onConnectionStateChanged(
+                remoteControlConnected, browsingConnected, device);
+    }
+
+    // Called by JNI to notify Avrcp of a remote device's Cover Art PSM
+    @VisibleForTesting
+    void getRcPsm(byte[] address, int psm) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "getRcPsm: device=" + device + " psm=" + psm);
+        }
+
+        mAvrcpController.getRcPsm(device, psm);
+    }
+
+    // Called by JNI to report remote Player's capabilities
+    void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp, int rspLen) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "handlePlayerAppSetting: device=" + device + " rspLen=" + rspLen);
+        }
+
+        mAvrcpController.handlePlayerAppSetting(device, playerAttribRsp, rspLen);
+    }
+
+    @VisibleForTesting
+    void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp, int rspLen) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "onPlayerAppSettingChanged: device=" + device);
+        }
+
+        mAvrcpController.onPlayerAppSettingChanged(device, playerAttribRsp, rspLen);
+    }
+
+    // Called by JNI when remote wants to set absolute volume.
+    void handleSetAbsVolume(byte[] address, byte absVol, byte label) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "handleSetAbsVolume: device=" + device);
+        }
+
+        mAvrcpController.handleSetAbsVolume(device, absVol, label);
+    }
+
+    // Called by JNI when remote wants to receive absolute volume notifications.
+    void handleRegisterNotificationAbsVol(byte[] address, byte label) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "handleRegisterNotificationAbsVol: device=" + device);
+        }
+
+        mAvrcpController.handleRegisterNotificationAbsVol(device, label);
+    }
+
+    // Called by JNI when a track changes and local AvrcpController is registered for updates.
+    void onTrackChanged(byte[] address, byte numAttributes, int[] attributes, String[] attribVals) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "onTrackChanged: device=" + device);
+        }
+
+        mAvrcpController.onTrackChanged(device, numAttributes, attributes, attribVals);
+    }
+
+    // Called by JNI periodically based upon timer to update play position
+    void onPlayPositionChanged(byte[] address, int songLen, int currSongPosition) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "onPlayPositionChanged: device=" + device + " pos=" + currSongPosition);
+        }
+
+        mAvrcpController.onPlayPositionChanged(device, songLen, currSongPosition);
+    }
+
+    // Called by JNI on changes of play status
+    void onPlayStatusChanged(byte[] address, byte playStatus) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "onPlayStatusChanged: device=" + device + " playStatus=" + playStatus);
+        }
+
+        mAvrcpController.onPlayStatusChanged(device, toPlaybackStateFromJni(playStatus));
+    }
+
+    // Browsing related JNI callbacks.
+    void handleGetFolderItemsRsp(byte[] address, int status, AvrcpItem[] items) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(
+                    TAG,
+                    "handleGetFolderItemsRsp:"
+                            + (" device=" + device)
+                            + (" status=" + status)
+                            + (" NumberOfItems=" + items.length));
+        }
+
+        mAvrcpController.handleGetFolderItemsRsp(device, status, items);
+    }
+
+    void handleGetPlayerItemsRsp(byte[] address, AvrcpPlayer[] items) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(
+                    TAG,
+                    "handleGetFolderItemsRsp:"
+                            + (" device=" + device)
+                            + (" NumberOfItems=" + items.length));
+        }
+
+        mAvrcpController.handleGetPlayerItemsRsp(device, Arrays.asList(items));
+    }
+
+    // JNI Helper functions to convert native objects to java.
+    static AvrcpItem createFromNativeMediaItem(
+            byte[] address, long uid, int type, String name, int[] attrIds, String[] attrVals) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (VDBG) {
+            Log.d(
+                    TAG,
+                    "createFromNativeMediaItem:"
+                            + (" device=" + device)
+                            + (" uid=" + uid)
+                            + (" type=" + type)
+                            + (" name=" + name)
+                            + (" attrids=" + Arrays.toString(attrIds))
+                            + (" attrVals=" + Arrays.toString(attrVals)));
+        }
+
+        return new AvrcpItem.Builder()
+                .fromAvrcpAttributeArray(attrIds, attrVals)
+                .setDevice(device)
+                .setItemType(AvrcpItem.TYPE_MEDIA)
+                .setType(type)
+                .setUid(uid)
+                .setUuid(UUID.randomUUID().toString())
+                .setPlayable(true)
+                .build();
+    }
+
+    static AvrcpItem createFromNativeFolderItem(
+            byte[] address, long uid, int type, String name, int playable) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (VDBG) {
+            Log.d(
+                    TAG,
+                    "createFromNativeFolderItem:"
+                            + (" device=" + device)
+                            + (" uid=" + uid)
+                            + (" type=" + type)
+                            + (" name=" + name)
+                            + (" playable=" + playable));
+        }
+
+        return new AvrcpItem.Builder()
+                .setDevice(device)
+                .setItemType(AvrcpItem.TYPE_FOLDER)
+                .setType(type)
+                .setUid(uid)
+                .setUuid(UUID.randomUUID().toString())
+                .setDisplayableName(name)
+                .setPlayable(playable == 0x01)
+                .setBrowsable(true)
+                .build();
+    }
+
+    static AvrcpPlayer createFromNativePlayerItem(
+            byte[] address,
+            int id,
+            String name,
+            byte[] transportFlags,
+            int playStatus,
+            int playerType) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (VDBG) {
+            Log.d(
+                    TAG,
+                    "createFromNativePlayerItem:"
+                            + (" device=" + device)
+                            + (" name=" + name)
+                            + (" transportFlags=" + Arrays.toString(transportFlags))
+                            + (" playStatus=" + playStatus)
+                            + (" playerType=" + playerType));
+        }
+
+        return new AvrcpPlayer.Builder()
+                .setDevice(device)
+                .setPlayerId(id)
+                .setPlayerType(playerType)
+                .setSupportedFeatures(transportFlags)
+                .setName(name)
+                .setPlayStatus(toPlaybackStateFromJni(playStatus))
+                .build();
+    }
+
+    void handleChangeFolderRsp(byte[] address, int count) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "handleChangeFolderRsp: device=" + device + " count=" + count);
+        }
+
+        mAvrcpController.handleChangeFolderRsp(device, count);
+    }
+
+    void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "handleSetBrowsedPlayerRsp: device=" + device + " depth=" + depth);
+        }
+
+        mAvrcpController.handleSetBrowsedPlayerRsp(device, items, depth);
+    }
+
+    void handleSetAddressedPlayerRsp(byte[] address, int status) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "handleSetAddressedPlayerRsp device=" + device + " status=" + status);
+        }
+
+        mAvrcpController.handleSetAddressedPlayerRsp(device, status);
+    }
+
+    void handleAddressedPlayerChanged(byte[] address, int id) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "handleAddressedPlayerChanged: device=" + device + " id=" + id);
+        }
+
+        mAvrcpController.handleAddressedPlayerChanged(device, id);
+    }
+
+    void handleNowPlayingContentChanged(byte[] address) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "handleNowPlayingContentChanged: device=" + device);
+        }
+        mAvrcpController.handleNowPlayingContentChanged(device);
+    }
+
+    void onAvailablePlayerChanged(byte[] address) {
+        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+        if (DBG) {
+            Log.d(TAG, "onAvailablePlayerChanged: device=" + device);
+        }
+        mAvrcpController.onAvailablePlayerChanged(device);
+    }
+
+    /*
+     *  Play State Values from JNI
+     */
+    private static final byte JNI_PLAY_STATUS_STOPPED = 0x00;
+    private static final byte JNI_PLAY_STATUS_PLAYING = 0x01;
+    private static final byte JNI_PLAY_STATUS_PAUSED = 0x02;
+    private static final byte JNI_PLAY_STATUS_FWD_SEEK = 0x03;
+    private static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04;
+
+    private static int toPlaybackStateFromJni(int fromJni) {
+        switch (fromJni) {
+            case JNI_PLAY_STATUS_STOPPED:
+                return PlaybackStateCompat.STATE_STOPPED;
+            case JNI_PLAY_STATUS_PLAYING:
+                return PlaybackStateCompat.STATE_PLAYING;
+            case JNI_PLAY_STATUS_PAUSED:
+                return PlaybackStateCompat.STATE_PAUSED;
+            case JNI_PLAY_STATUS_FWD_SEEK:
+                return PlaybackStateCompat.STATE_FAST_FORWARDING;
+            case JNI_PLAY_STATUS_REV_SEEK:
+                return PlaybackStateCompat.STATE_REWINDING;
+            default:
+                return PlaybackStateCompat.STATE_NONE;
+        }
+    }
+
+    /**********************************************************************************************/
+    /******************************************* native *******************************************/
+    /**********************************************************************************************/
+
+    private native void initNative();
+
+    private native void cleanupNative();
+
+    /**
+     * Send button press commands to addressed device
+     *
+     * @param keyCode key code as defined in AVRCP specification
+     * @param keyState 0 = key pressed, 1 = key released
+     * @return command was sent
+     */
+    private native boolean sendPassThroughCommandNative(byte[] address, int keyCode, int keyState);
+
+    /**
+     * TODO DELETE: This method is not used Send group navigation commands
+     *
+     * @param keyCode next/previous
+     * @param keyState state
+     * @return command was sent
+     */
+    private native boolean sendGroupNavigationCommandNative(
+            byte[] address, int keyCode, int keyState);
+
+    /**
+     * Change player specific settings such as shuffle
+     *
+     * @param numAttrib number of settings being sent
+     * @param attribIds list of settings to be changed
+     * @param attribVal list of settings values
+     */
+    private native void setPlayerApplicationSettingValuesNative(
+            byte[] address, byte numAttrib, byte[] attribIds, byte[] attribVal);
+
+    /**
+     * Send response to set absolute volume
+     *
+     * @param absVol new volume
+     * @param label label
+     */
+    private native void sendAbsVolRspNative(byte[] address, int absVol, int label);
+
+    /**
+     * Register for any volume level changes
+     *
+     * @param rspType type of response
+     * @param absVol current volume
+     * @param label label
+     */
+    private native void sendRegisterAbsVolRspNative(
+            byte[] address, byte rspType, int absVol, int label);
+
+    /**
+     * Fetch the current track's metadata
+     *
+     * <p>This method is specifically meant to allow us to fetch image handles that may not have
+     * been sent to us yet, prior to having a BIP client connection. See the AVRCP 1.6+
+     * specification, section 4.1.7, for more details.
+     */
+    private native void getCurrentMetadataNative(byte[] address);
+
+    /** Fetch the playback state */
+    private native void getPlaybackStateNative(byte[] address);
+
+    /**
+     * Fetch the current now playing list
+     *
+     * @param start first index to retrieve
+     * @param end last index to retrieve
+     */
+    private native void getNowPlayingListNative(byte[] address, int start, int end);
+
+    /**
+     * Fetch the current folder's listing
+     *
+     * @param start first index to retrieve
+     * @param end last index to retrieve
+     */
+    private native void getFolderListNative(byte[] address, int start, int end);
+
+    /**
+     * Fetch the listing of players
+     *
+     * @param start first index to retrieve
+     * @param end last index to retrieve
+     */
+    private native void getPlayerListNative(byte[] address, int start, int end);
+
+    /**
+     * Change the current browsed folder
+     *
+     * @param direction up/down
+     * @param uid folder unique id
+     */
+    private native void changeFolderPathNative(byte[] address, byte direction, long uid);
+
+    /**
+     * Play item with provided uid
+     *
+     * @param scope scope of item to played
+     * @param uid song unique id
+     * @param uidCounter counter
+     */
+    private native void playItemNative(byte[] address, byte scope, long uid, int uidCounter);
+
+    /**
+     * Set a specific player for browsing
+     *
+     * @param playerId player number
+     */
+    private native void setBrowsedPlayerNative(byte[] address, int playerId);
+
+    /**
+     * TODO DELETE: This method is not used Set a specific player for handling playback commands
+     *
+     * @param playerId player number
+     */
+    private native void setAddressedPlayerNative(byte[] address, int playerId);
+}
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index a43594d..08d84ee 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -16,6 +16,8 @@
 
 package com.android.bluetooth.avrcpcontroller;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.RequiresPermission;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothAvrcpPlayerSettings;
@@ -23,10 +25,10 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.IBluetoothAvrcpController;
 import android.content.AttributionSource;
+import android.content.Context;
 import android.content.Intent;
 import android.media.AudioManager;
 import android.support.v4.media.MediaBrowserCompat.MediaItem;
-import android.support.v4.media.session.PlaybackStateCompat;
 import android.sysprop.BluetoothProperties;
 import android.util.Log;
 
@@ -51,11 +53,12 @@
  * Provides Bluetooth AVRCP Controller profile, as a service in the Bluetooth application.
  */
 public class AvrcpControllerService extends ProfileService {
-    static final String TAG = "AvrcpControllerService";
-    static final int MAXIMUM_CONNECTED_DEVICES = 5;
+    static final String TAG = AvrcpControllerService.class.getSimpleName();
     static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
     static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
 
+    static final int MAXIMUM_CONNECTED_DEVICES = 5;
+
     /**
      * Owned Components
      */
@@ -63,17 +66,6 @@
             BluetoothPrefs.class.getCanonicalName();
     private static final String COVER_ART_PROVIDER = AvrcpCoverArtProvider.class.getCanonicalName();
 
-    /*
-     *  Play State Values from JNI
-     */
-    private static final byte JNI_PLAY_STATUS_STOPPED = 0x00;
-    private static final byte JNI_PLAY_STATUS_PLAYING = 0x01;
-    private static final byte JNI_PLAY_STATUS_PAUSED = 0x02;
-    private static final byte JNI_PLAY_STATUS_FWD_SEEK = 0x03;
-    @VisibleForTesting
-    static final byte JNI_PLAY_STATUS_REV_SEEK = 0x04;
-    private static final byte JNI_PLAY_STATUS_ERROR = -1;
-
     /* Folder/Media Item scopes.
      * Keep in sync with AVRCP 1.6 sec. 6.10.1
      */
@@ -113,6 +105,7 @@
     private static AvrcpControllerService sService;
 
     private AdapterService mAdapterService;
+    private final AvrcpControllerNativeInterface mNativeInterface;
 
     protected Map<BluetoothDevice, AvrcpControllerStateMachine> mDeviceStateMap =
             new ConcurrentHashMap<>(1);
@@ -141,8 +134,15 @@
         }
     }
 
-    static {
-        classInitNative();
+    AvrcpControllerService() {
+        mNativeInterface = AvrcpControllerNativeInterface.getInstance();
+    }
+
+    @VisibleForTesting
+    AvrcpControllerService(Context ctx, AvrcpControllerNativeInterface nativeInterface) {
+        attachBaseContext(ctx);
+        mNativeInterface = requireNonNull(nativeInterface);
+        onCreate();
     }
 
     public static boolean isEnabled() {
@@ -151,7 +151,7 @@
 
     @Override
     protected synchronized boolean start() {
-        initNative();
+        mNativeInterface.init(this);
         setComponentAvailable(ON_ERROR_SETTINGS_ACTIVITY, true);
         mAdapterService = AdapterService.getAdapterService();
         mCoverArtEnabled = getResources().getBoolean(R.bool.avrcp_controller_enable_cover_art);
@@ -187,6 +187,7 @@
             setComponentAvailable(COVER_ART_PROVIDER, false);
         }
         setComponentAvailable(ON_ERROR_SETTINGS_ACTIVITY, false);
+        mNativeInterface.cleanup();
         return true;
     }
 
@@ -251,33 +252,6 @@
         return false;
     }
 
-    private int toPlaybackStateFromJni(int fromJni) {
-        int playbackState = PlaybackStateCompat.STATE_NONE;
-        switch (fromJni) {
-            case JNI_PLAY_STATUS_STOPPED:
-                playbackState = PlaybackStateCompat.STATE_STOPPED;
-                break;
-            case JNI_PLAY_STATUS_PLAYING:
-                playbackState = PlaybackStateCompat.STATE_PLAYING;
-                break;
-            case JNI_PLAY_STATUS_PAUSED:
-                playbackState = PlaybackStateCompat.STATE_PAUSED;
-                break;
-            case JNI_PLAY_STATUS_FWD_SEEK:
-                playbackState = PlaybackStateCompat.STATE_FAST_FORWARDING;
-                break;
-            case JNI_PLAY_STATUS_REV_SEEK:
-                playbackState = PlaybackStateCompat.STATE_REWINDING;
-                break;
-            default:
-                playbackState = PlaybackStateCompat.STATE_NONE;
-        }
-        return playbackState;
-    }
-
-    protected AvrcpControllerStateMachine newStateMachine(BluetoothDevice device) {
-        return new AvrcpControllerStateMachine(device, this);
-    }
 
     protected void getCurrentMetadataIfNoCoverArt(BluetoothDevice device) {
         if (device == null) return;
@@ -285,7 +259,7 @@
         if (stateMachine == null) return;
         AvrcpItem track = stateMachine.getCurrentTrack();
         if (track != null && track.getCoverArtLocation() == null) {
-            getCurrentMetadataNative(Utils.getByteAddress(device));
+            mNativeInterface.getCurrentMetadata(Utils.getByteAddress(device));
         }
     }
 
@@ -499,35 +473,10 @@
         }
     }
 
-
-    /* JNI API*/
-    // Called by JNI when a passthrough key was received.
-    @VisibleForTesting
-    void handlePassthroughRsp(int id, int keyState, byte[] address) {
-        if (DBG) {
-            Log.d(TAG, "passthrough response received as: key: " + id
-                    + " state: " + keyState + "address:" + Arrays.toString(address));
-        }
-    }
-
-    @VisibleForTesting
-    void handleGroupNavigationRsp(int id, int keyState) {
-        if (DBG) {
-            Log.d(TAG, "group navigation response received as: key: " + id + " state: "
-                    + keyState);
-        }
-    }
-
     // Called by JNI when a device has connected or disconnected.
     @VisibleForTesting
-    synchronized void onConnectionStateChanged(boolean remoteControlConnected,
-            boolean browsingConnected, byte[] address) {
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
-        if (DBG) {
-            Log.d(TAG, "onConnectionStateChanged " + remoteControlConnected + " "
-                    + browsingConnected + device);
-        }
-
+    synchronized void onConnectionStateChanged(
+            boolean remoteControlConnected, boolean browsingConnected, BluetoothDevice device) {
         StackEvent event =
                 StackEvent.connectionStateChanged(remoteControlConnected, browsingConnected);
         AvrcpControllerStateMachine stateMachine = getOrCreateStateMachine(device);
@@ -545,37 +494,17 @@
         }
     }
 
-    // Called by JNI to notify Avrcp of features supported by the Remote device.
-    @VisibleForTesting
-    void getRcFeatures(byte[] address, int features) {
-        /* Do Nothing. */
-    }
-
     // Called by JNI to notify Avrcp of a remote device's Cover Art PSM
     @VisibleForTesting
-    void getRcPsm(byte[] address, int psm) {
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
-        if (DBG) Log.d(TAG, "getRcPsm(device=" + device + ", psm=" + psm + ")");
+    void getRcPsm(BluetoothDevice device, int psm) {
         AvrcpControllerStateMachine stateMachine = getOrCreateStateMachine(device);
-        if (stateMachine != null) {
-            stateMachine.sendMessage(
-                    AvrcpControllerStateMachine.MESSAGE_PROCESS_RECEIVED_COVER_ART_PSM, psm);
-        }
-    }
-
-    // Called by JNI
-    @VisibleForTesting
-    void setPlayerAppSettingRsp(byte[] address, byte accepted) {
-        /* Do Nothing. */
+        stateMachine.sendMessage(
+                AvrcpControllerStateMachine.MESSAGE_PROCESS_RECEIVED_COVER_ART_PSM, psm);
     }
 
     // Called by JNI when remote wants to receive absolute volume notifications.
     @VisibleForTesting
-    synchronized void handleRegisterNotificationAbsVol(byte[] address, byte label) {
-        if (DBG) {
-            Log.d(TAG, "handleRegisterNotificationAbsVol");
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+    synchronized void handleRegisterNotificationAbsVol(BluetoothDevice device, byte label) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(
@@ -585,11 +514,7 @@
 
     // Called by JNI when remote wants to set absolute volume.
     @VisibleForTesting
-    synchronized void handleSetAbsVolume(byte[] address, byte absVol, byte label) {
-        if (DBG) {
-            Log.d(TAG, "handleSetAbsVolume ");
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+    synchronized void handleSetAbsVolume(BluetoothDevice device, byte absVol, byte label) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD,
@@ -634,13 +559,8 @@
 
     // Called by JNI when a track changes and local AvrcpController is registered for updates.
     @VisibleForTesting
-    synchronized void onTrackChanged(byte[] address, byte numAttributes, int[] attributes,
-            String[] attribVals) {
-        if (DBG) {
-            Log.d(TAG, "onTrackChanged");
-        }
-
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+    synchronized void onTrackChanged(
+            BluetoothDevice device, byte numAttributes, int[] attributes, String[] attribVals) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             AvrcpItem.Builder aib = new AvrcpItem.Builder();
@@ -662,12 +582,8 @@
 
     // Called by JNI periodically based upon timer to update play position
     @VisibleForTesting
-    synchronized void onPlayPositionChanged(byte[] address, int songLen,
-            int currSongPosition) {
-        if (DBG) {
-            Log.d(TAG, "onPlayPositionChanged pos " + currSongPosition);
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+    synchronized void onPlayPositionChanged(
+            BluetoothDevice device, int songLen, int currSongPosition) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(
@@ -678,27 +594,18 @@
 
     // Called by JNI on changes of play status
     @VisibleForTesting
-    synchronized void onPlayStatusChanged(byte[] address, byte playStatus) {
-        if (DBG) {
-            Log.d(TAG, "onPlayStatusChanged " + playStatus);
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+    synchronized void onPlayStatusChanged(BluetoothDevice device, int playbackState) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(
-                    AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
-                    toPlaybackStateFromJni(playStatus));
+                    AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, playbackState);
         }
     }
 
     // Called by JNI to report remote Player's capabilities
     @VisibleForTesting
-    synchronized void handlePlayerAppSetting(byte[] address, byte[] playerAttribRsp,
-            int rspLen) {
-        if (DBG) {
-            Log.d(TAG, "handlePlayerAppSetting rspLen = " + rspLen);
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+    synchronized void handlePlayerAppSetting(
+            BluetoothDevice device, byte[] playerAttribRsp, int rspLen) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             PlayerApplicationSettings supportedSettings =
@@ -710,12 +617,8 @@
     }
 
     @VisibleForTesting
-    synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp,
-            int rspLen) {
-        if (DBG) {
-            Log.d(TAG, "onPlayerAppSettingChanged ");
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+    synchronized void onPlayerAppSettingChanged(
+            BluetoothDevice device, byte[] playerAttribRsp, int rspLen) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
 
@@ -728,12 +631,7 @@
     }
 
     @VisibleForTesting
-    void onAvailablePlayerChanged(byte[] address) {
-        if (DBG) {
-            Log.d(TAG," onAvailablePlayerChanged");
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
-
+    void onAvailablePlayerChanged(BluetoothDevice device) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);
@@ -741,13 +639,7 @@
     }
 
     // Browsing related JNI callbacks.
-    void handleGetFolderItemsRsp(byte[] address, int status, AvrcpItem[] items) {
-        if (DBG) {
-            Log.d(TAG, "handleGetFolderItemsRsp called with status " + status + " items "
-                    + items.length + " items.");
-        }
-
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+    void handleGetFolderItemsRsp(BluetoothDevice device, int status, AvrcpItem[] items) {
         List<AvrcpItem> itemsList = new ArrayList<>();
         for (AvrcpItem item : items) {
             if (VDBG) Log.d(TAG, item.toString());
@@ -767,18 +659,7 @@
         }
     }
 
-    void handleGetPlayerItemsRsp(byte[] address, AvrcpPlayer[] items) {
-        if (DBG) {
-            Log.d(TAG, "handleGetFolderItemsRsp called with " + items.length + " items.");
-        }
-
-        List<AvrcpPlayer> itemsList = new ArrayList<>();
-        for (AvrcpPlayer item : items) {
-            if (VDBG) Log.d(TAG, "bt player item: " + item);
-            itemsList.add(item);
-        }
-
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+    void handleGetPlayerItemsRsp(BluetoothDevice device, List<AvrcpPlayer> itemsList) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS,
@@ -786,72 +667,8 @@
         }
     }
 
-    // JNI Helper functions to convert native objects to java.
-    AvrcpItem createFromNativeMediaItem(byte[] address, long uid, int type, String name,
-            int[] attrIds, String[] attrVals) {
-        if (VDBG) {
-            Log.d(TAG, "createFromNativeMediaItem uid: " + uid + " type: " + type + " name: " + name
-                    + " attrids: " + Arrays.toString(attrIds)
-                    + " attrVals: " + Arrays.toString(attrVals));
-        }
-
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
-        AvrcpItem.Builder aib = new AvrcpItem.Builder().fromAvrcpAttributeArray(attrIds, attrVals);
-        aib.setDevice(device);
-        aib.setItemType(AvrcpItem.TYPE_MEDIA);
-        aib.setType(type);
-        aib.setUid(uid);
-        aib.setUuid(UUID.randomUUID().toString());
-        aib.setPlayable(true);
-        AvrcpItem item = aib.build();
-        return item;
-    }
-
-    AvrcpItem createFromNativeFolderItem(byte[] address, long uid, int type, String name,
-            int playable) {
-        if (VDBG) {
-            Log.d(TAG, "createFromNativeFolderItem uid: " + uid + " type " + type + " name "
-                    + name + " playable " + playable);
-        }
-
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
-        AvrcpItem.Builder aib = new AvrcpItem.Builder();
-        aib.setDevice(device);
-        aib.setItemType(AvrcpItem.TYPE_FOLDER);
-        aib.setType(type);
-        aib.setUid(uid);
-        aib.setUuid(UUID.randomUUID().toString());
-        aib.setDisplayableName(name);
-        aib.setPlayable(playable == 0x01);
-        aib.setBrowsable(true);
-        return aib.build();
-    }
-
-    AvrcpPlayer createFromNativePlayerItem(byte[] address, int id, String name,
-            byte[] transportFlags, int playStatus, int playerType) {
-        if (VDBG) {
-            Log.d(TAG, "createFromNativePlayerItem name: " + name
-                    + " transportFlags " + Arrays.toString(transportFlags)
-                    + " play status " + playStatus
-                    + " player type " + playerType);
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
-        AvrcpPlayer.Builder apb = new AvrcpPlayer.Builder();
-        apb.setDevice(device);
-        apb.setPlayerId(id);
-        apb.setPlayerType(playerType);
-        apb.setSupportedFeatures(transportFlags);
-        apb.setName(name);
-        apb.setPlayStatus(toPlaybackStateFromJni(playStatus));
-        return apb.build();
-    }
-
     @VisibleForTesting
-    void handleChangeFolderRsp(byte[] address, int count) {
-        if (DBG) {
-            Log.d(TAG, "handleChangeFolderRsp count: " + count);
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
+    void handleChangeFolderRsp(BluetoothDevice device, int count) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH,
@@ -860,12 +677,7 @@
     }
 
     @VisibleForTesting
-    void handleSetBrowsedPlayerRsp(byte[] address, int items, int depth) {
-        if (DBG) {
-            Log.d(TAG, "handleSetBrowsedPlayerRsp depth: " + depth);
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
-
+    void handleSetBrowsedPlayerRsp(BluetoothDevice device, int items, int depth) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_BROWSED_PLAYER,
@@ -874,12 +686,7 @@
     }
 
     @VisibleForTesting
-    void handleSetAddressedPlayerRsp(byte[] address, int status) {
-        if (DBG) {
-            Log.d(TAG, "handleSetAddressedPlayerRsp status: " + status);
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
-
+    void handleSetAddressedPlayerRsp(BluetoothDevice device, int status) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(
@@ -888,12 +695,7 @@
     }
 
     @VisibleForTesting
-    void handleAddressedPlayerChanged(byte[] address, int id) {
-        if (DBG) {
-            Log.d(TAG, "handleAddressedPlayerChanged id: " + id);
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
-
+    void handleAddressedPlayerChanged(BluetoothDevice device, int id) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.sendMessage(
@@ -902,12 +704,7 @@
     }
 
     @VisibleForTesting
-    void handleNowPlayingContentChanged(byte[] address) {
-        if (DBG) {
-            Log.d(TAG, "handleNowPlayingContentChanged");
-        }
-        BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
-
+    void handleNowPlayingContentChanged(BluetoothDevice device) {
         AvrcpControllerStateMachine stateMachine = getStateMachine(device);
         if (stateMachine != null) {
             stateMachine.nowPlayingContentChanged();
@@ -943,11 +740,15 @@
      * Remove state machine from device map once it is no longer needed.
      */
     public void removeStateMachine(AvrcpControllerStateMachine stateMachine) {
+        if (stateMachine == null) {
+            return;
+        }
         BluetoothDevice device = stateMachine.getDevice();
         if (device.equals(getActiveDevice())) {
             setActiveDevice(null);
         }
         mDeviceStateMap.remove(stateMachine.getDevice());
+        stateMachine.quitNow();
     }
 
     public List<BluetoothDevice> getConnectedDevices() {
@@ -962,13 +763,23 @@
     }
 
     protected AvrcpControllerStateMachine getOrCreateStateMachine(BluetoothDevice device) {
-        AvrcpControllerStateMachine stateMachine = mDeviceStateMap.get(device);
-        if (stateMachine == null) {
-            stateMachine = newStateMachine(device);
-            mDeviceStateMap.put(device, stateMachine);
-            stateMachine.start();
+        AvrcpControllerStateMachine newStateMachine =
+                new AvrcpControllerStateMachine(device, this, mNativeInterface);
+        AvrcpControllerStateMachine existingStateMachine =
+                mDeviceStateMap.putIfAbsent(device, newStateMachine);
+        // Given null is not a valid value in our map, ConcurrentHashMap will return null if the
+        // key was absent and our new value was added. We should then start and return it. Else
+        // we quit the new one so we don't leak a thread
+        if (existingStateMachine == null) {
+            newStateMachine.start();
+            return newStateMachine;
+        } else {
+            // If you try to quit a StateMachine that hasn't been constructed yet, the StateMachine
+            // spits out an NPE trying to read a state stack array that only gets made on start().
+            // We can just quit the thread made explicitly
+            newStateMachine.getHandler().getLooper().quit();
         }
-        return stateMachine;
+        return existingStateMachine;
     }
 
     protected AvrcpCoverArtManager getCoverArtManager() {
@@ -1021,127 +832,4 @@
 
         sb.append("\n  " + BluetoothMediaBrowserService.dump() + "\n");
     }
-
-    /*JNI*/
-    private static native void classInitNative();
-
-    private native void initNative();
-
-    private native void cleanupNative();
-
-    /**
-     * Send button press commands to addressed device
-     *
-     * @param keyCode  key code as defined in AVRCP specification
-     * @param keyState 0 = key pressed, 1 = key released
-     * @return command was sent
-     */
-    public native boolean sendPassThroughCommandNative(byte[] address, int keyCode, int keyState);
-
-    /**
-     * Send group navigation commands
-     *
-     * @param keyCode  next/previous
-     * @param keyState state
-     * @return command was sent
-     */
-    public native boolean sendGroupNavigationCommandNative(byte[] address, int keyCode,
-            int keyState);
-
-    /**
-     * Change player specific settings such as shuffle
-     *
-     * @param numAttrib number of settings being sent
-     * @param attribIds list of settings to be changed
-     * @param attribVal list of settings values
-     */
-    public native void setPlayerApplicationSettingValuesNative(byte[] address, byte numAttrib,
-            byte[] attribIds, byte[] attribVal);
-
-    /**
-     * Send response to set absolute volume
-     *
-     * @param absVol new volume
-     * @param label  label
-     */
-    public native void sendAbsVolRspNative(byte[] address, int absVol, int label);
-
-    /**
-     * Register for any volume level changes
-     *
-     * @param rspType type of response
-     * @param absVol  current volume
-     * @param label   label
-     */
-    public native void sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol,
-            int label);
-
-    /**
-     * Fetch the current track's metadata
-     *
-     * This method is specifically meant to allow us to fetch image handles that may not have been
-     * sent to us yet, prior to having a BIP client connection. See the AVRCP 1.6+ specification,
-     * section 4.1.7, for more details.
-     */
-    public native void getCurrentMetadataNative(byte[] address);
-
-    /**
-     * Fetch the playback state
-     */
-    public native void getPlaybackStateNative(byte[] address);
-
-    /**
-     * Fetch the current now playing list
-     *
-     * @param start first index to retrieve
-     * @param end   last index to retrieve
-     */
-    public native void getNowPlayingListNative(byte[] address, int start, int end);
-
-    /**
-     * Fetch the current folder's listing
-     *
-     * @param start first index to retrieve
-     * @param end   last index to retrieve
-     */
-    public native void getFolderListNative(byte[] address, int start, int end);
-
-    /**
-     * Fetch the listing of players
-     *
-     * @param start first index to retrieve
-     * @param end   last index to retrieve
-     */
-    public native void getPlayerListNative(byte[] address, int start, int end);
-
-    /**
-     * Change the current browsed folder
-     *
-     * @param direction up/down
-     * @param uid       folder unique id
-     */
-    public native void changeFolderPathNative(byte[] address, byte direction, long uid);
-
-    /**
-     * Play item with provided uid
-     *
-     * @param scope      scope of item to played
-     * @param uid        song unique id
-     * @param uidCounter counter
-     */
-    public native void playItemNative(byte[] address, byte scope, long uid, int uidCounter);
-
-    /**
-     * Set a specific player for browsing
-     *
-     * @param playerId player number
-     */
-    public native void setBrowsedPlayerNative(byte[] address, int playerId);
-
-    /**
-     * Set a specific player for handling playback commands
-     *
-     * @param playerId player number
-     */
-    public native void setAddressedPlayerNative(byte[] address, int playerId);
 }
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index 9f43d81..a6140ae 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -18,6 +18,8 @@
 
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
 
+import static java.util.Objects.requireNonNull;
+
 import android.bluetooth.BluetoothAvrcpController;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
@@ -117,6 +119,7 @@
     protected final BluetoothDevice mDevice;
     protected final byte[] mDeviceAddress;
     protected final AvrcpControllerService mService;
+    protected final AvrcpControllerNativeInterface mNativeInterface;
     protected int mCoverArtPsm;
     protected final AvrcpCoverArtManager mCoverArtManager;
     protected final Disconnected mDisconnected;
@@ -144,11 +147,15 @@
     static final int CMD_TIMEOUT_MILLIS = 10000;
     static final int ABS_VOL_TIMEOUT_MILLIS = 1000; //1s
 
-    AvrcpControllerStateMachine(BluetoothDevice device, AvrcpControllerService service) {
+    AvrcpControllerStateMachine(
+            BluetoothDevice device,
+            AvrcpControllerService service,
+            AvrcpControllerNativeInterface nativeInterface) {
         super(TAG);
         mDevice = device;
         mDeviceAddress = Utils.getByteAddress(mDevice);
         mService = service;
+        mNativeInterface = requireNonNull(nativeInterface);
         mCoverArtPsm = 0;
         mCoverArtManager = service.getCoverArtManager();
         logD(device.toString());
@@ -548,9 +555,11 @@
 
                 case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION:
                     mVolumeNotificationLabel = msg.arg1;
-                    mService.sendRegisterAbsVolRspNative(mDeviceAddress,
+                    mNativeInterface.sendRegisterAbsVolRsp(
+                            mDeviceAddress,
                             NOTIFICATION_RSP_TYPE_INTERIM,
-                            getAbsVolume(), mVolumeNotificationLabel);
+                            getAbsVolume(),
+                            mVolumeNotificationLabel);
                     return true;
 
                 case MESSAGE_GET_FOLDER_ITEMS:
@@ -669,8 +678,8 @@
                     // playing list. However, now playing is mandatory if browsing is supported,
                     // even if the player doesn't support it. A list of one item can be returned
                     // instead.
-                    mService.getCurrentMetadataNative(Utils.getByteAddress(mDevice));
-                    mService.getPlaybackStateNative(Utils.getByteAddress(mDevice));
+                    mNativeInterface.getCurrentMetadata(mDeviceAddress);
+                    mNativeInterface.getPlaybackState(mDeviceAddress);
                     requestContents(mBrowseTree.mNowPlayingNode);
                     logD("AddressedPlayer = " + mAddressedPlayer);
                     return true;
@@ -739,19 +748,18 @@
         private void processPlayItem(BrowseTree.BrowseNode node) {
             if (node == null) {
                 Log.w(TAG, "Invalid item to play");
-            } else {
-                mService.playItemNative(
-                        mDeviceAddress, node.getScope(),
-                        node.getBluetoothID(), 0);
+                return;
             }
+            mNativeInterface.playItem(mDeviceAddress, node.getScope(), node.getBluetoothID(), 0);
         }
 
         private synchronized void passThru(int cmd) {
             logD("msgPassThru " + cmd);
             // Some keys should be held until the next event.
             if (mCurrentlyHeldKey != 0) {
-                mService.sendPassThroughCommandNative(
-                        mDeviceAddress, mCurrentlyHeldKey,
+                mNativeInterface.sendPassThroughCommand(
+                        mDeviceAddress,
+                        mCurrentlyHeldKey,
                         AvrcpControllerService.KEY_STATE_RELEASED);
 
                 if (mCurrentlyHeldKey == cmd) {
@@ -767,15 +775,15 @@
             }
 
             // Send the pass through.
-            mService.sendPassThroughCommandNative(mDeviceAddress, cmd,
-                    AvrcpControllerService.KEY_STATE_PRESSED);
+            mNativeInterface.sendPassThroughCommand(
+                    mDeviceAddress, cmd, AvrcpControllerService.KEY_STATE_PRESSED);
 
             if (isHoldableKey(cmd)) {
                 // Release cmd next time a command is sent.
                 mCurrentlyHeldKey = cmd;
             } else {
-                mService.sendPassThroughCommandNative(mDeviceAddress,
-                        cmd, AvrcpControllerService.KEY_STATE_RELEASED);
+                mNativeInterface.sendPassThroughCommand(
+                        mDeviceAddress, cmd, AvrcpControllerService.KEY_STATE_RELEASED);
             }
         }
 
@@ -785,17 +793,25 @@
         }
 
         private void setRepeat(int repeatMode) {
-            mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1,
-                    new byte[]{PlayerApplicationSettings.REPEAT_STATUS}, new byte[]{
-                            PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal(
-                                    PlayerApplicationSettings.REPEAT_STATUS, repeatMode)});
+            mNativeInterface.setPlayerApplicationSettingValues(
+                    mDeviceAddress,
+                    (byte) 1,
+                    new byte[] {PlayerApplicationSettings.REPEAT_STATUS},
+                    new byte[] {
+                        PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal(
+                                PlayerApplicationSettings.REPEAT_STATUS, repeatMode)
+                    });
         }
 
         private void setShuffle(int shuffleMode) {
-            mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1,
-                    new byte[]{PlayerApplicationSettings.SHUFFLE_STATUS}, new byte[]{
-                            PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal(
-                                    PlayerApplicationSettings.SHUFFLE_STATUS, shuffleMode)});
+            mNativeInterface.setPlayerApplicationSettingValues(
+                    mDeviceAddress,
+                    (byte) 1,
+                    new byte[] {PlayerApplicationSettings.SHUFFLE_STATUS},
+                    new byte[] {
+                        PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal(
+                                PlayerApplicationSettings.SHUFFLE_STATUS, shuffleMode)
+                    });
         }
 
         private void processAvailablePlayerChanged() {
@@ -949,8 +965,8 @@
                         } else {
                             logD("Update addressed player with new available player metadata");
                             mAddressedPlayer = mAvailablePlayerList.get(mAddressedPlayerId);
-                            mService.getCurrentMetadataNative(Utils.getByteAddress(mDevice));
-                            mService.getPlaybackStateNative(Utils.getByteAddress(mDevice));
+                            mNativeInterface.getCurrentMetadata(mDeviceAddress);
+                            mNativeInterface.getPlaybackState(mDeviceAddress);
                             requestContents(mBrowseTree.mNowPlayingNode);
                         }
                         logD("AddressedPlayer = " + mAddressedPlayer);
@@ -1033,16 +1049,13 @@
                     + target.getExpectedChildren() + ")");
             switch (target.getScope()) {
                 case AvrcpControllerService.BROWSE_SCOPE_PLAYER_LIST:
-                    mService.getPlayerListNative(mDeviceAddress,
-                            start, end);
+                    mNativeInterface.getPlayerList(mDeviceAddress, start, end);
                     break;
                 case AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING:
-                    mService.getNowPlayingListNative(
-                            mDeviceAddress, start, end);
+                    mNativeInterface.getNowPlayingList(mDeviceAddress, start, end);
                     break;
                 case AvrcpControllerService.BROWSE_SCOPE_VFS:
-                    mService.getFolderListNative(mDeviceAddress,
-                            start, end);
+                    mNativeInterface.getFolderList(mDeviceAddress, start, end);
                     break;
                 default:
                     Log.e(TAG, STATE_TAG + " Scope " + target.getScope()
@@ -1075,7 +1088,7 @@
             } else if (mNextStep.isPlayer()) {
                 logD("NAVIGATING Player " + mNextStep.toString());
                 if (mNextStep.isBrowsable()) {
-                    mService.setBrowsedPlayerNative(
+                    mNativeInterface.setBrowsedPlayer(
                             mDeviceAddress, (int) mNextStep.getBluetoothID());
                 } else {
                     logD("Player doesn't support browsing");
@@ -1087,14 +1100,12 @@
                 mNextStep = mBrowseTree.getCurrentBrowsedFolder().getParent();
                 mBrowseTree.getCurrentBrowsedFolder().setCached(false);
                 removeUnusedArtworkFromBrowseTree();
-                mService.changeFolderPathNative(
-                        mDeviceAddress,
-                        AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_UP,
-                        0);
+                mNativeInterface.changeFolderPath(
+                        mDeviceAddress, AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_UP, 0);
 
             } else {
                 logD("NAVIGATING DOWN " + mNextStep.toString());
-                mService.changeFolderPathNative(
+                mNativeInterface.changeFolderPath(
                         mDeviceAddress,
                         AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_DOWN,
                         mNextStep.getBluetoothID());
@@ -1151,7 +1162,7 @@
                     ABS_VOL_TIMEOUT_MILLIS);
             setAbsVolume(absVol);
         }
-        mService.sendAbsVolRspNative(mDeviceAddress, absVol, label);
+        mNativeInterface.sendAbsVolRsp(mDeviceAddress, absVol, label);
     }
 
     /**
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtManager.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtManager.java
index 2351a5b..5e5eaef 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtManager.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtManager.java
@@ -159,6 +159,7 @@
         debug("Connect " + device + ", psm: " + psm);
         if (mClients.containsKey(device)) return false;
         AvrcpBipClient client = new AvrcpBipClient(device, psm, new BipClientCallback(device));
+        client.connectAsync();
         mClients.put(device, client);
         mBipSessions.put(device, new AvrcpBipSession(device));
         return true;
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 38cb01c..4b6a4e5 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -22,7 +22,6 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothLeBroadcastAssistant;
 import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothLeBroadcastReceiveState;
 import android.bluetooth.BluetoothProfile;
@@ -64,6 +63,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -77,6 +77,7 @@
     private static final boolean DBG = true;
     private static final String TAG = BassClientService.class.getSimpleName();
     private static final int MAX_BASS_CLIENT_STATE_MACHINES = 10;
+    private static final int MAX_ACTIVE_SYNCED_SOURCES_NUM = 4;
 
     private static BassClientService sService;
 
@@ -88,13 +89,16 @@
             new ConcurrentHashMap<>();
     private final Map<BluetoothDevice, List<Integer>> mGroupManagedSources =
             new ConcurrentHashMap<>();
+    private final Map<BluetoothDevice, HashSet<BluetoothDevice>> mActiveSourceMap =
+            new ConcurrentHashMap<>();
 
     private HandlerThread mStateMachinesThread;
     private HandlerThread mCallbackHandlerThread;
+    private Handler mHandler = null;
     private AdapterService mAdapterService;
     private DatabaseManager mDatabaseManager;
     private BluetoothAdapter mBluetoothAdapter = null;
-    private Map<BluetoothDevice, BluetoothDevice> mActiveSourceMap;
+
     /* Caching the PeriodicAdvertisementResult from Broadcast source */
     /* This is stored at service so that each device state machine can access
     and use it as needed. Once the periodic sync in cancelled, this data will bre
@@ -219,34 +223,61 @@
         return base;
     }
 
-    void setActiveSyncedSource(BluetoothDevice scanDelegator, BluetoothDevice sourceDevice) {
+    void removeActiveSyncedSource(BluetoothDevice scanDelegator, BluetoothDevice sourceDevice) {
         if (mActiveSourceMap == null) {
-            Log.e(TAG, "setActiveSyncedSource: mActiveSourceMap is null");
+            Log.e(TAG, "removeActiveSyncedSource: mActiveSourceMap is null");
             return;
         }
 
-        log("setActiveSyncedSource, scanDelegator: " + scanDelegator + ", sourceDevice: " +
-            sourceDevice);
+        log("removeActiveSyncedSource, scanDelegator: " + scanDelegator + ", sourceDevice: "
+                + sourceDevice);
         if (sourceDevice == null) {
+            // remove all sources for this scanDelegator
             mActiveSourceMap.remove(scanDelegator);
         } else {
-            mActiveSourceMap.put(scanDelegator, sourceDevice);
+            HashSet<BluetoothDevice> sources = mActiveSourceMap.get(scanDelegator);
+            if (sources != null) {
+                sources.remove(sourceDevice);
+                if (sources.isEmpty()) {
+                    mActiveSourceMap.remove(scanDelegator);
+                }
+            }
         }
+        sEventLogger.logd(DBG, TAG, "Broadcast Source Unsynced: scanDelegator= " + scanDelegator
+                + ", sourceDevice= " + sourceDevice);
     }
 
-    BluetoothDevice getActiveSyncedSource(BluetoothDevice scanDelegator) {
+    void addActiveSyncedSource(BluetoothDevice scanDelegator, BluetoothDevice sourceDevice) {
         if (mActiveSourceMap == null) {
-            Log.e(TAG, "getActiveSyncedSource: mActiveSourceMap is null");
+            Log.e(TAG, "addActiveSyncedSource: mActiveSourceMap is null");
+            return;
+        }
+
+        log("addActiveSyncedSource, scanDelegator: " + scanDelegator + ", sourceDevice: "
+                + sourceDevice);
+        if (sourceDevice != null) {
+            mActiveSourceMap.putIfAbsent(scanDelegator, new HashSet<>());
+            mActiveSourceMap.get(scanDelegator).add(sourceDevice);
+        }
+        sEventLogger.logd(DBG, TAG, "Broadcast Source Synced: scanDelegator= " + scanDelegator
+                + ", sourceDevice= " + sourceDevice);
+    }
+
+    HashSet<BluetoothDevice> getActiveSyncedSources(BluetoothDevice scanDelegator) {
+        if (mActiveSourceMap == null) {
+            Log.e(TAG, "getActiveSyncedSources: mActiveSourceMap is null");
             return null;
         }
 
-        BluetoothDevice currentSource = mActiveSourceMap.get(scanDelegator);
-        log(
-                "getActiveSyncedSource: scanDelegator: "
-                        + scanDelegator
-                        + ", returning: "
-                        + currentSource);
-        return currentSource;
+        HashSet<BluetoothDevice> currentSources = mActiveSourceMap.get(scanDelegator);
+        if (currentSources != null) {
+            log("getActiveSyncedSources: scanDelegator: " + scanDelegator
+                    + ", sources num: " + currentSources.size());
+        } else {
+            log("getActiveSyncedSources: scanDelegator: " + scanDelegator
+                    + ", currentSources is null");
+        }
+        return currentSources;
     }
 
     public Callbacks getCallbacks() {
@@ -278,10 +309,12 @@
         mCallbackHandlerThread.start();
         mCallbacks = new Callbacks(mCallbackHandlerThread.getLooper());
 
+        // Setup Handler to handle local broadcast use cases.
+        mHandler = new Handler(Looper.getMainLooper());
+
         IntentFilter filter = new IntentFilter();
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
-        filter.addAction(BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED);
         mIntentReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -296,13 +329,6 @@
                             "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
                     bondStateChanged(device, state);
 
-                } else if (action.equals(
-                            BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED)) {
-                    BluetoothDevice device =
-                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                    int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-                    int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
-                    connectionStateChanged(device, fromState, toState);
                 }
             }
         };
@@ -314,7 +340,6 @@
         mPeriodicAdvertisementResultMap = new HashMap<BluetoothDevice,
                 PeriodicAdvertisementResult>();
         mSyncHandleToBaseDataMap = new HashMap<Integer, BaseData>();
-        mActiveSourceMap = new HashMap<BluetoothDevice, BluetoothDevice>();
         mSearchScanCallback = null;
         return true;
     }
@@ -355,7 +380,6 @@
         }
         if (mActiveSourceMap != null) {
             mActiveSourceMap.clear();
-            mActiveSourceMap = null;
         }
         if (mPendingGroupOp != null) {
             mPendingGroupOp.clear();
@@ -381,6 +405,9 @@
                 break;
             }
         }
+        if (device == null) {
+            Log.w(TAG, "No device found for sync handle: " + syncHandle);
+        }
         return device;
     }
 
@@ -644,8 +671,17 @@
         mPeriodicAdvertisementResultMap.remove(device);
     }
 
+    void handleConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+        mHandler.post(() -> connectionStateChanged(device, fromState, toState));
+    }
+
     synchronized void connectionStateChanged(BluetoothDevice device, int fromState,
                                              int toState) {
+        if (!isAvailable()) {
+            Log.w(TAG, "connectionStateChanged: service is not available");
+            return;
+        }
+
         if ((device == null) || (fromState == toState)) {
             Log.e(TAG, "connectionStateChanged: unexpected invocation. device=" + device
                     + " fromState=" + fromState + " toState=" + toState);
@@ -983,6 +1019,11 @@
                 }
             };
             mScanBroadcasts.clear();
+            // clear previous sources notify flag before scanning new result
+            // this is to make sure the active sources are notified even if already synced
+            if (mPeriodicAdvertisementResultMap != null) {
+                clearNotifiedFlags();
+            }
             ScanSettings settings = new ScanSettings.Builder().setCallbackType(
                     ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
                     .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
@@ -1050,6 +1091,15 @@
             return;
         }
 
+        HashSet<BluetoothDevice> activeSyncedSrc = getActiveSyncedSources(sink);
+        if (activeSyncedSrc != null
+                && (activeSyncedSrc.size() >= MAX_ACTIVE_SYNCED_SOURCES_NUM
+                        || activeSyncedSrc.contains(result.getDevice()))) {
+            log("selectSource : found num of active sources: " + activeSyncedSrc.size()
+                    + ", is source synced: " + activeSyncedSrc.contains(result.getDevice()));
+            return;
+        }
+
         synchronized (mStateMachines) {
             sEventLogger.logd(DBG, TAG, "Select Broadcast Source");
 
diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
index d625d92..10e1a93 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
@@ -67,6 +67,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -159,8 +160,6 @@
     private PeriodicAdvertisingManager mPeriodicAdvManager;
     @VisibleForTesting
     boolean mAutoTriggered = false;
-    @VisibleForTesting
-    boolean mNoStopScanOffload = false;
     private boolean mDefNoPAS = false;
     private boolean mForceSB = false;
     private int mBroadcastSourceIdLength = 3;
@@ -383,11 +382,6 @@
         mPASyncRetryCounter = 1;
         // Cache Scan res for Retrys
         mScanRes = scanRes;
-        /*This is an override case if Previous sync is still active, cancel It, but don't stop the
-         * Scan offload as we still trying to assist remote
-         */
-        mNoStopScanOffload = true;
-        cancelActiveSync(null);
         try {
             BluetoothMethodProxy.getInstance().periodicAdvertisingManagerRegisterSync(
                     mPeriodicAdvManager, scanRes, 0, BassConstants.PSYNC_TIMEOUT,
@@ -435,26 +429,26 @@
 
     private void cancelActiveSync(BluetoothDevice sourceDev) {
         log("cancelActiveSync: sourceDev = " + sourceDev);
-        BluetoothDevice activeSyncedSrc = mService.getActiveSyncedSource(mDevice);
+        HashSet<BluetoothDevice> activeSyncedSrc = mService.getActiveSyncedSources(mDevice);
 
         /* Stop sync if there is some running */
-        if (activeSyncedSrc != null && (sourceDev == null || activeSyncedSrc.equals(sourceDev))) {
-            removeMessages(PSYNC_ACTIVE_TIMEOUT);
-            try {
-                log("calling unregisterSync");
-                mPeriodicAdvManager.unregisterSync(mPeriodicAdvCallback);
-            } catch (IllegalArgumentException ex) {
-                Log.w(TAG, "unregisterSync:IllegalArgumentException");
-            }
-            mService.clearNotifiedFlags();
-            mService.setActiveSyncedSource(mDevice, null);
-            if (!mNoStopScanOffload) {
+        if (activeSyncedSrc != null && (sourceDev == null || activeSyncedSrc.contains(sourceDev))) {
+            // clean up if sourceDev is null or this is the only source
+            if (sourceDev == null || (activeSyncedSrc.size() == 0x1)) {
+                removeMessages(PSYNC_ACTIVE_TIMEOUT);
+                try {
+                    log("calling unregisterSync");
+                    mPeriodicAdvManager.unregisterSync(mPeriodicAdvCallback);
+                } catch (IllegalArgumentException ex) {
+                    Log.w(TAG, "unregisterSync:IllegalArgumentException");
+                }
+                mService.clearNotifiedFlags();
                 // trigger scan stop here
                 Message message = obtainMessage(STOP_SCAN_OFFLOAD);
                 sendMessage(message);
             }
+            mService.removeActiveSyncedSource(mDevice, sourceDev);
         }
-        mNoStopScanOffload = false;
     }
 
     private void resetBluetoothGatt() {
@@ -589,9 +583,11 @@
                                 BassConstants.INVALID_BROADCAST_ID,
                                 null,
                                 null);
-                        sendMessageDelayed(PSYNC_ACTIVE_TIMEOUT,
-                                BassConstants.PSYNC_ACTIVE_TIMEOUT_MS);
-                        mService.setActiveSyncedSource(mDevice, device);
+                        removeMessages(PSYNC_ACTIVE_TIMEOUT);
+                        // Refresh sync timeout if another source synced
+                        sendMessageDelayed(
+                                PSYNC_ACTIVE_TIMEOUT, BassConstants.PSYNC_ACTIVE_TIMEOUT_MS);
+                        mService.addActiveSyncedSource(mDevice, device);
                         mFirstTimeBisDiscoveryMap.put(syncHandle, true);
                     } else {
                         log("failed to sync to PA: " + mPASyncRetryCounter);
@@ -1579,6 +1575,18 @@
                     break;
                 case ADD_BCAST_SOURCE:
                     metaData = (BluetoothLeBroadcastMetadata) message.obj;
+
+                    HashSet<BluetoothDevice> activeSyncedSrc =
+                            mService.getActiveSyncedSources(mDevice);
+                    if (!mService.isLocalBroadcast(metaData)
+                            && (activeSyncedSrc == null
+                                    || !activeSyncedSrc.contains(metaData.getSourceDevice()))) {
+                        log("Adding non-active synced source: " + metaData.getSourceDevice());
+                        mService.getCallbacks().notifySourceAddFailed(mDevice, metaData,
+                                BluetoothStatusCodes.ERROR_UNKNOWN);
+                        break;
+                    }
+
                     byte[] addSourceInfo = convertMetadataToAddSourceByteArray(metaData);
                     if (addSourceInfo == null) {
                         Log.e(TAG, "add source: source Info is NULL");
@@ -1875,6 +1883,7 @@
             return;
         }
 
+        mService.handleConnectionStateChanged(device, fromState, toState);
         Intent intent = new Intent(BluetoothLeBroadcastAssistant.ACTION_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, fromState);
         intent.putExtra(BluetoothProfile.EXTRA_STATE, toState);
diff --git a/android/app/src/com/android/bluetooth/bass_client/OWNERS b/android/app/src/com/android/bluetooth/bass_client/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/bass_client/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
index 73530ed..854da1d 100644
--- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
@@ -26,10 +26,6 @@
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothSinkAudioPolicy;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.media.AudioDeviceCallback;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
@@ -101,7 +97,7 @@
  *      will have no impact. E.g., music will continue streaming over the
  *      active Bluetooth device.
  */
-public class ActiveDeviceManager {
+public class ActiveDeviceManager implements AdapterService.BluetoothStateCallback {
     private static final String TAG = "ActiveDeviceManager";
     private static final boolean DBG = true;
     @VisibleForTesting
@@ -144,135 +140,80 @@
     private BluetoothDevice mClassicDeviceToBeActivated = null;
     private BluetoothDevice mClassicDeviceNotToBeActivated = null;
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (DBG) {
-                Log.d(TAG, "Received intent: action=" + intent.getAction() + ", extras="
-                        + intent.getExtras());
-            }
-            String action = intent.getAction();
-            if (action == null) {
-                Log.e(TAG, "Received intent with null action");
-                return;
-            }
-
-            if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
-                int currentState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
-                mHandler.post(() -> handleAdapterStateChanged(currentState));
-            }
-        }
-    };
+    @Override
+    public void onBluetoothStateChange(int prevState, int newState) {
+        mHandler.post(() -> handleAdapterStateChanged(newState));
+    }
 
     /**
-     * Called when A2DP connection state changed by A2dpService
+     * Called when audio profile connection state changed
      *
+     * @param profile The Bluetooth profile of which connection state changed
      * @param device The device of which connection state was changed
      * @param fromState The previous connection state of the device
      * @param toState The new connection state of the device
      */
-    public void a2dpConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+    public void profileConnectionStateChanged(
+            int profile, BluetoothDevice device, int fromState, int toState) {
         if (toState == BluetoothProfile.STATE_CONNECTED) {
-            mHandler.post(() -> handleA2dpConnected(device));
+            switch (profile) {
+                case BluetoothProfile.A2DP:
+                    mHandler.post(() -> handleA2dpConnected(device));
+                    break;
+                case BluetoothProfile.HEADSET:
+                    mHandler.post(() -> handleHfpConnected(device));
+                    break;
+                case BluetoothProfile.LE_AUDIO:
+                    mHandler.post(() -> handleLeAudioConnected(device));
+                    break;
+                case BluetoothProfile.HEARING_AID:
+                    mHandler.post(() -> handleHearingAidConnected(device));
+                    break;
+                case BluetoothProfile.HAP_CLIENT:
+                    mHandler.post(() -> handleHapConnected(device));
+                    break;
+            }
         } else if (fromState == BluetoothProfile.STATE_CONNECTED) {
-            mHandler.post(() -> handleA2dpDisconnected(device));
+            switch (profile) {
+                case BluetoothProfile.A2DP:
+                    mHandler.post(() -> handleA2dpDisconnected(device));
+                    break;
+                case BluetoothProfile.HEADSET:
+                    mHandler.post(() -> handleHfpDisconnected(device));
+                    break;
+                case BluetoothProfile.LE_AUDIO:
+                    mHandler.post(() -> handleLeAudioDisconnected(device));
+                    break;
+                case BluetoothProfile.HEARING_AID:
+                    mHandler.post(() -> handleHearingAidDisconnected(device));
+                    break;
+                case BluetoothProfile.HAP_CLIENT:
+                    mHandler.post(() -> handleHapDisconnected(device));
+                    break;
+            }
         }
     }
 
     /**
-     * Called when A2DP active state changed by A2dpService
+     * Called when active state of audio profiles changed
      *
+     * @param profile The Bluetooth profile of which active state changed
      * @param device The device currently activated. {@code null} if no A2DP device activated
      */
-    public void a2dpActiveStateChanged(BluetoothDevice device) {
-        mHandler.post(() -> handleA2dpActiveDeviceChanged(device));
-    }
-
-    /**
-     * Called when HFP connection state changed by HeadsetService
-     *
-     * @param device The device of which connection state was changed
-     * @param prevState The previous connection state of the device
-     * @param newState The new connection state of the device
-     */
-    public void hfpConnectionStateChanged(BluetoothDevice device, int prevState, int newState) {
-        if (newState == BluetoothProfile.STATE_CONNECTED) {
-            mHandler.post(() -> handleHfpConnected(device));
-        } else if (prevState == BluetoothProfile.STATE_CONNECTED) {
-            mHandler.post(() -> handleHfpDisconnected(device));
-        }
-    }
-
-    /**
-     * Called when HFP active state changed by HeadsetService
-     *
-     * @param device The device currently activated. {@code null} if no HFP device activated
-     */
-    public void hfpActiveStateChanged(BluetoothDevice device) {
-        mHandler.post(() -> handleHfpActiveDeviceChanged(device));
-    }
-
-    /**
-     * Called when LE audio connection state changed by LeAudioService
-     *
-     * @param device The device of which connection state was changed
-     * @param prevState The previous connection state of the device
-     * @param newState The new connection state of the device
-     */
-    public void leAudioConnectionStateChanged(BluetoothDevice device, int prevState, int newState) {
-        if (newState == BluetoothProfile.STATE_CONNECTED) {
-            mHandler.post(() -> handleLeAudioConnected(device));
-        } else if (prevState == BluetoothProfile.STATE_CONNECTED) {
-            mHandler.post(() -> handleLeAudioDisconnected(device));
-        }
-    }
-
-    /**
-     * Called when LE audio active state changed by LeAudioService
-     *
-     * @param device The device currently activated. {@code null} if no LE audio device activated
-     */
-    public void leAudioActiveStateChanged(BluetoothDevice device) {
-        mHandler.post(() -> handleLeAudioActiveDeviceChanged(device));
-    }
-
-    /**
-     * Called when HearingAid connection state changed by HearingAidService
-     *
-     * @param device The device of which connection state was changed
-     * @param prevState The previous connection state of the device
-     * @param newState The new connection state of the device
-     */
-    public void hearingAidConnectionStateChanged(
-            BluetoothDevice device, int prevState, int newState) {
-        if (newState == BluetoothProfile.STATE_CONNECTED) {
-            mHandler.post(() -> handleHearingAidConnected(device));
-        } else if (prevState == BluetoothProfile.STATE_CONNECTED) {
-            mHandler.post(() -> handleHearingAidDisconnected(device));
-        }
-    }
-
-    /**
-     * Called when HearingAid active state changed by HearingAidService
-     *
-     * @param device The device currently activated. {@code null} if no HearingAid device activated
-     */
-    public void hearingAidActiveStateChanged(BluetoothDevice device) {
-        mHandler.post(() -> handleHearingAidActiveDeviceChanged(device));
-    }
-
-    /**
-     * Called when HAP connection state changed by HapClientService
-     *
-     * @param device The device of which connection state was changed
-     * @param prevState The previous connection state of the device
-     * @param newState The new connection state of the device
-     */
-    public void hapConnectionStateChanged(BluetoothDevice device, int prevState, int newState) {
-        if (newState == BluetoothProfile.STATE_CONNECTED) {
-            mHandler.post(() -> handleHapConnected(device));
-        } else if (prevState == BluetoothProfile.STATE_CONNECTED) {
-            mHandler.post(() -> handleHapDisconnected(device));
+    public void profileActiveDeviceChanged(int profile, BluetoothDevice device) {
+        switch (profile) {
+            case BluetoothProfile.A2DP:
+                mHandler.post(() -> handleA2dpActiveDeviceChanged(device));
+                break;
+            case BluetoothProfile.HEADSET:
+                mHandler.post(() -> handleHfpActiveDeviceChanged(device));
+                break;
+            case BluetoothProfile.LE_AUDIO:
+                mHandler.post(() -> handleLeAudioActiveDeviceChanged(device));
+                break;
+            case BluetoothProfile.HEARING_AID:
+                mHandler.post(() -> handleHearingAidActiveDeviceChanged(device));
+                break;
         }
     }
 
@@ -432,12 +373,12 @@
                 return;
             }
             mHearingAidConnectedDevices.add(device);
-            // New connected device: select it as active
-            if (setHearingAidActiveDevice(device)) {
-                setA2dpActiveDevice(null, true);
-                setHfpActiveDevice(null);
-                setLeAudioActiveDevice(null, true);
-            }
+        }
+        // New connected device: select it as active
+        if (setHearingAidActiveDevice(device)) {
+            setA2dpActiveDevice(null, true);
+            setHfpActiveDevice(null);
+            setLeAudioActiveDevice(null, true);
         }
     }
 
@@ -728,8 +669,12 @@
     private void handleHearingAidActiveDeviceChanged(BluetoothDevice device) {
         synchronized (mLock) {
             if (DBG) {
-                Log.d(TAG, "handleHearingAidActiveDeviceChanged: " + device
-                        + ", mHearingAidActiveDevices=" + mHearingAidActiveDevices);
+                Log.d(
+                        TAG,
+                        "handleHearingAidActiveDeviceChanged: "
+                                + device
+                                + ", mHearingAidActiveDevices="
+                                + mHearingAidActiveDevices);
             }
             // Just assign locally the new value
             final HearingAidService hearingAidService = mFactory.getHearingAidService();
@@ -743,11 +688,11 @@
                             hearingAidService.getConnectedPeerDevices(hiSyncId));
                 }
             }
-            if (device != null) {
-                setA2dpActiveDevice(null, true);
-                setHfpActiveDevice(null);
-                setLeAudioActiveDevice(null, true);
-            }
+        }
+        if (device != null) {
+            setA2dpActiveDevice(null, true);
+            setHfpActiveDevice(null);
+            setLeAudioActiveDevice(null, true);
         }
     }
 
@@ -835,12 +780,8 @@
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
 
-        IntentFilter filter = new IntentFilter();
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
-        mAdapterService.registerReceiver(mReceiver, filter, Context.RECEIVER_EXPORTED);
-
         mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler);
+        mAdapterService.registerBluetoothStateCallback((command) -> mHandler.post(command), this);
     }
 
     void cleanup() {
@@ -849,7 +790,7 @@
         }
 
         mAudioManager.unregisterAudioDeviceCallback(mAudioManagerAudioDeviceCallback);
-        mAdapterService.unregisterReceiver(mReceiver);
+        mAdapterService.unregisterBluetoothStateCallback(this);
         if (mHandlerThread != null) {
             mHandlerThread.quit();
             mHandlerThread = null;
@@ -912,26 +853,29 @@
 
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     private boolean setHfpActiveDevice(BluetoothDevice device) {
+        if (DBG) {
+            Log.d(TAG, "setHfpActiveDevice(" + device + ")");
+        }
         synchronized (mLock) {
-            if (DBG) {
-                Log.d(TAG, "setHfpActiveDevice(" + device + ")");
-            }
             if (mPendingActiveDevice != null) {
                 mHandler.removeCallbacksAndMessages(mPendingActiveDevice);
                 mPendingActiveDevice = null;
             }
-            final HeadsetService headsetService = mFactory.getHeadsetService();
-            if (headsetService == null) {
-                return false;
-            }
-            BluetoothSinkAudioPolicy audioPolicy = headsetService.getHfpCallAudioPolicy(device);
-            if (audioPolicy != null && audioPolicy.getActiveDevicePolicyAfterConnection()
-                    == BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED) {
-                return false;
-            }
-            if (!headsetService.setActiveDevice(device)) {
-                return false;
-            }
+        }
+        final HeadsetService headsetService = mFactory.getHeadsetService();
+        if (headsetService == null) {
+            return false;
+        }
+        BluetoothSinkAudioPolicy audioPolicy = headsetService.getHfpCallAudioPolicy(device);
+        if (audioPolicy != null
+                && audioPolicy.getActiveDevicePolicyAfterConnection()
+                        == BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED) {
+            return false;
+        }
+        if (!headsetService.setActiveDevice(device)) {
+            return false;
+        }
+        synchronized (mLock) {
             mHfpActiveDevice = device;
         }
         return true;
@@ -986,22 +930,22 @@
             Log.d(TAG, "setLeAudioActiveDevice(" + device + ")"
                     + (device == null ? " hasFallbackDevice=" + hasFallbackDevice : ""));
         }
+        final LeAudioService leAudioService = mFactory.getLeAudioService();
+        if (leAudioService == null) {
+            return false;
+        }
+        boolean success;
+        if (device == null) {
+            success = leAudioService.removeActiveDevice(hasFallbackDevice);
+        } else {
+            success = leAudioService.setActiveDevice(device);
+        }
+
+        if (!success) {
+            return false;
+        }
+
         synchronized (mLock) {
-            final LeAudioService leAudioService = mFactory.getLeAudioService();
-            if (leAudioService == null) {
-                return false;
-            }
-            boolean success;
-            if (device == null) {
-                success = leAudioService.removeActiveDevice(hasFallbackDevice);
-            } else {
-                success = leAudioService.setActiveDevice(device);
-            }
-
-            if (!success) {
-                return false;
-            }
-
             mLeAudioActiveDevice = device;
             if (device == null) {
                 mLeHearingAidActiveDevice = null;
@@ -1075,13 +1019,13 @@
         A2dpService a2dpService = mFactory.getA2dpService();
         BluetoothDevice a2dpFallbackDevice = null;
         if (a2dpService != null) {
-            a2dpFallbackDevice = a2dpService.getFallbackDevice();
+            a2dpFallbackDevice = getA2dpFallbackDevice();
         }
 
         HeadsetService headsetService = mFactory.getHeadsetService();
         BluetoothDevice headsetFallbackDevice = null;
         if (headsetService != null) {
-            headsetFallbackDevice = headsetService.getFallbackDevice();
+            headsetFallbackDevice = getHfpFallbackDevice();
         }
 
         List<BluetoothDevice> connectedDevices = new ArrayList<>();
@@ -1191,9 +1135,48 @@
         }
     }
 
-    @VisibleForTesting
-    BroadcastReceiver getBroadcastReceiver() {
-        return mReceiver;
+    /** Retrieves the most recently connected device in the A2DP connected devices list. */
+    public BluetoothDevice getA2dpFallbackDevice() {
+        DatabaseManager dbManager = mAdapterService.getDatabase();
+        synchronized (mLock) {
+            return dbManager != null
+                    ? dbManager.getMostRecentlyConnectedDevicesInList(mA2dpConnectedDevices)
+                    : null;
+        }
+    }
+
+    /** Retrieves the most recently connected device in the A2DP connected devices list. */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public BluetoothDevice getHfpFallbackDevice() {
+        DatabaseManager dbManager = mAdapterService.getDatabase();
+        return dbManager != null
+                ? dbManager.getMostRecentlyConnectedDevicesInList(getHfpFallbackCandidates())
+                : null;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    List<BluetoothDevice> getHfpFallbackCandidates() {
+        List<BluetoothDevice> fallbackCandidates;
+        synchronized (mLock) {
+            fallbackCandidates = new ArrayList<>(mHfpConnectedDevices);
+        }
+        List<BluetoothDevice> uninterestedCandidates = new ArrayList<>();
+        for (BluetoothDevice device : fallbackCandidates) {
+            byte[] deviceType =
+                    mDbManager.getCustomMeta(device, BluetoothDevice.METADATA_DEVICE_TYPE);
+            BluetoothClass deviceClass = device.getBluetoothClass();
+            if ((deviceClass != null
+                            && deviceClass.getMajorDeviceClass()
+                                    == BluetoothClass.Device.WEARABLE_WRIST_WATCH)
+                    || (deviceType != null
+                            && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) {
+                uninterestedCandidates.add(device);
+            }
+        }
+        for (BluetoothDevice device : uninterestedCandidates) {
+            fallbackCandidates.remove(device);
+        }
+        return fallbackCandidates;
     }
 
     @VisibleForTesting
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterApp.java b/android/app/src/com/android/bluetooth/btservice/AdapterApp.java
index 99e3f4f..06ceeb6 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterApp.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterApp.java
@@ -13,16 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-/**
- * @hide
- */
-
 package com.android.bluetooth.btservice;
 
 import android.app.Application;
 import android.util.Log;
 
+import com.android.bluetooth.Utils;
+
 public class AdapterApp extends Application {
     private static final String TAG = "BluetoothAdapterApp";
     private static final boolean DBG = false;
@@ -33,7 +30,11 @@
         if (DBG) {
             Log.d(TAG, "Loading JNI Library");
         }
-        System.loadLibrary("bluetooth_jni");
+        if (Utils.isInstrumentationTestMode()) {
+            Log.w(TAG, "App is instrumented. Skip loading the native");
+        } else {
+            System.loadLibrary("bluetooth_jni");
+        }
     }
 
     public AdapterApp() {
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
new file mode 100644
index 0000000..67f5f6a
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.btservice;
+
+import android.bluetooth.OobData;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+
+/** Native interface to be used by AdapterService */
+public class AdapterNativeInterface {
+    private static final String TAG = AdapterNativeInterface.class.getSimpleName();
+
+    private JniCallbacks mJniCallbacks;
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static AdapterNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
+    private AdapterNativeInterface() {}
+
+    static AdapterNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new AdapterNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(AdapterNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
+    JniCallbacks getCallbacks() {
+        return mJniCallbacks;
+    }
+
+    boolean init(
+            AdapterService service,
+            AdapterProperties adapterProperties,
+            boolean startRestricted,
+            boolean isCommonCriteriaMode,
+            int configCompareResult,
+            String[] initFlags,
+            boolean isAtvDevice,
+            String userDataDirectory) {
+        mJniCallbacks = new JniCallbacks(service, adapterProperties);
+        return initNative(
+                startRestricted,
+                isCommonCriteriaMode,
+                configCompareResult,
+                initFlags,
+                isAtvDevice,
+                userDataDirectory);
+    }
+
+    void cleanup() {
+        cleanupNative();
+    }
+
+    boolean enable() {
+        return enableNative();
+    }
+
+    boolean disable() {
+        return disableNative();
+    }
+
+    boolean setAdapterProperty(int type, byte[] val) {
+        return setAdapterPropertyNative(type, val);
+    }
+
+    boolean getAdapterProperties() {
+        return getAdapterPropertiesNative();
+    }
+
+    boolean getAdapterProperty(int type) {
+        return getAdapterPropertyNative(type);
+    }
+
+    boolean setAdapterProperty(int type) {
+        return setAdapterPropertyNative(type);
+    }
+
+    boolean setDeviceProperty(byte[] address, int type, byte[] val) {
+        return setDevicePropertyNative(address, type, val);
+    }
+
+    boolean getDeviceProperty(byte[] address, int type) {
+        return getDevicePropertyNative(address, type);
+    }
+
+    boolean createBond(byte[] address, int addressType, int transport) {
+        return createBondNative(address, addressType, transport);
+    }
+
+    boolean createBondOutOfBand(byte[] address, int transport, OobData p192Data, OobData p256Data) {
+        return createBondOutOfBandNative(address, transport, p192Data, p256Data);
+    }
+
+    boolean removeBond(byte[] address) {
+        return removeBondNative(address);
+    }
+
+    boolean cancelBond(byte[] address) {
+        return cancelBondNative(address);
+    }
+
+    void generateLocalOobData(int transport) {
+        generateLocalOobDataNative(transport);
+    }
+
+    boolean sdpSearch(byte[] address, byte[] uuid) {
+        return sdpSearchNative(address, uuid);
+    }
+
+    int getConnectionState(byte[] address) {
+        return getConnectionStateNative(address);
+    }
+
+    boolean startDiscovery() {
+        return startDiscoveryNative();
+    }
+
+    boolean cancelDiscovery() {
+        return cancelDiscoveryNative();
+    }
+
+    boolean pinReply(byte[] address, boolean accept, int len, byte[] pin) {
+        return pinReplyNative(address, accept, len, pin);
+    }
+
+    boolean sspReply(byte[] address, int type, boolean accept, int passkey) {
+        return sspReplyNative(address, type, accept, passkey);
+    }
+
+    boolean getRemoteServices(byte[] address, int transport) {
+        return getRemoteServicesNative(address, transport);
+    }
+
+    boolean getRemoteMasInstances(byte[] address) {
+        return getRemoteMasInstancesNative(address);
+    }
+
+    int readEnergyInfo() {
+        return readEnergyInfoNative();
+    }
+
+    boolean factoryReset() {
+        return factoryResetNative();
+    }
+
+    void dump(FileDescriptor fd, String[] arguments) {
+        dumpNative(fd, arguments);
+    }
+
+    byte[] dumpMetrics() {
+        return dumpMetricsNative();
+    }
+
+    byte[] obfuscateAddress(byte[] address) {
+        return obfuscateAddressNative(address);
+    }
+
+    boolean setBufferLengthMillis(int codec, int value) {
+        return setBufferLengthMillisNative(codec, value);
+    }
+
+    int getMetricId(byte[] address) {
+        return getMetricIdNative(address);
+    }
+
+    int connectSocket(byte[] address, int type, byte[] uuid, int port, int flag, int callingUid) {
+        return connectSocketNative(address, type, uuid, port, flag, callingUid);
+    }
+
+    int createSocketChannel(
+            int type, String serviceName, byte[] uuid, int port, int flag, int callingUid) {
+        return createSocketChannelNative(type, serviceName, uuid, port, flag, callingUid);
+    }
+
+    void requestMaximumTxDataLength(byte[] address) {
+        requestMaximumTxDataLengthNative(address);
+    }
+
+    boolean allowLowLatencyAudio(boolean allowed, byte[] address) {
+        return allowLowLatencyAudioNative(allowed, address);
+    }
+
+    void metadataChanged(byte[] address, int key, byte[] value) {
+        metadataChangedNative(address, key, value);
+    }
+
+    boolean interopMatchAddr(String featureName, String address) {
+        return interopMatchAddrNative(featureName, address);
+    }
+
+    boolean interopMatchName(String featureName, String name) {
+        return interopMatchNameNative(featureName, name);
+    }
+
+    boolean interopMatchAddrOrName(String featureName, String address) {
+        return interopMatchAddrOrNameNative(featureName, address);
+    }
+
+    void interopDatabaseAddRemoveAddr(
+            boolean doAdd, String featureName, String address, int length) {
+        interopDatabaseAddRemoveAddrNative(doAdd, featureName, address, length);
+    }
+
+    void interopDatabaseAddRemoveName(boolean doAdd, String featureName, String name) {
+        interopDatabaseAddRemoveNameNative(doAdd, featureName, name);
+    }
+
+    int getRemotePbapPceVersion(String address) {
+        return getRemotePbapPceVersionNative(address);
+    }
+
+    boolean pbapPseDynamicVersionUpgradeIsEnabled() {
+        return pbapPseDynamicVersionUpgradeIsEnabledNative();
+    }
+
+    boolean isLogRedactionEnabled() {
+        return isLogRedactionEnabledNative();
+    }
+
+    /**********************************************************************************************/
+    /*********************************** callbacks from native ************************************/
+    /**********************************************************************************************/
+
+    // See JniCallbacks.java
+
+    /**********************************************************************************************/
+    /******************************************* native *******************************************/
+    /**********************************************************************************************/
+
+    private native boolean initNative(
+            boolean startRestricted,
+            boolean isCommonCriteriaMode,
+            int configCompareResult,
+            String[] initFlags,
+            boolean isAtvDevice,
+            String userDataDirectory);
+
+    private native void cleanupNative();
+
+    private native boolean enableNative();
+
+    private native boolean disableNative();
+
+    private native boolean setAdapterPropertyNative(int type, byte[] val);
+
+    private native boolean getAdapterPropertiesNative();
+
+    private native boolean getAdapterPropertyNative(int type);
+
+    private native boolean setAdapterPropertyNative(int type);
+
+    private native boolean setDevicePropertyNative(byte[] address, int type, byte[] val);
+
+    private native boolean getDevicePropertyNative(byte[] address, int type);
+
+    private native boolean createBondNative(byte[] address, int addressType, int transport);
+
+    private native boolean createBondOutOfBandNative(
+            byte[] address, int transport, OobData p192Data, OobData p256Data);
+
+    private native boolean removeBondNative(byte[] address);
+
+    private native boolean cancelBondNative(byte[] address);
+
+    private native void generateLocalOobDataNative(int transport);
+
+    private native boolean sdpSearchNative(byte[] address, byte[] uuid);
+
+    private native int getConnectionStateNative(byte[] address);
+
+    private native boolean startDiscoveryNative();
+
+    private native boolean cancelDiscoveryNative();
+
+    private native boolean pinReplyNative(byte[] address, boolean accept, int len, byte[] pin);
+
+    private native boolean sspReplyNative(byte[] address, int type, boolean accept, int passkey);
+
+    private native boolean getRemoteServicesNative(byte[] address, int transport);
+
+    private native boolean getRemoteMasInstancesNative(byte[] address);
+
+    private native int readEnergyInfoNative();
+
+    private native boolean factoryResetNative();
+
+    private native void dumpNative(FileDescriptor fd, String[] arguments);
+
+    private native byte[] dumpMetricsNative();
+
+    private native byte[] obfuscateAddressNative(byte[] address);
+
+    private native boolean setBufferLengthMillisNative(int codec, int value);
+
+    private native int getMetricIdNative(byte[] address);
+
+    private native int connectSocketNative(
+            byte[] address, int type, byte[] uuid, int port, int flag, int callingUid);
+
+    private native int createSocketChannelNative(
+            int type, String serviceName, byte[] uuid, int port, int flag, int callingUid);
+
+    private native void requestMaximumTxDataLengthNative(byte[] address);
+
+    private native boolean allowLowLatencyAudioNative(boolean allowed, byte[] address);
+
+    private native void metadataChangedNative(byte[] address, int key, byte[] value);
+
+    private native boolean interopMatchAddrNative(String featureName, String address);
+
+    private native boolean interopMatchNameNative(String featureName, String name);
+
+    private native boolean interopMatchAddrOrNameNative(String featureName, String address);
+
+    private native void interopDatabaseAddRemoveAddrNative(
+            boolean doAdd, String featureName, String address, int length);
+
+    private native void interopDatabaseAddRemoveNameNative(
+            boolean doAdd, String featureName, String name);
+
+    private native int getRemotePbapPceVersionNative(String address);
+
+    private native boolean pbapPseDynamicVersionUpgradeIsEnabledNative();
+
+    private native boolean isLogRedactionEnabledNative();
+}
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
index 4affca7..2095fd4 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -86,6 +86,7 @@
             "persist.bluetooth.a2dp_offload.disabled";
 
     private static final long DEFAULT_DISCOVERY_TIMEOUT_MS = 12800;
+    @VisibleForTesting static final int BLUETOOTH_NAME_MAX_LENGTH_BYTES = 248;
     private static final int BD_ADDR_LEN = 6; // in bytes
 
     private volatile String mName;
@@ -321,15 +322,22 @@
      */
     boolean setName(String name) {
         synchronized (mObject) {
-            return mService.setAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDNAME,
-                    name.getBytes());
+            return mService.getNative()
+                    .setAdapterProperty(
+                            AbstractionLayer.BT_PROPERTY_BDNAME,
+                            Utils.truncateStringForUtf8Storage(
+                                            name, BLUETOOTH_NAME_MAX_LENGTH_BYTES)
+                                    .getBytes());
         }
     }
 
     boolean setIoCapability(int capability) {
         synchronized (mObject) {
-            boolean result = mService.setAdapterPropertyNative(
-                    AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS, Utils.intToByteArray(capability));
+            boolean result =
+                    mService.getNative()
+                            .setAdapterProperty(
+                                    AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS,
+                                    Utils.intToByteArray(capability));
 
             if (result) {
                 mLocalIOCapability = capability;
@@ -364,8 +372,10 @@
     boolean setScanMode(int scanMode) {
         addScanChangeLog(scanMode);
         synchronized (mObject) {
-            return mService.setAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE,
-                    Utils.intToByteArray(AdapterService.convertScanModeToHal(scanMode)));
+            return mService.getNative()
+                    .setAdapterProperty(
+                            AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE,
+                            Utils.intToByteArray(AdapterService.convertScanModeToHal(scanMode)));
         }
     }
 
@@ -590,7 +600,7 @@
      * @param size the size to set
      */
     boolean setBufferLengthMillis(int codec, int size) {
-        return mService.setBufferLengthMillisNative(codec, size);
+        return mService.getNative().setBufferLengthMillis(codec, size);
     }
 
     /**
@@ -657,7 +667,8 @@
             String address = device.getAddress();
             String identityAddress = mService.getIdentityAddress(address);
             if (currentIdentityAddress.equals(identityAddress) && !currentAddress.equals(address)) {
-                if (mService.removeBondNative(Utils.getBytesFromAddress(device.getAddress()))) {
+                if (mService.getNative()
+                        .removeBond(Utils.getBytesFromAddress(device.getAddress()))) {
                     mBondedDevices.remove(device);
                     infoLog("Removing old bond record: "
                                     + device
@@ -680,9 +691,10 @@
 
     boolean setDiscoverableTimeout(int timeout) {
         synchronized (mObject) {
-            return mService.setAdapterPropertyNative(
-                    AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT,
-                    Utils.intToByteArray(timeout));
+            return mService.getNative()
+                    .setAdapterProperty(
+                            AbstractionLayer.BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT,
+                            Utils.intToByteArray(timeout));
         }
     }
 
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index 6f016e5..fa5adb6 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -41,7 +41,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
-import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.app.Service;
@@ -80,10 +79,8 @@
 import android.bluetooth.UidTraffic;
 import android.companion.CompanionDeviceManager;
 import android.content.AttributionSource;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.os.AsyncTask;
@@ -121,7 +118,6 @@
 import com.android.bluetooth.bass_client.BassClientService;
 import com.android.bluetooth.btservice.InteropUtil.InteropFeature;
 import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties;
-import com.android.bluetooth.btservice.activityattribution.ActivityAttributionService;
 import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreNativeInterface;
 import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
@@ -208,9 +204,6 @@
     public static final String EXTRA_ACTION = "action";
     public static final int PROFILE_CONN_REJECTED = 2;
 
-    private static final String ACTION_ALARM_WAKEUP =
-            "com.android.bluetooth.btservice.action.ALARM_WAKEUP";
-
     private static BluetoothProperties.snoop_log_mode_values sSnoopLogSettingAtEnable =
             BluetoothProperties.snoop_log_mode_values.EMPTY;
     private static String sDefaultSnoopLogSettingAtEnable = "empty";
@@ -239,9 +232,6 @@
 
     private static final int CONTROLLER_ENERGY_UPDATE_TIMEOUT_MILLIS = 30;
 
-    public static final String ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS =
-            "no_active_device_address";
-
     // Report ID definition
     public enum BqrQualityReportId {
         QUALITY_REPORT_ID_MONITOR_MODE(0x01),
@@ -267,11 +257,8 @@
 
     private final ArrayList<DiscoveringPackage> mDiscoveringPackages = new ArrayList<>();
 
-    static {
-        classInitNative();
-    }
-
     private static AdapterService sAdapterService;
+    private final AdapterNativeInterface mNativeInterface = AdapterNativeInterface.getInstance();
 
     // Keep a constructor for ActivityThread.handleCreateService
     AdapterService() {}
@@ -285,6 +272,10 @@
         return sAdapterService;
     }
 
+    AdapterNativeInterface getNative() {
+        return mNativeInterface;
+    }
+
     /** Allow test to set an AdapterService to be return by AdapterService.getAdapterService() */
     @VisibleForTesting
     public static synchronized void setAdapterService(AdapterService instance) {
@@ -315,7 +306,6 @@
     @VisibleForTesting AdapterProperties mAdapterProperties;
     private AdapterState mAdapterStateMachine;
     private BondStateMachine mBondStateMachine;
-    private JniCallbacks mJniCallbacks;
     private RemoteDevices mRemoteDevices;
 
     /* TODO: Consider to remove the search API from this class, if changed to use call-back */
@@ -335,7 +325,8 @@
     private final Map<Integer, PendingAudioProfilePreferenceRequest>
             mCsipGroupsPendingAudioProfileChanges = new HashMap<>();
     // Only BluetoothManagerService should be registered
-    private RemoteCallbackList<IBluetoothCallback> mCallbacks;
+    private RemoteCallbackList<IBluetoothCallback> mRemoteCallbacks;
+    private final Map<BluetoothStateCallback, Executor> mLocalCallbacks = new ConcurrentHashMap<>();
     private int mCurrentRequestId;
     private boolean mQuietmode = false;
     private HashMap<String, CallerInfo> mBondAttemptCallerInfo = new HashMap<>();
@@ -343,8 +334,6 @@
     private final Map<UUID, RfcommListenerData> mBluetoothServerSockets = new ConcurrentHashMap<>();
     private final Executor mSocketServersExecutor = r -> new Thread(r).start();
 
-    private AlarmManager mAlarmManager;
-    private PendingIntent mPendingAlarm;
     private BatteryStatsManager mBatteryStatsManager;
     private PowerManager mPowerManager;
     private PowerManager.WakeLock mWakeLock;
@@ -364,7 +353,6 @@
     private BluetoothKeystoreService mBluetoothKeystoreService;
     private A2dpService mA2dpService;
     private A2dpSinkService mA2dpSinkService;
-    private ActivityAttributionService mActivityAttributionService;
     private HeadsetService mHeadsetService;
     private HeadsetClientService mHeadsetClientService;
     private BluetoothMapService mMapService;
@@ -514,14 +502,16 @@
                     // TODO(b/228875190): GATT is assumed supported. GATT starting triggers hardware
                     // initializtion. Configuring a device without GATT causes start up failures.
                     if (GattService.class.getSimpleName().equals(profile.getName())) {
-                        enableNative();
+                        mNativeInterface.enable();
                     } else if (mRegisteredProfiles.size() == Config.getSupportedProfiles().length
                             && mRegisteredProfiles.size() == mRunningProfiles.size()) {
                         mAdapterProperties.onBluetoothReady();
                         updateUuids();
                         initProfileServices();
-                        getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS);
-                        getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_DYNAMIC_AUDIO_BUFFER);
+                        mNativeInterface.getAdapterProperty(
+                                AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS);
+                        mNativeInterface.getAdapterProperty(
+                                AbstractionLayer.BT_PROPERTY_DYNAMIC_AUDIO_BUFFER);
                         mAdapterStateMachine.sendMessage(AdapterState.BREDR_STARTED);
                         mBtCompanionManager.loadCompanionInfo();
                     }
@@ -545,7 +535,7 @@
                                     .equals(mRunningProfiles.get(0).getName())))) {
                         mAdapterStateMachine.sendMessage(AdapterState.BREDR_STOPPED);
                     } else if (mRunningProfiles.size() == 0) {
-                        disableNative();
+                        mNativeInterface.disable();
                     }
                     break;
                 default:
@@ -613,7 +603,6 @@
 
         mUserManager = getNonNullSystemService(UserManager.class);
         mAppOps = getNonNullSystemService(AppOpsManager.class);
-        mAlarmManager = getNonNullSystemService(AlarmManager.class);
         mPowerManager = getNonNullSystemService(PowerManager.class);
         mBatteryStatsManager = getNonNullSystemService(BatteryStatsManager.class);
         mCompanionDeviceManager = getNonNullSystemService(CompanionDeviceManager.class);
@@ -625,10 +614,9 @@
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mAdapterProperties = new AdapterProperties(this);
         mAdapterStateMachine = new AdapterState(this, mLooper);
-        mJniCallbacks = new JniCallbacks(this, mAdapterProperties);
         mBluetoothKeystoreService =
                 new BluetoothKeystoreService(
-                        new BluetoothKeystoreNativeInterface(), isCommonCriteriaMode());
+                        BluetoothKeystoreNativeInterface.getInstance(), isCommonCriteriaMode());
         mBluetoothKeystoreService.start();
         int configCompareResult = mBluetoothKeystoreService.getCompareResult();
 
@@ -640,7 +628,9 @@
                 getApplicationContext()
                         .getPackageManager()
                         .hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY);
-        initNative(
+        mNativeInterface.init(
+                this,
+                mAdapterProperties,
                 mUserManager.isGuestUser(),
                 isCommonCriteriaMode(),
                 configCompareResult,
@@ -652,11 +642,11 @@
                 new RemoteCallbackList<IBluetoothPreferredAudioProfilesCallback>();
         mBluetoothQualityReportReadyCallbacks =
                 new RemoteCallbackList<IBluetoothQualityReportReadyCallback>();
-        mCallbacks = new RemoteCallbackList<IBluetoothCallback>();
+        mRemoteCallbacks = new RemoteCallbackList<IBluetoothCallback>();
         // Load the name and address
-        getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDADDR);
-        getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_BDNAME);
-        getAdapterPropertyNative(AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE);
+        mNativeInterface.getAdapterProperty(AbstractionLayer.BT_PROPERTY_BDADDR);
+        mNativeInterface.getAdapterProperty(AbstractionLayer.BT_PROPERTY_BDNAME);
+        mNativeInterface.getAdapterProperty(AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE);
 
         mBluetoothKeystoreService.initJni();
 
@@ -667,9 +657,6 @@
         mBluetoothQualityReportNativeInterface.init();
 
         mSdpManager = SdpManager.init(this);
-        IntentFilter filter = new IntentFilter(ACTION_ALARM_WAKEUP);
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        registerReceiver(mAlarmBroadcastReceiver, filter);
         loadLeAudioAllowDevices();
 
         mDatabaseManager = new DatabaseManager(this);
@@ -704,8 +691,6 @@
 
         mBluetoothSocketManagerBinder = new BluetoothSocketManagerBinder(this);
 
-        mActivityAttributionService = new ActivityAttributionService();
-
         setAdapterService(this);
 
         invalidateBluetoothCaches();
@@ -768,6 +753,10 @@
         return mActiveDeviceManager;
     }
 
+    public SilenceDeviceManager getSilenceDeviceManager() {
+        return mSilenceDeviceManager;
+    }
+
     private boolean initMetricsLogger() {
         if (mMetricsLogger != null) {
             return false;
@@ -915,7 +904,7 @@
         debugLog("bleOnProcessStart() - Make Bond State Machine");
         mBondStateMachine = BondStateMachine.make(this, mAdapterProperties, mRemoteDevices);
 
-        mJniCallbacks.init(mBondStateMachine, mRemoteDevices);
+        mNativeInterface.getCallbacks().init(mBondStateMachine, mRemoteDevices);
 
         mBatteryStatsManager.reportBleScanReset();
         BluetoothStatsLog.write_non_chained(
@@ -973,7 +962,7 @@
 
     void stopProfileServices() {
         // Make sure to stop classic background tasks now
-        cancelDiscoveryNative();
+        mNativeInterface.cancelDiscovery();
         mAdapterProperties.setScanMode(BluetoothAdapter.SCAN_MODE_NONE);
 
         Class[] supportedProfileServices = Config.getSupportedProfiles();
@@ -1049,8 +1038,9 @@
     void updateAdapterState(int prevState, int newState) {
         mAdapterProperties.setState(newState);
         invalidateBluetoothGetStateCache();
-        if (mCallbacks != null) {
-            int n = mCallbacks.beginBroadcast();
+
+        if (mRemoteCallbacks != null) {
+            int n = mRemoteCallbacks.beginBroadcast();
             debugLog(
                     "updateAdapterState() - Broadcasting state "
                             + BluetoothAdapter.nameForState(newState)
@@ -1059,12 +1049,18 @@
                             + " receivers.");
             for (int i = 0; i < n; i++) {
                 try {
-                    mCallbacks.getBroadcastItem(i).onBluetoothStateChange(prevState, newState);
+                    mRemoteCallbacks
+                            .getBroadcastItem(i)
+                            .onBluetoothStateChange(prevState, newState);
                 } catch (RemoteException e) {
                     debugLog("updateAdapterState() - Callback #" + i + " failed (" + e + ")");
                 }
             }
-            mCallbacks.finishBroadcast();
+            mRemoteCallbacks.finishBroadcast();
+        }
+
+        for (Map.Entry<BluetoothStateCallback, Executor> e : mLocalCallbacks.entrySet()) {
+            e.getValue().execute(() -> e.getKey().onBluetoothStateChange(prevState, newState));
         }
 
         // Turn the Adapter all the way off if we are disabling and the snoop log setting changed.
@@ -1266,15 +1262,8 @@
         mCleaningUp = true;
         invalidateBluetoothCaches();
 
-        unregisterReceiver(mAlarmBroadcastReceiver);
-
         stopRfcommServerSockets();
 
-        if (mPendingAlarm != null) {
-            mAlarmManager.cancel(mPendingAlarm);
-            mPendingAlarm = null;
-        }
-
         // This wake lock release may also be called concurrently by
         // {@link #releaseWakeLock(String lockName)}, so a synchronization is needed here.
         synchronized (this) {
@@ -1309,7 +1298,7 @@
 
         if (mNativeAvailable) {
             debugLog("cleanup() - Cleaning up adapter native");
-            cleanupNative();
+            mNativeInterface.cleanup();
             mNativeAvailable = false;
         }
 
@@ -1317,8 +1306,8 @@
             mAdapterProperties.cleanup();
         }
 
-        if (mJniCallbacks != null) {
-            mJniCallbacks.cleanup();
+        if (mNativeInterface.getCallbacks() != null) {
+            mNativeInterface.getCallbacks().cleanup();
         }
 
         if (mBluetoothKeystoreService != null) {
@@ -1360,8 +1349,8 @@
             mBluetoothQualityReportReadyCallbacks.kill();
         }
 
-        if (mCallbacks != null) {
-            mCallbacks.kill();
+        if (mRemoteCallbacks != null) {
+            mRemoteCallbacks.kill();
         }
     }
 
@@ -1475,7 +1464,15 @@
         ParcelUuid[] remoteDeviceUuids = getRemoteUuids(device);
         ParcelUuid[] localDeviceUuids = mAdapterProperties.getUuids();
         if (remoteDeviceUuids == null || remoteDeviceUuids.length == 0) {
-            Log.e(TAG, "isSupported: Remote Device Uuids Empty");
+            Log.e(TAG, "isProfileSupported(device=" + device + ", profile="
+                    + BluetoothProfile.getProfileName(profile) + "): remote device Uuids Empty");
+        }
+
+        if (VERBOSE) {
+            Log.v(TAG, "isProfileSupported(device=" + device + ", profile="
+                    + BluetoothProfile.getProfileName(profile) + "): local_uuids="
+                    + Arrays.toString(localDeviceUuids) + ", remote_uuids="
+                    + Arrays.toString(remoteDeviceUuids));
         }
 
         if (profile == BluetoothProfile.HEADSET) {
@@ -1517,7 +1514,8 @@
             return mPbapService.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED;
         }
         if (profile == BluetoothProfile.MAP_CLIENT) {
-            return true;
+            return Utils.arrayContains(localDeviceUuids, BluetoothUuid.MNS)
+                    && Utils.arrayContains(remoteDeviceUuids, BluetoothUuid.MAS);
         }
         if (profile == BluetoothProfile.PBAP_CLIENT) {
             return Utils.arrayContains(localDeviceUuids, BluetoothUuid.PBAP_PCE)
@@ -2184,7 +2182,7 @@
                 // by default return true
                 receiver.send(true);
             } else {
-                receiver.send(service.isLogRedactionEnabled());
+                receiver.send(service.mNativeInterface.isLogRedactionEnabled());
             }
         }
 
@@ -2502,7 +2500,7 @@
             }
 
             service.debugLog("cancelDiscovery");
-            return service.cancelDiscoveryNative();
+            return service.mNativeInterface.cancelDiscovery();
         }
 
         @Override
@@ -2743,7 +2741,7 @@
                 deviceProp.setBondingInitiatedLocally(false);
             }
 
-            return service.cancelBondNative(getBytesFromAddress(device.getAddress()));
+            return service.mNativeInterface.cancelBond(getBytesFromAddress(device.getAddress()));
         }
 
         @Override
@@ -3457,7 +3455,7 @@
             }
             service.logUserBondResponse(
                     device, accept, BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_PIN_REPLIED);
-            return service.pinReplyNative(
+            return service.mNativeInterface.pinReply(
                     getBytesFromAddress(device.getAddress()), accept, len, pinCode);
         }
 
@@ -3502,7 +3500,7 @@
             }
             service.logUserBondResponse(
                     device, accept, BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_SSP_REPLIED);
-            return service.sspReplyNative(
+            return service.mNativeInterface.sspReply(
                     getBytesFromAddress(device.getAddress()),
                     AbstractionLayer.BT_SSP_VARIANT_PASSKEY_ENTRY,
                     accept,
@@ -3544,7 +3542,7 @@
             }
             service.logUserBondResponse(
                     device, accept, BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_SSP_REPLIED);
-            return service.sspReplyNative(
+            return service.mNativeInterface.sspReply(
                     getBytesFromAddress(device.getAddress()),
                     AbstractionLayer.BT_SSP_VARIANT_PASSKEY_CONFIRMATION,
                     accept,
@@ -4096,7 +4094,7 @@
 
             enforceBluetoothPrivilegedPermission(service);
 
-            service.registerCallback(callback);
+            service.registerRemoteCallback(callback);
         }
 
         @Override
@@ -4121,7 +4119,7 @@
         private void unregisterCallback(IBluetoothCallback callback, AttributionSource source) {
             AdapterService service = getService();
             if (service == null
-                    || service.mCallbacks == null
+                    || service.mRemoteCallbacks == null
                     || !callerIsSystemOrActiveOrManagedUser(service, TAG, "unregisterCallback")
                     || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
                 return;
@@ -4129,7 +4127,7 @@
 
             enforceBluetoothPrivilegedPermission(service);
 
-            service.unregisterCallback(callback);
+            service.unregisterRemoteCallback(callback);
         }
 
         @Override
@@ -5639,8 +5637,6 @@
         return mAdapterProperties.getName();
     }
 
-    private native boolean isLogRedactionEnabled();
-
     public int getNameLengthForAdvertise() {
         return mAdapterProperties.getName().length();
     }
@@ -5696,7 +5692,7 @@
             mDiscoveringPackages.add(
                     new DiscoveringPackage(callingPackage, permission, hasDisavowedLocation));
         }
-        return startDiscoveryNative();
+        return mNativeInterface.startDiscovery();
     }
 
     /**
@@ -5775,7 +5771,7 @@
 
         // Pairing is unreliable while scanning, so cancel discovery
         // Note, remove this when native stack improves
-        cancelDiscoveryNative();
+        mNativeInterface.cancelDiscovery();
 
         Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND);
         msg.obj = device;
@@ -5826,7 +5822,7 @@
         mHandler.postDelayed(
                 () -> removeFromOobDataCallbackQueue(callback),
                 GENERATE_LOCAL_OOB_DATA_TIMEOUT.toMillis());
-        generateLocalOobDataNative(transport);
+        mNativeInterface.generateLocalOobData(transport);
     }
 
     private synchronized void removeFromOobDataCallbackQueue(IBluetoothOobDataCallback callback) {
@@ -5910,7 +5906,7 @@
     }
 
     int getConnectionState(BluetoothDevice device) {
-        return getConnectionStateNative(getBytesFromAddress(device.getAddress()));
+        return mNativeInterface.getConnectionState(getBytesFromAddress(device.getAddress()));
     }
 
     int getConnectionHandle(BluetoothDevice device, int transport) {
@@ -6723,14 +6719,24 @@
         return mAdapterProperties.isA2dpOffloadEnabled();
     }
 
-    @VisibleForTesting
-    void registerCallback(IBluetoothCallback callback) {
-        mCallbacks.register(callback);
+    /** Register a bluetooth state callback */
+    public void registerBluetoothStateCallback(Executor executor, BluetoothStateCallback callback) {
+        mLocalCallbacks.put(callback, executor);
+    }
+
+    /** Unregister a bluetooth state callback */
+    public void unregisterBluetoothStateCallback(BluetoothStateCallback callback) {
+        mLocalCallbacks.remove(callback);
     }
 
     @VisibleForTesting
-    void unregisterCallback(IBluetoothCallback callback) {
-        mCallbacks.unregister(callback);
+    void registerRemoteCallback(IBluetoothCallback callback) {
+        mRemoteCallbacks.register(callback);
+    }
+
+    @VisibleForTesting
+    void unregisterRemoteCallback(IBluetoothCallback callback) {
+        mRemoteCallbacks.unregister(callback);
     }
 
     @VisibleForTesting
@@ -6757,7 +6763,7 @@
             mBtCompanionManager.factoryReset();
         }
 
-        return factoryResetNative();
+        return mNativeInterface.factoryReset();
     }
 
     @VisibleForTesting
@@ -6777,7 +6783,7 @@
         }
 
         // Pull the data. The callback will notify mEnergyInfoLock.
-        readEnergyInfo();
+        mNativeInterface.readEnergyInfo();
 
         synchronized (mEnergyInfoLock) {
             long now = System.currentTimeMillis();
@@ -6854,17 +6860,6 @@
         }
     }
 
-    /**
-     * Notify the UID and package name of the app, and the address of associated active device
-     *
-     * @param source The attribution source that starts the activity
-     * @param deviceAddress The address of the active device associated with the app
-     */
-    public void notifyActivityAttributionInfo(AttributionSource source, String deviceAddress) {
-        mActivityAttributionService.notifyActivityAttributionInfo(
-                source.getUid(), source.getPackageName(), deviceAddress);
-    }
-
     static int convertScanModeToHal(int mode) {
         switch (mode) {
             case BluetoothAdapter.SCAN_MODE_NONE:
@@ -6891,40 +6886,12 @@
         return -1;
     }
 
-    // This function is called from JNI. It allows native code to set a single wake
-    // alarm. If an alarm is already pending and a new request comes in, the alarm
-    // will be rescheduled (i.e. the previously set alarm will be cancelled).
-    @RequiresPermission(android.Manifest.permission.SCHEDULE_EXACT_ALARM)
-    private boolean setWakeAlarm(long delayMillis, boolean shouldWake) {
-        synchronized (this) {
-            if (mPendingAlarm != null) {
-                mAlarmManager.cancel(mPendingAlarm);
-            }
-
-            long wakeupTime = SystemClock.elapsedRealtime() + delayMillis;
-            int type =
-                    shouldWake
-                            ? AlarmManager.ELAPSED_REALTIME_WAKEUP
-                            : AlarmManager.ELAPSED_REALTIME;
-
-            Intent intent = new Intent(ACTION_ALARM_WAKEUP);
-            mPendingAlarm =
-                    PendingIntent.getBroadcast(
-                            this,
-                            0,
-                            intent,
-                            PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
-            mAlarmManager.setExact(type, wakeupTime, mPendingAlarm);
-            return true;
-        }
-    }
-
     // This function is called from JNI. It allows native code to acquire a single wake lock.
     // If the wake lock is already held, this function returns success. Although this function
     // only supports acquiring a single wake lock at a time right now, it will eventually be
     // extended to allow acquiring an arbitrary number of wake locks. The current interface
     // takes |lockName| as a parameter in anticipation of that implementation.
-    private boolean acquireWakeLock(String lockName) {
+    boolean acquireWakeLock(String lockName) {
         synchronized (this) {
             if (mWakeLock == null) {
                 mWakeLockName = lockName;
@@ -6942,7 +6909,7 @@
     // by |acquireWakeLock|. If the wake lock is not held, this function returns failure.
     // Note that the release() call is also invoked by {@link #cleanup()} so a synchronization is
     // needed here. See the comment for |acquireWakeLock| for an explanation of the interface.
-    private boolean releaseWakeLock(String lockName) {
+    boolean releaseWakeLock(String lockName) {
         synchronized (this) {
             if (mWakeLock == null) {
                 errorLog("Repeated wake lock release; aborting release: " + lockName);
@@ -6956,15 +6923,14 @@
         return true;
     }
 
-    private void energyInfoCallback(
+    void energyInfoCallback(
             int status,
             int ctrlState,
             long txTime,
             long rxTime,
             long idleTime,
             long energyUsed,
-            UidTraffic[] data)
-            throws RemoteException {
+            UidTraffic[] data) {
         if (ctrlState >= BluetoothActivityEnergyInfo.BT_STACK_STATE_INVALID
                 && ctrlState <= BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_IDLE) {
             // Energy is product of mA, V and ms. If the chipset doesn't
@@ -7041,7 +7007,7 @@
 
         // pass just interesting metadata to native, to reduce spam
         if (key == BluetoothDevice.METADATA_LE_AUDIO) {
-            metadataChangedNative(Utils.getBytesFromAddress(address), key, value);
+            mNativeInterface.metadataChanged(Utils.getBytesFromAddress(address), key, value);
         }
 
         if (mMetadataListeners.containsKey(device)) {
@@ -7072,8 +7038,7 @@
         return BluetoothProperties.getHardwareOperatingVoltageMv().orElse(0) / 1000.0;
     }
 
-    @VisibleForTesting
-    protected RemoteDevices getRemoteDevices() {
+    public RemoteDevices getRemoteDevices() {
         return mRemoteDevices;
     }
 
@@ -7132,14 +7097,14 @@
             writer.println("Not dumping, since Bluetooth is turning off");
             writer.println();
         } else {
-            dumpNative(fd, args);
+            mNativeInterface.dump(fd, args);
         }
     }
 
     private void dumpMetrics(FileDescriptor fd) {
         BluetoothMetricsProto.BluetoothLog.Builder metricsBuilder =
                 BluetoothMetricsProto.BluetoothLog.newBuilder();
-        byte[] nativeMetricsBytes = dumpMetricsNative();
+        byte[] nativeMetricsBytes = mNativeInterface.dumpMetrics();
         debugLog("dumpMetrics: native metrics size is " + nativeMetricsBytes.length);
         if (nativeMetricsBytes.length > 0) {
             try {
@@ -7179,17 +7144,6 @@
         Log.e(TAG, msg);
     }
 
-    private final BroadcastReceiver mAlarmBroadcastReceiver =
-            new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    synchronized (AdapterService.this) {
-                        mPendingAlarm = null;
-                        alarmFiredNative();
-                    }
-                }
-            };
-
     private boolean isCommonCriteriaMode() {
         return getNonNullSystemService(DevicePolicyManager.class).isCommonCriteriaModeEnabled(null);
     }
@@ -7457,6 +7411,20 @@
         }
     }
 
+    /** A callback that will be called when AdapterState is changed */
+    public interface BluetoothStateCallback {
+        /**
+         * Called when the status of bluetooth adapter is changing. {@code prevState} and {@code
+         * newState} takes one of following values defined in BluetoothAdapter.java: STATE_OFF,
+         * STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, STATE_BLE_TURNING_ON, STATE_BLE_ON,
+         * STATE_BLE_TURNING_OFF
+         *
+         * @param prevState the previous Bluetooth state.
+         * @param newState the new Bluetooth state.
+         */
+        void onBluetoothStateChange(int prevState, int newState);
+    }
+
     /**
      * Obfuscate Bluetooth MAC address into a PII free ID string
      *
@@ -7468,7 +7436,7 @@
         if (device == null) {
             return new byte[0];
         }
-        return obfuscateAddressNative(Utils.getByteAddress(device));
+        return mNativeInterface.obfuscateAddress(Utils.getByteAddress(device));
     }
 
     /**
@@ -7513,7 +7481,7 @@
         if (device == null) {
             return 0;
         }
-        return getMetricIdNative(Utils.getByteAddress(device));
+        return mNativeInterface.getMetricId(Utils.getByteAddress(device));
     }
 
     public CompanionManager getCompanionManager() {
@@ -7602,7 +7570,7 @@
      * @return boolean true if audio low latency is successfully allowed or disallowed
      */
     public boolean allowLowLatencyAudio(boolean allowed, BluetoothDevice device) {
-        return allowLowLatencyAudioNative(allowed, Utils.getByteAddress(device));
+        return mNativeInterface.allowLowLatencyAudio(allowed, Utils.getByteAddress(device));
     }
 
     /**
@@ -7612,7 +7580,7 @@
      * @return int value other than 0 if remote PBAP PCE version is found
      */
     public int getRemotePbapPceVersion(String address) {
-        return getRemotePbapPceVersionNative(address);
+        return mNativeInterface.getRemotePbapPceVersion(address);
     }
 
     /**
@@ -7621,7 +7589,7 @@
      * @return true/false.
      */
     public boolean pbapPseDynamicVersionUpgradeIsEnabled() {
-        return pbapPseDynamicVersionUpgradeIsEnabledNative();
+        return mNativeInterface.pbapPseDynamicVersionUpgradeIsEnabled();
     }
 
     /** Sets the battery level of the remote device */
@@ -7634,31 +7602,31 @@
     }
 
     public boolean interopMatchAddr(InteropFeature feature, String address) {
-        return interopMatchAddrNative(feature.name(), address);
+        return mNativeInterface.interopMatchAddr(feature.name(), address);
     }
 
     public boolean interopMatchName(InteropFeature feature, String name) {
-        return interopMatchNameNative(feature.name(), name);
+        return mNativeInterface.interopMatchName(feature.name(), name);
     }
 
     public boolean interopMatchAddrOrName(InteropFeature feature, String address) {
-        return interopMatchAddrOrNameNative(feature.name(), address);
+        return mNativeInterface.interopMatchAddrOrName(feature.name(), address);
     }
 
     public void interopDatabaseAddAddr(InteropFeature feature, String address, int length) {
-        interopDatabaseAddRemoveAddrNative(true, feature.name(), address, length);
+        mNativeInterface.interopDatabaseAddRemoveAddr(true, feature.name(), address, length);
     }
 
     public void interopDatabaseRemoveAddr(InteropFeature feature, String address) {
-        interopDatabaseAddRemoveAddrNative(false, feature.name(), address, 0);
+        mNativeInterface.interopDatabaseAddRemoveAddr(false, feature.name(), address, 0);
     }
 
     public void interopDatabaseAddName(InteropFeature feature, String name) {
-        interopDatabaseAddRemoveNameNative(true, feature.name(), name);
+        mNativeInterface.interopDatabaseAddRemoveName(true, feature.name(), name);
     }
 
     public void interopDatabaseRemoveName(InteropFeature feature, String name) {
-        interopDatabaseAddRemoveNameNative(false, feature.name(), name);
+        mNativeInterface.interopDatabaseAddRemoveName(false, feature.name(), name);
     }
 
     private void loadLeAudioAllowDevices() {
@@ -7741,123 +7709,6 @@
         }
     }
 
-    static native void classInitNative();
-
-    native boolean initNative(
-            boolean startRestricted,
-            boolean isCommonCriteriaMode,
-            int configCompareResult,
-            String[] initFlags,
-            boolean isAtvDevice,
-            String userDataDirectory);
-
-    native void cleanupNative();
-
-    /*package*/
-    native boolean enableNative();
-
-    /*package*/
-    native boolean disableNative();
-
-    /*package*/
-    native boolean setAdapterPropertyNative(int type, byte[] val);
-
-    /*package*/
-    native boolean getAdapterPropertiesNative();
-
-    /*package*/
-    native boolean getAdapterPropertyNative(int type);
-
-    /*package*/
-    native boolean setAdapterPropertyNative(int type);
-
-    /*package*/
-    native boolean setDevicePropertyNative(byte[] address, int type, byte[] val);
-
-    /*package*/
-    native boolean getDevicePropertyNative(byte[] address, int type);
-
-    /** package */
-    public native boolean createBondNative(byte[] address, int addressType, int transport);
-
-    /*package*/
-    native boolean createBondOutOfBandNative(
-            byte[] address, int transport, OobData p192Data, OobData p256Data);
-
-    /*package*/
-    public native boolean removeBondNative(byte[] address);
-
-    /*package*/
-    native boolean cancelBondNative(byte[] address);
-
-    /*package*/
-    native void generateLocalOobDataNative(int transport);
-
-    /*package*/
-    native boolean sdpSearchNative(byte[] address, byte[] uuid);
-
-    /*package*/
-    native int getConnectionStateNative(byte[] address);
-
-    private native boolean startDiscoveryNative();
-
-    private native boolean cancelDiscoveryNative();
-
-    private native boolean pinReplyNative(byte[] address, boolean accept, int len, byte[] pin);
-
-    private native boolean sspReplyNative(byte[] address, int type, boolean accept, int passkey);
-
-    /*package*/
-    native boolean getRemoteServicesNative(byte[] address, int transport);
-
-    /*package*/
-    native boolean getRemoteMasInstancesNative(byte[] address);
-
-    private native int readEnergyInfo();
-
-    /*package*/
-    native boolean factoryResetNative();
-
-    private native void alarmFiredNative();
-
-    private native void dumpNative(FileDescriptor fd, String[] arguments);
-
-    private native byte[] dumpMetricsNative();
-
-    private native byte[] obfuscateAddressNative(byte[] address);
-
-    native boolean setBufferLengthMillisNative(int codec, int value);
-
-    private native int getMetricIdNative(byte[] address);
-
-    /*package*/ native int connectSocketNative(
-            byte[] address, int type, byte[] uuid, int port, int flag, int callingUid);
-
-    /*package*/ native int createSocketChannelNative(
-            int type, String serviceName, byte[] uuid, int port, int flag, int callingUid);
-
-    /*package*/ native void requestMaximumTxDataLengthNative(byte[] address);
-
-    private native boolean allowLowLatencyAudioNative(boolean allowed, byte[] address);
-
-    private native void metadataChangedNative(byte[] address, int key, byte[] value);
-
-    private native boolean interopMatchAddrNative(String featureName, String address);
-
-    private native boolean interopMatchNameNative(String featureName, String name);
-
-    private native boolean interopMatchAddrOrNameNative(String featureName, String address);
-
-    private native void interopDatabaseAddRemoveAddrNative(
-            boolean doAdd, String featureName, String address, int length);
-
-    private native void interopDatabaseAddRemoveNameNative(
-            boolean doAdd, String featureBame, String name);
-
-    private native int getRemotePbapPceVersionNative(String address);
-
-    private native boolean pbapPseDynamicVersionUpgradeIsEnabledNative();
-
     // Returns if this is a mock object. This is currently used in testing so that we may not call
     // System.exit() while finalizing the object. Otherwise GC of mock objects unfortunately ends up
     // calling finalize() which in turn calls System.exit() and the process crashes.
diff --git a/android/app/src/com/android/bluetooth/btservice/BluetoothQualityReportNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/BluetoothQualityReportNativeInterface.java
index 2eda99c..3fa40e4 100644
--- a/android/app/src/com/android/bluetooth/btservice/BluetoothQualityReportNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/btservice/BluetoothQualityReportNativeInterface.java
@@ -25,9 +25,10 @@
 
 import com.android.bluetooth.Utils;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
-final class BluetoothQualityReportNativeInterface {
-
+/** Native interface to BQR */
+public class BluetoothQualityReportNativeInterface {
     private static final String TAG = "BluetoothQualityReportNativeInterface";
 
     @GuardedBy("INSTANCE_LOCK")
@@ -35,10 +36,6 @@
 
     private static final Object INSTANCE_LOCK = new Object();
 
-    static {
-        classInitNative();
-    }
-
     private BluetoothQualityReportNativeInterface() {}
 
     /** Get singleton instance. */
@@ -51,6 +48,14 @@
         }
     }
 
+    /** Set singleton instance. */
+    @VisibleForTesting
+    static void setInstance(BluetoothQualityReportNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
     /**
      * Initializes the native interface.
      *
@@ -125,8 +130,6 @@
     }
 
     // Native methods that call into the JNI interface
-    private static native void classInitNative();
-
     private native void initNative();
 
     private native void cleanupNative();
diff --git a/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java
index 13a9f4c..ab7f58c 100644
--- a/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java
+++ b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java
@@ -51,16 +51,17 @@
         }
 
         return marshalFd(
-                mService.connectSocketNative(
-                        Utils.getBytesFromAddress(
-                                type == BluetoothSocket.TYPE_L2CAP_LE
-                                        ? device.getAddress()
-                                        : mService.getIdentityAddress(device.getAddress())),
-                        type,
-                        Utils.uuidToByteArray(uuid),
-                        port,
-                        flag,
-                        Binder.getCallingUid()));
+                mService.getNative()
+                        .connectSocket(
+                                Utils.getBytesFromAddress(
+                                        type == BluetoothSocket.TYPE_L2CAP_LE
+                                                ? device.getAddress()
+                                                : mService.getIdentityAddress(device.getAddress())),
+                                type,
+                                Utils.uuidToByteArray(uuid),
+                                port,
+                                flag,
+                                Binder.getCallingUid()));
     }
 
     @Override
@@ -73,14 +74,15 @@
             return null;
         }
 
-        return marshalFd(mService.createSocketChannelNative(
-            type,
-            serviceName,
-            Utils.uuidToByteArray(uuid),
-            port,
-            flag,
-            Binder.getCallingUid()));
-
+        return marshalFd(
+                mService.getNative()
+                        .createSocketChannel(
+                                type,
+                                serviceName,
+                                Utils.uuidToByteArray(uuid),
+                                port,
+                                flag,
+                                Binder.getCallingUid()));
     }
 
     @Override
@@ -91,7 +93,8 @@
             return;
         }
 
-        mService.requestMaximumTxDataLengthNative(Utils.getBytesFromAddress(device.getAddress()));
+        mService.getNative()
+                .requestMaximumTxDataLength(Utils.getBytesFromAddress(device.getAddress()));
     }
 
     private void enforceActiveUser() {
diff --git a/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java b/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java
index 2bd4abb..d565a9c 100644
--- a/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java
+++ b/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java
@@ -273,9 +273,10 @@
                     }
 
                     BluetoothClass btClass = dev.getBluetoothClass();
-                    int btDeviceClass = btClass.getDeviceClass();
-                    if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD || btDeviceClass
-                            == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) {
+                    int btDeviceClass = btClass == null ? 0 : btClass.getDeviceClass();
+                    if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD
+                            || btDeviceClass
+                                    == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) {
                         // Its a keyboard. Follow the HID spec recommendation of creating the
                         // passkey and displaying it to the user. If the keyboard doesn't follow
                         // the spec recommendation, check if the keyboard has a fixed PIN zero
@@ -320,7 +321,7 @@
     private boolean cancelBond(BluetoothDevice dev) {
         if (dev.getBondState() == BluetoothDevice.BOND_BONDING) {
             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
-            if (!mAdapterService.cancelBondNative(addr)) {
+            if (!mAdapterService.getNative().cancelBond(addr)) {
                 Log.e(TAG, "Unexpected error while cancelling bond:");
             } else {
                 return true;
@@ -333,7 +334,7 @@
         DeviceProperties devProp = mRemoteDevices.getDeviceProperties(dev);
         if (devProp != null && devProp.getBondState() == BluetoothDevice.BOND_BONDED) {
             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
-            if (!mAdapterService.removeBondNative(addr)) {
+            if (!mAdapterService.getNative().removeBond(addr)) {
                 Log.e(TAG, "Unexpected error while removing bond:");
             } else {
                 if (transition) {
@@ -352,7 +353,7 @@
     private boolean createBond(BluetoothDevice dev, int transport, OobData remoteP192Data,
             OobData remoteP256Data, boolean transition) {
         if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
-            infoLog("Bond address is:" + dev);
+            infoLog("Bond address is:" + dev + ", transport is: " + transport);
             byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
             int addrType = dev.getAddressType();
             boolean result;
@@ -363,15 +364,18 @@
                       BluetoothDevice.BOND_BONDING,
                       BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_START_PAIRING_OOB,
                       BluetoothProtoEnums.UNBOND_REASON_UNKNOWN, mAdapterService.getMetricId(dev));
-                result = mAdapterService.createBondOutOfBandNative(addr, transport,
-                    remoteP192Data, remoteP256Data);
+                result =
+                        mAdapterService
+                                .getNative()
+                                .createBondOutOfBand(
+                                        addr, transport, remoteP192Data, remoteP256Data);
             } else {
                 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED,
                       mAdapterService.obfuscateAddress(dev), transport, dev.getType(),
                       BluetoothDevice.BOND_BONDING,
                       BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_START_PAIRING,
                       BluetoothProtoEnums.UNBOND_REASON_UNKNOWN, mAdapterService.getMetricId(dev));
-                result = mAdapterService.createBondNative(addr, addrType, transport);
+                result = mAdapterService.getNative().createBond(addr, addrType, transport);
             }
             BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_DEVICE_NAME_REPORTED,
                     mAdapterService.getMetricId(dev), dev.getName());
diff --git a/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java b/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java
index f7108f4..de0e943 100644
--- a/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java
+++ b/android/app/src/com/android/bluetooth/btservice/JniCallbacks.java
@@ -17,8 +17,9 @@
 package com.android.bluetooth.btservice;
 
 import android.bluetooth.OobData;
+import android.bluetooth.UidTraffic;
 
-final class JniCallbacks {
+class JniCallbacks {
 
     private RemoteDevices mRemoteDevices;
     private AdapterProperties mAdapterProperties;
@@ -118,4 +119,23 @@
         mAdapterService.switchCodecCallback(is_low_latency_buffer_size);
     }
 
+    boolean acquireWakeLock(String lockName) {
+        return mAdapterService.acquireWakeLock(lockName);
+    }
+
+    boolean releaseWakeLock(String lockName) {
+        return mAdapterService.releaseWakeLock(lockName);
+    }
+
+    void energyInfoCallback(
+            int status,
+            int ctrlState,
+            long txTime,
+            long rxTime,
+            long idleTime,
+            long energyUsed,
+            UidTraffic[] data) {
+        mAdapterService.energyInfoCallback(
+                status, ctrlState, txTime, rxTime, idleTime, energyUsed, data);
+    }
 }
diff --git a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
index b21ffee..7f92b5c 100644
--- a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
+++ b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java
@@ -750,6 +750,13 @@
             warnLog("processConnectOtherProfiles, adapter is not ON " + mAdapterService.getState());
             return;
         }
+
+        /* Make sure that device is still connected before connecting other profiles */
+        if (mAdapterService.getConnectionState(device) != BluetoothAdapter.STATE_CONNECTED) {
+            debugLog("processConnectOtherProfiles: device is not connected anymore " + device);
+            return;
+        }
+
         if (handleAllProfilesDisconnected(device)) {
             debugLog("processConnectOtherProfiles: all profiles disconnected for " + device);
             return;
diff --git a/android/app/src/com/android/bluetooth/btservice/ProfileService.java b/android/app/src/com/android/bluetooth/btservice/ProfileService.java
index acd72c7..1b0fdae 100644
--- a/android/app/src/com/android/bluetooth/btservice/ProfileService.java
+++ b/android/app/src/com/android/bluetooth/btservice/ProfileService.java
@@ -31,6 +31,7 @@
 import android.util.Log;
 
 import com.android.bluetooth.BluetoothMetricsProto;
+import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * Base class for a background service that runs a Bluetooth profile
@@ -297,12 +298,14 @@
         super.onDestroy();
     }
 
+    /** start the profile and inform AdapterService */
     @RequiresPermission(
             anyOf = {
                 android.Manifest.permission.MANAGE_USERS,
                 android.Manifest.permission.INTERACT_ACROSS_USERS
             })
-    void doStart() {
+    @VisibleForTesting
+    public void doStart() {
         Log.v(mName, "doStart");
         if (mAdapter == null) {
             Log.w(mName, "Can't start profile service: device does not have BT");
@@ -328,7 +331,9 @@
         mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_ON);
     }
 
-    void doStop() {
+    /** stop the profile and inform AdapterService */
+    @VisibleForTesting
+    public void doStop() {
         Log.v(mName, "doStop");
         if (mAdapterService == null || mAdapterService.isStartedProfile(mName)) {
             Log.w(mName, "Unexpectedly do Stop, don't stop.");
diff --git a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
index 7c16d1c..c874b02 100644
--- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -26,16 +26,13 @@
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothHeadsetClient;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProtoEnums;
 import android.bluetooth.BluetoothSinkAudioPolicy;
 import android.bluetooth.IBluetoothConnectionCallback;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.MacAddress;
 import android.os.Handler;
 import android.os.Looper;
@@ -60,7 +57,8 @@
 import java.util.Set;
 import java.util.function.Predicate;
 
-final class RemoteDevices {
+/** Remote device manager. This class is currently mostly used for HF and AG remote devices. */
+public class RemoteDevices {
     private static final boolean DBG = false;
     private static final String TAG = "BluetoothRemoteDevices";
 
@@ -99,6 +97,8 @@
     private static final int HFP_BATTERY_CHARGE_INDICATOR_5 = 100;
 
     private final Handler mHandler;
+    private final Handler mMainHandler;
+
     private class RemoteDevicesHandler extends Handler {
 
         /**
@@ -130,33 +130,6 @@
         }
     }
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            switch (action) {
-                case BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED:
-                    onHfIndicatorValueChanged(intent);
-                    break;
-                case BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT:
-                    onVendorSpecificHeadsetEvent(intent);
-                    break;
-                case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
-                    onHeadsetConnectionStateChanged(intent);
-                    break;
-                case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED:
-                    onHeadsetClientConnectionStateChanged(intent);
-                    break;
-                case BluetoothHeadsetClient.ACTION_AG_EVENT:
-                    onAgIndicatorValueChanged(intent);
-                    break;
-                default:
-                    Log.w(TAG, "Unhandled intent: " + intent);
-                    break;
-            }
-        }
-    };
-
     /**
      * Predicate that tests if the given {@link BluetoothDevice} is well-known
      * to be used for physical location.
@@ -183,33 +156,17 @@
         mDualDevicesMap = new HashMap<String, String>();
         mDeviceQueue = new ArrayDeque<>();
         mHandler = new RemoteDevicesHandler(looper);
+        mMainHandler = new Handler(Looper.getMainLooper());
     }
 
-    /**
-     * Init should be called before using this RemoteDevices object
-     */
-    void init() {
-        IntentFilter filter = new IntentFilter();
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        filter.addAction(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
-        filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
-        filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
-                + BluetoothAssignedNumbers.PLANTRONICS);
-        filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
-                + BluetoothAssignedNumbers.APPLE);
-        filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-        filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
-        filter.addAction(BluetoothHeadsetClient.ACTION_AG_EVENT);
-        mAdapterService.registerReceiver(mReceiver, filter);
-    }
+    /** Init should be called before using this RemoteDevices object */
+    void init() {}
 
     /**
      * Clean up should be called when this object is no longer needed, must be called after init()
      */
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
     void cleanup() {
-        // Unregister receiver first, mAdapterService is never null
-        mAdapterService.unregisterReceiver(mReceiver);
         reset();
     }
 
@@ -547,8 +504,12 @@
         void setAlias(BluetoothDevice device, String mAlias) {
             synchronized (mObject) {
                 this.mAlias = mAlias;
-                mAdapterService.setDevicePropertyNative(mAddress,
-                        AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME, mAlias.getBytes());
+                mAdapterService
+                        .getNative()
+                        .setDeviceProperty(
+                                mAddress,
+                                AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME,
+                                mAlias.getBytes());
                 Intent intent = new Intent(BluetoothDevice.ACTION_ALIAS_CHANGED);
                 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
                 intent.putExtra(BluetoothDevice.EXTRA_NAME, mAlias);
@@ -1275,8 +1236,9 @@
         // Uses cached UUIDs if we are bonding. If not, we fetch the UUIDs with SDP.
         if (deviceProperties == null || !deviceProperties.isBonding()) {
             // SDP Invoked native code to spin up SDP cycle
-            mAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress()),
-                    transport);
+            mAdapterService
+                    .getNative()
+                    .getRemoteServices(Utils.getBytesFromAddress(device.getAddress()), transport);
             MetricsLogger.getInstance().cacheCount(
                     BluetoothProtoEnums.SDP_INVOKE_SDP_CYCLE, 1);
         }
@@ -1288,65 +1250,71 @@
         mHandler.sendMessage(message);
     }
 
-    /**
-     * Handles headset connection state change event
-     * @param intent must be {@link BluetoothHeadset#ACTION_CONNECTION_STATE_CHANGED} intent
-     */
+    /** Handles headset connection state change event */
+    public void handleHeadsetConnectionStateChanged(
+            BluetoothDevice device, int fromState, int toState) {
+        mMainHandler.post(() -> onHeadsetConnectionStateChanged(device, fromState, toState));
+    }
+
     @VisibleForTesting
-    void onHeadsetConnectionStateChanged(Intent intent) {
-        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+    void onHeadsetConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
         if (device == null) {
             Log.e(TAG, "onHeadsetConnectionStateChanged() remote device is null");
             return;
         }
-        if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED)
-                == BluetoothProfile.STATE_DISCONNECTED
-                && !hasBatteryService(device)) {
+        if (toState == BluetoothProfile.STATE_DISCONNECTED && !hasBatteryService(device)) {
             resetBatteryLevel(device, /*isBas=*/ false);
         }
     }
 
+    /** Handle indication events from Hands-free. */
+    public void handleHfIndicatorValueChanged(
+            BluetoothDevice device, int indicatorId, int indicatorValue) {
+        mMainHandler.post(() -> onHfIndicatorValueChanged(device, indicatorId, indicatorValue));
+    }
+
     @VisibleForTesting
-    void onHfIndicatorValueChanged(Intent intent) {
-        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+    void onHfIndicatorValueChanged(BluetoothDevice device, int indicatorId, int indicatorValue) {
         if (device == null) {
             Log.e(TAG, "onHfIndicatorValueChanged() remote device is null");
             return;
         }
-        int indicatorId = intent.getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1);
-        int indicatorValue = intent.getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -1);
         if (indicatorId == HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS) {
             updateBatteryLevel(device, indicatorValue, /*isBas=*/ false);
         }
     }
 
-    /**
-     * Handle {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
-     * @param intent must be {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
-     */
+    /** Handles Headset specific Bluetooth events */
+    public void handleVendorSpecificHeadsetEvent(
+            BluetoothDevice device, String cmd, int companyId, int cmdType, Object[] args) {
+        mMainHandler.post(
+                () -> onVendorSpecificHeadsetEvent(device, cmd, companyId, cmdType, args));
+    }
+
     @VisibleForTesting
-    void onVendorSpecificHeadsetEvent(Intent intent) {
-        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+    void onVendorSpecificHeadsetEvent(
+            BluetoothDevice device, String cmd, int companyId, int cmdType, Object[] args) {
         if (device == null) {
             Log.e(TAG, "onVendorSpecificHeadsetEvent() remote device is null");
             return;
         }
-        String cmd =
-                intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
+        if (companyId != BluetoothAssignedNumbers.PLANTRONICS
+                && companyId != BluetoothAssignedNumbers.APPLE) {
+            Log.i(
+                    TAG,
+                    "onVendorSpecificHeadsetEvent() filtered out non-PLANTRONICS and non-APPLE "
+                            + "vendor commands");
+            return;
+        }
         if (cmd == null) {
             Log.e(TAG, "onVendorSpecificHeadsetEvent() command is null");
             return;
         }
-        int cmdType =
-                intent.getIntExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE,
-                        -1);
         // Only process set command
         if (cmdType != BluetoothHeadset.AT_CMD_TYPE_SET) {
             debugLog("onVendorSpecificHeadsetEvent() only SET command is processed");
             return;
         }
-        Object[] args = (Object[]) intent.getExtras()
-                .get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
         if (args == null) {
             Log.e(TAG, "onVendorSpecificHeadsetEvent() arguments are null");
             return;
@@ -1471,37 +1439,36 @@
                 && batteryService.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED;
     }
 
-    /**
-     * Handles headset client connection state change event
-     * @param intent must be {@link BluetoothHeadsetClient#ACTION_CONNECTION_STATE_CHANGED} intent
-     */
+    /** Handles headset client connection state change event. */
+    public void handleHeadsetClientConnectionStateChanged(
+            BluetoothDevice device, int fromState, int toState) {
+        mMainHandler.post(() -> onHeadsetClientConnectionStateChanged(device, fromState, toState));
+    }
+
     @VisibleForTesting
-    void onHeadsetClientConnectionStateChanged(Intent intent) {
-        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+    void onHeadsetClientConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
         if (device == null) {
             Log.e(TAG, "onHeadsetClientConnectionStateChanged() remote device is null");
             return;
         }
-        if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED)
-                == BluetoothProfile.STATE_DISCONNECTED
-                && !hasBatteryService(device)) {
+        if (toState == BluetoothProfile.STATE_DISCONNECTED && !hasBatteryService(device)) {
             resetBatteryLevel(device, /*isBas=*/ false);
         }
     }
 
+    /** Handle battery level changes indication events from Audio Gateway. */
+    public void handleAgBatteryLevelChanged(BluetoothDevice device, int batteryLevel) {
+        mMainHandler.post(() -> onAgBatteryLevelChanged(device, batteryLevel));
+    }
+
     @VisibleForTesting
-    void onAgIndicatorValueChanged(Intent intent) {
-        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+    void onAgBatteryLevelChanged(BluetoothDevice device, int batteryLevel) {
         if (device == null) {
-            Log.e(TAG, "onAgIndicatorValueChanged() remote device is null");
+            Log.e(TAG, "onAgBatteryLevelChanged() remote device is null");
             return;
         }
-
-        if (intent.hasExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL)) {
-            int batteryLevel = intent.getIntExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, -1);
-            updateBatteryLevel(
-                    device, batteryChargeIndicatorToPercentge(batteryLevel), /*isBas=*/ false);
-        }
+        updateBatteryLevel(
+                device, batteryChargeIndicatorToPercentge(batteryLevel), /*isBas=*/ false);
     }
 
     private static void errorLog(String msg) {
diff --git a/android/app/src/com/android/bluetooth/btservice/SilenceDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/SilenceDeviceManager.java
index c75ad49..7c2f7aa 100644
--- a/android/app/src/com/android/bluetooth/btservice/SilenceDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/SilenceDeviceManager.java
@@ -19,14 +19,9 @@
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
 
 import android.annotation.RequiresPermission;
-import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothProfile;
-import android.content.BroadcastReceiver;
-import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -76,43 +71,52 @@
     private static final int MSG_SILENCE_DEVICE_STATE_CHANGED = 1;
     private static final int MSG_A2DP_CONNECTION_STATE_CHANGED = 10;
     private static final int MSG_HFP_CONNECTION_STATE_CHANGED = 11;
-    private static final int MSG_A2DP_ACTIVE_DEIVCE_CHANGED = 20;
+    private static final int MSG_A2DP_ACTIVE_DEVICE_CHANGED = 20;
     private static final int MSG_HFP_ACTIVE_DEVICE_CHANGED = 21;
     private static final int ENABLE_SILENCE = 0;
     private static final int DISABLE_SILENCE = 1;
 
-    // Broadcast receiver for all changes
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (action == null) {
-                Log.e(TAG, "Received intent with null action");
-                return;
-            }
-            switch (action) {
-                case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
-                    mHandler.obtainMessage(MSG_A2DP_CONNECTION_STATE_CHANGED,
-                                           intent).sendToTarget();
-                    break;
-                case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
-                    mHandler.obtainMessage(MSG_HFP_CONNECTION_STATE_CHANGED,
-                                           intent).sendToTarget();
-                    break;
-                case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
-                    mHandler.obtainMessage(MSG_A2DP_ACTIVE_DEIVCE_CHANGED,
-                                           intent).sendToTarget();
-                    break;
-                case BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED:
-                    mHandler.obtainMessage(MSG_HFP_ACTIVE_DEVICE_CHANGED,
-                        intent).sendToTarget();
-                    break;
-                default:
-                    Log.e(TAG, "Received unexpected intent, action=" + action);
-                    break;
-            }
-        }
-    };
+    /**
+     * Called when A2DP connection state changed by A2dpService
+     *
+     * @param device The device of which connection state was changed
+     * @param fromState The previous connection state of the device
+     * @param toState The new connection state of the device
+     */
+    public void a2dpConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+        mHandler.obtainMessage(MSG_A2DP_CONNECTION_STATE_CHANGED, fromState, toState, device)
+                .sendToTarget();
+    }
+
+    /**
+     * Called when A2DP active device changed by A2dpService
+     *
+     * @param device The device currently activated. {@code null} if no A2DP device activated
+     */
+    public void a2dpActiveDeviceChanged(BluetoothDevice device) {
+        mHandler.obtainMessage(MSG_A2DP_ACTIVE_DEVICE_CHANGED, device).sendToTarget();
+    }
+
+    /**
+     * Called when HFP connection state changed by HeadsetService
+     *
+     * @param device The device of which connection state was changed
+     * @param fromState The previous connection state of the device
+     * @param toState The new connection state of the device
+     */
+    public void hfpConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+        mHandler.obtainMessage(MSG_HFP_CONNECTION_STATE_CHANGED, fromState, toState, device)
+                .sendToTarget();
+    }
+
+    /**
+     * Called when HFP active device is changed by HeadsetService
+     *
+     * @param device The device currently activated. {@code null} if no HFP device activated
+     */
+    public void hfpActiveDeviceChanged(BluetoothDevice device) {
+        mHandler.obtainMessage(MSG_HFP_ACTIVE_DEVICE_CHANGED, device).sendToTarget();
+    }
 
     class SilenceDeviceManagerHandler extends Handler {
         SilenceDeviceManagerHandler(Looper looper) {
@@ -132,12 +136,10 @@
                 }
                 break;
 
-                case MSG_A2DP_CONNECTION_STATE_CHANGED: {
-                    Intent intent = (Intent) msg.obj;
-                    BluetoothDevice device =
-                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                    int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
-                    int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+                case MSG_A2DP_CONNECTION_STATE_CHANGED:
+                    BluetoothDevice device = (BluetoothDevice) msg.obj;
+                    int prevState = msg.arg1;
+                    int nextState = msg.arg2;
 
                     if (nextState == BluetoothProfile.STATE_CONNECTED) {
                         // enter connected state
@@ -153,62 +155,51 @@
                             mSilenceDevices.remove(device);
                         }
                     }
-                }
-                break;
+                    break;
 
-                case MSG_HFP_CONNECTION_STATE_CHANGED: {
-                    Intent intent = (Intent) msg.obj;
-                    BluetoothDevice device =
-                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                    int prevState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
-                    int nextState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+                case MSG_HFP_CONNECTION_STATE_CHANGED:
+                    BluetoothDevice bluetoothDevice = (BluetoothDevice) msg.obj;
+                    int prev = msg.arg1;
+                    int next = msg.arg2;
 
-                    if (nextState == BluetoothProfile.STATE_CONNECTED) {
+                    if (next == BluetoothProfile.STATE_CONNECTED) {
                         // enter connected state
-                        addConnectedDevice(device, BluetoothProfile.HEADSET);
-                        if (!mSilenceDevices.containsKey(device)) {
-                            mSilenceDevices.put(device, false);
+                        addConnectedDevice(bluetoothDevice, BluetoothProfile.HEADSET);
+                        if (!mSilenceDevices.containsKey(bluetoothDevice)) {
+                            mSilenceDevices.put(bluetoothDevice, false);
                         }
-                    } else if (prevState == BluetoothProfile.STATE_CONNECTED) {
+                    } else if (prev == BluetoothProfile.STATE_CONNECTED) {
                         // exiting from connected state
-                        removeConnectedDevice(device, BluetoothProfile.HEADSET);
-                        if (!isBluetoothAudioConnected(device)) {
-                            handleSilenceDeviceStateChanged(device, false);
-                            mSilenceDevices.remove(device);
+                        removeConnectedDevice(bluetoothDevice, BluetoothProfile.HEADSET);
+                        if (!isBluetoothAudioConnected(bluetoothDevice)) {
+                            handleSilenceDeviceStateChanged(bluetoothDevice, false);
+                            mSilenceDevices.remove(bluetoothDevice);
                         }
                     }
-                }
-                break;
+                    break;
 
-                case MSG_A2DP_ACTIVE_DEIVCE_CHANGED: {
-                    Intent intent = (Intent) msg.obj;
-                    BluetoothDevice a2dpActiveDevice =
-                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                case MSG_A2DP_ACTIVE_DEVICE_CHANGED:
+                    BluetoothDevice a2dpActiveDevice = (BluetoothDevice) msg.obj;
                     if (getSilenceMode(a2dpActiveDevice)) {
                         // Resume the device from silence mode.
                         setSilenceMode(a2dpActiveDevice, false);
                     }
-                }
-                break;
+                    break;
 
-                case MSG_HFP_ACTIVE_DEVICE_CHANGED: {
-                    Intent intent = (Intent) msg.obj;
-                    BluetoothDevice hfpActiveDevice =
-                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                case MSG_HFP_ACTIVE_DEVICE_CHANGED:
+                    BluetoothDevice hfpActiveDevice = (BluetoothDevice) msg.obj;
                     if (getSilenceMode(hfpActiveDevice)) {
                         // Resume the device from silence mode.
                         setSilenceMode(hfpActiveDevice, false);
                     }
-                }
-                break;
+                    break;
 
-                default: {
+                default:
                     Log.e(TAG, "Unknown message: " + msg.what);
-                }
-                break;
+                    break;
             }
         }
-    };
+    }
 
     SilenceDeviceManager(AdapterService service, ServiceFactory factory, Looper looper) {
         mAdapterService = service;
@@ -221,13 +212,6 @@
             Log.v(TAG, "start()");
         }
         mHandler = new SilenceDeviceManagerHandler(mLooper);
-        IntentFilter filter = new IntentFilter();
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
-        filter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
-        filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-        filter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
-        mAdapterService.registerReceiver(mReceiver, filter);
     }
 
     void cleanup() {
@@ -235,7 +219,6 @@
             Log.v(TAG, "cleanup()");
         }
         mSilenceDevices.clear();
-        mAdapterService.unregisterReceiver(mReceiver);
     }
 
     @VisibleForTesting
@@ -347,9 +330,4 @@
             writer.println("  " + device+ "  | " + getSilenceMode(device));
         }
     }
-
-    @VisibleForTesting
-    BroadcastReceiver getBroadcastReceiver() {
-        return mReceiver;
-    }
 }
diff --git a/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionNativeInterface.java
deleted file mode 100644
index 7755ce2..0000000
--- a/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionNativeInterface.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-/*
- * Defines the native interface that is used by state machine/service to
- * send or receive messages from the native stack. This file is registered
- * for the native methods in the corresponding JNI C++ file.
- */
-package com.android.bluetooth.btservice.activityattribution;
-
-import com.android.internal.annotations.GuardedBy;
-
-/** ActivityAttribution Native Interface to/from JNI. */
-public class ActivityAttributionNativeInterface {
-    @GuardedBy("INSTANCE_LOCK")
-    private static ActivityAttributionNativeInterface sInstance;
-
-    private static final Object INSTANCE_LOCK = new Object();
-
-    /** Get singleton instance. */
-    public static ActivityAttributionNativeInterface getInstance() {
-        synchronized (INSTANCE_LOCK) {
-            if (sInstance == null) {
-                sInstance = new ActivityAttributionNativeInterface();
-            }
-            return sInstance;
-        }
-    }
-
-    /** Notify the UID and package name of the app, and the address of associated active device */
-    public void notifyActivityAttributionInfo(int uid, String packageName, String deviceAddress) {
-        notifyActivityAttributionInfoNative(uid, packageName, deviceAddress);
-    }
-
-    private native void notifyActivityAttributionInfoNative(
-            int uid, String packageName, String deviceAddress);
-}
diff --git a/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionService.java b/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionService.java
deleted file mode 100644
index 5194a253..0000000
--- a/android/app/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionService.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.btservice.activityattribution;
-
-import static java.util.Objects.requireNonNull;
-
-import android.util.Log;
-
-/**
- * Service used for attributes wakeup, wakelock and Bluetooth traffic into per-app and per-device
- * based activities.
- */
-public class ActivityAttributionService {
-    private static final String TAG = ActivityAttributionService.class.getSimpleName();
-
-    private final ActivityAttributionNativeInterface mActivityAttributionNativeInterface =
-            requireNonNull(
-                    ActivityAttributionNativeInterface.getInstance(),
-                    "ActivityAttributionNativeInterface cannot be null");
-
-    /** Notify the UID and package name of the app, and the address of associated active device */
-    public void notifyActivityAttributionInfo(int uid, String packageName, String deviceAddress) {
-        Log.d(
-                TAG,
-                "notifyActivityAttributionInfo()"
-                        + (" UID=" + uid)
-                        + (" packageName=" + packageName)
-                        + (" deviceAddress=" + deviceAddress));
-        mActivityAttributionNativeInterface.notifyActivityAttributionInfo(
-                uid, packageName, deviceAddress);
-    }
-}
diff --git a/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreNativeInterface.java
index 0b616ac..d87fca8 100644
--- a/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/btservice/bluetoothKeystore/BluetoothKeystoreNativeInterface.java
@@ -18,6 +18,8 @@
 
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.IOException;
 import java.security.NoSuchAlgorithmException;
@@ -26,10 +28,31 @@
 public class BluetoothKeystoreNativeInterface {
     private static final String TAG = BluetoothKeystoreNativeInterface.class.getSimpleName();
 
-    private BluetoothKeystoreService mBluetoothKeystoreService = null;
+    private BluetoothKeystoreService mBluetoothKeystoreService;
 
-    static {
-        classInitNative();
+    @GuardedBy("INSTANCE_LOCK")
+    private static BluetoothKeystoreNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
+    private BluetoothKeystoreNativeInterface() {}
+
+    /** return static native instance */
+    public static BluetoothKeystoreNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new BluetoothKeystoreNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(BluetoothKeystoreNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
     }
 
     /**
@@ -88,7 +111,6 @@
     }
 
     // Native methods that call into the JNI interface
-    private static native void classInitNative();
     private native void initNative();
     private native void cleanupNative();
 }
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface.java
index 87f0c95..71ff678 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorNativeInterface.java
@@ -35,12 +35,10 @@
     private static final boolean DBG = false;
     private BluetoothAdapter mAdapter;
 
-    @GuardedBy("INSTANCE_LOCK") private static CsipSetCoordinatorNativeInterface sInstance;
-    private static final Object INSTANCE_LOCK = new Object();
+    @GuardedBy("INSTANCE_LOCK")
+    private static CsipSetCoordinatorNativeInterface sInstance;
 
-    static {
-        classInitNative();
-    }
+    private static final Object INSTANCE_LOCK = new Object();
 
     private CsipSetCoordinatorNativeInterface() {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -61,6 +59,14 @@
         }
     }
 
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(CsipSetCoordinatorNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
     /**
      * Initializes the native interface.
      *
@@ -216,7 +222,6 @@
     }
 
     // Native methods that call into the JNI interface
-    private static native void classInitNative();
     private native void initNative();
     private native void cleanupNative();
     private native boolean connectNative(byte[] address);
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
index 2809b08..97a582e 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
@@ -37,7 +37,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
 import android.sysprop.BluetoothProperties;
@@ -81,6 +83,8 @@
     private static final int MAX_CSIS_STATE_MACHINES = 10;
     private static CsipSetCoordinatorService sCsipSetCoordinatorService;
 
+    private Handler mHandler = null;
+
     private AdapterService mAdapterService;
     private LeAudioService mLeAudioService;
     private DatabaseManager mDatabaseManager;
@@ -107,7 +111,6 @@
             new ConcurrentHashMap<>();
 
     private BroadcastReceiver mBondStateChangedReceiver;
-    private BroadcastReceiver mConnectionStateChangedReceiver;
 
     public static boolean isEnabled() {
         return BluetoothProperties.isProfileCsipSetCoordinatorEnabled().orElse(false);
@@ -145,6 +148,9 @@
                 "CsipSetCoordinatorNativeInterface cannot be null when"
                 .concat("CsipSetCoordinatorService starts"));
 
+        // Setup Handler.
+        mHandler = new Handler(Looper.getMainLooper());
+
         // Get LE Audio service (can be null)
         mLeAudioService = mServiceFactory.getLeAudioService();
 
@@ -159,20 +165,12 @@
         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mBondStateChangedReceiver = new BondStateChangedReceiver();
         registerReceiver(mBondStateChangedReceiver, filter);
-        filter = new IntentFilter();
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        filter.addAction(BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED);
-        mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver();
-        registerReceiver(mConnectionStateChangedReceiver, filter);
 
         // Mark service as started
         setCsipSetCoordinatorService(this);
 
         // Initialize native interface
         mCsipSetCoordinatorNativeInterface.init();
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
-
         return true;
     }
 
@@ -186,8 +184,6 @@
             return true;
         }
 
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
         // Cleanup native interface
         mCsipSetCoordinatorNativeInterface.cleanup();
         mCsipSetCoordinatorNativeInterface = null;
@@ -198,8 +194,6 @@
         // Unregister broadcast receivers
         unregisterReceiver(mBondStateChangedReceiver);
         mBondStateChangedReceiver = null;
-        unregisterReceiver(mConnectionStateChangedReceiver);
-        mConnectionStateChangedReceiver = null;
 
         // Destroy state machines and stop handler thread
         synchronized (mStateMachines) {
@@ -274,7 +268,8 @@
      * @return true if connection is successful, false otherwise.
      */
     public boolean connect(BluetoothDevice device) {
-        enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
+        enforceCallingOrSelfPermission(
+                BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
         if (DBG) {
             Log.d(TAG, "connect(): " + device);
         }
@@ -309,7 +304,8 @@
      * @return true if disconnect is successful, false otherwise.
      */
     public boolean disconnect(BluetoothDevice device) {
-        enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
+        enforceCallingOrSelfPermission(
+                BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
         if (DBG) {
             Log.d(TAG, "disconnect(): " + device);
         }
@@ -480,7 +476,8 @@
      * @return true on success, otherwise false
      */
     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
-        enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
+        enforceCallingOrSelfPermission(
+                BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
         if (DBG) {
             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
         }
@@ -501,7 +498,8 @@
      * @return connection policy of the specified device
      */
     public int getConnectionPolicy(BluetoothDevice device) {
-        enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
+        enforceCallingOrSelfPermission(
+                BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
         return mDatabaseManager.getProfileConnectionPolicy(
                 device, BluetoothProfile.CSIP_SET_COORDINATOR);
     }
@@ -1018,8 +1016,17 @@
         }
     }
 
+    void handleConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+        mHandler.post(() -> connectionStateChanged(device, fromState, toState));
+    }
+
     @VisibleForTesting
     synchronized void connectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+        if (!isAvailable()) {
+            Log.w(TAG, "connectionStateChanged: service is not available");
+            return;
+        }
+
         if ((device == null) || (fromState == toState)) {
             Log.e(TAG,
                     "connectionStateChanged: unexpected invocation. device=" + device
@@ -1051,20 +1058,6 @@
         }
     }
 
-    private class ConnectionStateChangedReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED.equals(
-                        intent.getAction())) {
-                return;
-            }
-            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-            int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-            int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
-            connectionStateChanged(device, fromState, toState);
-        }
-    }
-
     /**
      * Binder object: must be a static class or memory leak may occur
      */
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java
index 062b004..5f2ed95 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java
@@ -516,6 +516,7 @@
     private void csipConnectionState(int newState, int prevState) {
         log("Connection state " + mDevice + ": " + profileStateToString(prevState) + "->"
                 + profileStateToString(newState));
+        mService.handleConnectionStateChanged(mDevice, prevState, newState);
 
         Intent intent =
                 new Intent(BluetoothCsipSetCoordinator.ACTION_CSIS_CONNECTION_STATE_CHANGED);
diff --git a/android/app/src/com/android/bluetooth/csip/OWNERS b/android/app/src/com/android/bluetooth/csip/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/csip/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
index a632ef9..5aefb21 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
@@ -23,6 +23,7 @@
 import android.bluetooth.le.AdvertisingSetParameters;
 import android.bluetooth.le.IAdvertisingSetCallback;
 import android.bluetooth.le.PeriodicAdvertisingParameters;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -122,15 +123,19 @@
 
     class AdvertisingSetDeathRecipient implements IBinder.DeathRecipient {
         public IAdvertisingSetCallback callback;
+        private String mPackageName;
 
-        AdvertisingSetDeathRecipient(IAdvertisingSetCallback callback) {
+        AdvertisingSetDeathRecipient(IAdvertisingSetCallback callback, String packageName) {
             this.callback = callback;
+            this.mPackageName = packageName;
         }
 
         @Override
         public void binderDied() {
             if (DBG) {
-                Log.d(TAG, "Binder is dead - unregistering advertising set");
+                Log.d(
+                        TAG,
+                        "Binder is dead - unregistering advertising set (" + mPackageName + ")!");
             }
             stopAdvertisingSet(callback);
         }
@@ -228,7 +233,16 @@
             return;
         }
 
-        AdvertisingSetDeathRecipient deathRecipient = new AdvertisingSetDeathRecipient(callback);
+        int appUid = Binder.getCallingUid();
+        String packageName = null;
+        if (mService != null && mService.getPackageManager() != null) {
+            packageName = mService.getPackageManager().getNameForUid(appUid);
+        }
+        if (packageName == null) {
+            packageName = "Unknown package name (UID: " + appUid + ")";
+        }
+        AdvertisingSetDeathRecipient deathRecipient =
+                new AdvertisingSetDeathRecipient(callback, packageName);
         IBinder binder = toBinder(callback);
         try {
             binder.linkToDeath(deathRecipient, 0);
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
index 7529bdb..22ae061 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java
@@ -19,12 +19,38 @@
 import android.bluetooth.le.AdvertisingSetParameters;
 import android.bluetooth.le.PeriodicAdvertisingParameters;
 
-import androidx.annotation.VisibleForTesting;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 /** Native interface for AdvertiseManager */
 @VisibleForTesting
 public class AdvertiseManagerNativeInterface {
-    AdvertiseManager mManager;
+    private static final String TAG = AdvertiseManagerNativeInterface.class.getSimpleName();
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static AdvertiseManagerNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
+    private AdvertiseManager mManager;
+
+    /** Get singleton instance. */
+    public static AdvertiseManagerNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new AdvertiseManagerNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(AdvertiseManagerNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
 
     void init(AdvertiseManager manager) {
         mManager = manager;
@@ -95,10 +121,6 @@
         setPeriodicAdvertisingEnableNative(advertiserId, enable);
     }
 
-    static {
-        classInitNative();
-    }
-
     void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status)
             throws Exception {
         mManager.onAdvertisingSetStarted(regId, advertiserId, txPower, status);
@@ -138,8 +160,6 @@
         mManager.onPeriodicAdvertisingEnabled(advertiserId, enable, status);
     }
 
-    private static native void classInitNative();
-
     private native void initializeNative();
 
     private native void cleanupNative();
diff --git a/android/app/src/com/android/bluetooth/gatt/AppScanStats.java b/android/app/src/com/android/bluetooth/gatt/AppScanStats.java
index 46617b1..ac0c02a 100644
--- a/android/app/src/com/android/bluetooth/gatt/AppScanStats.java
+++ b/android/app/src/com/android/bluetooth/gatt/AppScanStats.java
@@ -379,6 +379,18 @@
                 .cacheCount(BluetoothProtoEnums.LE_SCAN_ABUSE_COUNT_SCAN_TIMEOUT, 1);
     }
 
+    synchronized void recordHwFilterNotAvailableCountMetrics() {
+        MetricsLogger.getInstance()
+                .cacheCount(BluetoothProtoEnums.LE_SCAN_ABUSE_COUNT_HW_FILTER_NOT_AVAILABLE, 1);
+    }
+
+    synchronized void recordTrackingHwFilterNotAvailableCountMetrics() {
+        MetricsLogger.getInstance()
+                .cacheCount(
+                        BluetoothProtoEnums.LE_SCAN_ABUSE_COUNT_TRACKING_HW_FILTER_NOT_AVAILABLE,
+                        1);
+    }
+
     static void initScanRadioState() {
         synchronized (sLock) {
             sIsRadioStarted = false;
diff --git a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementNativeInterface.java
index 43a0e18..62e15c9 100644
--- a/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/gatt/DistanceMeasurementNativeInterface.java
@@ -18,17 +18,21 @@
 
 import android.bluetooth.BluetoothStatusCodes;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * Distance Measurement Native Interface to/from JNI.
- *
- * @hide
  */
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public class DistanceMeasurementNativeInterface {
-    private static DistanceMeasurementNativeInterface sInterface;
+    private static final String TAG = DistanceMeasurementNativeInterface.class.getSimpleName();
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static DistanceMeasurementNativeInterface sInstance;
+
     private static final Object INSTANCE_LOCK = new Object();
+
     private DistanceMeasurementManager mDistanceMeasurementManager;
 
     /**
@@ -54,11 +58,19 @@
      */
     public static DistanceMeasurementNativeInterface getInstance() {
         synchronized (INSTANCE_LOCK) {
-            if (sInterface == null) {
-                sInterface = new DistanceMeasurementNativeInterface();
+            if (sInstance == null) {
+                sInstance = new DistanceMeasurementNativeInterface();
             }
+            return sInstance;
         }
-        return sInterface;
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(DistanceMeasurementNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
     }
 
     void init(DistanceMeasurementManager manager) {
@@ -123,11 +135,9 @@
         }
     }
 
-    static {
-        classInitNative();
-    }
-
-    private static native void classInitNative();
+    /**********************************************************************************************/
+    /******************************************* native *******************************************/
+    /**********************************************************************************************/
 
     private native void initializeNative();
 
diff --git a/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java
index 58df03a..9c8fa53 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattNativeInterface.java
@@ -16,9 +16,11 @@
 
 package com.android.bluetooth.gatt;
 
-import android.bluetooth.BluetoothDevice;
 import android.os.RemoteException;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -28,15 +30,13 @@
 public class GattNativeInterface {
     private static final String TAG = GattNativeInterface.class.getSimpleName();
 
-    static {
-        classInitNative();
-    }
-
-    private static GattNativeInterface sInterface;
-    private static final Object INSTANCE_LOCK = new Object();
-
     private GattService mGattService;
 
+    @GuardedBy("INSTANCE_LOCK")
+    private static GattNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
     private GattNativeInterface() {}
 
     GattService getGattService() {
@@ -50,13 +50,20 @@
      */
     public static GattNativeInterface getInstance() {
         synchronized (INSTANCE_LOCK) {
-            if (sInterface == null) {
-                sInterface = new GattNativeInterface();
+            if (sInstance == null) {
+                sInstance = new GattNativeInterface();
             }
+            return sInstance;
         }
-        return sInterface;
     }
 
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(GattNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
 
     /* Callbacks */
 
@@ -304,9 +311,12 @@
         getGattService().onMtuChanged(connId, mtu);
     }
 
-    /* Native methods */
-    private static native void classInitNative();
+    /**********************************************************************************************/
+    /******************************************* native *******************************************/
+    /**********************************************************************************************/
+
     private native void initializeNative();
+
     private native void cleanupNative();
     private native int gattClientGetDeviceTypeNative(String address);
     private native void gattClientRegisterAppNative(long appUuidLsb, long appUuidMsb,
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index 0156835..01e5f91 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -16,12 +16,15 @@
 
 package com.android.bluetooth.gatt;
 
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+
 import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
 import static com.android.bluetooth.Utils.checkCallerTargetSdk;
 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
 
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.app.Service;
@@ -289,6 +292,7 @@
     private CompanionDeviceManager mCompanionManager;
     private String mExposureNotificationPackage;
     private Handler mTestModeHandler;
+    private ActivityManager mActivityManager;
     private final Object mTestModeLock = new Object();
 
     public GattService(Context ctx) {
@@ -317,8 +321,6 @@
         return false;
     };
 
-    private static GattService sGattService;
-
     /**
      * Reliable write queue
      */
@@ -350,7 +352,7 @@
         mAdvertiseManager =
                 new AdvertiseManager(
                         this,
-                        new AdvertiseManagerNativeInterface(),
+                        AdvertiseManagerNativeInterface.getInstance(),
                         mAdapterService,
                         mAdvertiserMap);
         mAdvertiseManager.start();
@@ -367,7 +369,8 @@
                 .createDistanceMeasurementManager(mAdapterService);
         mDistanceMeasurementManager.start();
 
-        setGattService(this);
+        mActivityManager = getSystemService(ActivityManager.class);
+
         return true;
     }
 
@@ -376,7 +379,6 @@
         if (DBG) {
             Log.d(TAG, "stop()");
         }
-        setGattService(null);
         mScannerMap.clear();
         mAdvertiserMap.clear();
         mClientMap.clear();
@@ -445,24 +447,6 @@
         }
     }
 
-    /**
-     * Get the current instance of {@link GattService}
-     *
-     * @return current instance of {@link GattService}
-     */
-    @VisibleForTesting
-    public static synchronized GattService getGattService() {
-        if (sGattService == null) {
-            Log.w(TAG, "getGattService(): service is null");
-            return null;
-        }
-        if (!sGattService.isAvailable()) {
-            Log.w(TAG, "getGattService(): service is not available");
-            return null;
-        }
-        return sGattService;
-    }
-
     @VisibleForTesting
     ScanManager getScanManager() {
         if (mScanManager == null) {
@@ -472,13 +456,6 @@
         return mScanManager;
     }
 
-    private static synchronized void setGattService(GattService instance) {
-        if (DBG) {
-            Log.d(TAG, "setGattService(): set to: " + instance);
-        }
-        sGattService = instance;
-    }
-
     // Suppressed because we are conditionally enforcing
     @SuppressLint("AndroidFrameworkRequiresPermission")
     private void permissionCheck(UUID characteristicUuid) {
@@ -527,15 +504,23 @@
 
     class ScannerDeathRecipient implements IBinder.DeathRecipient {
         int mScannerId;
+        private String mPackageName;
 
-        ScannerDeathRecipient(int scannerId) {
+        ScannerDeathRecipient(int scannerId, String packageName) {
             mScannerId = scannerId;
+            mPackageName = packageName;
         }
 
         @Override
         public void binderDied() {
             if (DBG) {
-                Log.d(TAG, "Binder is dead - unregistering scanner (" + mScannerId + ")!");
+                Log.d(
+                        TAG,
+                        "Binder is dead - unregistering scanner ("
+                                + mPackageName
+                                + " "
+                                + mScannerId
+                                + ")!");
             }
 
             ScanClient client = getScanClient(mScannerId);
@@ -562,15 +547,23 @@
 
     class ServerDeathRecipient implements IBinder.DeathRecipient {
         int mAppIf;
+        private String mPackageName;
 
-        ServerDeathRecipient(int appIf) {
+        ServerDeathRecipient(int appIf, String packageName) {
             mAppIf = appIf;
+            mPackageName = packageName;
         }
 
         @Override
         public void binderDied() {
             if (DBG) {
-                Log.d(TAG, "Binder is dead - unregistering server (" + mAppIf + ")!");
+                Log.d(
+                        TAG,
+                        "Binder is dead - unregistering server ("
+                                + mPackageName
+                                + " "
+                                + mAppIf
+                                + ")!");
             }
             unregisterServer(mAppIf, getAttributionSource());
         }
@@ -578,15 +571,23 @@
 
     class ClientDeathRecipient implements IBinder.DeathRecipient {
         int mAppIf;
+        private String mPackageName;
 
-        ClientDeathRecipient(int appIf) {
+        ClientDeathRecipient(int appIf, String packageName) {
             mAppIf = appIf;
+            mPackageName = packageName;
         }
 
         @Override
         public void binderDied() {
             if (DBG) {
-                Log.d(TAG, "Binder is dead - unregistering client (" + mAppIf + ")!");
+                Log.d(
+                        TAG,
+                        "Binder is dead - unregistering client ("
+                                + mPackageName
+                                + " "
+                                + mAppIf
+                                + ")!");
             }
             unregisterClient(mAppIf, getAttributionSource());
         }
@@ -2084,7 +2085,7 @@
                 // If app is callback based, setup a death recipient. App will initiate the start.
                 // Otherwise, if PendingIntent based, start the scan directly.
                 if (cbApp.callback != null) {
-                    cbApp.linkToDeath(new ScannerDeathRecipient(scannerId));
+                    cbApp.linkToDeath(new ScannerDeathRecipient(scannerId, cbApp.name));
                 } else {
                     continuePiStartScan(scannerId, cbApp);
                 }
@@ -2145,7 +2146,7 @@
         if (app != null) {
             if (status == 0) {
                 app.id = clientIf;
-                app.linkToDeath(new ClientDeathRecipient(clientIf));
+                app.linkToDeath(new ClientDeathRecipient(clientIf, app.name));
             } else {
                 mClientMap.remove(uuid);
             }
@@ -3231,8 +3232,6 @@
         }
 
         mScanManager.startScan(scanClient);
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
     }
 
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@@ -3363,8 +3362,6 @@
         }
 
         mScanManager.stopScan(scannerId);
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
     }
 
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
@@ -3635,11 +3632,34 @@
                     + opportunistic + ", phy=" + phy);
         }
         statsLogAppPackage(address, attributionSource.getUid(), clientIf);
+
+        boolean isForegroundService =
+                mActivityManager.getUidImportance(attributionSource.getUid())
+                        == IMPORTANCE_FOREGROUND_SERVICE;
+
         if (isDirect) {
-          MetricsLogger.getInstance().count(BluetoothProtoEnums.GATT_CLIENT_CONNECT_IS_DIRECT, 1);
+            MetricsLogger.getInstance().count(BluetoothProtoEnums.GATT_CLIENT_CONNECT_IS_DIRECT, 1);
+            MetricsLogger.getInstance()
+                    .count(
+                            isForegroundService
+                                    ? BluetoothProtoEnums
+                                            .GATT_CLIENT_CONNECT_IS_DIRECT_IN_FOREGROUND
+                                    : BluetoothProtoEnums
+                                            .GATT_CLIENT_CONNECT_IS_DIRECT_NOT_IN_FOREGROUND,
+                            1);
         } else {
-          MetricsLogger.getInstance().count(BluetoothProtoEnums.GATT_CLIENT_CONNECT_IS_AUTOCONNECT, 1);
+            MetricsLogger.getInstance()
+                    .count(BluetoothProtoEnums.GATT_CLIENT_CONNECT_IS_AUTOCONNECT, 1);
+            MetricsLogger.getInstance()
+                    .count(
+                            isForegroundService
+                                    ? BluetoothProtoEnums
+                                            .GATT_CLIENT_CONNECT_IS_AUTOCONNECT_IN_FOREGROUND
+                                    : BluetoothProtoEnums
+                                            .GATT_CLIENT_CONNECT_IS_AUTOCONNECT_NOT_IN_FOREGROUND,
+                            1);
         }
+
         statsLogGattConnectionStateChange(
                 BluetoothProfile.GATT, address, clientIf,
                 BluetoothProtoEnums.CONNECTION_STATE_CONNECTING, -1);
@@ -4202,7 +4222,7 @@
         ServerMap.App app = mServerMap.getByUuid(uuid);
         if (app != null) {
             app.id = serverIf;
-            app.linkToDeath(new ServerDeathRecipient(serverIf));
+            app.linkToDeath(new ServerDeathRecipient(serverIf, app.name));
             app.callback.onServerRegistered(status, serverIf);
         }
     }
diff --git a/android/app/src/com/android/bluetooth/gatt/PeriodicScanManager.java b/android/app/src/com/android/bluetooth/gatt/PeriodicScanManager.java
index 8d9320e..83659e3 100644
--- a/android/app/src/com/android/bluetooth/gatt/PeriodicScanManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/PeriodicScanManager.java
@@ -46,6 +46,7 @@
     private static final String TAG = GattServiceConfig.TAG_PREFIX + "SyncManager";
 
     private final BluetoothAdapter mAdapter;
+    private final PeriodicScanNativeInterface mNativeInterface;
     Map<IBinder, SyncInfo> mSyncs = new ConcurrentHashMap<>();
     Map<IBinder, SyncTransferInfo> mSyncTransfers = Collections.synchronizedMap(new HashMap<>());
     static int sTempRegistrationId = -1;
@@ -59,17 +60,18 @@
             Log.d(TAG, "advertise manager created");
         }
         mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mNativeInterface = PeriodicScanNativeInterface.getInstance();
     }
 
     void start() {
-        initializeNative();
+        mNativeInterface.init(this);
     }
 
     void cleanup() {
         if (DBG) {
             Log.d(TAG, "cleanup()");
         }
-        cleanupNative();
+        mNativeInterface.cleanup();
         mSyncs.clear();
         sTempRegistrationId = -1;
     }
@@ -180,15 +182,10 @@
 
     void onSyncStarted(int regId, int syncHandle, int sid, int addressType, String address, int phy,
             int interval, int status) throws Exception {
-        if (DBG) {
-            Log.d(TAG,
-                    "onSyncStarted() - regId=" + regId + ", syncHandle=" + syncHandle + ", status="
-                            + status);
-        }
         Map<IBinder, SyncInfo> syncMap = findAllSync(regId);
         if (syncMap.size() == 0) {
             Log.d(TAG, "onSyncStarted() - no callback found for regId " + regId);
-            stopSyncNative(syncHandle);
+            mNativeInterface.stopSync(syncHandle);
             return;
         }
 
@@ -220,10 +217,6 @@
 
     void onSyncReport(int syncHandle, int txPower, int rssi, int dataStatus, byte[] data)
             throws Exception {
-        if (DBG) {
-            Log.d(TAG, "onSyncReport() - syncHandle=" + syncHandle);
-        }
-
         Map<IBinder, SyncInfo> syncMap = findAllSync(syncHandle);
         if (syncMap.isEmpty()) {
             Log.i(TAG, "onSyncReport() - no callback found for syncHandle " + syncHandle);
@@ -239,9 +232,6 @@
     }
 
     void onSyncLost(int syncHandle) throws Exception {
-        if (DBG) {
-            Log.d(TAG, "onSyncLost() - syncHandle=" + syncHandle);
-        }
         Map<IBinder, SyncInfo> syncMap = findAllSync(syncHandle);
         if (syncMap.isEmpty()) {
             Log.i(TAG, "onSyncLost() - no callback found for syncHandle " + syncHandle);
@@ -258,12 +248,7 @@
         }
     }
 
-    void onBigInfoReport(int syncHandle, boolean encrypted)
-        throws Exception {
-        if (DBG) {
-            Log.d(TAG, "onBigInfoReport() - syncHandle=" + syncHandle +
-                    " , encrypted=" + encrypted);
-        }
+    void onBigInfoReport(int syncHandle, boolean encrypted) throws Exception {
         Map<IBinder, SyncInfo> syncMap = findAllSync(syncHandle);
         if (syncMap.isEmpty()) {
             Log.i(TAG, "onBigInfoReport() - no callback found for syncHandle " + syncHandle);
@@ -323,7 +308,7 @@
         if (DBG) {
             Log.d(TAG, "startSync() - reg_id=" + cbId + ", callback: " + binder);
         }
-        startSyncNative(sid, address, skip, timeout, cbId);
+        mNativeInterface.startSync(sid, address, skip, timeout, cbId);
     }
 
     void stopSync(IPeriodicAdvertisingCallback callback) {
@@ -354,14 +339,13 @@
         Log.d(TAG, "calling stopSyncNative: " + syncHandle.intValue());
         if (syncHandle < 0) {
             Log.i(TAG, "cancelSync() - sync not established yet");
-            cancelSyncNative(sync.advSid, sync.address);
+            mNativeInterface.cancelSync(sync.advSid, sync.address);
         } else {
-            stopSyncNative(syncHandle.intValue());
+            mNativeInterface.stopSync(syncHandle.intValue());
         }
     }
 
     void onSyncTransferredCallback(int paSource, int status, String bda) {
-        Log.d(TAG, "onSyncTransferredCallback()");
         Map.Entry<IBinder, SyncTransferInfo> entry = findSyncTransfer(bda);
         if (entry != null) {
             mSyncTransfers.remove(entry);
@@ -384,7 +368,7 @@
         //check for duplicate transfers
         mSyncTransfers.put(entry.getKey(), new SyncTransferInfo(bda.getAddress(),
                            entry.getValue().callback));
-        syncTransferNative(PA_SOURCE_REMOTE, bda.getAddress(), serviceData, syncHandle);
+        mNativeInterface.syncTransfer(bda, serviceData, syncHandle);
     }
 
     void transferSetInfo(BluetoothDevice bda, int serviceData,
@@ -400,28 +384,6 @@
             throw new IllegalArgumentException("Can't link to periodic scanner death");
         }
         mSyncTransfers.put(binder, new SyncTransferInfo(bda.getAddress(), callback));
-        transferSetInfoNative(PA_SOURCE_LOCAL, bda.getAddress(), serviceData, advHandle);
+        mNativeInterface.transferSetInfo(bda, serviceData, advHandle);
     }
-
-    static {
-        classInitNative();
-    }
-
-    private static native void classInitNative();
-
-    private native void initializeNative();
-
-    private native void cleanupNative();
-
-    private native void startSyncNative(int sid, String address, int skip, int timeout, int regId);
-
-    private native void stopSyncNative(int syncHandle);
-
-    private native void cancelSyncNative(int sid, String address);
-
-    private native void syncTransferNative(int paSource, String address, int serviceData,
-                                           int syncHandle);
-
-    private native void transferSetInfoNative(int paSource, String address, int serviceData,
-                                              int advHandle);
 }
diff --git a/android/app/src/com/android/bluetooth/gatt/PeriodicScanNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/PeriodicScanNativeInterface.java
new file mode 100644
index 0000000..d6d31f2
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/gatt/PeriodicScanNativeInterface.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.gatt;
+
+import android.bluetooth.BluetoothDevice;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/** NativeInterface for PeriodicScanManager */
+public class PeriodicScanNativeInterface {
+    private static final String TAG = PeriodicScanNativeInterface.class.getSimpleName();
+    private static final boolean DBG = GattServiceConfig.DBG;
+
+    private static final int PA_SOURCE_LOCAL = 1;
+    private static final int PA_SOURCE_REMOTE = 2;
+
+    private PeriodicScanManager mManager;
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static PeriodicScanNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
+    private PeriodicScanNativeInterface() {}
+
+    static PeriodicScanNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new PeriodicScanNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(PeriodicScanNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
+    void init(PeriodicScanManager manager) {
+        mManager = manager;
+        initializeNative();
+    }
+
+    void cleanup() {
+        cleanupNative();
+    }
+
+    void startSync(int sid, String address, int skip, int timeout, int regId) {
+        startSyncNative(sid, address, skip, timeout, regId);
+    }
+
+    void stopSync(int syncHandle) {
+        stopSyncNative(syncHandle);
+    }
+
+    void cancelSync(int sid, String address) {
+        cancelSyncNative(sid, address);
+    }
+
+    void syncTransfer(BluetoothDevice bda, int serviceData, int syncHandle) {
+        syncTransferNative(PA_SOURCE_REMOTE, bda.getAddress(), serviceData, syncHandle);
+    }
+
+    void transferSetInfo(BluetoothDevice bda, int serviceData, int advHandle) {
+        transferSetInfoNative(PA_SOURCE_LOCAL, bda.getAddress(), serviceData, advHandle);
+    }
+
+    /**********************************************************************************************/
+    /*********************************** callbacks from native ************************************/
+    /**********************************************************************************************/
+
+    void onSyncStarted(
+            int regId,
+            int syncHandle,
+            int sid,
+            int addressType,
+            String address,
+            int phy,
+            int interval,
+            int status)
+            throws Exception {
+        if (DBG) {
+            Log.d(
+                    TAG,
+                    "onSyncStarted(): "
+                            + (" regId=" + regId)
+                            + (" syncHandle=" + syncHandle)
+                            + (" status=" + status));
+        }
+        mManager.onSyncStarted(regId, syncHandle, sid, addressType, address, phy, interval, status);
+    }
+
+    void onSyncReport(int syncHandle, int txPower, int rssi, int dataStatus, byte[] data)
+            throws Exception {
+        if (DBG) {
+            Log.d(TAG, "onSyncReport(): syncHandle=" + syncHandle);
+        }
+        mManager.onSyncReport(syncHandle, txPower, rssi, dataStatus, data);
+    }
+
+    void onSyncLost(int syncHandle) throws Exception {
+        if (DBG) {
+            Log.d(TAG, "onSyncLost(): syncHandle=" + syncHandle);
+        }
+        mManager.onSyncLost(syncHandle);
+    }
+
+    void onSyncTransferredCallback(int paSource, int status, String bda) {
+        if (DBG) {
+            Log.d(TAG, "onSyncTransferredCallback()");
+        }
+        mManager.onSyncTransferredCallback(paSource, status, bda);
+    }
+
+    void onBigInfoReport(int syncHandle, boolean encrypted) throws Exception {
+        if (DBG) {
+            Log.d(
+                    TAG,
+                    "onBigInfoReport():"
+                            + (" syncHandle=" + syncHandle)
+                            + (" encrypted=" + encrypted));
+        }
+        mManager.onBigInfoReport(syncHandle, encrypted);
+    }
+
+    /**********************************************************************************************/
+    /******************************************* native *******************************************/
+    /**********************************************************************************************/
+
+    private native void initializeNative();
+
+    private native void cleanupNative();
+
+    private native void startSyncNative(int sid, String address, int skip, int timeout, int regId);
+
+    private native void stopSyncNative(int syncHandle);
+
+    private native void cancelSyncNative(int sid, String address);
+
+    private native void syncTransferNative(
+            int paSource, String address, int serviceData, int syncHandle);
+
+    private native void transferSetInfoNative(
+            int paSource, String address, int serviceData, int advHandle);
+}
diff --git a/android/app/src/com/android/bluetooth/gatt/ScanManager.java b/android/app/src/com/android/bluetooth/gatt/ScanManager.java
index e22c214..7e6716a 100644
--- a/android/app/src/com/android/bluetooth/gatt/ScanManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/ScanManager.java
@@ -143,6 +143,8 @@
             ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
     private static final boolean DEFAULT_UID_IS_FOREGROUND = true;
     private static final int SCAN_MODE_APP_IN_BACKGROUND = ScanSettings.SCAN_MODE_LOW_POWER;
+    private static final int SCAN_MODE_FORCE_DOWNGRADED = ScanSettings.SCAN_MODE_LOW_POWER;
+    private static final int SCAN_MODE_MAX_IN_CONCURRENCY = ScanSettings.SCAN_MODE_BALANCED;
     private final SparseBooleanArray mIsUidForegroundMap = new SparseBooleanArray();
     private boolean mScreenOn = false;
     private boolean mIsConnecting;
@@ -734,10 +736,10 @@
         }
 
         private boolean updateScanModeScreenOff(ScanClient client) {
-            if (mScanNative.isForceDowngradedScanClient(client)) {
+            if (mScanNative.isOpportunisticScanClient(client)) {
                 return false;
             }
-            if (!isAppForeground(client) && !mScanNative.isOpportunisticScanClient(client)) {
+            if (!isAppForeground(client) || mScanNative.isForceDowngradedScanClient(client)) {
                 return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF);
             }
 
@@ -834,22 +836,25 @@
         }
 
         private boolean updateScanModeScreenOn(ScanClient client) {
-            if (mScanNative.isForceDowngradedScanClient(client)) {
+            if (mScanNative.isOpportunisticScanClient(client)) {
                 return false;
             }
-
-            int newScanMode =  (isAppForeground(client)
-                    || mScanNative.isOpportunisticScanClient(client))
-                    ? client.scanModeApp : SCAN_MODE_APP_IN_BACKGROUND;
-            return client.updateScanMode(newScanMode);
+            int scanMode =
+                    isAppForeground(client) ? client.scanModeApp : SCAN_MODE_APP_IN_BACKGROUND;
+            int maxScanMode =
+                    mScanNative.isForceDowngradedScanClient(client)
+                            ? SCAN_MODE_FORCE_DOWNGRADED
+                            : scanMode;
+            return client.updateScanMode(getMinScanMode(scanMode, maxScanMode));
         }
 
         private boolean downgradeScanModeFromMaxDuty(ScanClient client) {
             if ((client.stats == null) || mAdapterService.getScanDowngradeDurationMillis() == 0) {
                 return false;
             }
-            if (ScanSettings.SCAN_MODE_LOW_LATENCY == client.settings.getScanMode()) {
-                client.updateScanMode(ScanSettings.SCAN_MODE_BALANCED);
+            int scanMode = client.settings.getScanMode();
+            int maxScanMode = SCAN_MODE_MAX_IN_CONCURRENCY;
+            if (client.updateScanMode(getMinScanMode(scanMode, maxScanMode))) {
                 client.stats.setScanDowngrade(client.scannerId, true);
                 if (DBG) {
                     Log.d(TAG, "downgradeScanModeFromMaxDuty() for " + client);
@@ -1367,10 +1372,14 @@
                     removeScanFilters(client.scannerId);
 
                 } else {
-                    Log.w(TAG,
+                    Log.w(
+                            TAG,
                             "Moving filtered scan client to downgraded scan (scannerId "
-                                    + client.scannerId + ")");
-                    client.updateScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
+                                    + client.scannerId
+                                    + ")");
+                    int scanMode = client.settings.getScanMode();
+                    int maxScanMode = SCAN_MODE_FORCE_DOWNGRADED;
+                    client.updateScanMode(getMinScanMode(scanMode, maxScanMode));
                 }
                 client.stats.setScanTimeout(client.scannerId);
                 client.stats.recordScanTimeoutCountMetrics();
@@ -1517,6 +1526,7 @@
                         if (!manageAllocationOfTrackingAdvertisement(trackEntries, true)) {
                             Log.e(TAG, "No hardware resources for onfound/onlost filter "
                                     + trackEntries);
+                            client.stats.recordTrackingHwFilterNotAvailableCountMetrics();
                             try {
                                 mService.onScanManagerErrorCallback(scannerId,
                                         ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
@@ -1614,7 +1624,11 @@
             if (client.filters == null || client.filters.isEmpty()) {
                 return true;
             }
-            return client.filters.size() > mFilterIndexStack.size();
+            if (client.filters.size() > mFilterIndexStack.size()) {
+                client.stats.recordHwFilterNotAvailableCountMetrics();
+                return true;
+            }
+            return false;
         }
 
         private void initFilterIndexStack() {
@@ -2022,19 +2036,23 @@
         }
 
         for (ScanClient client : mRegularScanClients) {
-            if (client.appUid != uid || mScanNative.isForceDowngradedScanClient(client)) {
+            if (client.appUid != uid || mScanNative.isOpportunisticScanClient(client)) {
                 continue;
             }
             if (isForeground) {
-                if (client.updateScanMode(client.scanModeApp)) {
+                int scanMode = client.scanModeApp;
+                int maxScanMode =
+                        mScanNative.isForceDowngradedScanClient(client)
+                                ? SCAN_MODE_FORCE_DOWNGRADED
+                                : scanMode;
+                if (client.updateScanMode(getMinScanMode(scanMode, maxScanMode))) {
                     updatedScanParams = true;
                 }
             } else {
-                // Skip scan mode update in any of following cases
-                //   1. screen is already off which triggers handleScreenOff()
-                //   2. opportunistics scan
-                if (mScreenOn && !mScanNative.isOpportunisticScanClient(client)
-                        && client.updateScanMode(SCAN_MODE_APP_IN_BACKGROUND)) {
+                int scanMode = client.settings.getScanMode();
+                int maxScanMode =
+                        mScreenOn ? SCAN_MODE_APP_IN_BACKGROUND : ScanSettings.SCAN_MODE_SCREEN_OFF;
+                if (client.updateScanMode(getMinScanMode(scanMode, maxScanMode))) {
                     updatedScanParams = true;
                 }
             }
@@ -2048,4 +2066,10 @@
             mScanNative.configureRegularScanParams();
         }
     }
+
+    private int getMinScanMode(int oldScanMode, int newScanMode) {
+        return mPriorityMap.get(oldScanMode) <= mPriorityMap.get(newScanMode)
+                ? oldScanMode
+                : newScanMode;
+    }
 }
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientNativeInterface.java b/android/app/src/com/android/bluetooth/hap/HapClientNativeInterface.java
index 5fab43a..263a807 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientNativeInterface.java
@@ -29,21 +29,17 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 
-/**
- * Hearing Access Profile Client Native Interface to/from JNI.
- */
+/** Hearing Access Profile Client Native Interface to/from JNI. */
 public class HapClientNativeInterface {
-    private static final String TAG = "HapClientNativeInterface";
-    private static final boolean DBG = true;
+    private static final String TAG = HapClientNativeInterface.class.getSimpleName();
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+
     private final BluetoothAdapter mAdapter;
 
     @GuardedBy("INSTANCE_LOCK")
     private static HapClientNativeInterface sInstance;
-    private static final Object INSTANCE_LOCK = new Object();
 
-    static {
-        classInitNative();
-    }
+    private static final Object INSTANCE_LOCK = new Object();
 
     private HapClientNativeInterface() {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -64,6 +60,14 @@
         }
     }
 
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(HapClientNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
     /**
      * Initiates HapClientService connection to a remote device.
      *
@@ -346,7 +350,7 @@
         event.valueList = new ArrayList<>(Arrays.asList(presets));
 
         if (DBG) {
-            Log.d(TAG, "onPresetInfo: " + event);
+            Log.d(TAG, "onGroupPresetInfo: " + event);
         }
         sendMessageToService(event);
     }
@@ -360,7 +364,7 @@
         event.valueInt2 = presetIndex;
 
         if (DBG) {
-            Log.d(TAG, "OnPresetNameSetError: " + event);
+            Log.d(TAG, "onPresetNameSetError: " + event);
         }
         sendMessageToService(event);
     }
@@ -374,7 +378,7 @@
         event.valueInt3 = groupId;
 
         if (DBG) {
-            Log.d(TAG, "OnPresetNameSetError: " + event);
+            Log.d(TAG, "onGroupPresetNameSetError: " + event);
         }
         sendMessageToService(event);
     }
@@ -402,13 +406,12 @@
         event.valueInt3 = groupId;
 
         if (DBG) {
-            Log.d(TAG, "onPresetInfoError: " + event);
+            Log.d(TAG, "onGroupPresetInfoError: " + event);
         }
         sendMessageToService(event);
     }
 
     // Native methods that call into the JNI interface
-    private static native void classInitNative();
     private native void initNative();
     private native void cleanupNative();
     private native boolean connectHapClientNative(byte[] address);
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
index 54d7920..eb5e617 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientService.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -447,7 +447,8 @@
         }
         ActiveDeviceManager adManager = mAdapterService.getActiveDeviceManager();
         if (adManager != null) {
-            adManager.hapConnectionStateChanged(device, fromState, toState);
+            adManager.profileConnectionStateChanged(
+                    BluetoothProfile.HAP_CLIENT, device, fromState, toState);
         }
     }
 
diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java
index a2aabd0..0ee8dfa 100644
--- a/android/app/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java
@@ -39,11 +39,8 @@
 
     @GuardedBy("INSTANCE_LOCK")
     private static HearingAidNativeInterface sInstance;
-    private static final Object INSTANCE_LOCK = new Object();
 
-    static {
-        classInitNative();
-    }
+    private static final Object INSTANCE_LOCK = new Object();
 
     private HearingAidNativeInterface() {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -64,6 +61,14 @@
         }
     }
 
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(HearingAidNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
     /**
      * Initializes the native interface.
      *
@@ -178,7 +183,6 @@
     }
 
     // Native methods that call into the JNI interface
-    private static native void classInitNative();
     private native void initNative();
     private native void cleanupNative();
     private native boolean connectHearingAidNative(byte[] address);
diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
index 7618baf..53010f5 100644
--- a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
+++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java
@@ -161,9 +161,6 @@
 
         // Initialize native interface
         mHearingAidNativeInterface.init();
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
-
         return true;
     }
 
@@ -176,9 +173,6 @@
             Log.w(TAG, "stop() called before start()");
             return true;
         }
-
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
         // Cleanup native interface
         mHearingAidNativeInterface.cleanup();
         mHearingAidNativeInterface = null;
@@ -730,7 +724,9 @@
     }
 
     private void notifyActiveDeviceChanged() {
-        mAdapterService.getActiveDeviceManager().hearingAidActiveStateChanged(mActiveDevice);
+        mAdapterService
+                .getActiveDeviceManager()
+                .profileActiveDeviceChanged(BluetoothProfile.HEARING_AID, mActiveDevice);
         Intent intent = new Intent(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mActiveDevice);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
@@ -964,7 +960,8 @@
         }
         mAdapterService
                 .getActiveDeviceManager()
-                .hearingAidConnectionStateChanged(device, fromState, toState);
+                .profileConnectionStateChanged(
+                        BluetoothProfile.HEARING_AID, device, fromState, toState);
     }
 
     /**
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
index d3eb601..4e60750 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java
@@ -22,6 +22,7 @@
 
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Objects;
@@ -36,12 +37,11 @@
 
     private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
 
-    static {
-        classInitNative();
-    }
+    @GuardedBy("INSTANCE_LOCK")
+    private static HeadsetNativeInterface sInstance;
 
-    private static HeadsetNativeInterface sInterface;
     private static final Object INSTANCE_LOCK = new Object();
+
     private AdapterService mAdapterService;
 
     private HeadsetNativeInterface() {
@@ -56,11 +56,19 @@
      */
     public static HeadsetNativeInterface getInstance() {
         synchronized (INSTANCE_LOCK) {
-            if (sInterface == null) {
-                sInterface = new HeadsetNativeInterface();
+            if (sInstance == null) {
+                sInstance = new HeadsetNativeInterface();
             }
+            return sInstance;
         }
-        return sInterface;
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(HeadsetNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
     }
 
     private void sendMessageToService(HeadsetStackEvent event) {
@@ -499,8 +507,6 @@
     }
 
     /* Native methods */
-    private static native void classInitNative();
-
     private native boolean atResponseCodeNative(int responseCode, int errorCode, byte[] address);
 
     private native boolean atResponseStringNative(String responseString, byte[] address);
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
index 2c75685..e4d46a5 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -24,7 +24,6 @@
 
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothProfile;
@@ -54,12 +53,14 @@
 import com.android.bluetooth.BluetoothStatsLog;
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.a2dp.A2dpService;
+import com.android.bluetooth.btservice.ActiveDeviceManager;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.MetricsLogger;
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.bluetooth.btservice.ServiceFactory;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.bluetooth.hfpclient.HeadsetClientService;
+import com.android.bluetooth.hfpclient.HeadsetClientStateMachine;
 import com.android.bluetooth.le_audio.LeAudioService;
 import com.android.bluetooth.telephony.BluetoothInCallService;
 import com.android.internal.annotations.VisibleForTesting;
@@ -125,6 +126,7 @@
     private int mMaxHeadsetConnections = 1;
     private BluetoothDevice mActiveDevice;
     private AdapterService mAdapterService;
+    private ActiveDeviceManager mActiveDeviceManager;
     private DatabaseManager mDatabaseManager;
     private HandlerThread mStateMachinesThread;
     private Handler mStateMachinesThreadHandler;
@@ -183,6 +185,7 @@
                 "AdapterService cannot be null when HeadsetService starts");
         mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(),
                 "DatabaseManager cannot be null when HeadsetService starts");
+        mActiveDeviceManager = Objects.requireNonNull(mAdapterService.getActiveDeviceManager());
         // Step 2: Start handler thread for state machines
         mStateMachinesThread = new HandlerThread("HeadsetService.StateMachines");
         mStateMachinesThread.start();
@@ -210,11 +213,6 @@
         registerReceiver(mHeadsetReceiver, filter);
         // Step 7: Mark service as started
         mStarted = true;
-        BluetoothDevice activeDevice = getActiveDevice();
-        String deviceAddress = activeDevice != null ?
-                activeDevice.getAddress() :
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS;
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(), deviceAddress);
         return true;
     }
 
@@ -228,11 +226,6 @@
             return true;
         }
         // Step 7: Mark service as stopped
-        BluetoothDevice activeDevice = getActiveDevice();
-        String deviceAddress = activeDevice != null ?
-                activeDevice.getAddress() :
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS;
-        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(), deviceAddress);
         mStarted = false;
         // Step 6: Tear down broadcast receivers
         unregisterReceiver(mHeadsetReceiver);
@@ -1346,12 +1339,12 @@
      * Remove the active device
      */
     private void removeActiveDevice() {
+        BluetoothDevice fallbackDevice = mActiveDeviceManager.getHfpFallbackDevice();
         synchronized (mStateMachines) {
             // As per b/202602952, if we remove the active device due to a disconnection,
             // we need to check if another device is connected and set it active instead.
             // Calling this before any other active related calls has the same effect as
             // a classic active device switch.
-            BluetoothDevice fallbackDevice = getFallbackDevice();
             if (fallbackDevice != null && mActiveDevice != null
                     && getConnectionState(mActiveDevice) != BluetoothProfile.STATE_CONNECTED) {
                 setActiveDevice(fallbackDevice);
@@ -2005,9 +1998,14 @@
                 setActiveDevice(null);
             }
         }
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.HEADSET, device, fromState, toState);
         mAdapterService
-                .getActiveDeviceManager()
+                .getSilenceDeviceManager()
                 .hfpConnectionStateChanged(device, fromState, toState);
+        mAdapterService
+                .getRemoteDevices()
+                .handleHeadsetConnectionStateChanged(device, fromState, toState);
     }
 
     /**
@@ -2133,10 +2131,18 @@
 
     private void broadcastActiveDevice(BluetoothDevice device) {
         logD("broadcastActiveDevice: " + device);
-        mAdapterService.getActiveDeviceManager().hfpActiveStateChanged(device);
-        BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED,
-                BluetoothProfile.HEADSET, mAdapterService.obfuscateAddress(device),
+
+        mAdapterService
+                .getActiveDeviceManager()
+                .profileActiveDeviceChanged(BluetoothProfile.HEADSET, device);
+        mAdapterService.getSilenceDeviceManager().hfpActiveDeviceChanged(device);
+
+        BluetoothStatsLog.write(
+                BluetoothStatsLog.BLUETOOTH_ACTIVE_DEVICE_CHANGED,
+                BluetoothProfile.HEADSET,
+                mAdapterService.obfuscateAddress(device),
                 mAdapterService.getMetricId(device));
+
         Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
@@ -2255,38 +2261,6 @@
                 == mStateMachinesThread.getId());
     }
 
-    /**
-     * Retrieves the most recently connected device in the A2DP connected devices list.
-     */
-    public BluetoothDevice getFallbackDevice() {
-        DatabaseManager dbManager = mAdapterService.getDatabase();
-        return dbManager != null ? dbManager
-            .getMostRecentlyConnectedDevicesInList(getFallbackCandidates(dbManager))
-            : null;
-    }
-
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    List<BluetoothDevice> getFallbackCandidates(DatabaseManager dbManager) {
-        List<BluetoothDevice> fallbackCandidates = getConnectedDevices();
-        List<BluetoothDevice> uninterestedCandidates = new ArrayList<>();
-        for (BluetoothDevice device : fallbackCandidates) {
-            byte[] deviceType = dbManager.getCustomMeta(device,
-                    BluetoothDevice.METADATA_DEVICE_TYPE);
-            BluetoothClass deviceClass = device.getBluetoothClass();
-            if ((deviceClass != null
-                    && deviceClass.getMajorDeviceClass()
-                    == BluetoothClass.Device.WEARABLE_WRIST_WATCH)
-                    || (deviceType != null
-                    && BluetoothDevice.DEVICE_TYPE_WATCH.equals(new String(deviceType)))) {
-                uninterestedCandidates.add(device);
-            }
-        }
-        for (BluetoothDevice device : uninterestedCandidates) {
-            fallbackCandidates.remove(device);
-        }
-        return fallbackCandidates;
-    }
-
     @Override
     public void dump(StringBuilder sb) {
         boolean isScoOn = mSystemInterface.getAudioManager().isBluetoothScoOn();
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index d2d003e..e625638 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -1604,6 +1604,11 @@
     void broadcastVendorSpecificEventIntent(String command, int companyId, int commandType,
             Object[] arguments, BluetoothDevice device) {
         log("broadcastVendorSpecificEventIntent(" + command + ")");
+        mAdapterService
+                .getRemoteDevices()
+                .handleVendorSpecificHeadsetEvent(
+                        device, command, companyId, commandType, arguments);
+
         Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
         intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType);
@@ -2233,11 +2238,11 @@
      * @param indValue Indicator Value [0-65535], -1 means invalid but indId is supported
      */
     private void sendIndicatorIntent(BluetoothDevice device, int indId, int indValue) {
+        mAdapterService.getRemoteDevices().handleHfIndicatorValueChanged(device, indId, indValue);
         Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indId);
         intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, indValue);
-
         Utils.sendBroadcast(mHeadsetService, intent, BLUETOOTH_CONNECT,
                 Utils.getTempAllowlistBroadcastOptions());
     }
diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
index 37b4f8b..e861e0b 100644
--- a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
+++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
@@ -143,9 +143,6 @@
             mSmThread.start();
 
             setHeadsetClientService(this);
-            AdapterService.getAdapterService().notifyActivityAttributionInfo(
-                    getAttributionSource(),
-                    AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
             return true;
         }
     }
@@ -160,9 +157,6 @@
                 }
 
                 // Stop the HfpClientConnectionService.
-                AdapterService.getAdapterService().notifyActivityAttributionInfo(
-                        getAttributionSource(),
-                        AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
                 Intent stopIntent = new Intent(this, HfpClientConnectionService.class);
                 sHeadsetClientService.stopService(stopIntent);
             }
@@ -1378,6 +1372,12 @@
         return false;
     }
 
+    void handleBatteryLevelChanged(BluetoothDevice device, int batteryLevel) {
+        AdapterService.getAdapterService()
+                .getRemoteDevices()
+                .handleAgBatteryLevelChanged(device, batteryLevel);
+    }
+
     @Override
     public void dump(StringBuilder sb) {
         super.dump(sb);
diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
index 76fddba..a29fc07 100644
--- a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
@@ -1593,6 +1593,7 @@
                             break;
                         case StackEvent.EVENT_TYPE_BATTERY_LEVEL:
                             mIndicatorBatteryLevel = event.valueInt;
+                            mService.handleBatteryLevelChanged(event.device, event.valueInt);
 
                             intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
                             intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL,
diff --git a/android/app/src/com/android/bluetooth/hfpclient/HfpClientConnectionService.java b/android/app/src/com/android/bluetooth/hfpclient/HfpClientConnectionService.java
index 899688b..3eed340 100644
--- a/android/app/src/com/android/bluetooth/hfpclient/HfpClientConnectionService.java
+++ b/android/app/src/com/android/bluetooth/hfpclient/HfpClientConnectionService.java
@@ -31,6 +31,9 @@
 import android.telecom.TelecomManager;
 import android.util.Log;
 
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.pbapclient.PbapClientService;
+
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -148,6 +151,16 @@
                 block.cleanup();
             }
         }
+        AdapterService adapterService = AdapterService.getAdapterService();
+        if (adapterService != null && adapterService.getRemoteDevices() != null) {
+            adapterService
+                    .getRemoteDevices()
+                    .handleHeadsetClientConnectionStateChanged(device, oldState, newState);
+        }
+        if (PbapClientService.getPbapClientService() != null) {
+            PbapClientService.getPbapClientService()
+                    .handleHeadsetClientConnectionStateChanged(device, oldState, newState);
+        }
     }
 
     private void onCallChangedInternal(BluetoothDevice device, HfpClientCall call) {
diff --git a/android/app/src/com/android/bluetooth/hfpclient/NativeInterface.java b/android/app/src/com/android/bluetooth/hfpclient/NativeInterface.java
index d38660f..4ef93e7 100644
--- a/android/app/src/com/android/bluetooth/hfpclient/NativeInterface.java
+++ b/android/app/src/com/android/bluetooth/hfpclient/NativeInterface.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Objects;
@@ -36,18 +37,18 @@
 public class NativeInterface {
     private static final String TAG = "NativeInterface";
     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+
     private AdapterService mAdapterService;
 
-    static {
-        classInitNative();
-    }
+    @GuardedBy("INSTANCE_LOCK")
+    private static NativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
 
     private NativeInterface() {
         mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
                 "AdapterService cannot be null when NativeInterface init");
     }
-    private static NativeInterface sInterface;
-    private static final Object INSTANCE_LOCK = new Object();
 
     /**
      * This class is a singleton because native library should only be loaded once
@@ -56,11 +57,19 @@
      */
     public static NativeInterface getInstance() {
         synchronized (INSTANCE_LOCK) {
-            if (sInterface == null) {
-                sInterface = new NativeInterface();
+            if (sInstance == null) {
+                sInstance = new NativeInterface();
             }
+            return sInstance;
         }
-        return sInterface;
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(NativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
     }
 
     // Native wrappers to help unit testing
@@ -282,8 +291,9 @@
         return sendAndroidAtNative(getByteAddress(device), cmd);
     }
 
-    // Native methods that call into the JNI interface
-    private static native void classInitNative();
+    /**********************************************************************************************/
+    /******************************************* native *******************************************/
+    /**********************************************************************************************/
 
     private native void initializeNative();
 
diff --git a/android/app/src/com/android/bluetooth/hid/HidDeviceNativeInterface.java b/android/app/src/com/android/bluetooth/hid/HidDeviceNativeInterface.java
index 8d938f4..5aa7d24 100644
--- a/android/app/src/com/android/bluetooth/hid/HidDeviceNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/hid/HidDeviceNativeInterface.java
@@ -42,11 +42,8 @@
 
     @GuardedBy("INSTANCE_LOCK")
     private static HidDeviceNativeInterface sInstance;
-    private static final Object INSTANCE_LOCK = new Object();
 
-    static {
-        classInitNative();
-    }
+    private static final Object INSTANCE_LOCK = new Object();
 
     @VisibleForTesting
     private HidDeviceNativeInterface() {
@@ -64,19 +61,18 @@
     public static HidDeviceNativeInterface getInstance() {
         synchronized (INSTANCE_LOCK) {
             if (sInstance == null) {
-                setInstance(new HidDeviceNativeInterface());
+                sInstance = new HidDeviceNativeInterface();
             }
             return sInstance;
         }
     }
 
-    /**
-     * Set the singleton instance.
-     *
-     * @param nativeInterface native interface
-     */
-    private static void setInstance(HidDeviceNativeInterface nativeInterface) {
-        sInstance = nativeInterface;
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(HidDeviceNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
     }
 
     /**
@@ -268,8 +264,6 @@
         return mAdapterService.getByteIdentityAddress(device);
     }
 
-    private static native void classInitNative();
-
     private native void initNative();
 
     private native void cleanupNative();
diff --git a/android/app/src/com/android/bluetooth/hid/HidDeviceService.java b/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
index 18b9ee3..ffc9f02 100644
--- a/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
+++ b/android/app/src/com/android/bluetooth/hid/HidDeviceService.java
@@ -778,9 +778,6 @@
         mActivityManager.addOnUidImportanceListener(mUidImportanceListener,
                 FOREGROUND_IMPORTANCE_CUTOFF);
         setHidDeviceService(this);
-        AdapterService.getAdapterService().notifyActivityAttributionInfo(
-                getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
         return true;
     }
 
@@ -801,9 +798,6 @@
             mNativeAvailable = false;
         }
         mActivityManager.removeOnUidImportanceListener(mUidImportanceListener);
-        AdapterService.getAdapterService().notifyActivityAttributionInfo(
-                getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
         return true;
     }
 
diff --git a/android/app/src/com/android/bluetooth/hid/HidHostNativeInterface.java b/android/app/src/com/android/bluetooth/hid/HidHostNativeInterface.java
new file mode 100644
index 0000000..3a71702
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/hid/HidHostNativeInterface.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.hid;
+
+import android.bluetooth.BluetoothProfile;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/** Provides Bluetooth Hid Host profile, as a service in the Bluetooth application. */
+public class HidHostNativeInterface {
+    private static final String TAG = HidHostNativeInterface.class.getSimpleName();
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private HidHostService mHidHostService;
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static HidHostNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
+    static HidHostNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new HidHostNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(HidHostNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
+    void init(HidHostService service) {
+        mHidHostService = service;
+        initializeNative();
+    }
+
+    void cleanup() {
+        cleanupNative();
+    }
+
+    boolean connectHid(byte[] address) {
+        return connectHidNative(address);
+    }
+
+    boolean disconnectHid(byte[] address) {
+        return disconnectHidNative(address);
+    }
+
+    boolean getProtocolMode(byte[] address) {
+        return getProtocolModeNative(address);
+    }
+
+    boolean virtualUnPlug(byte[] address) {
+        return virtualUnPlugNative(address);
+    }
+
+    boolean setProtocolMode(byte[] address, byte protocolMode) {
+        return setProtocolModeNative(address, protocolMode);
+    }
+
+    boolean getReport(byte[] address, byte reportType, byte reportId, int bufferSize) {
+        return getReportNative(address, reportType, reportId, bufferSize);
+    }
+
+    boolean setReport(byte[] address, byte reportType, String report) {
+        return setReportNative(address, reportType, report);
+    }
+
+    boolean sendData(byte[] address, String report) {
+        return sendDataNative(address, report);
+    }
+
+    boolean setIdleTime(byte[] address, byte idleTime) {
+        return setIdleTimeNative(address, idleTime);
+    }
+
+    boolean getIdleTime(byte[] address) {
+        return getIdleTimeNative(address);
+    }
+
+    private static int convertHalState(int halState) {
+        switch (halState) {
+            case CONN_STATE_CONNECTED:
+                return BluetoothProfile.STATE_CONNECTED;
+            case CONN_STATE_CONNECTING:
+                return BluetoothProfile.STATE_CONNECTING;
+            case CONN_STATE_DISCONNECTED:
+                return BluetoothProfile.STATE_DISCONNECTED;
+            case CONN_STATE_DISCONNECTING:
+                return BluetoothProfile.STATE_DISCONNECTING;
+            default:
+                Log.e(TAG, "bad hid connection state: " + halState);
+                return BluetoothProfile.STATE_DISCONNECTED;
+        }
+    }
+
+    /**********************************************************************************************/
+    /*********************************** callbacks from native ************************************/
+    /**********************************************************************************************/
+
+    private void onConnectStateChanged(byte[] address, int state) {
+        if (DBG) Log.d(TAG, "onConnectStateChanged: state=" + state);
+        mHidHostService.onConnectStateChanged(address, convertHalState(state));
+    }
+
+    private void onGetProtocolMode(byte[] address, int mode) {
+        if (DBG) Log.d(TAG, "onGetProtocolMode()");
+        mHidHostService.onGetProtocolMode(address, mode);
+    }
+
+    private void onGetReport(byte[] address, byte[] report, int rptSize) {
+        if (DBG) Log.d(TAG, "onGetReport()");
+        mHidHostService.onGetReport(address, report, rptSize);
+    }
+
+    private void onHandshake(byte[] address, int status) {
+        if (DBG) Log.d(TAG, "onHandshake: status=" + status);
+        mHidHostService.onHandshake(address, status);
+    }
+
+    private void onVirtualUnplug(byte[] address, int status) {
+        if (DBG) Log.d(TAG, "onVirtualUnplug: status=" + status);
+        mHidHostService.onVirtualUnplug(address, status);
+    }
+
+    private void onGetIdleTime(byte[] address, int idleTime) {
+        if (DBG) Log.d(TAG, "onGetIdleTime()");
+        mHidHostService.onGetIdleTime(address, idleTime);
+    }
+
+    /**********************************************************************************************/
+    /******************************************* native *******************************************/
+    /**********************************************************************************************/
+
+    // Constants matching Hal header file bt_hh.h
+    // bthh_connection_state_t
+    private static final int CONN_STATE_CONNECTED = 0;
+
+    private static final int CONN_STATE_CONNECTING = 1;
+    private static final int CONN_STATE_DISCONNECTED = 2;
+    private static final int CONN_STATE_DISCONNECTING = 3;
+
+    private native void initializeNative();
+
+    private native void cleanupNative();
+
+    private native boolean connectHidNative(byte[] btAddress);
+
+    private native boolean disconnectHidNative(byte[] btAddress);
+
+    private native boolean getProtocolModeNative(byte[] btAddress);
+
+    private native boolean virtualUnPlugNative(byte[] btAddress);
+
+    private native boolean setProtocolModeNative(byte[] btAddress, byte protocolMode);
+
+    private native boolean getReportNative(
+            byte[] btAddress, byte reportType, byte reportId, int bufferSize);
+
+    private native boolean setReportNative(byte[] btAddress, byte reportType, String report);
+
+    private native boolean sendDataNative(byte[] btAddress, String report);
+
+    private native boolean setIdleTimeNative(byte[] btAddress, byte idleTime);
+
+    private native boolean getIdleTimeNative(byte[] btAddress);
+}
diff --git a/android/app/src/com/android/bluetooth/hid/HidHostService.java b/android/app/src/com/android/bluetooth/hid/HidHostService.java
index dfd7dd2..f98bf46 100644
--- a/android/app/src/com/android/bluetooth/hid/HidHostService.java
+++ b/android/app/src/com/android/bluetooth/hid/HidHostService.java
@@ -20,6 +20,8 @@
 
 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.RequiresPermission;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHidHost;
@@ -50,7 +52,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 /**
  * Provides Bluetooth Hid Host profile, as a service in
@@ -68,6 +69,7 @@
 
     private DatabaseManager mDatabaseManager;
     private AdapterService mAdapterService;
+    private final HidHostNativeInterface mNativeInterface;
 
     private static final int MESSAGE_CONNECT = 1;
     private static final int MESSAGE_DISCONNECT = 2;
@@ -85,8 +87,8 @@
     private static final int MESSAGE_ON_GET_IDLE_TIME = 15;
     private static final int MESSAGE_SET_IDLE_TIME = 16;
 
-    static {
-        classInitNative();
+    HidHostService() {
+        mNativeInterface = requireNonNull(HidHostNativeInterface.getInstance());
     }
 
     public static boolean isEnabled() {
@@ -100,13 +102,17 @@
 
     @Override
     protected boolean start() {
-        mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(),
-                "DatabaseManager cannot be null when HidHostService starts");
-        mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(),
-                "AdapterService cannot be null when HidHostService starts");
+        mDatabaseManager =
+                requireNonNull(
+                        AdapterService.getAdapterService().getDatabase(),
+                        "DatabaseManager cannot be null when HidHostService starts");
+        mAdapterService =
+                requireNonNull(
+                        AdapterService.getAdapterService(),
+                        "AdapterService cannot be null when HidHostService starts");
 
         mInputDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
-        initializeNative();
+        mNativeInterface.init(this);
         mNativeAvailable = true;
         setHidHostService(this);
         return true;
@@ -124,7 +130,7 @@
     protected void cleanup() {
         if (DBG) Log.d(TAG, "Stopping Bluetooth HidHostService");
         if (mNativeAvailable) {
-            cleanupNative();
+            mNativeInterface.cleanup();
             mNativeAvailable = false;
         }
 
@@ -179,149 +185,164 @@
 
             switch (msg.what) {
                 case MESSAGE_CONNECT: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    if (!connectHidNative(getByteAddress(device))) {
-                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
-                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
-                        break;
-                    }
-                    mTargetDevice = device;
-                }
-                break;
-                case MESSAGE_DISCONNECT: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    if (!disconnectHidNative(getByteAddress(device))) {
-                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
-                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
-                        break;
-                    }
-                }
-                break;
-                case MESSAGE_CONNECT_STATE_CHANGED: {
-                    BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
-                    int halState = msg.arg1;
-                    Integer prevStateInteger = mInputDevices.get(device);
-                    int prevState =
-                            (prevStateInteger == null) ? BluetoothHidHost.STATE_DISCONNECTED
-                                    : prevStateInteger;
-                    if (DBG) {
-                        Log.d(TAG, "MESSAGE_CONNECT_STATE_CHANGED newState:" + convertHalState(
-                                halState) + ", prevState:" + prevState);
-                    }
-                    if (halState == CONN_STATE_CONNECTED
-                            && prevState == BluetoothHidHost.STATE_DISCONNECTED
-                            && (!okToConnect(device))) {
-                        if (DBG) {
-                            Log.d(TAG, "Incoming HID connection rejected");
+                        BluetoothDevice device = (BluetoothDevice) msg.obj;
+                        if (!mNativeInterface.connectHid(getByteAddress(device))) {
+                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
+                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
+                            break;
                         }
-                        virtualUnPlugNative(getByteAddress(device));
-                    } else {
-                        broadcastConnectionState(device, convertHalState(halState));
+                        mTargetDevice = device;
                     }
-                    if (halState == CONN_STATE_CONNECTED && (mTargetDevice != null
-                            && mTargetDevice.equals(device))) {
-                        mTargetDevice = null;
-                        // local device originated connection to hid device, move out
-                        // of quiet mode
-                        AdapterService adapterService = AdapterService.getAdapterService();
-                        adapterService.enable(false);
+                    break;
+                case MESSAGE_DISCONNECT: {
+                        BluetoothDevice device = (BluetoothDevice) msg.obj;
+                        if (!mNativeInterface.disconnectHid(getByteAddress(device))) {
+                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
+                            broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
+                            break;
+                        }
                     }
-                }
-                break;
+                    break;
+                case MESSAGE_CONNECT_STATE_CHANGED: {
+                        BluetoothDevice device =
+                                mAdapterService.getDeviceFromByte((byte[]) msg.obj);
+                        int state = msg.arg1;
+                        Integer prevStateInteger = mInputDevices.get(device);
+                        int prevState =
+                                (prevStateInteger == null)
+                                        ? BluetoothProfile.STATE_DISCONNECTED
+                                        : prevStateInteger;
+                        if (DBG) {
+                            Log.d(
+                                    TAG,
+                                    "MESSAGE_CONNECT_STATE_CHANGED"
+                                            + (" newState=" + state)
+                                            + (" prevState=" + prevState));
+                        }
+                        if (state == BluetoothProfile.STATE_CONNECTED
+                                && prevState == BluetoothProfile.STATE_DISCONNECTED
+                                && (!okToConnect(device))) {
+                            if (DBG) {
+                                Log.d(TAG, "Incoming HID connection rejected");
+                            }
+                            mNativeInterface.virtualUnPlug(getByteAddress(device));
+                        } else {
+                            broadcastConnectionState(device, state);
+                        }
+                        if (state == BluetoothProfile.STATE_CONNECTED
+                                && (mTargetDevice != null
+                                        && mTargetDevice.equals(device))) {
+                            mTargetDevice = null;
+                            // local device originated connection to hid device, move out
+                            // of quiet mode
+                            AdapterService adapterService =
+                                    AdapterService.getAdapterService();
+                            adapterService.enable(false);
+                        }
+                    }
+                    break;
                 case MESSAGE_GET_PROTOCOL_MODE: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    if (!getProtocolModeNative(getByteAddress(device))) {
-                        Log.e(TAG, "Error: get protocol mode native returns false");
+                        BluetoothDevice device = (BluetoothDevice) msg.obj;
+                        if (!mNativeInterface.getProtocolMode(getByteAddress(device))) {
+                            Log.e(TAG, "Error: get protocol mode native returns false");
+                        }
                     }
-                }
-                break;
+                    break;
 
                 case MESSAGE_ON_GET_PROTOCOL_MODE: {
-                    BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
-                    int protocolMode = msg.arg1;
-                    broadcastProtocolMode(device, protocolMode);
-                }
-                break;
+                        BluetoothDevice device =
+                                mAdapterService.getDeviceFromByte((byte[]) msg.obj);
+                        int protocolMode = msg.arg1;
+                        broadcastProtocolMode(device, protocolMode);
+                    }
+                    break;
                 case MESSAGE_VIRTUAL_UNPLUG: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    if (!virtualUnPlugNative(getByteAddress(device))) {
-                        Log.e(TAG, "Error: virtual unplug native returns false");
+                        BluetoothDevice device = (BluetoothDevice) msg.obj;
+                        if (!mNativeInterface.virtualUnPlug(getByteAddress(device))) {
+                            Log.e(TAG, "Error: virtual unplug native returns false");
+                        }
                     }
-                }
-                break;
+                    break;
                 case MESSAGE_SET_PROTOCOL_MODE: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    byte protocolMode = (byte) msg.arg1;
-                    Log.d(TAG, "sending set protocol mode(" + protocolMode + ")");
-                    if (!setProtocolModeNative(getByteAddress(device), protocolMode)) {
-                        Log.e(TAG, "Error: set protocol mode native returns false");
+                        BluetoothDevice device = (BluetoothDevice) msg.obj;
+                        byte protocolMode = (byte) msg.arg1;
+                        Log.d(TAG, "sending set protocol mode(" + protocolMode + ")");
+                        if (!mNativeInterface.setProtocolMode(
+                                getByteAddress(device), protocolMode)) {
+                            Log.e(TAG, "Error: set protocol mode native returns false");
+                        }
                     }
-                }
-                break;
+                    break;
                 case MESSAGE_GET_REPORT: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    Bundle data = msg.getData();
-                    byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE);
-                    byte reportId = data.getByte(BluetoothHidHost.EXTRA_REPORT_ID);
-                    int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE);
-                    if (!getReportNative(getByteAddress(device), reportType, reportId,
-                            bufferSize)) {
-                        Log.e(TAG, "Error: get report native returns false");
+                        BluetoothDevice device = (BluetoothDevice) msg.obj;
+                        Bundle data = msg.getData();
+                        byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE);
+                        byte reportId = data.getByte(BluetoothHidHost.EXTRA_REPORT_ID);
+                        int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE);
+                        if (!mNativeInterface.getReport(
+                                getByteAddress(device), reportType, reportId, bufferSize)) {
+                            Log.e(TAG, "Error: get report native returns false");
+                        }
                     }
-                }
-                break;
+                    break;
                 case MESSAGE_ON_GET_REPORT: {
-                    BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
-                    Bundle data = msg.getData();
-                    byte[] report = data.getByteArray(BluetoothHidHost.EXTRA_REPORT);
-                    int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE);
-                    broadcastReport(device, report, bufferSize);
-                }
-                break;
+                        BluetoothDevice device =
+                                mAdapterService.getDeviceFromByte((byte[]) msg.obj);
+                        Bundle data = msg.getData();
+                        byte[] report = data.getByteArray(BluetoothHidHost.EXTRA_REPORT);
+                        int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE);
+                        broadcastReport(device, report, bufferSize);
+                    }
+                    break;
                 case MESSAGE_ON_HANDSHAKE: {
-                    BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
-                    int status = msg.arg1;
-                    broadcastHandshake(device, status);
-                }
-                break;
+                        BluetoothDevice device =
+                                mAdapterService.getDeviceFromByte((byte[]) msg.obj);
+                        int status = msg.arg1;
+                        broadcastHandshake(device, status);
+                    }
+                    break;
                 case MESSAGE_SET_REPORT: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    Bundle data = msg.getData();
-                    byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE);
-                    String report = data.getString(BluetoothHidHost.EXTRA_REPORT);
-                    if (!setReportNative(getByteAddress(device), reportType, report)) {
-                        Log.e(TAG, "Error: set report native returns false");
+                        BluetoothDevice device = (BluetoothDevice) msg.obj;
+                        Bundle data = msg.getData();
+                        byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE);
+                        String report = data.getString(BluetoothHidHost.EXTRA_REPORT);
+                        if (!mNativeInterface.setReport(
+                                getByteAddress(device), reportType, report)) {
+                            Log.e(TAG, "Error: set report native returns false");
+                        }
                     }
-                }
-                break;
+                    break;
                 case MESSAGE_ON_VIRTUAL_UNPLUG: {
-                    BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
-                    int status = msg.arg1;
-                    broadcastVirtualUnplugStatus(device, status);
-                }
-                break;
+                        BluetoothDevice device =
+                                mAdapterService.getDeviceFromByte((byte[]) msg.obj);
+                        int status = msg.arg1;
+                        broadcastVirtualUnplugStatus(device, status);
+                    }
+                    break;
                 case MESSAGE_GET_IDLE_TIME: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    if (!getIdleTimeNative(getByteAddress(device))) {
-                        Log.e(TAG, "Error: get idle time native returns false");
+                        BluetoothDevice device = (BluetoothDevice) msg.obj;
+                        if (!mNativeInterface.getIdleTime(getByteAddress(device))) {
+                            Log.e(TAG, "Error: get idle time native returns false");
+                        }
                     }
-                }
-                break;
+                    break;
                 case MESSAGE_ON_GET_IDLE_TIME: {
-                    BluetoothDevice device = mAdapterService.getDeviceFromByte((byte[]) msg.obj);
-                    int idleTime = msg.arg1;
-                    broadcastIdleTime(device, idleTime);
-                }
-                break;
-                case MESSAGE_SET_IDLE_TIME: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    Bundle data = msg.getData();
-                    byte idleTime = data.getByte(BluetoothHidHost.EXTRA_IDLE_TIME);
-                    if (!setIdleTimeNative(getByteAddress(device), idleTime)) {
-                        Log.e(TAG, "Error: get idle time native returns false");
+                        BluetoothDevice device =
+                                mAdapterService.getDeviceFromByte((byte[]) msg.obj);
+                        int idleTime = msg.arg1;
+                        broadcastIdleTime(device, idleTime);
                     }
-                }
-                break;
+                    break;
+                case MESSAGE_SET_IDLE_TIME: {
+                        BluetoothDevice device = (BluetoothDevice) msg.obj;
+                        Bundle data = msg.getData();
+                        byte idleTime = data.getByte(BluetoothHidHost.EXTRA_IDLE_TIME);
+                        if (!mNativeInterface.setIdleTime(
+                                getByteAddress(device), idleTime)) {
+                            Log.e(TAG, "Error: get idle time native returns false");
+                        }
+                    }
+                    break;
             }
         }
     };
@@ -393,7 +414,7 @@
                 SynchronousResultReceiver receiver) {
             try {
                 HidHostService service = getService(source);
-                int defaultValue = BluetoothHidHost.STATE_DISCONNECTED;
+                int defaultValue = BluetoothProfile.STATE_DISCONNECTED;
                 if (service != null) {
                     defaultValue = service.getConnectionState(device);
                 }
@@ -591,7 +612,7 @@
      */
     public boolean connect(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "connect: " + device);
-        if (getConnectionState(device) != BluetoothHidHost.STATE_DISCONNECTED) {
+        if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) {
             Log.e(TAG, "Hid Device not disconnected: " + device);
             return false;
         }
@@ -630,7 +651,7 @@
     public int getConnectionState(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "getConnectionState: " + device);
         if (mInputDevices.get(device) == null) {
-            return BluetoothHidHost.STATE_DISCONNECTED;
+            return BluetoothProfile.STATE_DISCONNECTED;
         }
         return mInputDevices.get(device);
     }
@@ -712,7 +733,7 @@
             Log.d(TAG, "getProtocolMode: " + device);
         }
         int state = this.getConnectionState(device);
-        if (state != BluetoothHidHost.STATE_CONNECTED) {
+        if (state != BluetoothProfile.STATE_CONNECTED) {
             return false;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE, device);
@@ -725,7 +746,7 @@
             Log.d(TAG, "virtualUnplug: " + device);
         }
         int state = this.getConnectionState(device);
-        if (state != BluetoothHidHost.STATE_CONNECTED) {
+        if (state != BluetoothProfile.STATE_CONNECTED) {
             return false;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG, device);
@@ -738,7 +759,7 @@
             Log.d(TAG, "setProtocolMode: " + device);
         }
         int state = this.getConnectionState(device);
-        if (state != BluetoothHidHost.STATE_CONNECTED) {
+        if (state != BluetoothProfile.STATE_CONNECTED) {
             return false;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE);
@@ -753,7 +774,7 @@
             Log.d(TAG, "getReport: " + device);
         }
         int state = this.getConnectionState(device);
-        if (state != BluetoothHidHost.STATE_CONNECTED) {
+        if (state != BluetoothProfile.STATE_CONNECTED) {
             return false;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
@@ -772,7 +793,7 @@
             Log.d(TAG, "setReport: " + device);
         }
         int state = this.getConnectionState(device);
-        if (state != BluetoothHidHost.STATE_CONNECTED) {
+        if (state != BluetoothProfile.STATE_CONNECTED) {
             return false;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
@@ -791,17 +812,17 @@
             Log.d(TAG, "sendData: " + device);
         }
         int state = this.getConnectionState(device);
-        if (state != BluetoothHidHost.STATE_CONNECTED) {
+        if (state != BluetoothProfile.STATE_CONNECTED) {
             return false;
         }
 
-        return sendDataNative(getByteAddress(device), report);
+        return mNativeInterface.sendData(getByteAddress(device), report);
     }
 
     boolean getIdleTime(BluetoothDevice device) {
         if (DBG) Log.d(TAG, "getIdleTime: " + device);
         int state = this.getConnectionState(device);
-        if (state != BluetoothHidHost.STATE_CONNECTED) {
+        if (state != BluetoothProfile.STATE_CONNECTED) {
             return false;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_GET_IDLE_TIME, device);
@@ -812,7 +833,7 @@
     boolean setIdleTime(BluetoothDevice device, byte idleTime) {
         if (DBG) Log.d(TAG, "setIdleTime: " + device);
         int state = this.getConnectionState(device);
-        if (state != BluetoothHidHost.STATE_CONNECTED) {
+        if (state != BluetoothProfile.STATE_CONNECTED) {
             return false;
         }
         Message msg = mHandler.obtainMessage(MESSAGE_SET_IDLE_TIME);
@@ -824,7 +845,7 @@
         return true;
     }
 
-    private void onGetProtocolMode(byte[] address, int mode) {
+    void onGetProtocolMode(byte[] address, int mode) {
         if (DBG) Log.d(TAG, "onGetProtocolMode()");
         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE);
         msg.obj = address;
@@ -832,7 +853,7 @@
         mHandler.sendMessage(msg);
     }
 
-    private void onGetIdleTime(byte[] address, int idleTime) {
+    void onGetIdleTime(byte[] address, int idleTime) {
         if (DBG) Log.d(TAG, "onGetIdleTime()");
         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_IDLE_TIME);
         msg.obj = address;
@@ -840,7 +861,7 @@
         mHandler.sendMessage(msg);
     }
 
-    private void onGetReport(byte[] address, byte[] report, int rptSize) {
+    void onGetReport(byte[] address, byte[] report, int rptSize) {
         if (DBG) Log.d(TAG, "onGetReport()");
         Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_REPORT);
         msg.obj = address;
@@ -851,7 +872,7 @@
         mHandler.sendMessage(msg);
     }
 
-    private void onHandshake(byte[] address, int status) {
+    void onHandshake(byte[] address, int status) {
         if (DBG) Log.d(TAG, "onHandshake: status=" + status);
         Message msg = mHandler.obtainMessage(MESSAGE_ON_HANDSHAKE);
         msg.obj = address;
@@ -859,7 +880,7 @@
         mHandler.sendMessage(msg);
     }
 
-    private void onVirtualUnplug(byte[] address, int status) {
+    void onVirtualUnplug(byte[] address, int status) {
         if (DBG) Log.d(TAG, "onVirtualUnplug: status=" + status);
         Message msg = mHandler.obtainMessage(MESSAGE_ON_VIRTUAL_UNPLUG);
         msg.obj = address;
@@ -867,7 +888,7 @@
         mHandler.sendMessage(msg);
     }
 
-    private void onConnectStateChanged(byte[] address, int state) {
+    void onConnectStateChanged(byte[] address, int state) {
         if (DBG) Log.d(TAG, "onConnectStateChanged: state=" + state);
         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
         msg.obj = address;
@@ -878,8 +899,8 @@
     // This method does not check for error conditon (newState == prevState)
     private void broadcastConnectionState(BluetoothDevice device, int newState) {
         Integer prevStateInteger = mInputDevices.get(device);
-        int prevState = (prevStateInteger == null) ? BluetoothHidHost.STATE_DISCONNECTED
-                : prevStateInteger;
+        int prevState =
+                (prevStateInteger == null) ? BluetoothProfile.STATE_DISCONNECTED : prevStateInteger;
         if (prevState == newState) {
             Log.w(TAG, "no state change: " + newState);
             return;
@@ -991,22 +1012,6 @@
         return true;
     }
 
-    private static int convertHalState(int halState) {
-        switch (halState) {
-            case CONN_STATE_CONNECTED:
-                return BluetoothProfile.STATE_CONNECTED;
-            case CONN_STATE_CONNECTING:
-                return BluetoothProfile.STATE_CONNECTING;
-            case CONN_STATE_DISCONNECTED:
-                return BluetoothProfile.STATE_DISCONNECTED;
-            case CONN_STATE_DISCONNECTING:
-                return BluetoothProfile.STATE_DISCONNECTING;
-            default:
-                Log.e(TAG, "bad hid connection state: " + halState);
-                return BluetoothProfile.STATE_DISCONNECTED;
-        }
-    }
-
     @Override
     public void dump(StringBuilder sb) {
         super.dump(sb);
@@ -1016,38 +1021,4 @@
             println(sb, "  " + device + " : " + mInputDevices.get(device));
         }
     }
-
-    // Constants matching Hal header file bt_hh.h
-    // bthh_connection_state_t
-    private static final int CONN_STATE_CONNECTED = 0;
-    private static final int CONN_STATE_CONNECTING = 1;
-    private static final int CONN_STATE_DISCONNECTED = 2;
-    private static final int CONN_STATE_DISCONNECTING = 3;
-
-    private static native void classInitNative();
-
-    private native void initializeNative();
-
-    private native void cleanupNative();
-
-    private native boolean connectHidNative(byte[] btAddress);
-
-    private native boolean disconnectHidNative(byte[] btAddress);
-
-    private native boolean getProtocolModeNative(byte[] btAddress);
-
-    private native boolean virtualUnPlugNative(byte[] btAddress);
-
-    private native boolean setProtocolModeNative(byte[] btAddress, byte protocolMode);
-
-    private native boolean getReportNative(byte[] btAddress, byte reportType, byte reportId,
-            int bufferSize);
-
-    private native boolean setReportNative(byte[] btAddress, byte reportType, String report);
-
-    private native boolean sendDataNative(byte[] btAddress, String report);
-
-    private native boolean setIdleTimeNative(byte[] btAddress, byte idleTime);
-
-    private native boolean getIdleTimeNative(byte[] btAddress);
 }
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 ff426aa..a76920a 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioBroadcasterNativeInterface.java
@@ -41,11 +41,8 @@
 
     @GuardedBy("INSTANCE_LOCK")
     private static LeAudioBroadcasterNativeInterface sInstance;
-    private static final Object INSTANCE_LOCK = new Object();
 
-    static {
-        classInitNative();
-    }
+    private static final Object INSTANCE_LOCK = new Object();
 
     private LeAudioBroadcasterNativeInterface() {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -66,6 +63,14 @@
         }
     }
 
+    /** Set singleton instance. */
+    @VisibleForTesting
+    static void setInstance(LeAudioBroadcasterNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
     private void sendMessageToService(LeAudioStackEvent event) {
         LeAudioService service = LeAudioService.getLeAudioService();
         if (service != null) {
@@ -248,7 +253,6 @@
     }
 
     // Native methods that call into the JNI interface
-    private static native void classInitNative();
     private native void initNative();
     private native void stopNative();
     private native void cleanupNative();
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java
index 7dc259a..bc506ff 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioNativeInterface.java
@@ -37,17 +37,15 @@
  * LeAudio Native Interface to/from JNI.
  */
 public class LeAudioNativeInterface {
-    private static final String TAG = "LeAudioNativeInterface";
+    private static final String TAG = LeAudioNativeInterface.class.getSimpleName();
     private static final boolean DBG = true;
+
     private BluetoothAdapter mAdapter;
 
     @GuardedBy("INSTANCE_LOCK")
     private static LeAudioNativeInterface sInstance;
-    private static final Object INSTANCE_LOCK = new Object();
 
-    static {
-        classInitNative();
-    }
+    private static final Object INSTANCE_LOCK = new Object();
 
     private LeAudioNativeInterface() {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -68,11 +66,9 @@
         }
     }
 
-    /**
-     * Set singleton instance.
-     */
+    /** Set singleton instance. */
     @VisibleForTesting
-    static void setInstance(LeAudioNativeInterface instance) {
+    public static void setInstance(LeAudioNativeInterface instance) {
         synchronized (INSTANCE_LOCK) {
             sInstance = instance;
         }
@@ -372,7 +368,6 @@
     }
 
     // Native methods that call into the JNI interface
-    private static native void classInitNative();
     private native void initNative(BluetoothLeAudioCodecConfig[] codecConfigOffloading);
     private native void cleanupNative();
     private native boolean connectLeAudioNative(byte[] address);
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 b1eaaa8..e145a18 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -1193,7 +1193,8 @@
 
         mAdapterService
                 .getActiveDeviceManager()
-                .leAudioConnectionStateChanged(device, prevState, newState);
+                .profileConnectionStateChanged(
+                        BluetoothProfile.LE_AUDIO, device, prevState, newState);
         Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
@@ -1217,7 +1218,9 @@
                     + ". Currently active device is " + mActiveAudioOutDevice);
         }
 
-        mAdapterService.getActiveDeviceManager().leAudioActiveStateChanged(device);
+        mAdapterService
+                .getActiveDeviceManager()
+                .profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, device);
         Intent intent = new Intent(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioTmapGattServer.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioTmapGattServer.java
index b204baa..48b3d8d 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioTmapGattServer.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioTmapGattServer.java
@@ -84,10 +84,11 @@
                 new BluetoothGattService(BluetoothUuid.TMAP.getUuid(),
                 BluetoothGattService.SERVICE_TYPE_PRIMARY);
 
-        BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(
-                UUID_TMAP_ROLE,
-                BluetoothGattCharacteristic.PROPERTY_READ,
-                BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED);
+        BluetoothGattCharacteristic characteristic =
+                new BluetoothGattCharacteristic(
+                        UUID_TMAP_ROLE,
+                        BluetoothGattCharacteristic.PROPERTY_READ,
+                        BluetoothGattCharacteristic.PERMISSION_READ);
 
         characteristic.setValue(roleMask, BluetoothGattCharacteristic.FORMAT_UINT16, 0);
         service.addCharacteristic(characteristic);
diff --git a/android/app/src/com/android/bluetooth/le_audio/OWNERS b/android/app/src/com/android/bluetooth/le_audio/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/le_audio/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java b/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
index 2567da6..686579e 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
@@ -29,7 +29,7 @@
 import com.android.bluetooth.ObexServerSockets;
 import com.android.bluetooth.map.BluetoothMapContentObserver.Msg;
 import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
-import com.android.bluetooth.sdp.SdpManager;
+import com.android.bluetooth.sdp.SdpManagerNativeInterface;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.obex.ServerSession;
 
@@ -137,12 +137,13 @@
     }
 
     private void removeSdpRecord() {
-        if (mAdapter != null && mSdpHandle >= 0 && SdpManager.getDefaultManager() != null) {
+        SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
+        if (mAdapter != null && mSdpHandle >= 0 && nativeInterface.isAvailable()) {
             if (V) {
                 Log.d(mTag, "Removing SDP record for MAS instance: " + mMasInstanceId
                         + " Object reference: " + this + "SDP handle: " + mSdpHandle);
             }
-            boolean status = SdpManager.getDefaultManager().removeSdpRecord(mSdpHandle);
+            boolean status = nativeInterface.removeSdpRecord(mSdpHandle);
             Log.d(mTag, "RemoveSDPrecord returns " + status);
             mSdpHandle = -1;
         }
@@ -371,9 +372,15 @@
                 sFeatureMask = SDP_MAP_MAS_FEATURES_1_4;
         }
 
-        return SdpManager.getDefaultManager()
-                .createMapMasRecord(masName, mMasInstanceId, rfcommChannel, l2capPsm,
-                        masVersion, messageTypeFlags, sFeatureMask);
+        return SdpManagerNativeInterface.getInstance()
+                .createMapMasRecord(
+                        masName,
+                        mMasInstanceId,
+                        rfcommChannel,
+                        l2capPsm,
+                        masVersion,
+                        messageTypeFlags,
+                        sFeatureMask);
     }
 
     /* Called for all MAS instances for each instance when auth. is completed, hence
diff --git a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
index ab2d565..b6cbb3b 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
@@ -155,6 +155,8 @@
             Log.d(TAG, "Statemachine exists for a device in unexpected state: " + state);
         }
         mMapInstanceMap.remove(device);
+        mapStateMachine.doQuit();
+
         addDeviceToMapAndConnect(device);
         if (DBG) {
             StringBuilder sb = new StringBuilder();
@@ -328,9 +330,6 @@
         removeUncleanAccounts();
         MapClientContent.clearAllContent(this);
         setMapClientService(this);
-        mAdapterService.notifyActivityAttributionInfo(
-                getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
         return true;
     }
 
@@ -340,11 +339,6 @@
             Log.d(TAG, "stop()");
         }
 
-        if (mAdapterService != null) {
-            mAdapterService.notifyActivityAttributionInfo(
-                    getAttributionSource(),
-                    AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
-        }
         if (mMapReceiver != null) {
             unregisterReceiver(mMapReceiver);
             mMapReceiver = null;
@@ -389,6 +383,7 @@
             MceStateMachine stateMachine = mMapInstanceMap.get(device);
             if (stateMachine != null) {
                 mMapInstanceMap.remove(device);
+                stateMachine.doQuit();
             }
         }
         if (DBG) {
diff --git a/android/app/src/com/android/bluetooth/mapclient/MnsService.java b/android/app/src/com/android/bluetooth/mapclient/MnsService.java
index 6df9a4d..358398e 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MnsService.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MnsService.java
@@ -27,7 +27,7 @@
 import com.android.bluetooth.IObexConnectionHandler;
 import com.android.bluetooth.ObexServerSockets;
 import com.android.bluetooth.Utils;
-import com.android.bluetooth.sdp.SdpManager;
+import com.android.bluetooth.sdp.SdpManagerNativeInterface;
 import com.android.obex.ServerSession;
 
 import java.io.IOException;
@@ -61,14 +61,18 @@
         sContext = context;
         sAcceptThread = new SocketAcceptor();
         sServerSockets = ObexServerSockets.create(sAcceptThread);
-        SdpManager sdpManager = SdpManager.getDefaultManager();
-        if (sdpManager == null) {
-            Log.e(TAG, "SdpManager is null");
+        SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
+        if (!nativeInterface.isAvailable()) {
+            Log.e(TAG, "SdpManagerNativeInterface is not available");
             return;
         }
-        mSdpHandle = sdpManager.createMapMnsRecord("MAP Message Notification Service",
-                sServerSockets.getRfcommChannel(), sServerSockets.getL2capPsm(), MNS_VERSION,
-                MasClient.MAP_SUPPORTED_FEATURES);
+        mSdpHandle =
+                nativeInterface.createMapMnsRecord(
+                        "MAP Message Notification Service",
+                        sServerSockets.getRfcommChannel(),
+                        sServerSockets.getL2capPsm(),
+                        MNS_VERSION,
+                        MasClient.MAP_SUPPORTED_FEATURES);
     }
 
     void stop() {
@@ -90,13 +94,17 @@
         }
         int sdpHandle = mSdpHandle;
         mSdpHandle = -1;
-        SdpManager sdpManager = SdpManager.getDefaultManager();
-        if (sdpManager == null) {
-            Log.e(TAG, "cleanUpSdpRecord failed, sdpManager is null, sdpHandle=" + sdpHandle);
+        SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
+        if (!nativeInterface.isAvailable()) {
+            Log.e(
+                    TAG,
+                    "cleanUpSdpRecord failed, SdpManagerNativeInterface is not available,"
+                            + " sdpHandle="
+                            + sdpHandle);
             return;
         }
         Log.i(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle);
-        if (!sdpManager.removeSdpRecord(sdpHandle)) {
+        if (!nativeInterface.removeSdpRecord(sdpHandle)) {
             Log.e(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle);
         }
     }
diff --git a/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java b/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java
index 45d4379..62d358a 100644
--- a/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java
+++ b/android/app/src/com/android/bluetooth/mcp/MediaControlGattService.java
@@ -393,11 +393,12 @@
 
     private void onUnauthorizedCharRead(BluetoothDevice device, GattOpContext op) {
         UUID charUuid = op.mCharacteristic.getUuid();
-        boolean allowToReadRealValue = false;
         byte[] buffer = null;
 
         if (charUuid.equals(UUID_PLAYER_NAME)) {
-            allowToReadRealValue = true;
+            ByteBuffer bb = ByteBuffer.allocate(0).order(ByteOrder.LITTLE_ENDIAN);
+            bb.put("".getBytes());
+            buffer = bb.array();
 
         } else if (charUuid.equals(UUID_PLAYER_ICON_OBJ_ID)) {
             buffer = objId2ByteArray(-1);
@@ -461,13 +462,17 @@
             buffer = bb.array();
 
         } else if (charUuid.equals(UUID_MEDIA_STATE)) {
-            allowToReadRealValue = true;
+            ByteBuffer bb = ByteBuffer.allocate(1).order(ByteOrder.LITTLE_ENDIAN);
+            bb.put((byte) MediaState.INACTIVE.getValue());
+            buffer = bb.array();
 
         } else if (charUuid.equals(UUID_MEDIA_CONTROL_POINT)) {
             // No read is available on this characteristic
 
         } else if (charUuid.equals(UUID_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED)) {
-            allowToReadRealValue = true;
+            ByteBuffer bb = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
+            bb.putInt((int) Request.SupportedOpcodes.NONE);
+            buffer = bb.array();
 
         } else if (charUuid.equals(UUID_SEARCH_RESULT_OBJ_ID)) {
             buffer = objId2ByteArray(-1);
@@ -476,10 +481,7 @@
             // No read is available on this characteristic
 
         } else if (charUuid.equals(UUID_CONTENT_CONTROL_ID)) {
-            allowToReadRealValue = true;
-        }
-
-        if (allowToReadRealValue) {
+            // It is ok, to send the real value for CCID
             if (op.mCharacteristic.getValue() != null) {
                 buffer =
                         Arrays.copyOfRange(
diff --git a/android/app/src/com/android/bluetooth/mcp/OWNERS b/android/app/src/com/android/bluetooth/mcp/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/mcp/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
index 5d8f386..fcef63a 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
@@ -61,7 +61,7 @@
 import com.android.bluetooth.ObexServerSockets;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ProfileService;
-import com.android.bluetooth.sdp.SdpManager;
+import com.android.bluetooth.sdp.SdpManagerNativeInterface;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.obex.ObexTransport;
 
@@ -271,9 +271,6 @@
         mNotifier.mNotificationMgr.cancelAll();
         updateFromProvider();
         setBluetoothOppService(this);
-        mAdapterService.notifyActivityAttributionInfo(
-                getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
         return true;
     }
 
@@ -283,9 +280,6 @@
             Log.w(TAG, "stop() called before start()");
             return true;
         }
-        mAdapterService.notifyActivityAttributionInfo(
-                getAttributionSource(),
-                AdapterService.ACTIVITY_ATTRIBUTION_NO_ACTIVE_DEVICE_ADDRESS);
         setBluetoothOppService(null);
         mHandler.sendMessage(mHandler.obtainMessage(STOP_LISTENER));
 
@@ -514,15 +508,22 @@
         stopListeners();
         mServerSocket = ObexServerSockets.createInsecure(this);
         acceptNewConnections();
-        SdpManager sdpManager = SdpManager.getDefaultManager();
-        if (sdpManager == null || mServerSocket == null) {
-            Log.e(TAG, "ERROR:serversocket object is NULL  sdp manager :" + sdpManager
-                    + " mServerSocket:" + mServerSocket);
+        SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
+        if (!nativeInterface.isAvailable()) {
+            Log.e(TAG, "ERROR:serversocket: SdpManagerNativeInterface is not available");
+            return;
+        }
+        if (mServerSocket == null) {
+            Log.e(TAG, "ERROR:serversocket: mServerSocket is null");
             return;
         }
         mOppSdpHandle =
-                sdpManager.createOppOpsRecord("OBEX Object Push", mServerSocket.getRfcommChannel(),
-                        mServerSocket.getL2capPsm(), 0x0102, SUPPORTED_OPP_FORMAT);
+                nativeInterface.createOppOpsRecord(
+                        "OBEX Object Push",
+                        mServerSocket.getRfcommChannel(),
+                        mServerSocket.getL2capPsm(),
+                        0x0102,
+                        SUPPORTED_OPP_FORMAT);
         if (D) {
             Log.d(TAG, "mOppSdpHandle :" + mOppSdpHandle);
         }
@@ -1238,12 +1239,12 @@
     }
 
     private void stopListeners() {
-        if (mAdapterService != null && mOppSdpHandle >= 0
-                && SdpManager.getDefaultManager() != null) {
+        SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
+        if (mAdapterService != null && mOppSdpHandle >= 0 && nativeInterface.isAvailable()) {
             if (D) {
                 Log.d(TAG, "Removing SDP record mOppSdpHandle :" + mOppSdpHandle);
             }
-            boolean status = SdpManager.getDefaultManager().removeSdpRecord(mOppSdpHandle);
+            boolean status = nativeInterface.removeSdpRecord(mOppSdpHandle);
             Log.d(TAG, "RemoveSDPrecord returns " + status);
             mOppSdpHandle = -1;
         }
diff --git a/android/app/src/com/android/bluetooth/pan/PanNativeInterface.java b/android/app/src/com/android/bluetooth/pan/PanNativeInterface.java
new file mode 100644
index 0000000..0580c58
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/pan/PanNativeInterface.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.pan;
+
+import static java.util.Objects.requireNonNull;
+
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/** Provides Bluetooth Pan native interface for the Pan service */
+public class PanNativeInterface {
+    private static final String TAG = PanNativeInterface.class.getSimpleName();
+    private PanService mPanService;
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static PanNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
+    private PanNativeInterface() {}
+
+    /** Get singleton instance. */
+    public static PanNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new PanNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(PanNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
+    void init(PanService panService) {
+        mPanService = panService;
+        initializeNative();
+    }
+
+    void cleanup() {
+        cleanupNative();
+    }
+
+    boolean connect(byte[] identityAddress) {
+        requireNonNull(identityAddress, "Identity address can not be null");
+        return connectPanNative(
+                identityAddress, BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
+    }
+
+    boolean disconnect(byte[] identityAddress) {
+        requireNonNull(identityAddress, "Identity address can not be null");
+        return disconnectPanNative(identityAddress);
+    }
+
+    /**********************************************************************************************/
+    /*********************************** callbacks from native ************************************/
+    /**********************************************************************************************/
+
+    void onControlStateChanged(int localRole, int halState, int error, String ifname) {
+        mPanService.onControlStateChanged(localRole, convertHalState(halState), error, ifname);
+    }
+
+    void onConnectStateChanged(
+            byte[] address, int halState, int error, int localRole, int remoteRole) {
+        mPanService.onConnectStateChanged(
+                address, convertHalState(halState), error, localRole, remoteRole);
+    }
+
+    @VisibleForTesting
+    static int convertHalState(int halState) {
+        switch (halState) {
+            case CONN_STATE_CONNECTED:
+                return BluetoothProfile.STATE_CONNECTED;
+            case CONN_STATE_CONNECTING:
+                return BluetoothProfile.STATE_CONNECTING;
+            case CONN_STATE_DISCONNECTED:
+                return BluetoothProfile.STATE_DISCONNECTED;
+            case CONN_STATE_DISCONNECTING:
+                return BluetoothProfile.STATE_DISCONNECTING;
+            default:
+                Log.e(TAG, "Invalid pan connection state: " + halState);
+                return BluetoothProfile.STATE_DISCONNECTED;
+        }
+    }
+
+    /**********************************************************************************************/
+    /******************************************* native *******************************************/
+    /**********************************************************************************************/
+
+    // Constants matching Hal header file bt_hh.h: bthh_connection_state_t
+
+    @VisibleForTesting static final int CONN_STATE_CONNECTED = 0;
+
+    @VisibleForTesting static final int CONN_STATE_CONNECTING = 1;
+
+    @VisibleForTesting static final int CONN_STATE_DISCONNECTED = 2;
+
+    @VisibleForTesting static final int CONN_STATE_DISCONNECTING = 3;
+
+    private native void initializeNative();
+
+    private native void cleanupNative();
+
+    private native boolean connectPanNative(byte[] btAddress, int localRole, int remoteRole);
+
+    private native boolean disconnectPanNative(byte[] btAddress);
+}
diff --git a/android/app/src/com/android/bluetooth/pan/PanService.java b/android/app/src/com/android/bluetooth/pan/PanService.java
index 0e9a6d8..c1bb060 100644
--- a/android/app/src/com/android/bluetooth/pan/PanService.java
+++ b/android/app/src/com/android/bluetooth/pan/PanService.java
@@ -94,17 +94,18 @@
 
     private AdapterService mAdapterService;
 
+    @VisibleForTesting PanNativeInterface mNativeInterface;
+
     TetheringManager.TetheringEventCallback mTetheringCallback =
             new TetheringManager.TetheringEventCallback() {
                 @Override
                 public void onError(TetheringInterface iface, int error) {
-                    if (mIsTethering
-                            && iface.getType() == TetheringManager.TETHERING_BLUETOOTH) {
+                    if (mIsTethering && iface.getType() == TetheringManager.TETHERING_BLUETOOTH) {
                         // tethering is fail because of @TetheringIfaceError error.
                         Log.e(TAG, "Error setting up tether interface: " + error);
                         for (Map.Entry device : mPanDevices.entrySet()) {
-                            disconnectPanNative(Utils.getByteAddress(
-                                    (BluetoothDevice) device.getKey()));
+                            mNativeInterface.disconnect(
+                                    Utils.getByteAddress((BluetoothDevice) device.getKey()));
                         }
                         mPanDevices.clear();
                         mIsTethering = false;
@@ -112,10 +113,6 @@
                 }
             };
 
-    static {
-        classInitNative();
-    }
-
     public static boolean isEnabled() {
         return BluetoothProperties.isProfilePanNapEnabled().orElse(false)
                 || BluetoothProperties.isProfilePanPanuEnabled().orElse(false);
@@ -151,6 +148,10 @@
                 "AdapterService cannot be null when PanService starts");
         mDatabaseManager = Objects.requireNonNull(AdapterService.getAdapterService().getDatabase(),
                 "DatabaseManager cannot be null when PanService starts");
+        mNativeInterface =
+                Objects.requireNonNull(
+                        PanNativeInterface.getInstance(),
+                        "PanNativeInterface cannot be null when PanService starts");
 
         mBluetoothTetheringCallbacks = new HashMap<>();
         mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
@@ -160,8 +161,7 @@
         } catch (NotFoundException e) {
             mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
         }
-        initializeNative();
-        mNativeAvailable = true;
+        mNativeInterface.init(this);
 
         mUserManager = getSystemService(UserManager.class);
 
@@ -184,6 +184,7 @@
             mTetheringManager.unregisterTetheringEventCallback(mTetheringCallback);
             mTetheringManager = null;
         }
+        mNativeInterface.cleanup();
         mHandler.removeCallbacksAndMessages(null);
         return true;
     }
@@ -192,10 +193,6 @@
     protected void cleanup() {
         // TODO(b/72948646): this should be moved to stop()
         setPanService(null);
-        if (mNativeAvailable) {
-            cleanupNative();
-            mNativeAvailable = false;
-        }
 
         mUserManager = null;
 
@@ -217,56 +214,75 @@
         }
     }
 
-    private final Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_CONNECT: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    if (!connectPanNative(mAdapterService.getByteIdentityAddress(device),
-                            BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) {
-                        handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_CONNECTING,
-                                BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
-                        handlePanDeviceStateChange(device, null,
-                                BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
-                                BluetoothPan.REMOTE_NAP_ROLE);
-                        break;
+    private final Handler mHandler =
+            new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case MESSAGE_CONNECT:
+                            BluetoothDevice connectDevice = (BluetoothDevice) msg.obj;
+                            if (!mNativeInterface.connect(
+                                    mAdapterService.getByteIdentityAddress(connectDevice))) {
+                                handlePanDeviceStateChange(
+                                        connectDevice,
+                                        null,
+                                        BluetoothProfile.STATE_CONNECTING,
+                                        BluetoothPan.LOCAL_PANU_ROLE,
+                                        BluetoothPan.REMOTE_NAP_ROLE);
+                                handlePanDeviceStateChange(
+                                        connectDevice,
+                                        null,
+                                        BluetoothProfile.STATE_DISCONNECTED,
+                                        BluetoothPan.LOCAL_PANU_ROLE,
+                                        BluetoothPan.REMOTE_NAP_ROLE);
+                            }
+                            break;
+                        case MESSAGE_DISCONNECT:
+                            BluetoothDevice disconnectDevice = (BluetoothDevice) msg.obj;
+                            if (!mNativeInterface.disconnect(
+                                    mAdapterService.getByteIdentityAddress(disconnectDevice))) {
+                                handlePanDeviceStateChange(
+                                        disconnectDevice,
+                                        mPanIfName,
+                                        BluetoothProfile.STATE_DISCONNECTING,
+                                        BluetoothPan.LOCAL_PANU_ROLE,
+                                        BluetoothPan.REMOTE_NAP_ROLE);
+                                handlePanDeviceStateChange(
+                                        disconnectDevice,
+                                        mPanIfName,
+                                        BluetoothProfile.STATE_DISCONNECTED,
+                                        BluetoothPan.LOCAL_PANU_ROLE,
+                                        BluetoothPan.REMOTE_NAP_ROLE);
+                            }
+                            break;
+                        case MESSAGE_CONNECT_STATE_CHANGED:
+                            ConnectState cs = (ConnectState) msg.obj;
+                            final BluetoothDevice device =
+                                    mAdapterService.getDeviceFromByte(cs.addr);
+                            // TBD get iface from the msg
+                            if (DBG) {
+                                Log.d(
+                                        TAG,
+                                        "MESSAGE_CONNECT_STATE_CHANGED: "
+                                                + device
+                                                + " state: "
+                                                + cs.state);
+                            }
+                            // It could be null if the connection up is coming when the
+                            // Bluetooth is turning off.
+                            if (device == null) {
+                                break;
+                            }
+                            handlePanDeviceStateChange(
+                                    device,
+                                    mPanIfName /* iface */,
+                                    cs.state,
+                                    cs.local_role,
+                                    cs.remote_role);
+                            break;
                     }
                 }
-                break;
-                case MESSAGE_DISCONNECT: {
-                    BluetoothDevice device = (BluetoothDevice) msg.obj;
-                    if (!disconnectPanNative(mAdapterService.getByteIdentityAddress(device))) {
-                        handlePanDeviceStateChange(device, mPanIfName,
-                                BluetoothProfile.STATE_DISCONNECTING, BluetoothPan.LOCAL_PANU_ROLE,
-                                BluetoothPan.REMOTE_NAP_ROLE);
-                        handlePanDeviceStateChange(device, mPanIfName,
-                                BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
-                                BluetoothPan.REMOTE_NAP_ROLE);
-                        break;
-                    }
-                }
-                break;
-                case MESSAGE_CONNECT_STATE_CHANGED: {
-                    ConnectState cs = (ConnectState) msg.obj;
-                    final BluetoothDevice device = mAdapterService.getDeviceFromByte(cs.addr);
-                    // TBD get iface from the msg
-                    if (DBG) {
-                        Log.d(TAG,
-                                "MESSAGE_CONNECT_STATE_CHANGED: " + device + " state: " + cs.state);
-                    }
-                    // It could be null if the connection up is coming when the Bluetooth is turning
-                    // off.
-                    if (device == null) {
-                        break;
-                    }
-                    handlePanDeviceStateChange(device, mPanIfName /* iface */,
-                            convertHalState(cs.state), cs.local_role, cs.remote_role);
-                }
-                break;
-            }
-        }
-    };
+            };
 
     /**
      * Handlers for incoming service calls
@@ -603,7 +619,6 @@
         public int remote_role;
     }
 
-    @VisibleForTesting
     void onConnectStateChanged(byte[] address, int state, int error, int localRole,
             int remoteRole) {
         if (DBG) {
@@ -626,22 +641,6 @@
         }
     }
 
-    @VisibleForTesting
-    static int convertHalState(int halState) {
-        switch (halState) {
-            case CONN_STATE_CONNECTED:
-                return BluetoothProfile.STATE_CONNECTED;
-            case CONN_STATE_CONNECTING:
-                return BluetoothProfile.STATE_CONNECTING;
-            case CONN_STATE_DISCONNECTED:
-                return BluetoothProfile.STATE_DISCONNECTED;
-            case CONN_STATE_DISCONNECTING:
-                return BluetoothProfile.STATE_DISCONNECTING;
-            default:
-                Log.e(TAG, "bad pan connection state: " + halState);
-                return BluetoothProfile.STATE_DISCONNECTED;
-        }
-    }
 
     void handlePanDeviceStateChange(BluetoothDevice device, String iface, int state,
             @LocalPanRole int localRole, @RemotePanRole int remoteRole) {
@@ -688,7 +687,7 @@
                     Log.d(TAG, "handlePanDeviceStateChange BT tethering is off/Local role"
                             + " is PANU drop the connection");
                     mPanDevices.remove(device);
-                    disconnectPanNative(Utils.getByteAddress(device));
+                    mNativeInterface.disconnect(Utils.getByteAddress(device));
                     return;
                 }
                 Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE");
@@ -776,26 +775,4 @@
             mRemoteRole = remoteRole;
         }
     }
-
-    // Constants matching Hal header file bt_hh.h
-    // bthh_connection_state_t
-    @VisibleForTesting
-    static final int CONN_STATE_CONNECTED = 0;
-    @VisibleForTesting
-    static final int CONN_STATE_CONNECTING = 1;
-    @VisibleForTesting
-    static final int CONN_STATE_DISCONNECTED = 2;
-    @VisibleForTesting
-    static final int CONN_STATE_DISCONNECTING = 3;
-
-    private static native void classInitNative();
-
-    private native void initializeNative();
-
-    private native void cleanupNative();
-
-    private native boolean connectPanNative(byte[] btAddress, int localRole, int remoteRole);
-
-    private native boolean disconnectPanNative(byte[] btAddress);
-
 }
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
index f910436..ae9714a 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
@@ -73,7 +73,7 @@
 import com.android.bluetooth.btservice.InteropUtil;
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
-import com.android.bluetooth.sdp.SdpManager;
+import com.android.bluetooth.sdp.SdpManagerNativeInterface;
 import com.android.bluetooth.util.DevicePolicyUtils;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -376,11 +376,15 @@
             return;
         }
 
-        mSdpHandle = SdpManager.getDefaultManager()
-                .createPbapPseRecord("OBEX Phonebook Access Server",
-                       mServerSockets.getRfcommChannel(), mServerSockets.getL2capPsm(),
-                       SDP_PBAP_SERVER_VERSION_1_2, SDP_PBAP_SUPPORTED_REPOSITORIES,
-                       SDP_PBAP_SUPPORTED_FEATURES);
+        mSdpHandle =
+                SdpManagerNativeInterface.getInstance()
+                        .createPbapPseRecord(
+                                "OBEX Phonebook Access Server",
+                                mServerSockets.getRfcommChannel(),
+                                mServerSockets.getL2capPsm(),
+                                SDP_PBAP_SERVER_VERSION_1_2,
+                                SDP_PBAP_SUPPORTED_REPOSITORIES,
+                                SDP_PBAP_SUPPORTED_FEATURES);
 
         if (DEBUG) {
             Log.d(TAG, "created Sdp record, mSdpHandle=" + mSdpHandle);
@@ -395,13 +399,13 @@
         }
         int sdpHandle = mSdpHandle;
         mSdpHandle = -1;
-        SdpManager sdpManager = SdpManager.getDefaultManager();
+        SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
         if (DEBUG) {
             Log.d(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle);
         }
-        if (sdpManager == null) {
-            Log.e(TAG, "sdpManager is null");
-        } else if (!sdpManager.removeSdpRecord(sdpHandle)) {
+        if (!nativeInterface.isAvailable()) {
+            Log.e(TAG, "SdpManagerNativeInterface is not available");
+        } else if (!nativeInterface.removeSdpRecord(sdpHandle)) {
             Log.w(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle);
         }
     }
diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
index 887978f..a5d4c34 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java
@@ -20,7 +20,6 @@
 import android.accounts.AccountManager;
 import android.annotation.RequiresPermission;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadsetClient;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.IBluetoothPbapClient;
 import android.content.AttributionSource;
@@ -41,7 +40,7 @@
 import com.android.bluetooth.btservice.ProfileService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.bluetooth.hfpclient.HfpClientConnectionService;
-import com.android.bluetooth.sdp.SdpManager;
+import com.android.bluetooth.sdp.SdpManagerNativeInterface;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.SynchronousResultReceiver;
 
@@ -140,9 +139,6 @@
         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
         // delay initial download until after the user is unlocked to add an account.
         filter.addAction(Intent.ACTION_USER_UNLOCKED);
-        // To remove call logs when PBAP was never connected while calls were made,
-        // we also listen for HFP to become disconnected.
-        filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
         try {
             registerReceiver(mPbapBroadcastReceiver, filter);
         } catch (Exception e) {
@@ -179,6 +175,7 @@
             PbapClientStateMachine pbapClientStateMachine = mPbapClientStateMachineMap.get(device);
             if (pbapClientStateMachine != null) {
                 mPbapClientStateMachineMap.remove(device);
+                pbapClientStateMachine.doQuit();
             }
         }
     }
@@ -257,13 +254,14 @@
     }
 
     private void registerSdpRecord() {
-        SdpManager sdpManager = SdpManager.getDefaultManager();
-        if (sdpManager == null) {
-            Log.e(TAG, "SdpManager is null");
+        SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
+        if (!nativeInterface.isAvailable()) {
+            Log.e(TAG, "SdpManagerNativeInterface is not available");
             return;
         }
-        mSdpHandle = sdpManager.createPbapPceRecord(SERVICE_NAME,
-                PbapClientConnectionHandler.PBAP_V1_2);
+        mSdpHandle =
+                nativeInterface.createPbapPceRecord(
+                        SERVICE_NAME, PbapClientConnectionHandler.PBAP_V1_2);
     }
 
     private void cleanUpSdpRecord() {
@@ -273,13 +271,17 @@
         }
         int sdpHandle = mSdpHandle;
         mSdpHandle = -1;
-        SdpManager sdpManager = SdpManager.getDefaultManager();
-        if (sdpManager == null) {
-            Log.e(TAG, "cleanUpSdpRecord failed, sdpManager is null, sdpHandle=" + sdpHandle);
+        SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
+        if (!nativeInterface.isAvailable()) {
+            Log.e(
+                    TAG,
+                    "cleanUpSdpRecord failed, SdpManagerNativeInterface is not available,"
+                            + " sdpHandle="
+                            + sdpHandle);
             return;
         }
         Log.i(TAG, "cleanUpSdpRecord, mSdpHandle=" + sdpHandle);
-        if (!sdpManager.removeSdpRecord(sdpHandle)) {
+        if (!nativeInterface.removeSdpRecord(sdpHandle)) {
             Log.e(TAG, "cleanUpSdpRecord, removeSdpRecord failed, sdpHandle=" + sdpHandle);
         }
     }
@@ -310,26 +312,28 @@
                 for (PbapClientStateMachine stateMachine : mPbapClientStateMachineMap.values()) {
                     stateMachine.tryDownloadIfConnected();
                 }
-            } else if (action.equals(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED)) {
-                // PbapClientConnectionHandler has code to remove calllogs when PBAP disconnects.
-                // However, if PBAP was never connected/enabled in the first place, and calls are
-                // made over HFP, these calllogs will not be removed when the device disconnects.
-                // This code ensures callogs are still removed in this case.
-                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-
-                if (newState == BluetoothProfile.STATE_DISCONNECTED) {
-                    if (DBG) {
-                        Log.d(TAG, "Received intent to disconnect HFP with " + device);
-                    }
-                    // HFP client stores entries in calllog.db by BD_ADDR and component name
-                    removeHfpCallLog(device.getAddress(), context);
-                }
             }
         }
     }
 
     /**
+     * Ensure that after HFP disconnects, we remove call logs. This addresses the situation when
+     * PBAP was never connected while calls were made. Ideally {@link PbapClientConnectionHandler}
+     * has code to remove calllogs when PBAP disconnects.
+     */
+    public void handleHeadsetClientConnectionStateChanged(
+            BluetoothDevice device, int oldState, int newState) {
+        if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+            if (DBG) {
+                Log.d(TAG, "Received intent to disconnect HFP with " + device);
+            }
+            // HFP client stores entries in calllog.db by BD_ADDR and component name
+            // Using the current Service as the context.
+            removeHfpCallLog(device.getAddress(), this);
+        }
+    }
+
+    /**
      * Handler for incoming service calls
      */
     @VisibleForTesting
diff --git a/android/app/src/com/android/bluetooth/sap/SapService.java b/android/app/src/com/android/bluetooth/sap/SapService.java
index 55b6af2..7915083 100644
--- a/android/app/src/com/android/bluetooth/sap/SapService.java
+++ b/android/app/src/com/android/bluetooth/sap/SapService.java
@@ -3,7 +3,6 @@
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
 
 import android.annotation.RequiresPermission;
-import android.annotation.TargetApi;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
@@ -19,7 +18,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.os.ParcelUuid;
@@ -35,7 +33,7 @@
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.MetricsLogger;
 import com.android.bluetooth.btservice.ProfileService;
-import com.android.bluetooth.sdp.SdpManager;
+import com.android.bluetooth.sdp.SdpManagerNativeInterface;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.modules.utils.SynchronousResultReceiver;
 
@@ -43,7 +41,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-public class SapService extends ProfileService {
+public class SapService extends ProfileService implements AdapterService.BluetoothStateCallback {
 
     private static final String SDP_SAP_SERVICE_NAME = "SIM Access";
     private static final int SDP_SAP_VERSION = 0x0102;
@@ -133,11 +131,12 @@
     }
 
     private void removeSdpRecord() {
-        if (mAdapterService != null && mSdpHandle >= 0 && SdpManager.getDefaultManager() != null) {
+        SdpManagerNativeInterface nativeInterface = SdpManagerNativeInterface.getInstance();
+        if (mAdapterService != null && mSdpHandle >= 0 && nativeInterface.isAvailable()) {
             if (VERBOSE) {
                 Log.d(TAG, "Removing SDP record handle: " + mSdpHandle);
             }
-            boolean status = SdpManager.getDefaultManager().removeSdpRecord(mSdpHandle);
+            nativeInterface.removeSdpRecord(mSdpHandle);
             mSdpHandle = -1;
         }
     }
@@ -173,9 +172,12 @@
                 mServerSocket = BluetoothAdapter.getDefaultAdapter().listenUsingRfcommOn(
                         BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, true, true);
                 removeSdpRecord();
-                mSdpHandle = SdpManager.getDefaultManager()
-                        .createSapsRecord(SDP_SAP_SERVICE_NAME, mServerSocket.getChannel(),
-                                SDP_SAP_VERSION);
+                mSdpHandle =
+                        SdpManagerNativeInterface.getInstance()
+                                .createSapsRecord(
+                                        SDP_SAP_SERVICE_NAME,
+                                        mServerSocket.getChannel(),
+                                        SDP_SAP_VERSION);
             } catch (IOException e) {
                 Log.e(TAG, "Error create RfcommServerSocket ", e);
                 initSocketOK = false;
@@ -680,7 +682,6 @@
         IntentFilter filter = new IntentFilter();
         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
-        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
         filter.addAction(USER_CONFIRM_TIMEOUT_ACTION);
 
@@ -692,6 +693,7 @@
         }
         mInterrupted = false;
         mAdapterService = AdapterService.getAdapterService();
+        mAdapterService.registerBluetoothStateCallback(getMainExecutor(), this);
         // start RFCOMM listener
         mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
         setSapService(this);
@@ -712,6 +714,7 @@
         } catch (Exception e) {
             Log.w(TAG, "Unable to unregister sap receiver", e);
         }
+        mAdapterService.unregisterBluetoothStateCallback(this);
         setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
         sendShutdownMessage();
         return true;
@@ -726,6 +729,22 @@
         }
     }
 
+    @Override
+    public void onBluetoothStateChange(int prevState, int newState) {
+        if (newState == BluetoothAdapter.STATE_TURNING_OFF) {
+            if (DEBUG) {
+                Log.d(TAG, "STATE_TURNING_OFF");
+            }
+            sendShutdownMessage();
+        } else if (newState == BluetoothAdapter.STATE_ON) {
+            if (DEBUG) {
+                Log.d(TAG, "STATE_ON");
+            }
+            // start RFCOMM listener
+            mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
+        }
+    }
+
     /**
      * Get the current instance of {@link SapService}
      *
@@ -827,25 +846,6 @@
                 Log.v(TAG, "onReceive");
             }
             String action = intent.getAction();
-            if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
-                int state =
-                        intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
-                if (state == BluetoothAdapter.STATE_TURNING_OFF) {
-                    if (DEBUG) {
-                        Log.d(TAG, "STATE_TURNING_OFF");
-                    }
-                    sendShutdownMessage();
-                } else if (state == BluetoothAdapter.STATE_ON) {
-                    if (DEBUG) {
-                        Log.d(TAG, "STATE_ON");
-                    }
-                    // start RFCOMM listener
-                    mSessionStatusHandler.sendMessage(
-                            mSessionStatusHandler.obtainMessage(START_LISTENER));
-                }
-                return;
-            }
-
             if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
                 Log.v(TAG, " - Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY");
 
diff --git a/android/app/src/com/android/bluetooth/sdp/SdpManager.java b/android/app/src/com/android/bluetooth/sdp/SdpManager.java
index 3aa94c5..169be31 100644
--- a/android/app/src/com/android/bluetooth/sdp/SdpManager.java
+++ b/android/app/src/com/android/bluetooth/sdp/SdpManager.java
@@ -39,10 +39,10 @@
 import java.util.Arrays;
 
 public class SdpManager {
+    private static final String TAG = SdpManager.class.getSimpleName();
 
     private static final boolean D = true;
     private static final boolean V = false;
-    private static final String TAG = "SdpManager";
 
     // TODO: When changing PBAP to use this new API.
     //       Move the defines to the profile (PBAP already have the feature bits)
@@ -70,38 +70,8 @@
     // This object is a singleton
     private static SdpManager sSdpManager = null;
 
-    static {
-        classInitNative();
-    }
-
-    private static native void classInitNative();
-
-    private native void initializeNative();
-
-    private native void cleanupNative();
-
-    private native boolean sdpSearchNative(byte[] address, byte[] uuid);
-
-    private native int sdpCreateMapMasRecordNative(String serviceName, int masId, int rfcommChannel,
-            int l2capPsm, int version, int msgTypes, int features);
-
-    private native int sdpCreateMapMnsRecordNative(String serviceName, int rfcommChannel,
-            int l2capPsm, int version, int features);
-
-    private native int sdpCreatePbapPceRecordNative(String serviceName,
-            int version);
-
-    private native int sdpCreatePbapPseRecordNative(String serviceName, int rfcommChannel,
-            int l2capPsm, int version, int repositories, int features);
-
-    private native int sdpCreateOppOpsRecordNative(String serviceName, int rfcommChannel,
-            int l2capPsm, int version, byte[] formatsList);
-
-    private native int sdpCreateSapsRecordNative(String serviceName, int rfcommChannel,
-            int version);
-
-    private native boolean sdpRemoveSdpRecordNative(int recordId);
-
+    private final SdpManagerNativeInterface mNativeInterface =
+            SdpManagerNativeInterface.getInstance();
 
     /* Inner class used for wrapping sdp search instance data */
     private class SdpSearchInstance {
@@ -212,7 +182,7 @@
     private SdpManager(AdapterService adapterService) {
         sSdpSearchTracker = new SdpSearchTracker();
         sAdapterService = adapterService;
-        initializeNative();
+        mNativeInterface.init(this);
         sNativeAvailable = true;
     }
 
@@ -234,7 +204,7 @@
         }
 
         if (sNativeAvailable) {
-            cleanupNative();
+            mNativeInterface.cleanup();
             sNativeAvailable = false;
         }
         sSdpManager = null;
@@ -249,7 +219,7 @@
             SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
             SdpMasRecord sdpRecord = null;
             if (inst == null) {
-                Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL");
+                Log.e(TAG, "sdpMasRecordFoundCallback: Search instance is NULL");
                 return;
             }
             inst.setStatus(status);
@@ -275,7 +245,7 @@
             SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
             SdpMnsRecord sdpRecord = null;
             if (inst == null) {
-                Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL");
+                Log.e(TAG, "sdpMnsRecordFoundCallback: Search instance is NULL");
                 return;
             }
             inst.setStatus(status);
@@ -300,7 +270,7 @@
             SdpSearchInstance inst = sSdpSearchTracker.getSearchInstance(address, uuid);
             SdpPseRecord sdpRecord = null;
             if (inst == null) {
-                Log.e(TAG, "sdpRecordFoundCallback: Search instance is NULL");
+                Log.e(TAG, "sdpPseRecordFoundCallback: Search instance is NULL");
                 return;
             }
             inst.setStatus(status);
@@ -465,7 +435,8 @@
 
             inst.startSearch(); // Trigger timeout message
 
-            sdpSearchNative(sAdapterService.getByteIdentityAddress(inst.getDevice()),
+            mNativeInterface.sdpSearch(
+                    sAdapterService.getByteIdentityAddress(inst.getDevice()),
                     Utils.uuidToByteArray(inst.getUuid()));
         } else { // Else queue is empty.
             if (D) {
@@ -517,182 +488,4 @@
             }
         }
     };
-
-    /**
-     * Create a server side Message Access Profile Service Record.
-     * Create the record once, and reuse it for all connections.
-     * If changes to a record is needed remove the old record using {@link removeSdpRecord}
-     * and then create a new one.
-     * @param serviceName   The textual name of the service
-     * @param masId         The MAS ID to associate with this SDP record
-     * @param rfcommChannel The RFCOMM channel that clients can connect to
-     *                      (obtain from BluetoothServerSocket)
-     * @param l2capPsm      The L2CAP PSM channel that clients can connect to
-     *                      (obtain from BluetoothServerSocket)
-     *                      Supply -1 to omit the L2CAP PSM from the record.
-     * @param version       The Profile version number (As specified in the Bluetooth
-     *                      MAP specification)
-     * @param msgTypes      The supported message types bit mask (As specified in
-     *                      the Bluetooth MAP specification)
-     * @param features      The feature bit mask (As specified in the Bluetooth
-     *                       MAP specification)
-     * @return a handle to the record created. The record can be removed again
-     *          using {@link removeSdpRecord}(). The record is not linked to the
-     *          creation/destruction of BluetoothSockets, hence SDP record cleanup
-     *          is a separate process.
-     */
-    public int createMapMasRecord(String serviceName, int masId, int rfcommChannel, int l2capPsm,
-            int version, int msgTypes, int features) {
-        if (!sNativeAvailable) {
-            throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
-        }
-        return sdpCreateMapMasRecordNative(serviceName, masId, rfcommChannel, l2capPsm, version,
-                msgTypes, features);
-    }
-
-    /**
-     * Create a client side Message Access Profile Service Record.
-     * Create the record once, and reuse it for all connections.
-     * If changes to a record is needed remove the old record using {@link removeSdpRecord}
-     * and then create a new one.
-     * @param serviceName   The textual name of the service
-     * @param rfcommChannel The RFCOMM channel that clients can connect to
-     *                      (obtain from BluetoothServerSocket)
-     * @param l2capPsm      The L2CAP PSM channel that clients can connect to
-     *                      (obtain from BluetoothServerSocket)
-     *                      Supply -1 to omit the L2CAP PSM from the record.
-     * @param version       The Profile version number (As specified in the Bluetooth
-     *                      MAP specification)
-     * @param features      The feature bit mask (As specified in the Bluetooth
-     *                       MAP specification)
-     * @return a handle to the record created. The record can be removed again
-     *          using {@link removeSdpRecord}(). The record is not linked to the
-     *          creation/destruction of BluetoothSockets, hence SDP record cleanup
-     *          is a separate process.
-     */
-    public int createMapMnsRecord(String serviceName, int rfcommChannel, int l2capPsm, int version,
-            int features) {
-        if (!sNativeAvailable) {
-            throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
-        }
-        return sdpCreateMapMnsRecordNative(serviceName, rfcommChannel, l2capPsm, version, features);
-    }
-
-     /**
-     * Create a Client side Phone Book Access Profile Service Record.
-     * Create the record once, and reuse it for all connections.
-     * If changes to a record is needed remove the old record using {@link removeSdpRecord}
-     * and then create a new one.
-     * @param serviceName   The textual name of the service
-     * @param version       The Profile version number (As specified in the Bluetooth
-     *                      PBAP specification)
-     * @return a handle to the record created. The record can be removed again
-     *          using {@link removeSdpRecord}(). The record is not linked to the
-     *          creation/destruction of BluetoothSockets, hence SDP record cleanup
-     *          is a separate process.
-     */
-    public int createPbapPceRecord(String serviceName, int version) {
-        if (!sNativeAvailable) {
-            throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
-        }
-        return sdpCreatePbapPceRecordNative(serviceName, version);
-    }
-
-
-    /**
-     * Create a Server side Phone Book Access Profile Service Record.
-     * Create the record once, and reuse it for all connections.
-     * If changes to a record is needed remove the old record using {@link removeSdpRecord}
-     * and then create a new one.
-     * @param serviceName   The textual name of the service
-     * @param rfcommChannel The RFCOMM channel that clients can connect to
-     *                      (obtain from BluetoothServerSocket)
-     * @param l2capPsm      The L2CAP PSM channel that clients can connect to
-     *                      (obtain from BluetoothServerSocket)
-     *                      Supply -1 to omit the L2CAP PSM from the record.
-     * @param version       The Profile version number (As specified in the Bluetooth
-     *                      PBAP specification)
-     * @param repositories  The supported repositories bit mask (As specified in
-     *                      the Bluetooth PBAP specification)
-     * @param features      The feature bit mask (As specified in the Bluetooth
-     *                      PBAP specification)
-     * @return a handle to the record created. The record can be removed again
-     *          using {@link removeSdpRecord}(). The record is not linked to the
-     *          creation/destruction of BluetoothSockets, hence SDP record cleanup
-     *          is a separate process.
-     */
-    public int createPbapPseRecord(String serviceName, int rfcommChannel, int l2capPsm, int version,
-            int repositories, int features) {
-        if (!sNativeAvailable) {
-            throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
-        }
-        return sdpCreatePbapPseRecordNative(serviceName, rfcommChannel, l2capPsm, version,
-                repositories, features);
-    }
-
-    /**
-     * Create a Server side Object Push Profile Service Record.
-     * Create the record once, and reuse it for all connections.
-     * If changes to a record is needed remove the old record using {@link removeSdpRecord}
-     * and then create a new one.
-     * @param serviceName   The textual name of the service
-     * @param rfcommChannel The RFCOMM channel that clients can connect to
-     *                      (obtain from BluetoothServerSocket)
-     * @param l2capPsm      The L2CAP PSM channel that clients can connect to
-     *                      (obtain from BluetoothServerSocket)
-     *                      Supply -1 to omit the L2CAP PSM from the record.
-     * @param version       The Profile version number (As specified in the Bluetooth
-     *                      OPP specification)
-     * @param formatsList  A list of the supported formats (As specified in
-     *                      the Bluetooth OPP specification)
-     * @return a handle to the record created. The record can be removed again
-     *          using {@link removeSdpRecord}(). The record is not linked to the
-     *          creation/destruction of BluetoothSockets, hence SDP record cleanup
-     *          is a separate process.
-     */
-    public int createOppOpsRecord(String serviceName, int rfcommChannel, int l2capPsm, int version,
-            byte[] formatsList) {
-        if (!sNativeAvailable) {
-            throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
-        }
-        return sdpCreateOppOpsRecordNative(serviceName, rfcommChannel, l2capPsm, version,
-                formatsList);
-    }
-
-    /**
-     * Create a server side Sim Access Profile Service Record.
-     * Create the record once, and reuse it for all connections.
-     * If changes to a record is needed remove the old record using {@link removeSdpRecord}
-     * and then create a new one.
-     * @param serviceName   The textual name of the service
-     * @param rfcommChannel The RFCOMM channel that clients can connect to
-     *                      (obtain from BluetoothServerSocket)
-     * @param version       The Profile version number (As specified in the Bluetooth
-     *                      SAP specification)
-     * @return a handle to the record created. The record can be removed again
-     *          using {@link removeSdpRecord}(). The record is not linked to the
-     *          creation/destruction of BluetoothSockets, hence SDP record cleanup
-     *          is a separate process.
-     */
-    public int createSapsRecord(String serviceName, int rfcommChannel, int version) {
-        if (!sNativeAvailable) {
-            throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
-        }
-        return sdpCreateSapsRecordNative(serviceName, rfcommChannel, version);
-    }
-
-    /**
-     * Remove a SDP record.
-     * When Bluetooth is disabled all records will be deleted, hence there
-     * is no need to call this function when bluetooth is disabled.
-     * @param recordId The Id returned by on of the createXxxXxxRecord() functions.
-     * @return TRUE if the record removal was initiated successfully. FALSE if the record
-     *         handle is not known/have already been removed.
-     */
-    public boolean removeSdpRecord(int recordId) {
-        if (!sNativeAvailable) {
-            throw new RuntimeException(TAG + " sNativeAvailable == false - native not initialized");
-        }
-        return sdpRemoveSdpRecordNative(recordId);
-    }
 }
diff --git a/android/app/src/com/android/bluetooth/sdp/SdpManagerNativeInterface.java b/android/app/src/com/android/bluetooth/sdp/SdpManagerNativeInterface.java
new file mode 100644
index 0000000..f009dd9
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/sdp/SdpManagerNativeInterface.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.sdp;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/** Native interface to be used by SdpManager */
+public class SdpManagerNativeInterface {
+    private static final String TAG = SdpManagerNativeInterface.class.getSimpleName();
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static SdpManagerNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
+    private SdpManager mSdpManager;
+    private boolean mNativeAvailable = false;
+
+    /** This class is a singleton because native library should only be loaded once */
+    public static SdpManagerNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new SdpManagerNativeInterface();
+            }
+            return sInstance;
+        }
+    }
+
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(SdpManagerNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
+    void init(SdpManager sdpManager) {
+        mSdpManager = sdpManager;
+        initializeNative();
+        mNativeAvailable = true;
+    }
+
+    void cleanup() {
+        mNativeAvailable = false;
+        cleanupNative();
+    }
+
+    public boolean isAvailable() {
+        return mNativeAvailable;
+    }
+
+    void sdpSearch(byte[] address, byte[] uuid) {
+        sdpSearchNative(address, uuid);
+    }
+
+    /**
+     * Create a server side Message Access Profile Service Record. Create the record once, and reuse
+     * it for all connections. If changes to a record is needed remove the old record using {@link
+     * removeSdpRecord} and then create a new one.
+     *
+     * @param serviceName The textual name of the service
+     * @param masId The MAS ID to associate with this SDP record
+     * @param rfcommChannel The RFCOMM channel that clients can connect to (obtain from
+     *     BluetoothServerSocket)
+     * @param l2capPsm The L2CAP PSM channel that clients can connect to (obtain from
+     *     BluetoothServerSocket) Supply -1 to omit the L2CAP PSM from the record.
+     * @param version The Profile version number (As specified in the Bluetooth MAP specification)
+     * @param msgTypes The supported message types bit mask (As specified in the Bluetooth MAP
+     *     specification)
+     * @param features The feature bit mask (As specified in the Bluetooth MAP specification)
+     * @return a handle to the record created. The record can be removed again using {@link
+     *     removeSdpRecord}(). The record is not linked to the creation/destruction of
+     *     BluetoothSockets, hence SDP record cleanup is a separate process.
+     */
+    public int createMapMasRecord(
+            String serviceName,
+            int masId,
+            int rfcommChannel,
+            int l2capPsm,
+            int version,
+            int msgTypes,
+            int features) {
+        if (!mNativeAvailable) {
+            throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
+        }
+        return sdpCreateMapMasRecordNative(
+                serviceName, masId, rfcommChannel, l2capPsm, version, msgTypes, features);
+    }
+
+    /**
+     * Create a client side Message Access Profile Service Record. Create the record once, and reuse
+     * it for all connections. If changes to a record is needed remove the old record using {@link
+     * removeSdpRecord} and then create a new one.
+     *
+     * @param serviceName The textual name of the service
+     * @param rfcommChannel The RFCOMM channel that clients can connect to (obtain from
+     *     BluetoothServerSocket)
+     * @param l2capPsm The L2CAP PSM channel that clients can connect to (obtain from
+     *     BluetoothServerSocket) Supply -1 to omit the L2CAP PSM from the record.
+     * @param version The Profile version number (As specified in the Bluetooth MAP specification)
+     * @param features The feature bit mask (As specified in the Bluetooth MAP specification)
+     * @return a handle to the record created. The record can be removed again using {@link
+     *     removeSdpRecord}(). The record is not linked to the creation/destruction of
+     *     BluetoothSockets, hence SDP record cleanup is a separate process.
+     */
+    public int createMapMnsRecord(
+            String serviceName, int rfcommChannel, int l2capPsm, int version, int features) {
+        if (!mNativeAvailable) {
+            throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
+        }
+        return sdpCreateMapMnsRecordNative(serviceName, rfcommChannel, l2capPsm, version, features);
+    }
+
+    /**
+     * Create a Client side Phone Book Access Profile Service Record. Create the record once, and
+     * reuse it for all connections. If changes to a record is needed remove the old record using
+     * {@link removeSdpRecord} and then create a new one.
+     *
+     * @param serviceName The textual name of the service
+     * @param version The Profile version number (As specified in the Bluetooth PBAP specification)
+     * @return a handle to the record created. The record can be removed again using {@link
+     *     removeSdpRecord}(). The record is not linked to the creation/destruction of
+     *     BluetoothSockets, hence SDP record cleanup is a separate process.
+     */
+    public int createPbapPceRecord(String serviceName, int version) {
+        if (!mNativeAvailable) {
+            throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
+        }
+        return sdpCreatePbapPceRecordNative(serviceName, version);
+    }
+
+    /**
+     * Create a Server side Phone Book Access Profile Service Record. Create the record once, and
+     * reuse it for all connections. If changes to a record is needed remove the old record using
+     * {@link removeSdpRecord} and then create a new one.
+     *
+     * @param serviceName The textual name of the service
+     * @param rfcommChannel The RFCOMM channel that clients can connect to (obtain from
+     *     BluetoothServerSocket)
+     * @param l2capPsm The L2CAP PSM channel that clients can connect to (obtain from
+     *     BluetoothServerSocket) Supply -1 to omit the L2CAP PSM from the record.
+     * @param version The Profile version number (As specified in the Bluetooth PBAP specification)
+     * @param repositories The supported repositories bit mask (As specified in the Bluetooth PBAP
+     *     specification)
+     * @param features The feature bit mask (As specified in the Bluetooth PBAP specification)
+     * @return a handle to the record created. The record can be removed again using {@link
+     *     removeSdpRecord}(). The record is not linked to the creation/destruction of
+     *     BluetoothSockets, hence SDP record cleanup is a separate process.
+     */
+    public int createPbapPseRecord(
+            String serviceName,
+            int rfcommChannel,
+            int l2capPsm,
+            int version,
+            int repositories,
+            int features) {
+        if (!mNativeAvailable) {
+            throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
+        }
+        return sdpCreatePbapPseRecordNative(
+                serviceName, rfcommChannel, l2capPsm, version, repositories, features);
+    }
+
+    /**
+     * Create a Server side Object Push Profile Service Record. Create the record once, and reuse it
+     * for all connections. If changes to a record is needed remove the old record using {@link
+     * removeSdpRecord} and then create a new one.
+     *
+     * @param serviceName The textual name of the service
+     * @param rfcommChannel The RFCOMM channel that clients can connect to (obtain from
+     *     BluetoothServerSocket)
+     * @param l2capPsm The L2CAP PSM channel that clients can connect to (obtain from
+     *     BluetoothServerSocket) Supply -1 to omit the L2CAP PSM from the record.
+     * @param version The Profile version number (As specified in the Bluetooth OPP specification)
+     * @param formatsList A list of the supported formats (As specified in the Bluetooth OPP
+     *     specification)
+     * @return a handle to the record created. The record can be removed again using {@link
+     *     removeSdpRecord}(). The record is not linked to the creation/destruction of
+     *     BluetoothSockets, hence SDP record cleanup is a separate process.
+     */
+    public int createOppOpsRecord(
+            String serviceName, int rfcommChannel, int l2capPsm, int version, byte[] formatsList) {
+        if (!mNativeAvailable) {
+            throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
+        }
+        return sdpCreateOppOpsRecordNative(
+                serviceName, rfcommChannel, l2capPsm, version, formatsList);
+    }
+
+    /**
+     * Create a server side Sim Access Profile Service Record. Create the record once, and reuse it
+     * for all connections. If changes to a record is needed remove the old record using {@link
+     * removeSdpRecord} and then create a new one.
+     *
+     * @param serviceName The textual name of the service
+     * @param rfcommChannel The RFCOMM channel that clients can connect to (obtain from
+     *     BluetoothServerSocket)
+     * @param version The Profile version number (As specified in the Bluetooth SAP specification)
+     * @return a handle to the record created. The record can be removed again using {@link
+     *     removeSdpRecord}(). The record is not linked to the creation/destruction of
+     *     BluetoothSockets, hence SDP record cleanup is a separate process.
+     */
+    public int createSapsRecord(String serviceName, int rfcommChannel, int version) {
+        if (!mNativeAvailable) {
+            throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
+        }
+        return sdpCreateSapsRecordNative(serviceName, rfcommChannel, version);
+    }
+
+    /**
+     * Remove a SDP record. When Bluetooth is disabled all records will be deleted, hence there is
+     * no need to call this function when bluetooth is disabled.
+     *
+     * @param recordId The Id returned by on of the createXxxXxxRecord() functions.
+     * @return TRUE if the record removal was initiated successfully. FALSE if the record handle is
+     *     not known/have already been removed.
+     */
+    public boolean removeSdpRecord(int recordId) {
+        if (!mNativeAvailable) {
+            throw new RuntimeException(TAG + " mNativeAvailable == false - native not initialized");
+        }
+        return sdpRemoveSdpRecordNative(recordId);
+    }
+
+    /**********************************************************************************************/
+    /*********************************** callbacks from native ************************************/
+    /**********************************************************************************************/
+
+    void sdpRecordFoundCallback(
+            int status, byte[] address, byte[] uuid, int sizeRecord, byte[] record) {
+        mSdpManager.sdpRecordFoundCallback(status, address, uuid, sizeRecord, record);
+    }
+
+    void sdpMasRecordFoundCallback(
+            int status,
+            byte[] address,
+            byte[] uuid,
+            int masInstanceId,
+            int l2capPsm,
+            int rfcommCannelNumber,
+            int profileVersion,
+            int supportedFeatures,
+            int supportedMessageTypes,
+            String serviceName,
+            boolean moreResults) {
+        mSdpManager.sdpMasRecordFoundCallback(
+                status,
+                address,
+                uuid,
+                masInstanceId,
+                l2capPsm,
+                rfcommCannelNumber,
+                profileVersion,
+                supportedFeatures,
+                supportedMessageTypes,
+                serviceName,
+                moreResults);
+    }
+
+    void sdpMnsRecordFoundCallback(
+            int status,
+            byte[] address,
+            byte[] uuid,
+            int l2capPsm,
+            int rfcommCannelNumber,
+            int profileVersion,
+            int supportedFeatures,
+            String serviceName,
+            boolean moreResults) {
+        mSdpManager.sdpMnsRecordFoundCallback(
+                status,
+                address,
+                uuid,
+                l2capPsm,
+                rfcommCannelNumber,
+                profileVersion,
+                supportedFeatures,
+                serviceName,
+                moreResults);
+    }
+
+    void sdpPseRecordFoundCallback(
+            int status,
+            byte[] address,
+            byte[] uuid,
+            int l2capPsm,
+            int rfcommCannelNumber,
+            int profileVersion,
+            int supportedFeatures,
+            int supportedRepositories,
+            String serviceName,
+            boolean moreResults) {
+        mSdpManager.sdpPseRecordFoundCallback(
+                status,
+                address,
+                uuid,
+                l2capPsm,
+                rfcommCannelNumber,
+                profileVersion,
+                supportedFeatures,
+                supportedRepositories,
+                serviceName,
+                moreResults);
+    }
+
+    void sdpOppOpsRecordFoundCallback(
+            int status,
+            byte[] address,
+            byte[] uuid,
+            int l2capPsm,
+            int rfcommCannelNumber,
+            int profileVersion,
+            String serviceName,
+            byte[] formatsList,
+            boolean moreResults) {
+        mSdpManager.sdpOppOpsRecordFoundCallback(
+                status,
+                address,
+                uuid,
+                l2capPsm,
+                rfcommCannelNumber,
+                profileVersion,
+                serviceName,
+                formatsList,
+                moreResults);
+    }
+
+    void sdpSapsRecordFoundCallback(
+            int status,
+            byte[] address,
+            byte[] uuid,
+            int rfcommCannelNumber,
+            int profileVersion,
+            String serviceName,
+            boolean moreResults) {
+        mSdpManager.sdpSapsRecordFoundCallback(
+                status,
+                address,
+                uuid,
+                rfcommCannelNumber,
+                profileVersion,
+                serviceName,
+                moreResults);
+    }
+
+    void sdpDipRecordFoundCallback(
+            int status,
+            byte[] address,
+            byte[] uuid,
+            int specificationId,
+            int vendorId,
+            int vendorIdSource,
+            int productId,
+            int version,
+            boolean primaryRecord,
+            boolean moreResults) {
+        mSdpManager.sdpDipRecordFoundCallback(
+                status,
+                address,
+                uuid,
+                specificationId,
+                vendorId,
+                vendorIdSource,
+                productId,
+                version,
+                primaryRecord,
+                moreResults);
+    }
+
+    /**********************************************************************************************/
+    /******************************************* native *******************************************/
+    /**********************************************************************************************/
+
+    private native void initializeNative();
+
+    private native void cleanupNative();
+
+    private native boolean sdpSearchNative(byte[] address, byte[] uuid);
+
+    private native int sdpCreateMapMasRecordNative(
+            String serviceName,
+            int masId,
+            int rfcommChannel,
+            int l2capPsm,
+            int version,
+            int msgTypes,
+            int features);
+
+    private native int sdpCreateMapMnsRecordNative(
+            String serviceName, int rfcommChannel, int l2capPsm, int version, int features);
+
+    private native int sdpCreatePbapPceRecordNative(String serviceName, int version);
+
+    private native int sdpCreatePbapPseRecordNative(
+            String serviceName,
+            int rfcommChannel,
+            int l2capPsm,
+            int version,
+            int repositories,
+            int features);
+
+    private native int sdpCreateOppOpsRecordNative(
+            String serviceName, int rfcommChannel, int l2capPsm, int version, byte[] formatsList);
+
+    private native int sdpCreateSapsRecordNative(
+            String serviceName, int rfcommChannel, int version);
+
+    private native boolean sdpRemoveSdpRecordNative(int recordId);
+}
diff --git a/android/app/src/com/android/bluetooth/telephony/BluetoothCall.java b/android/app/src/com/android/bluetooth/telephony/BluetoothCall.java
index 12856cc..20296e3 100644
--- a/android/app/src/com/android/bluetooth/telephony/BluetoothCall.java
+++ b/android/app/src/com/android/bluetooth/telephony/BluetoothCall.java
@@ -48,6 +48,9 @@
     private Call mCall;
     private UUID mCallId;
 
+    // An index used to identify calls for CLCC (C* List Current Calls).
+    int mClccIndex = -1;
+
     public Call getCall() {
         return mCall;
     }
diff --git a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
index 3ff9dce..943d970 100644
--- a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
+++ b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
@@ -59,11 +59,12 @@
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Queue;
 import java.util.SortedMap;
+import java.util.SortedSet;
 import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.UUID;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -139,14 +140,9 @@
     private final HashMap<Integer, BluetoothCall> mBluetoothConferenceCallInference =
             new HashMap<>();
 
-    private final Map<String, Integer> mClccInferenceIndexMap = new HashMap<>();
-
     // A queue record the removal order of bluetooth calls
     private final Queue<Integer> mBluetoothCallQueue = new ArrayDeque<>();
 
-    // A map from Calls to indexes used to identify calls for CLCC (C* List Current Calls).
-    private final Map<String, Integer> mClccIndexMap = new HashMap<>();
-
     private static BluetoothInCallService sInstance = null;
 
     public CallInfo mCallInfo = new CallInfo();
@@ -652,11 +648,6 @@
                 Log.d(TAG, "add inference call with reason: " + cause.getReason());
                 mBluetoothCallQueue.add(call.getId());
                 mBluetoothConferenceCallInference.put(call.getId(), call);
-                Integer indexValue = mClccIndexMap.get(getClccMapKey(call));
-                mClccInferenceIndexMap.put(getClccMapKey(call), indexValue);
-                if (indexValue == null) {
-                    Log.w(TAG, "CLCC index value is null");
-                }
                 // queue size limited to 2 because merge operation only happens on 2 calls
                 // we are only interested in last 2 calls merged
                 if (mBluetoothCallQueue.size() > 2) {
@@ -669,12 +660,10 @@
             if (call.isConference()) {
                 Log.d(TAG, "conference call ends, clear inference");
                 mBluetoothConferenceCallInference.clear();
-                mClccInferenceIndexMap.clear();
                 mBluetoothCallQueue.clear();
             }
         }
 
-        mClccIndexMap.remove(getClccMapKey(call));
         updateHeadsetWithCallState(false /* force */);
 
         if (mBluetoothLeCallControl != null) {
@@ -748,9 +737,7 @@
         sInstance = null;
         mCallbacks.clear();
         mBluetoothCallHashMap.clear();
-        mClccIndexMap.clear();
         mBluetoothConferenceCallInference.clear();
-        mClccInferenceIndexMap.clear();
         mBluetoothCallQueue.clear();
         mMaxNumberOfCalls = 0;
     }
@@ -766,10 +753,13 @@
         boolean isInferenceEnabled =
                 DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_BLUETOOTH, CLCC_INFERENCE, false);
         Log.d(TAG, "is conference call inference enabled: " + isInferenceEnabled);
+        // either do conference call CLCC index inference or normal conference call
+        BluetoothCall conferenceCallChildrenNotReady = null;
         for (BluetoothCall call : calls) {
-            if (isInferenceEnabled && call.isConference()
+            // find the conference call parent among calls
+            if (isInferenceEnabled
+                    && call.isConference()
                     && !mBluetoothConferenceCallInference.isEmpty()) {
-                SortedMap<Integer, Object[]> clccResponseMap = new TreeMap<>();
                 Log.d(
                         TAG,
                         "conference call inferred size: "
@@ -777,83 +767,115 @@
                                 + " current size: "
                                 + mBluetoothCallHashMap.size());
                 // Do conference call inference until at least 2 children arrive
-                // If carrier does send children info, then inference will end when info arrives.
-                // If carrier does not send children info, then inference won't impact actual value.
+                // If carrier sends children info, then inference will end when info arrives.
+                // If carrier doesn't send children info, then inference won't impact actual value.
                 if (call.getChildrenIds().size() >= 2) {
                     mBluetoothConferenceCallInference.clear();
                     break;
                 }
-                for (BluetoothCall inferredCall : mBluetoothConferenceCallInference.values()) {
-                    String clccMapKey = getClccMapKey(inferredCall);
-                    if (!mClccInferenceIndexMap.containsKey(clccMapKey)) {
-                        Log.w(TAG, "Inference Index Map does not have: " + clccMapKey);
-                        continue;
-                    }
-                    if (mClccInferenceIndexMap.get(clccMapKey) == null) {
-                        Log.w(TAG, "inferred index is null");
-                        continue;
-                    }
-                    int index = mClccInferenceIndexMap.get(clccMapKey);
-                    // save the index so later on when real children arrive, index is the same
-                    mClccIndexMap.put(clccMapKey, index);
-                    int direction = inferredCall.isIncoming() ? 1 : 0;
-                    int state = CALL_STATE_ACTIVE;
-                    boolean isPartOfConference = true;
-                    final Uri addressUri;
-                    if (inferredCall.getGatewayInfo() != null) {
-                        addressUri = inferredCall.getGatewayInfo().getOriginalAddress();
-                    } else {
-                        addressUri = inferredCall.getHandle();
-                    }
-                    String address = addressUri == null ? null : addressUri.getSchemeSpecificPart();
-                    if (address != null) {
-                        address = PhoneNumberUtils.stripSeparators(address);
-                    }
-                    int addressType =
-                            address == null ? -1 : PhoneNumberUtils.toaFromString(address);
-                    clccResponseMap.put(
-                            index,
-                            new Object[] {
-                                index, direction, state, 0, isPartOfConference, address, addressType
-                            });
-                }
-                // ensure response is sorted by index
-                for (Object[] response : clccResponseMap.values()) {
-                    if (response.length < 7) {
-                        Log.e(TAG, "clccResponseMap entry too short");
-                        continue;
-                    }
-                    Log.i(
-                            TAG,
-                            String.format(
-                                    "sending inferred clcc for BluetoothCall: index %d, direction"
-                                        + " %d, state %d, isPartOfConference %b, addressType %d",
-                                    (int) response[0],
-                                    (int) response[1],
-                                    (int) response[2],
-                                    (boolean) response[4],
-                                    (int) response[6]));
-                    mBluetoothHeadset.clccResponse(
-                            (int) response[0],
-                            (int) response[1],
-                            (int) response[2],
-                            (int) response[3],
-                            (boolean) response[4],
-                            (String) response[5],
-                            (int) response[6]);
-                }
-                sendClccEndMarker();
-                return;
+                conferenceCallChildrenNotReady = call;
             }
         }
+        if (conferenceCallChildrenNotReady != null) {
+            SortedMap<Integer, Object[]> clccResponseMap = new TreeMap<>();
+            for (BluetoothCall inferredCall : mBluetoothConferenceCallInference.values()) {
+                if (inferredCall.isCallNull() || inferredCall.getHandle() == null) {
+                    Log.w(TAG, "inferredCall does not have handle");
+                    continue;
+                }
+                // save the index so later on when real children arrive, index is the same
+                int index = inferredCall.mClccIndex;
+                if (index == -1) {
+                    Log.w(TAG, "inferred index is not valid");
+                    continue;
+                }
+
+                // associate existing bluetoothCall with inferredCall based on call handle
+                for (BluetoothCall bluetoothCall : mBluetoothCallHashMap.values()) {
+                    if (bluetoothCall.getHandle() == null) {
+                        Log.w(TAG, "call id: " + bluetoothCall.getId() + " handle is null");
+                        continue;
+                    }
+                    if (mTelephonyManager == null) {
+                        Log.w(TAG, "mTelephonyManager is null");
+                        continue;
+                    }
+                    boolean isSame =
+                            PhoneNumberUtils.areSamePhoneNumber(
+                                    bluetoothCall.getHandle().toString(),
+                                    inferredCall.getHandle().toString(),
+                                    mTelephonyManager.getNetworkCountryIso());
+                    if (isSame) {
+                        Log.d(
+                                TAG,
+                                "found conference call children that has same call handle, "
+                                        + "call id: "
+                                        + bluetoothCall.getId());
+                        bluetoothCall.mClccIndex = inferredCall.mClccIndex;
+                        break;
+                    }
+                }
+
+                int direction = inferredCall.isIncoming() ? 1 : 0;
+                int state = CALL_STATE_ACTIVE;
+                boolean isPartOfConference = true;
+                final Uri addressUri;
+                if (inferredCall.getGatewayInfo() != null) {
+                    addressUri = inferredCall.getGatewayInfo().getOriginalAddress();
+                } else {
+                    addressUri = inferredCall.getHandle();
+                }
+                String address = addressUri == null ? null : addressUri.getSchemeSpecificPart();
+                if (address != null) {
+                    address = PhoneNumberUtils.stripSeparators(address);
+                }
+                int addressType = address == null ? -1 : PhoneNumberUtils.toaFromString(address);
+                clccResponseMap.put(
+                        index,
+                        new Object[] {
+                            index, direction, state, 0, isPartOfConference, address, addressType
+                        });
+            }
+            // sort CLCC response based on index
+            for (Object[] response : clccResponseMap.values()) {
+                if (response.length < 7) {
+                    Log.e(TAG, "clccResponseMap entry too short");
+                    continue;
+                }
+                Log.i(
+                        TAG,
+                        String.format(
+                                "sending inferred clcc for BluetoothCall: index %d, direction"
+                                        + " %d, state %d, isPartOfConference %b, addressType %d",
+                                (int) response[0],
+                                (int) response[1],
+                                (int) response[2],
+                                (boolean) response[4],
+                                (int) response[6]));
+                mBluetoothHeadset.clccResponse(
+                        (int) response[0],
+                        (int) response[1],
+                        (int) response[2],
+                        (int) response[3],
+                        (boolean) response[4],
+                        (String) response[5],
+                        (int) response[6]);
+            }
+            sendClccEndMarker();
+            return;
+        }
 
         for (BluetoothCall call : calls) {
             // We don't send the parent conference BluetoothCall to the bluetooth device.
             // We do, however want to send conferences that have no children to the bluetooth
             // device (e.g. IMS Conference).
             boolean isConferenceWithNoChildren = isConferenceWithNoChildren(call);
-            Log.i(TAG, "sendListOfCalls isConferenceWithNoChildren " + isConferenceWithNoChildren
-                + ", call.getChildrenIds() size " + call.getChildrenIds().size());
+            Log.i(
+                    TAG,
+                    "sendListOfCalls isConferenceWithNoChildren "
+                            + isConferenceWithNoChildren
+                            + ", call.getChildrenIds() size "
+                            + call.getChildrenIds().size());
             if (!call.isConference() || isConferenceWithNoChildren) {
                 sendClccForCall(call, shouldLog);
             }
@@ -985,31 +1007,48 @@
         return key;
     }
 
+    int getNextAvailableClccIndex(int index) {
+        // find the next available smallest index
+        SortedSet<Integer> availableIndex = new TreeSet<>();
+        for (int i = index; i <= mMaxNumberOfCalls + 1; i++) {
+            availableIndex.add(i);
+        }
+        for (BluetoothCall bluetoothCall : mBluetoothCallHashMap.values()) {
+            int callCLCCIndex = bluetoothCall.mClccIndex;
+            if (availableIndex.contains(callCLCCIndex)) {
+                availableIndex.remove(callCLCCIndex);
+            }
+        }
+        Log.d(TAG, "availableIndex first: " + availableIndex.first());
+        return availableIndex.first();
+    }
+
     /**
-     * Returns the caches index for the specified call.  If no such index exists, then an index is
-     * given (smallest number starting from 1 that isn't already taken).
+     * Returns the caches index for the specified call. If no such index exists, then an index is
+     * given (the smallest number starting from 1 that isn't already taken).
      */
     private int getIndexForCall(BluetoothCall call) {
-        String key = getClccMapKey(call);
-        if (mClccIndexMap.containsKey(key)) {
-            return mClccIndexMap.get(key);
+        if (mCallInfo.isNullCall(call)) {
+            Log.w(TAG, "empty or null call");
+            return -1;
         }
+        if (call.mClccIndex >= 1) {
+            return call.mClccIndex;
+        }
+
         int index = 1; // Indexes for bluetooth clcc are 1-based.
         if (call.isConference()) {
             index = mMaxNumberOfCalls + 1; // The conference call should have a higher index
-            Log.i(TAG,
-                  "getIndexForCall for conference call starting from "
-                  + mMaxNumberOfCalls);
-        }
-        while (mClccIndexMap.containsValue(index)) {
-            index++;
+            Log.i(TAG, "getIndexForCall for conference call starting from " + mMaxNumberOfCalls);
         }
 
         // NOTE: Indexes are removed in {@link #onCallRemoved}.
-        mClccIndexMap.put(key, index);
-        return index;
+        call.mClccIndex = getNextAvailableClccIndex(index);
+        Log.d(TAG, "call " + call.getId() + " CLCC index is " + call.mClccIndex);
+        return call.mClccIndex;
     }
 
+
     private boolean _processChld(int chld) {
         BluetoothCall activeCall = mCallInfo.getActiveCall();
         BluetoothCall ringingCall = mCallInfo.getRingingOrSimulatedRingingCall();
diff --git a/android/app/src/com/android/bluetooth/vc/OWNERS b/android/app/src/com/android/bluetooth/vc/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/vc/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java b/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
index de76659..e76d8ad 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
@@ -32,11 +32,8 @@
 
     @GuardedBy("INSTANCE_LOCK")
     private static VolumeControlNativeInterface sInstance;
-    private static final Object INSTANCE_LOCK = new Object();
 
-    static {
-        classInitNative();
-    }
+    private static final Object INSTANCE_LOCK = new Object();
 
     private VolumeControlNativeInterface() {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -57,6 +54,14 @@
         }
     }
 
+    /** Set singleton instance. */
+    @VisibleForTesting
+    public static void setInstance(VolumeControlNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
     /**
      * Initializes the native interface.
      *
@@ -374,7 +379,6 @@
     }
 
     // Native methods that call into the JNI interface
-    private static native void classInitNative();
     private native void initNative();
     private native void cleanupNative();
     private native boolean connectVolumeControlNative(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 142a4e8..e8b10a1 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
@@ -25,7 +25,6 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
-import android.bluetooth.BluetoothVolumeControl;
 import android.bluetooth.IBluetoothCsipSetCoordinator;
 import android.bluetooth.IBluetoothLeAudio;
 import android.bluetooth.IBluetoothVolumeControl;
@@ -36,7 +35,9 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.media.AudioManager;
+import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.ParcelUuid;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -77,6 +78,7 @@
     private DatabaseManager mDatabaseManager;
     private HandlerThread mStateMachinesThread;
     private BluetoothDevice mPreviousAudioDevice;
+    private Handler mHandler = null;
 
     @VisibleForTesting
     RemoteCallbackList<IBluetoothVolumeControlCallback> mCallbacks;
@@ -182,7 +184,6 @@
         }
     }
 
-    @VisibleForTesting
     VolumeControlNativeInterface mVolumeControlNativeInterface;
     @VisibleForTesting
     AudioManager mAudioManager;
@@ -194,7 +195,6 @@
     private final Map<Integer, Boolean> mGroupMuteCache = new HashMap<>();
 
     private BroadcastReceiver mBondStateChangedReceiver;
-    private BroadcastReceiver mConnectionStateChangedReceiver;
 
     @VisibleForTesting
     ServiceFactory mFactory = new ServiceFactory();
@@ -238,6 +238,7 @@
                 "AudioManager cannot be null when VolumeControlService starts");
 
         // Start handler thread for state machines
+        mHandler = new Handler(Looper.getMainLooper());
         mStateMachines.clear();
         mStateMachinesThread = new HandlerThread("VolumeControlService.StateMachines");
         mStateMachinesThread.start();
@@ -248,11 +249,6 @@
         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mBondStateChangedReceiver = new BondStateChangedReceiver();
         registerReceiver(mBondStateChangedReceiver, filter);
-        filter = new IntentFilter();
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        filter.addAction(BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED);
-        mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver();
-        registerReceiver(mConnectionStateChangedReceiver, filter);
 
         mAudioOffsets.clear();
         mGroupVolumeCache.clear();
@@ -284,8 +280,6 @@
         // Unregister broadcast receivers
         unregisterReceiver(mBondStateChangedReceiver);
         mBondStateChangedReceiver = null;
-        unregisterReceiver(mConnectionStateChangedReceiver);
-        mConnectionStateChangedReceiver = null;
 
         // Destroy state machines and stop handler thread
         synchronized (mStateMachines) {
@@ -720,6 +714,29 @@
         }
     }
 
+    void updateGroupCacheAndAudioSystem(int groupId, int volume, boolean mute) {
+        Log.d(
+                TAG,
+                " updateGroupCacheAndAudioSystem: groupId: "
+                        + groupId
+                        + ", vol: "
+                        + volume
+                        + ", mute: "
+                        + mute);
+
+        mGroupVolumeCache.put(groupId, volume);
+        mGroupMuteCache.put(groupId, mute);
+
+        int streamType = getBluetoothContextualVolumeStream();
+        int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME;
+        mAudioManager.setStreamVolume(streamType, getDeviceVolume(streamType, volume), flags);
+
+        if (mAudioManager.isStreamMute(streamType) != mute) {
+            int adjustment = mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE;
+            mAudioManager.adjustStreamVolume(streamType, adjustment, flags);
+        }
+    }
+
     void handleVolumeControlChanged(BluetoothDevice device, int groupId,
                                     int volume, boolean mute, boolean isAutonomous) {
 
@@ -745,6 +762,15 @@
         int groupVolume = getGroupVolume(groupId);
         Boolean groupMute = getGroupMute(groupId);
 
+        if (groupVolume == IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME) {
+            /* We are here, because system was just started and LeAudio device just connected.
+             * In such case, we take Volume stored on remote device and apply it to our cache and
+             * audio system.
+             */
+            updateGroupCacheAndAudioSystem(groupId, volume, mute);
+            return;
+        }
+
         if (!isAutonomous) {
             /* If the change is triggered by Android device, the stream is already changed.
              * However it might be called with isAutonomous, one the first read of after
@@ -768,8 +794,7 @@
                     }
                 }
 
-                if (can_change_volume && (groupVolume != volume) && (groupVolume
-                            != IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME)) {
+                if (can_change_volume && (groupVolume != volume)) {
                     Log.i(TAG, "Setting value:" + groupVolume + " to " + device);
                     mVolumeControlNativeInterface.setVolume(device, groupVolume);
                 }
@@ -787,17 +812,7 @@
             }
         } else {
             /* Received group notification for autonomous change. Update cache and audio system. */
-            mGroupVolumeCache.put(groupId, volume);
-            mGroupMuteCache.put(groupId, mute);
-
-            int streamType = getBluetoothContextualVolumeStream();
-            int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME;
-            mAudioManager.setStreamVolume(streamType, getDeviceVolume(streamType, volume), flags);
-
-            if (mAudioManager.isStreamMute(streamType) != mute) {
-                int adjustment = mute ? AudioManager.ADJUST_MUTE : AudioManager.ADJUST_UNMUTE;
-                mAudioManager.adjustStreamVolume(streamType, adjustment, flags);
-            }
+            updateGroupCacheAndAudioSystem(groupId, volume, mute);
         }
     }
 
@@ -1077,9 +1092,18 @@
         }
     }
 
+    void handleConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+        mHandler.post(() -> connectionStateChanged(device, fromState, toState));
+    }
+
     @VisibleForTesting
     synchronized void connectionStateChanged(BluetoothDevice device, int fromState,
                                              int toState) {
+        if (!isAvailable()) {
+            Log.w(TAG, "connectionStateChanged: service is not available");
+            return;
+        }
+
         if ((device == null) || (fromState == toState)) {
             Log.e(TAG, "connectionStateChanged: unexpected invocation. device=" + device
                     + " fromState=" + fromState + " toState=" + toState);
@@ -1116,19 +1140,6 @@
         }
     }
 
-    private class ConnectionStateChangedReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
-                return;
-            }
-            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-            int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-            int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
-            connectionStateChanged(device, fromState, toState);
-        }
-    }
-
     /**
      * Binder object: must be a static class or memory leak may occur
      */
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlStateMachine.java b/android/app/src/com/android/bluetooth/vc/VolumeControlStateMachine.java
index bc0c990..10ddb8a 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlStateMachine.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlStateMachine.java
@@ -506,6 +506,7 @@
         log("Connection state " + mDevice + ": " + profileStateToString(prevState)
                 + "->" + profileStateToString(newState));
 
+        mService.handleConnectionStateChanged(mDevice, prevState, newState);
         Intent intent = new Intent(BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED);
         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
         intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java b/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java
index d605ef2..24f43bc 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/TestUtils.java
@@ -28,7 +28,9 @@
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.MessageQueue;
+import android.os.test.TestLooper;
 import android.service.media.MediaBrowserService;
 import android.util.Log;
 
@@ -56,6 +58,7 @@
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.stream.IntStream;
 
 /**
  * A set of methods useful in Bluetooth instrumentation tests
@@ -285,6 +288,28 @@
     }
 
     /**
+     * Dispatch all the message on the Loopper and check that the `what` is expected
+     *
+     * @param looper looper to execute the message from
+     * @param what list of Messages.what that are expected to be run by the handler
+     */
+    public static void syncHandler(TestLooper looper, int... what) {
+        IntStream.of(what)
+                .forEach(
+                        w -> {
+                            Message msg = looper.nextMessage();
+                            assertWithMessage("Expecting [" + w + "] instead of null Msg")
+                                    .that(msg)
+                                    .isNotNull();
+                            assertWithMessage("Not the expected Message:\n" + msg)
+                                    .that(msg.what)
+                                    .isEqualTo(w);
+                            Log.d(TAG, "Processing message: " + msg);
+                            msg.getTarget().dispatchMessage(msg);
+                        });
+    }
+
+    /**
      * Wait for looper to become idle
      *
      * @param looper looper of interest
@@ -369,6 +394,7 @@
                 }
                 if (line.startsWith("[")) {
                     if (line.charAt(line.length() - 1) != ']') {
+                        Log.e(TAG, "readAdapterConfig: config line is not correct: " + line);
                         return null;
                     }
                     section = line.substring(1, line.length() - 1);
@@ -380,6 +406,7 @@
                 }
             }
         } catch (IOException e) {
+            Log.e(TAG, "readAdapterConfig: Exception while reading the config" + e);
             return null;
         }
         return adapterConfig;
diff --git a/android/app/tests/unit/src/com/android/bluetooth/UtilsTest.java b/android/app/tests/unit/src/com/android/bluetooth/UtilsTest.java
index 20e830c..ae7752c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/UtilsTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/UtilsTest.java
@@ -47,6 +47,7 @@
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
 import java.util.UUID;
 
 /**
@@ -249,4 +250,73 @@
         doThrow(new IOException()).when(os).close();
         Utils.safeCloseStream(os);
     }
+
+    @Test
+    public void truncateUtf8_toZeroLength_isEmpty() {
+        assertThat(Utils.truncateStringForUtf8Storage("abc", 0)).isEmpty();
+    }
+
+    @Test
+    public void truncateUtf8_longCase_isExpectedResult() {
+        StringBuilder builder = new StringBuilder();
+
+        int n = 50;
+        for (int i = 0; i < 2 * n; i++) {
+            builder.append("哈");
+        }
+        String initial = builder.toString();
+        String result = Utils.truncateStringForUtf8Storage(initial, n);
+
+        // Result should be the beginning of initial
+        assertThat(initial.startsWith(result)).isTrue();
+
+        // Result should take less than n bytes in UTF-8
+        assertThat(result.getBytes(StandardCharsets.UTF_8).length).isAtMost(n);
+
+        // result + the next codePoint should take strictly more than
+        // n bytes in UTF-8
+        assertThat(
+                        initial.substring(0, initial.offsetByCodePoints(result.length(), 1))
+                                .getBytes(StandardCharsets.UTF_8)
+                                .length)
+                .isGreaterThan(n);
+    }
+
+    @Test
+    public void truncateUtf8_untruncatedString_isEqual() {
+        String s = "sf\u20ACgk\u00E9ls\u00E9fg";
+        assertThat(Utils.truncateStringForUtf8Storage(s, 100)).isEqualTo(s);
+    }
+
+    @Test
+    public void truncateUtf8_inMiddleOfSurrogate_isStillUtf8() {
+        StringBuilder builder = new StringBuilder();
+        String beginning = "a";
+        builder.append(beginning);
+        builder.append(Character.toChars(0x1D11E));
+
+        // \u1D11E is a surrogate and needs 4 bytes in UTF-8. beginning == "a" uses
+        // only 1 bytes in UTF8
+        // As we allow only 3 bytes for the whole string, so just 2 for this
+        // codePoint, there is not enough place and the string will be truncated
+        // just before it
+        assertThat(Utils.truncateStringForUtf8Storage(builder.toString(), 3)).isEqualTo(beginning);
+    }
+
+    @Test
+    public void truncateUtf8_inMiddleOfChar_isStillUtf8() {
+        StringBuilder builder = new StringBuilder();
+        String beginning = "a";
+        builder.append(beginning);
+        builder.append(Character.toChars(0x20AC));
+
+        // Like above, \u20AC uses 3 bytes in UTF-8, with "beginning", that makes
+        // 4 bytes so it is too big and should be truncated
+        assertThat(Utils.truncateStringForUtf8Storage(builder.toString(), 3)).isEqualTo(beginning);
+    }
+
+    @Test(expected = IndexOutOfBoundsException.class)
+    public void truncateUtf8_toNegativeSize_ThrowsException() {
+        Utils.truncateStringForUtf8Storage("abc", -1);
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
index 95ab67d7..8114dba 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
@@ -42,6 +42,7 @@
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.ActiveDeviceManager;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.SilenceDeviceManager;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
@@ -58,7 +59,6 @@
 import java.util.List;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeoutException;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -78,8 +78,9 @@
 
     @Mock private AdapterService mAdapterService;
     @Mock private ActiveDeviceManager mActiveDeviceManager;
-    @Mock private A2dpNativeInterface mA2dpNativeInterface;
+    @Mock private A2dpNativeInterface mMockNativeInterface;
     @Mock private DatabaseManager mDatabaseManager;
+    @Mock private SilenceDeviceManager mSilenceDeviceManager;
 
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
@@ -100,11 +101,12 @@
         doReturn(false).when(mAdapterService).isQuietModeEnabled();
         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
         doReturn(mActiveDeviceManager).when(mAdapterService).getActiveDeviceManager();
+        doReturn(mSilenceDeviceManager).when(mAdapterService).getSilenceDeviceManager();
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
 
-        startService();
-        mA2dpService.mA2dpNativeInterface = mA2dpNativeInterface;
+        mA2dpService = new A2dpService(mTargetContext, mMockNativeInterface);
+        mA2dpService.doStart();
 
         // Override the timeout value to speed up the test
         A2dpStateMachine.sConnectTimeoutMs = TIMEOUT_MS;    // 1s
@@ -128,7 +130,7 @@
 
     @After
     public void tearDown() throws Exception {
-        stopService();
+        mA2dpService.doStop();
         mTargetContext.unregisterReceiver(mA2dpIntentReceiver);
         mConnectionStateChangedQueue.clear();
         mAudioStateChangedQueue.clear();
@@ -136,19 +138,6 @@
         TestUtils.clearAdapterService(mAdapterService);
     }
 
-    private void startService() throws TimeoutException {
-        TestUtils.startService(mServiceRule, A2dpService.class);
-        mA2dpService = A2dpService.getA2dpService();
-        Assert.assertNotNull(mA2dpService);
-        verify(mAdapterService).notifyActivityAttributionInfo(any(), any());
-    }
-
-    private void stopService() throws TimeoutException {
-        TestUtils.stopService(mServiceRule, A2dpService.class);
-        mA2dpService = A2dpService.getA2dpService();
-        Assert.assertNull(mA2dpService);
-    }
-
     private class A2dpIntentReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -225,38 +214,24 @@
         Assert.assertNull(intent);
     }
 
-    /**
-     * Test getting A2DP Service: getA2dpService()
-     */
     @Test
     public void testGetA2dpService() {
         Assert.assertEquals(mA2dpService, A2dpService.getA2dpService());
     }
 
-    /**
-     * Test stop A2DP Service
-     */
     @Test
     public void testStopA2dpService() {
         // Prepare: connect and set active device
-        doReturn(true).when(mA2dpNativeInterface).setActiveDevice(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).setActiveDevice(any(BluetoothDevice.class));
         connectDevice(mTestDevice);
         Assert.assertTrue(mA2dpService.setActiveDevice(mTestDevice));
-        verify(mA2dpNativeInterface).setActiveDevice(mTestDevice);
-        // A2DP Service is already running: test stop(). Note: must be done on the main thread.
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            public void run() {
-                Assert.assertTrue(mA2dpService.stop());
-            }
-        });
+        verify(mMockNativeInterface).setActiveDevice(mTestDevice);
+
+        Assert.assertTrue(mA2dpService.stop());
+
         // Verify that setActiveDevice(null) was called during shutdown
-        verify(mA2dpNativeInterface).setActiveDevice(null);
-        // Try to restart the service. Note: must be done on the main thread.
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
-            public void run() {
-                Assert.assertTrue(mA2dpService.start());
-            }
-        });
+        verify(mMockNativeInterface).setActiveDevice(null);
+        Assert.assertTrue(mA2dpService.start());
     }
 
     /**
@@ -333,8 +308,8 @@
         // Update the device priority so okToConnect() returns true
         when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).connectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
 
         // Return AudioSource UUID instead of AudioSink
         doReturn(new ParcelUuid[]{BluetoothUuid.A2DP_SOURCE}).when(mAdapterService)
@@ -349,8 +324,8 @@
      */
     @Test
     public void testOutgoingConnectPriorityOff() {
-        doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).connectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
 
         // Set the device priority to PRIORITY_OFF so connect() should fail
         when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP))
@@ -368,8 +343,8 @@
         // Update the device priority so okToConnect() returns true
         when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).connectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
 
         // Send a connect request
         Assert.assertTrue("Connect failed", mA2dpService.connect(mTestDevice));
@@ -398,8 +373,8 @@
         // Update the device priority so okToConnect() returns true
         when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).connectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
 
         // Send a connect request
         Assert.assertTrue("Connect failed", mA2dpService.connect(mTestDevice));
@@ -459,8 +434,8 @@
         BluetoothDevice[] testDevices = new BluetoothDevice[MAX_CONNECTED_AUDIO_DEVICES];
         BluetoothDevice extraTestDevice;
 
-        doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).connectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
 
         // Prepare and connect all test devices
         for (int i = 0; i < MAX_CONNECTED_AUDIO_DEVICES; i++) {
@@ -510,8 +485,8 @@
         // Update the device priority so okToConnect() returns true
         when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).connectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
 
         // A2DP stack event: CONNECTION_STATE_CONNECTING - state machine should be created
         generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_CONNECTING,
@@ -589,8 +564,8 @@
         // Update the device priority so okToConnect() returns true
         when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).connectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
 
         // A2DP stack event: EVENT_TYPE_AUDIO_STATE_CHANGED - state machine should not be created
         generateUnexpectedAudioMessageFromNative(mTestDevice, A2dpStackEvent.AUDIO_STATE_STARTED,
@@ -653,8 +628,8 @@
         // Update the device priority so okToConnect() returns true
         when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).connectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
 
         // A2DP stack event: CONNECTION_STATE_CONNECTING - state machine should be created
         generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_CONNECTING,
@@ -711,8 +686,8 @@
         // Update the device priority so okToConnect() returns true
         when(mDatabaseManager.getProfileConnectionPolicy(mTestDevice, BluetoothProfile.A2DP))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).connectA2dp(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).disconnectA2dp(any(BluetoothDevice.class));
 
         // A2DP stack event: CONNECTION_STATE_CONNECTING - state machine should be created
         generateConnectionMessageFromNative(mTestDevice, BluetoothProfile.STATE_CONNECTING,
@@ -756,32 +731,33 @@
         BluetoothDevice otherDevice = mAdapter.getRemoteDevice("05:04:03:02:01:00");
         connectDevice(mTestDevice);
         connectDevice(otherDevice);
-        doReturn(true).when(mA2dpNativeInterface).setActiveDevice(any(BluetoothDevice.class));
-        doReturn(true).when(mA2dpNativeInterface).setSilenceDevice(any(BluetoothDevice.class),
-                anyBoolean());
+        doReturn(true).when(mMockNativeInterface).setActiveDevice(any(BluetoothDevice.class));
+        doReturn(true)
+                .when(mMockNativeInterface)
+                .setSilenceDevice(any(BluetoothDevice.class), anyBoolean());
 
         // Test whether active device been removed after enable silence mode.
         Assert.assertTrue(mA2dpService.setActiveDevice(mTestDevice));
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
         Assert.assertTrue(mA2dpService.setSilenceMode(mTestDevice, true));
-        verify(mA2dpNativeInterface).setSilenceDevice(mTestDevice, true);
+        verify(mMockNativeInterface).setSilenceDevice(mTestDevice, true);
         Assert.assertNull(mA2dpService.getActiveDevice());
 
         // Test whether active device been resumeed after disable silence mode.
         Assert.assertTrue(mA2dpService.setSilenceMode(mTestDevice, false));
-        verify(mA2dpNativeInterface).setSilenceDevice(mTestDevice, false);
+        verify(mMockNativeInterface).setSilenceDevice(mTestDevice, false);
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
 
         // Test that active device should not be changed when silence a non-active device
         Assert.assertTrue(mA2dpService.setActiveDevice(mTestDevice));
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
         Assert.assertTrue(mA2dpService.setSilenceMode(otherDevice, true));
-        verify(mA2dpNativeInterface).setSilenceDevice(otherDevice, true);
+        verify(mMockNativeInterface).setSilenceDevice(otherDevice, true);
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
 
         // Test that active device should not be changed when another device exits silence mode
         Assert.assertTrue(mA2dpService.setSilenceMode(otherDevice, false));
-        verify(mA2dpNativeInterface).setSilenceDevice(otherDevice, false);
+        verify(mMockNativeInterface).setSilenceDevice(otherDevice, false);
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
     }
 
@@ -799,7 +775,7 @@
     @Test
     public void testRemoveActiveDevice_whenStopAudioIsFalse_suppressNoisyIntent() {
         connectDevice(mTestDevice);
-        doReturn(true).when(mA2dpNativeInterface).setActiveDevice(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).setActiveDevice(any(BluetoothDevice.class));
         Assert.assertTrue(mA2dpService.setActiveDevice(mTestDevice));
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
         AudioManager audioManager = mock(AudioManager.class);
@@ -828,7 +804,7 @@
     @Test
     public void testRemoveActiveDevice_whenStopAudioIsFalse_doesNotSuppressNoisyIntent() {
         connectDevice(mTestDevice);
-        doReturn(true).when(mA2dpNativeInterface).setActiveDevice(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).setActiveDevice(any(BluetoothDevice.class));
         Assert.assertTrue(mA2dpService.setActiveDevice(mTestDevice));
         Assert.assertEquals(mTestDevice, mA2dpService.getActiveDevice());
         AudioManager audioManager = mock(AudioManager.class);
@@ -941,7 +917,7 @@
      */
     @Test
     public void testSendPreferredAudioProfileChangeToAudioFramework() {
-        doReturn(true).when(mA2dpNativeInterface).setActiveDevice(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).setActiveDevice(any(BluetoothDevice.class));
         Assert.assertTrue(mA2dpService.removeActiveDevice(true));
         Assert.assertNull(mA2dpService.getActiveDevice());
 
@@ -973,10 +949,12 @@
         // Update the device priority so okToConnect() returns true
         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.A2DP))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mA2dpNativeInterface).connectA2dp(device);
-        doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(device);
-        doReturn(true).when(mA2dpNativeInterface).setCodecConfigPreference(
-                any(BluetoothDevice.class), any(BluetoothCodecConfig[].class));
+        doReturn(true).when(mMockNativeInterface).connectA2dp(device);
+        doReturn(true).when(mMockNativeInterface).disconnectA2dp(device);
+        doReturn(true)
+                .when(mMockNativeInterface)
+                .setCodecConfigPreference(
+                        any(BluetoothDevice.class), any(BluetoothCodecConfig[].class));
 
         // Send a connect request
         Assert.assertTrue("Connect failed", mA2dpService.connect(device));
@@ -1119,7 +1097,7 @@
     private void testUpdateOptionalCodecsSupportCase(int previousSupport, boolean support,
             int previousEnabled, int verifySupportTime, int verifyNotSupportTime,
             int verifyEnabledTime) {
-        doReturn(true).when(mA2dpNativeInterface).setActiveDevice(any(BluetoothDevice.class));
+        doReturn(true).when(mMockNativeInterface).setActiveDevice(any(BluetoothDevice.class));
 
         BluetoothCodecConfig codecConfigSbc =
                 buildBluetoothCodecConfig(
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java
index 1a5e6f3..979f26e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java
@@ -35,6 +35,7 @@
 
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.ActiveDeviceManager;
+import com.android.bluetooth.btservice.SilenceDeviceManager;
 import com.android.bluetooth.btservice.AdapterService;
 
 import org.hamcrest.core.IsInstanceOf;
@@ -65,6 +66,7 @@
 
     @Mock private AdapterService mAdapterService;
     @Mock private ActiveDeviceManager mActiveDeviceManager;
+    @Mock private SilenceDeviceManager mSilenceDeviceManager;
     @Mock private A2dpService mA2dpService;
     @Mock private A2dpNativeInterface mA2dpNativeInterface;
 
@@ -74,6 +76,7 @@
         // Set up mocks and test assets
         MockitoAnnotations.initMocks(this);
         doReturn(mActiveDeviceManager).when(mAdapterService).getActiveDeviceManager();
+        doReturn(mSilenceDeviceManager).when(mAdapterService).getSilenceDeviceManager();
 
         TestUtils.setAdapterService(mAdapterService);
 
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java
index 1f63faa..3a08995 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java
@@ -26,19 +26,18 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.media.AudioFormat;
+import android.os.test.TestLooper;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -69,6 +68,8 @@
     private BluetoothDevice mDevice5;
     private BluetoothDevice mDevice6;
 
+    private TestLooper mLooper;
+
     private static final int TEST_SAMPLE_RATE = 44;
     private static final int TEST_CHANNEL_COUNT = 1;
 
@@ -77,75 +78,65 @@
         mTargetContext = InstrumentationRegistry.getTargetContext();
         MockitoAnnotations.initMocks(this);
 
+        mLooper = new TestLooper();
+
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         assertThat(mAdapter).isNotNull();
-        mDevice1 = makeBluetoothDevice("11:11:11:11:11:11");
-        mDevice2 = makeBluetoothDevice("22:22:22:22:22:22");
-        mDevice3 = makeBluetoothDevice("33:33:33:33:33:33");
-        mDevice4 = makeBluetoothDevice("44:44:44:44:44:44");
-        mDevice5 = makeBluetoothDevice("55:55:55:55:55:55");
-        mDevice6 = makeBluetoothDevice("66:66:66:66:66:66");
+        mDevice1 = mAdapter.getRemoteDevice("11:11:11:11:11:11");
+        mDevice2 = mAdapter.getRemoteDevice("22:22:22:22:22:22");
+        mDevice3 = mAdapter.getRemoteDevice("33:33:33:33:33:33");
+        mDevice4 = mAdapter.getRemoteDevice("44:44:44:44:44:44");
+        mDevice5 = mAdapter.getRemoteDevice("55:55:55:55:55:55");
+        mDevice6 = mAdapter.getRemoteDevice("66:66:66:66:66:66");
         BluetoothDevice[] bondedDevices = new BluetoothDevice[]{
             mDevice1, mDevice2, mDevice3, mDevice4, mDevice5, mDevice6
         };
 
-        // Setup the adapter service and start our service under test
-        TestUtils.setAdapterService(mAdapterService);
+        doReturn(true).when(mDatabaseManager).setProfileConnectionPolicy(any(), anyInt(), anyInt());
+
         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
         doReturn(bondedDevices).when(mAdapterService).getBondedDevices();
-        when(mDatabaseManager.setProfileConnectionPolicy(any(), anyInt(),
-                anyInt())).thenReturn(true);
-        setMaxConnectedAudioDevices(1);
-        TestUtils.startService(mServiceRule, A2dpSinkService.class);
-        mService = A2dpSinkService.getA2dpSinkService();
-        assertThat(mService).isNotNull();
-        verify(mAdapterService).notifyActivityAttributionInfo(any(), any());
+        doReturn(1).when(mAdapterService).getMaxConnectedAudioDevices();
 
-        mService.mNativeInterface = mNativeInterface;
+        TestUtils.setAdapterService(mAdapterService);
+
         doReturn(true).when(mNativeInterface).setActiveDevice(any());
+
+        mService = new A2dpSinkService(mTargetContext, mNativeInterface, mLooper.getLooper());
+        mService.doStart();
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     @After
     public void tearDown() throws Exception {
-        TestUtils.stopService(mServiceRule, A2dpSinkService.class);
-        mService = A2dpSinkService.getA2dpSinkService();
-        assertThat(mService).isNull();
+        assertThat(mLooper.nextMessage()).isNull();
+
+        mService.doStop();
+        assertThat(A2dpSinkService.getA2dpSinkService()).isNull();
         TestUtils.clearAdapterService(mAdapterService);
     }
 
+    private void syncHandler(int... what) {
+        TestUtils.syncHandler(mLooper, what);
+    }
+
     private void setupDeviceConnection(BluetoothDevice device) {
+        assertThat(mLooper.nextMessage()).isNull();
         assertThat(mService.getConnectionState(device)).isEqualTo(
                 BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mLooper.nextMessage()).isNull();
+
         assertThat(mService.connect(device)).isTrue();
-        sendConnectionEvent(device, StackEvent.CONNECTION_STATE_CONNECTED);
-        waitForDeviceProcessing(device);
+        syncHandler(-2 /* SM_INIT_CMD */, A2dpSinkStateMachine.CONNECT);
+        StackEvent nativeEvent =
+                StackEvent.connectionStateChanged(device, StackEvent.CONNECTION_STATE_CONNECTED);
+        mService.messageFromNative(nativeEvent);
+        syncHandler(A2dpSinkStateMachine.STACK_EVENT);
         assertThat(mService.getConnectionState(device)).isEqualTo(
                 BluetoothProfile.STATE_CONNECTED);
     }
 
-    private void sendConnectionEvent(BluetoothDevice device, int newState) {
-        StackEvent event = StackEvent.connectionStateChanged(device, newState);
-        mService.messageFromNative(event);
-    }
-
-    private void waitForDeviceProcessing(BluetoothDevice device) {
-        A2dpSinkStateMachine sm = mService.getStateMachineForDevice(device);
-        if (sm == null) return;
-        TestUtils.waitForLooperToFinishScheduledTask(sm.getHandler().getLooper());
-    }
-
-    private BluetoothDevice makeBluetoothDevice(String address) {
-        return mAdapter.getRemoteDevice(address);
-    }
-
-    /**
-     * Set the upper connected device limit
-     */
-    private void setMaxConnectedAudioDevices(int maxConnectedAudioDevices) {
-        when(mAdapterService.getMaxConnectedAudioDevices()).thenReturn(maxConnectedAudioDevices);
-    }
-
     /**
      * Mock the priority of a bluetooth device
      *
@@ -162,7 +153,7 @@
      */
     @Test
     public void testInitialize() {
-        assertThat(A2dpSinkService.getA2dpSinkService()).isNotNull();
+        assertThat(A2dpSinkService.getA2dpSinkService()).isEqualTo(mService);
     }
 
     /**
@@ -207,7 +198,7 @@
      */
     @Test
     public void testConnectMultipleDevices() {
-        setMaxConnectedAudioDevices(5);
+        doReturn(5).when(mAdapterService).getMaxConnectedAudioDevices();
 
         mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
         mockDevicePriority(mDevice2, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
@@ -232,9 +223,11 @@
         setupDeviceConnection(mDevice1);
 
         assertThat(mService.disconnect(mDevice1)).isTrue();
-        waitForDeviceProcessing(mDevice1);
+        syncHandler(A2dpSinkStateMachine.DISCONNECT);
         assertThat(mService.getConnectionState(mDevice1)).isEqualTo(
                 BluetoothProfile.STATE_DISCONNECTED);
+
+        syncHandler(A2dpSinkStateMachine.CLEANUP, -1 /* SM_QUIT_CMD */);
     }
 
     /**
@@ -295,7 +288,7 @@
         StackEvent audioConfigChanged =
                 StackEvent.audioConfigChanged(mDevice1, TEST_SAMPLE_RATE, TEST_CHANNEL_COUNT);
         mService.messageFromNative(audioConfigChanged);
-        waitForDeviceProcessing(mDevice1);
+        syncHandler(A2dpSinkStateMachine.STACK_EVENT);
 
         BluetoothAudioConfig expected = new BluetoothAudioConfig(TEST_SAMPLE_RATE,
                 TEST_CHANNEL_COUNT, AudioFormat.ENCODING_PCM_16BIT);
@@ -409,8 +402,11 @@
     public void testSetConnectionPolicyDeviceAllowed() {
         assertThat(mService.setConnectionPolicy(mDevice1,
                 BluetoothProfile.CONNECTION_POLICY_ALLOWED)).isTrue();
-        verify(mDatabaseManager, times(1)).setProfileConnectionPolicy(mDevice1,
-                BluetoothProfile.A2DP_SINK, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        verify(mDatabaseManager)
+                .setProfileConnectionPolicy(
+                        mDevice1,
+                        BluetoothProfile.A2DP_SINK,
+                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
     }
 
     /**
@@ -420,8 +416,11 @@
     public void testSetConnectionPolicyDeviceForbiddenWhileNotConnected() {
         assertThat(mService.setConnectionPolicy(mDevice1,
                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)).isTrue();
-        verify(mDatabaseManager, times(1)).setProfileConnectionPolicy(mDevice1,
-                BluetoothProfile.A2DP_SINK, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+        verify(mDatabaseManager)
+                .setProfileConnectionPolicy(
+                        mDevice1,
+                        BluetoothProfile.A2DP_SINK,
+                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
     }
 
     /**
@@ -435,12 +434,18 @@
 
         assertThat(mService.setConnectionPolicy(mDevice1,
                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)).isTrue();
-        verify(mDatabaseManager, times(1)).setProfileConnectionPolicy(mDevice1,
-                BluetoothProfile.A2DP_SINK, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+        verify(mDatabaseManager)
+                .setProfileConnectionPolicy(
+                        mDevice1,
+                        BluetoothProfile.A2DP_SINK,
+                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
 
-        waitForDeviceProcessing(mDevice1);
+        syncHandler(A2dpSinkStateMachine.DISCONNECT);
+        verify(mNativeInterface).disconnectA2dpSink(eq(mDevice1));
         assertThat(mService.getConnectionState(mDevice1)).isEqualTo(
                 BluetoothProfile.STATE_DISCONNECTED);
+
+        syncHandler(A2dpSinkStateMachine.CLEANUP, -1 /* SM_QUIT_CMD */);
     }
 
     /**
@@ -450,8 +455,11 @@
     public void testSetConnectionPolicyDeviceUnknown() {
         assertThat(mService.setConnectionPolicy(mDevice1,
                 BluetoothProfile.CONNECTION_POLICY_UNKNOWN)).isTrue();
-        verify(mDatabaseManager, times(1)).setProfileConnectionPolicy(mDevice1,
-                BluetoothProfile.A2DP_SINK, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+        verify(mDatabaseManager)
+                .setProfileConnectionPolicy(
+                        mDevice1,
+                        BluetoothProfile.A2DP_SINK,
+                        BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
     }
 
     /**
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachineTest.java
index 6e2197f..03d95a3 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachineTest.java
@@ -20,7 +20,6 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -30,15 +29,14 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.media.AudioFormat;
+import android.os.test.TestLooper;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 
 import org.junit.After;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -47,32 +45,36 @@
 
 @RunWith(AndroidJUnit4.class)
 public class A2dpSinkStateMachineTest {
-    private Context mTargetContext;
+    private static final String DEVICE_ADDRESS = "11:11:11:11:11:11";
+    private static final int UNHANDLED_MESSAGE = 9999;
 
-    private BluetoothAdapter mAdapter;
-    private BluetoothDevice mDevice;
-    private final String mDeviceAddress = "11:11:11:11:11:11";
     @Mock private A2dpSinkService mService;
     @Mock private A2dpSinkNativeInterface mNativeInterface;
 
-    A2dpSinkStateMachine mStateMachine;
-    private static final int TIMEOUT_MS = 1000;
-    private static final int CONNECT_TIMEOUT_MS = 11000;
-    private static final int UNHANDLED_MESSAGE = 9999;
+    private A2dpSinkStateMachine mStateMachine;
+    private BluetoothAdapter mAdapter;
+    private BluetoothDevice mDevice;
+    private Context mTargetContext;
+    private TestLooper mLooper;
 
     @Before
     public void setUp() throws Exception {
         mTargetContext = InstrumentationRegistry.getTargetContext();
         MockitoAnnotations.initMocks(this);
 
+        mLooper = new TestLooper();
+
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         assertThat(mAdapter).isNotNull();
-        mDevice = mAdapter.getRemoteDevice(mDeviceAddress);
+        mDevice = mAdapter.getRemoteDevice(DEVICE_ADDRESS);
 
         doNothing().when(mService).removeStateMachine(any(A2dpSinkStateMachine.class));
 
-        mStateMachine = new A2dpSinkStateMachine(mDevice, mService, mNativeInterface);
+        mStateMachine =
+                new A2dpSinkStateMachine(mLooper.getLooper(), mDevice, mService, mNativeInterface);
         mStateMachine.start();
+        syncHandler(-2 /* SM_INIT_CMD */);
+
         assertThat(mStateMachine.getDevice()).isEqualTo(mDevice);
         assertThat(mStateMachine.getAudioConfig()).isNull();
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
@@ -80,11 +82,17 @@
 
     @After
     public void tearDown() throws Exception {
+        assertThat(mLooper.nextMessage()).isNull();
+
         mStateMachine = null;
         mDevice = null;
         mAdapter = null;
     }
 
+    private void syncHandler(int... what) {
+        TestUtils.syncHandler(mLooper, what);
+    }
+
     private void mockDeviceConnectionPolicy(BluetoothDevice device, int policy) {
         doReturn(policy).when(mService).getConnectionPolicy(device);
     }
@@ -92,11 +100,13 @@
     private void sendConnectionEvent(int state) {
         mStateMachine.sendMessage(A2dpSinkStateMachine.STACK_EVENT,
                 StackEvent.connectionStateChanged(mDevice, state));
+        syncHandler(A2dpSinkStateMachine.STACK_EVENT);
     }
 
     private void sendAudioConfigChangedEvent(int sampleRate, int channelCount) {
         mStateMachine.sendMessage(A2dpSinkStateMachine.STACK_EVENT,
                 StackEvent.audioConfigChanged(mDevice, sampleRate, channelCount));
+        syncHandler(A2dpSinkStateMachine.STACK_EVENT);
     }
 
     /**********************************************************************************************
@@ -105,44 +115,37 @@
 
     @Test
     public void testConnectInDisconnected() {
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
         mStateMachine.connect();
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
-        verify(mNativeInterface, timeout(TIMEOUT_MS).times(1)).connectA2dpSink(mDevice);
+        syncHandler(A2dpSinkStateMachine.CONNECT);
+        verify(mNativeInterface).connectA2dpSink(mDevice);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
     }
 
     @Test
     public void testDisconnectInDisconnected() {
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
         mStateMachine.disconnect();
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
+        syncHandler(A2dpSinkStateMachine.DISCONNECT);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
     }
 
     @Test
     public void testAudioConfigChangedInDisconnected() {
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
         sendAudioConfigChangedEvent(44, 1);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
         assertThat(mStateMachine.getAudioConfig()).isNull();
     }
 
     @Test
     public void testIncomingConnectedInDisconnected() {
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
         sendConnectionEvent(BluetoothProfile.STATE_CONNECTED);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
     }
 
     @Test
     public void testAllowedIncomingConnectionInDisconnected() {
         mockDeviceConnectionPolicy(mDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+
         sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
         verify(mNativeInterface, times(0)).connectA2dpSink(mDevice);
     }
@@ -150,54 +153,48 @@
     @Test
     public void testForbiddenIncomingConnectionInDisconnected() {
         mockDeviceConnectionPolicy(mDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+
         sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
-        verify(mNativeInterface, times(1)).disconnectA2dpSink(mDevice);
+        verify(mNativeInterface).disconnectA2dpSink(mDevice);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
     }
 
     @Test
     public void testUnknownIncomingConnectionInDisconnected() {
         mockDeviceConnectionPolicy(mDevice, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+
         sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
         verify(mNativeInterface, times(0)).connectA2dpSink(mDevice);
     }
 
     @Test
     public void testIncomingDisconnectInDisconnected() {
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
         sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTED);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
-        verify(mService, timeout(TIMEOUT_MS).times(1)).removeStateMachine(mStateMachine);
+
+        syncHandler(A2dpSinkStateMachine.CLEANUP);
+        verify(mService).removeStateMachine(mStateMachine);
     }
 
     @Test
     public void testIncomingDisconnectingInDisconnected() {
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
         sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTING);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
         verify(mService, times(0)).removeStateMachine(mStateMachine);
     }
 
     @Test
     public void testIncomingConnectingInDisconnected() {
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
         sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
     }
 
     @Test
     public void testUnhandledMessageInDisconnected() {
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
         mStateMachine.sendMessage(UNHANDLED_MESSAGE);
         mStateMachine.sendMessage(UNHANDLED_MESSAGE, 0 /* arbitrary payload */);
+        syncHandler(UNHANDLED_MESSAGE, UNHANDLED_MESSAGE);
     }
 
     /**********************************************************************************************
@@ -207,54 +204,55 @@
     @Test
     public void testConnectedInConnecting() {
         testConnectInDisconnected();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+
         sendConnectionEvent(BluetoothProfile.STATE_CONNECTED);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
     }
 
     @Test
     public void testConnectingInConnecting() {
         testConnectInDisconnected();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+
         sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
     }
 
     @Test
     public void testDisconnectingInConnecting() {
         testConnectInDisconnected();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+
         sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTING);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
     }
 
     @Test
     public void testDisconnectedInConnecting() {
         testConnectInDisconnected();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+
         sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTED);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
-        verify(mService, timeout(TIMEOUT_MS).times(1)).removeStateMachine(mStateMachine);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+
+        syncHandler(A2dpSinkStateMachine.CLEANUP);
+        verify(mService).removeStateMachine(mStateMachine);
     }
 
     @Test
     public void testConnectionTimeoutInConnecting() {
         testConnectInDisconnected();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
-        verify(mService, timeout(CONNECT_TIMEOUT_MS).times(1)).removeStateMachine(mStateMachine);
+
+        mLooper.moveTimeForward(120_000); // Skip time so the timeout fires
+        syncHandler(A2dpSinkStateMachine.CONNECT_TIMEOUT);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+
+        syncHandler(A2dpSinkStateMachine.CLEANUP);
+        verify(mService).removeStateMachine(mStateMachine);
     }
 
     @Test
     public void testAudioStateChangeInConnecting() {
         testConnectInDisconnected();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+
         sendAudioConfigChangedEvent(44, 1);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
         assertThat(mStateMachine.getAudioConfig()).isNull();
     }
@@ -262,19 +260,30 @@
     @Test
     public void testConnectInConnecting() {
         testConnectInDisconnected();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+
         mStateMachine.connect();
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
+        syncHandler(A2dpSinkStateMachine.CONNECT);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
     }
 
     @Test
-    public void testDisconnectInConnecting() {
+    public void testDisconnectInConnecting_disconnectDeferredAndProcessed() {
         testConnectInDisconnected();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+
         mStateMachine.disconnect();
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
+        syncHandler(A2dpSinkStateMachine.DISCONNECT);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+
+        // send connected, disconnect should get processed
+        sendConnectionEvent(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
+        syncHandler(A2dpSinkStateMachine.DISCONNECT); // message was defer
+        verify(mNativeInterface).disconnectA2dpSink(mDevice);
+        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+
+        syncHandler(A2dpSinkStateMachine.CLEANUP);
+        verify(mService).removeStateMachine(mStateMachine);
     }
 
     /**********************************************************************************************
@@ -284,72 +293,73 @@
     @Test
     public void testConnectInConnected() {
         testConnectedInConnecting();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
         mStateMachine.connect();
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
+        syncHandler(A2dpSinkStateMachine.CONNECT);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
     }
 
     @Test
     public void testDisconnectInConnected() {
         testConnectedInConnecting();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
         mStateMachine.disconnect();
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
-        verify(mNativeInterface, times(1)).disconnectA2dpSink(mDevice);
-        verify(mService, timeout(TIMEOUT_MS).times(1)).removeStateMachine(mStateMachine);
+        syncHandler(A2dpSinkStateMachine.DISCONNECT);
+        verify(mNativeInterface).disconnectA2dpSink(mDevice);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+
+        syncHandler(A2dpSinkStateMachine.CLEANUP);
+        verify(mService).removeStateMachine(mStateMachine);
     }
 
     @Test
     public void testAudioStateChangeInConnected() {
         testConnectedInConnecting();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
         sendAudioConfigChangedEvent(44, 1);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
         BluetoothAudioConfig expected =
                 new BluetoothAudioConfig(44, 1, AudioFormat.ENCODING_PCM_16BIT);
-        BluetoothAudioConfig config = mStateMachine.getAudioConfig();
-        assertThat(config).isEqualTo(expected);
+        assertThat(mStateMachine.getAudioConfig()).isEqualTo(expected);
     }
 
     @Test
     public void testConnectedInConnected() {
         testConnectedInConnecting();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
         sendConnectionEvent(BluetoothProfile.STATE_CONNECTED);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
     }
 
     @Test
     public void testConnectingInConnected() {
         testConnectedInConnecting();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
         sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
     }
 
     @Test
     public void testDisconnectingInConnected() {
         testConnectedInConnecting();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
         sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTING);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
-        verify(mService, timeout(TIMEOUT_MS).times(1)).removeStateMachine(mStateMachine);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+
+        syncHandler(A2dpSinkStateMachine.CLEANUP);
+        verify(mService).removeStateMachine(mStateMachine);
     }
 
     @Test
     public void testDisconnectedInConnected() {
         testConnectedInConnecting();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+
         sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTED);
-        TestUtils.waitForLooperToFinishScheduledTask(mStateMachine.getHandler().getLooper());
-        verify(mService, timeout(TIMEOUT_MS).times(1)).removeStateMachine(mStateMachine);
         assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+
+        syncHandler(A2dpSinkStateMachine.CLEANUP);
+        verify(mService).removeStateMachine(mStateMachine);
     }
 
     /**********************************************************************************************
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
index 3cec1ea..91b0283 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandlerTest.java
@@ -33,9 +33,9 @@
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
+import com.android.bluetooth.avrcpcontroller.AvrcpControllerNativeInterface;
 import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService;
 import com.android.bluetooth.btservice.AdapterService;
 
@@ -58,6 +58,7 @@
     @Mock private A2dpSinkService mMockA2dpSink;
 
     @Mock private A2dpSinkNativeInterface mMockNativeInterface;
+    @Mock private AvrcpControllerNativeInterface mMockAvrcpControllerNativeInterface;
 
     @Mock private AudioManager mMockAudioManager;
 
@@ -84,6 +85,7 @@
         }
         TestUtils.setAdapterService(mAdapterService);
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
+        AvrcpControllerNativeInterface.setInstance(mMockAvrcpControllerNativeInterface);
         TestUtils.startService(mServiceRule, AvrcpControllerService.class);
         final Intent bluetoothBrowserMediaServiceStartIntent =
                 TestUtils.prepareIntentToStartBluetoothBrowserMediaService();
@@ -112,6 +114,7 @@
     @After
     public void tearDown() throws Exception {
         TestUtils.stopService(mServiceRule, AvrcpControllerService.class);
+        AvrcpControllerNativeInterface.setInstance(null);
         TestUtils.clearAdapterService(mAdapterService);
     }
 
diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowsablePlayerConnectorTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowsablePlayerConnectorTest.java
new file mode 100644
index 0000000..4a6f1ec
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowsablePlayerConnectorTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.audio_util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public final class BrowsablePlayerConnectorTest {
+    private static final int TIMEOUT_MS = 300;
+
+    Context mContext;
+    TestLooper mTestLooper;
+    List<ResolveInfo> mPlayerList;
+    @Mock MediaBrowser mMediaBrowser;
+    MediaBrowser.ConnectionCallback mConnectionCallback;
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = InstrumentationRegistry.getTargetContext();
+        mTestLooper = new TestLooper();
+
+        doAnswer(invocation -> {
+            mConnectionCallback = invocation.getArgument(2);
+            return null;
+        }).when(mMediaBrowser).testInit(any(), any(), any(), any());
+        doAnswer(invocation -> {
+            mConnectionCallback.onConnected();
+            return null;
+        }).when(mMediaBrowser).connect();
+        doAnswer(invocation -> {
+            String id = invocation.getArgument(0);
+            android.media.browse.MediaBrowser.SubscriptionCallback callback
+                    = invocation.getArgument(1);
+            callback.onChildrenLoaded(id, Collections.emptyList());
+            return null;
+        }).when(mMediaBrowser).subscribe(any(), any());
+        doReturn("testRoot").when(mMediaBrowser).getRoot();
+        MediaBrowserFactory.inject(mMediaBrowser);
+
+        ResolveInfo player = new ResolveInfo();
+        player.serviceInfo = new ServiceInfo();
+        player.serviceInfo.packageName = "com.android.bluetooth.test";
+        player.serviceInfo.name = "TestPlayer";
+        mPlayerList = new ArrayList();
+        mPlayerList.add(player);
+    }
+
+    @Test
+    public void browsablePlayerConnectorCallback_calledAfterConnection()
+            throws InterruptedException {
+        mTestLooper.startAutoDispatch();
+        CountDownLatch latch = new CountDownLatch(1);
+        BrowsablePlayerConnector connector =
+                BrowsablePlayerConnector.connectToPlayers(
+                        mContext,
+                        mTestLooper.getLooper(),
+                        mPlayerList,
+                        (List<BrowsedPlayerWrapper> players) -> latch.countDown());
+        verify(mMediaBrowser, timeout(TIMEOUT_MS)).connect();
+        assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
+        connector.cleanup();
+        mTestLooper.stopAutoDispatch();
+        mTestLooper.dispatchAll();
+    }
+
+    @Test
+    public void cleanup_doesNotCrash() {
+        BrowsablePlayerConnector connector =
+                BrowsablePlayerConnector.connectToPlayers(
+                        mContext,
+                        mTestLooper.getLooper(),
+                        mPlayerList,
+                        (List<BrowsedPlayerWrapper> players) -> {});
+        verify(mMediaBrowser, timeout(TIMEOUT_MS)).connect();
+        connector.cleanup();
+        mTestLooper.dispatchAll();
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java
index 8b3ceae..9ed1fa0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java
@@ -21,7 +21,6 @@
 import static org.mockito.Mockito.*;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -378,6 +377,38 @@
     }
 
     @Test
+    public void testNullPlayback() {
+        // Create the wrapper object and register the looper with the timeout handler
+        TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+        wrapper.registerCallback(mTestCbs);
+
+        // Return null when getting the queue
+        doReturn(null).when(mMockController).getQueue();
+
+        // Grab the callbacks the wrapper registered with the controller
+        verify(mMockController).registerCallback(mControllerCbs.capture(), any());
+        MediaController.Callback controllerCallbacks = mControllerCbs.getValue();
+
+        // Update Metadata returned by controller
+        mTestState.setState(PlaybackState.STATE_PLAYING, 1000, 1.0f);
+        doReturn(mTestState.build()).when(mMockController).getPlaybackState();
+
+        // Call the callback
+        controllerCallbacks.onPlaybackStateChanged(null);
+
+        // Assert that the metadata returned by getPlaybackState() is used instead of null
+
+        verify(mTestCbs, times(1)).mediaUpdatedCallback(mMediaUpdateData.capture());
+        MediaData data = mMediaUpdateData.getValue();
+        Assert.assertEquals(
+                "Returned PlaybackState is incorrect",
+                data.state.toString(),
+                mTestState.build().toString());
+    }
+
+    @Test
     public void testNullQueue() {
         // Create the wrapper object and register the looper with the timeout handler
         TestLooperManager looperManager = new TestLooperManager(mThread.getLooper());
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClientTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClientTest.java
index 7cb1692..719ab07 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClientTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpBipClientTest.java
@@ -53,8 +53,8 @@
     @Rule
     public final ServiceTestRule mBluetoothBrowserMediaServiceTestRule = new ServiceTestRule();
 
-    @Mock
-    private AdapterService mAdapterService;
+    @Mock private AdapterService mAdapterService;
+    @Mock private AvrcpControllerNativeInterface mNativeInterface;
 
     private BluetoothAdapter mAdapter;
     private BluetoothDevice mTestDevice;
@@ -67,6 +67,7 @@
         MockitoAnnotations.initMocks(this);
         TestUtils.setAdapterService(mAdapterService);
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
+        AvrcpControllerNativeInterface.setInstance(mNativeInterface);
         TestUtils.startService(mServiceRule, AvrcpControllerService.class);
         mService = AvrcpControllerService.getAvrcpControllerService();
         final Intent bluetoothBrowserMediaServiceStartIntent =
@@ -87,6 +88,7 @@
     @After
     public void tearDown() throws Exception {
         TestUtils.stopService(mServiceRule, AvrcpControllerService.class);
+        AvrcpControllerNativeInterface.setInstance(null);
         mService = AvrcpControllerService.getAvrcpControllerService();
         assertThat(mService).isNull();
         TestUtils.clearAdapterService(mAdapterService);
@@ -141,11 +143,12 @@
 
     @Test
     public void toString_returnsClientInfo() {
-        AvrcpBipClient client = new AvrcpBipClient(mTestDevice, TEST_PSM,
-                mArtManager.new BipClientCallback(mTestDevice));
-
-        String expected = "<AvrcpBipClient" + " device=" + mTestDevice + " psm="
-                + TEST_PSM + " state=" + client.getStateName() + ">";
-        assertThat(client.toString()).isEqualTo(expected);
+        String expected =
+                "<AvrcpBipClient"
+                        + (" device=" + mTestDevice)
+                        + (" psm=" + TEST_PSM)
+                        + (" state=" + mClient.getStateName())
+                        + ">";
+        assertThat(mClient.toString()).isEqualTo(expected);
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerNativeInterfaceTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerNativeInterfaceTest.java
new file mode 100644
index 0000000..38938e8
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerNativeInterfaceTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bluetooth.avrcpcontroller;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.v4.media.session.PlaybackStateCompat;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AvrcpControllerNativeInterfaceTest {
+    private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00";
+    private static final byte[] REMOTE_DEVICE_ADDRESS_AS_ARRAY = new byte[] {0, 0, 0, 0, 0, 0};
+
+    @Test
+    public void createFromNativeMediaItem() {
+        long uid = 1;
+        int type = 2;
+        int[] attrIds = new int[] {0x01}; // MEDIA_ATTRIBUTE_TITLE
+        String[] attrVals = new String[] {"test_title"};
+
+        AvrcpItem item =
+                AvrcpControllerNativeInterface.createFromNativeMediaItem(
+                        REMOTE_DEVICE_ADDRESS_AS_ARRAY,
+                        uid,
+                        type,
+                        "unused_name",
+                        attrIds,
+                        attrVals);
+
+        assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS);
+        assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_MEDIA);
+        assertThat(item.getType()).isEqualTo(type);
+        assertThat(item.getUid()).isEqualTo(uid);
+        assertThat(item.getUuid()).isNotNull(); // Random uuid
+        assertThat(item.getTitle()).isEqualTo(attrVals[0]);
+        assertThat(item.isPlayable()).isTrue();
+    }
+
+    @Test
+    public void createFromNativeFolderItem() {
+        long uid = 1;
+        int type = 2;
+        String folderName = "test_folder_name";
+        int playable = 0x01; // Playable folder
+
+        AvrcpItem item =
+                AvrcpControllerNativeInterface.createFromNativeFolderItem(
+                        REMOTE_DEVICE_ADDRESS_AS_ARRAY, uid, type, folderName, playable);
+
+        assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS);
+        assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_FOLDER);
+        assertThat(item.getType()).isEqualTo(type);
+        assertThat(item.getUid()).isEqualTo(uid);
+        assertThat(item.getUuid()).isNotNull(); // Random uuid
+        assertThat(item.getDisplayableName()).isEqualTo(folderName);
+        assertThat(item.isPlayable()).isTrue();
+    }
+
+    @Test
+    public void createFromNativePlayerItem() {
+        int playerId = 1;
+        String name = "test_name";
+        byte[] transportFlags = new byte[] {1, 0, 0, 0, 0, 0, 0, 0};
+        int playStatus = 0x04; // JNI_PLAY_STATUS_REV_SEEK;
+        int playerType = AvrcpPlayer.TYPE_AUDIO; // No getter exists
+
+        AvrcpPlayer player =
+                AvrcpControllerNativeInterface.createFromNativePlayerItem(
+                        REMOTE_DEVICE_ADDRESS_AS_ARRAY,
+                        playerId,
+                        name,
+                        transportFlags,
+                        playStatus,
+                        playerType);
+
+        assertThat(player.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS);
+        assertThat(player.getId()).isEqualTo(playerId);
+        assertThat(player.supportsFeature(0)).isTrue();
+        assertThat(player.getName()).isEqualTo(name);
+        assertThat(player.getPlayStatus()).isEqualTo(PlaybackStateCompat.STATE_REWINDING);
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceTest.java
index d1b3963..35ab6a7 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceTest.java
@@ -53,6 +53,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -69,6 +70,7 @@
 
     @Mock private AdapterService mAdapterService;
     @Mock private AvrcpControllerStateMachine mStateMachine;
+    @Mock private AvrcpControllerNativeInterface mNativeInterface;
 
     private BluetoothDevice mRemoteDevice;
 
@@ -77,6 +79,7 @@
         MockitoAnnotations.initMocks(this);
         TestUtils.setAdapterService(mAdapterService);
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
+        AvrcpControllerNativeInterface.setInstance(mNativeInterface);
         TestUtils.startService(mServiceRule, AvrcpControllerService.class);
         mService = AvrcpControllerService.getAvrcpControllerService();
         assertThat(mService).isNotNull();
@@ -93,6 +96,7 @@
     @After
     public void tearDown() throws Exception {
         TestUtils.stopService(mServiceRule, AvrcpControllerService.class);
+        AvrcpControllerNativeInterface.setInstance(null);
         mService = AvrcpControllerService.getAvrcpControllerService();
         assertThat(mService).isNull();
         TestUtils.clearAdapterService(mAdapterService);
@@ -264,70 +268,13 @@
     }
 
     @Test
-    public void createFromNativeMediaItem() {
-        long uid = 1;
-        int type = 2;
-        int[] attrIds = new int[] { 0x01 }; // MEDIA_ATTRIBUTE_TITLE}
-        String[] attrVals = new String[] {"test_title"};
-
-        AvrcpItem item = mService.createFromNativeMediaItem(
-                REMOTE_DEVICE_ADDRESS_AS_ARRAY, uid, type, "unused_name", attrIds, attrVals);
-
-        assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS);
-        assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_MEDIA);
-        assertThat(item.getType()).isEqualTo(type);
-        assertThat(item.getUid()).isEqualTo(uid);
-        assertThat(item.getUuid()).isNotNull(); // Random uuid
-        assertThat(item.getTitle()).isEqualTo(attrVals[0]);
-        assertThat(item.isPlayable()).isTrue();
-    }
-
-    @Test
-    public void createFromNativeFolderItem() {
-        long uid = 1;
-        int type = 2;
-        String folderName = "test_folder_name";
-        int playable = 0x01; // Playable folder
-
-        AvrcpItem item = mService.createFromNativeFolderItem(
-                REMOTE_DEVICE_ADDRESS_AS_ARRAY, uid, type, folderName, playable);
-
-        assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS);
-        assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_FOLDER);
-        assertThat(item.getType()).isEqualTo(type);
-        assertThat(item.getUid()).isEqualTo(uid);
-        assertThat(item.getUuid()).isNotNull(); // Random uuid
-        assertThat(item.getDisplayableName()).isEqualTo(folderName);
-        assertThat(item.isPlayable()).isTrue();
-    }
-
-    @Test
-    public void createFromNativePlayerItem() {
-        int playerId = 1;
-        String name = "test_name";
-        byte[] transportFlags = new byte[] {1, 0, 0, 0, 0, 0, 0, 0};
-        int playStatus = AvrcpControllerService.JNI_PLAY_STATUS_REV_SEEK;
-        int playerType = AvrcpPlayer.TYPE_AUDIO; // No getter exists
-
-        AvrcpPlayer player = mService.createFromNativePlayerItem(
-                REMOTE_DEVICE_ADDRESS_AS_ARRAY, playerId, name, transportFlags,
-                playStatus, playerType);
-
-        assertThat(player.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS);
-        assertThat(player.getId()).isEqualTo(playerId);
-        assertThat(player.supportsFeature(0)).isTrue();
-        assertThat(player.getName()).isEqualTo(name);
-        assertThat(player.getPlayStatus()).isEqualTo(PlaybackStateCompat.STATE_REWINDING);
-    }
-
-    @Test
     public void handleChangeFolderRsp() {
         int count = 1;
 
-        mService.handleChangeFolderRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, count);
+        mService.handleChangeFolderRsp(mRemoteDevice, count);
 
-        verify(mStateMachine).sendMessage(
-                AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH, count);
+        verify(mStateMachine)
+                .sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH, count);
     }
 
     @Test
@@ -335,54 +282,49 @@
         int items = 3;
         int depth = 5;
 
-        mService.handleSetBrowsedPlayerRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, items, depth);
+        mService.handleSetBrowsedPlayerRsp(mRemoteDevice, items, depth);
 
-        verify(mStateMachine).sendMessage(
-                AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_BROWSED_PLAYER, items, depth);
+        verify(mStateMachine)
+                .sendMessage(
+                        AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_BROWSED_PLAYER,
+                        items,
+                        depth);
     }
 
     @Test
     public void handleSetAddressedPlayerRsp() {
         int status = 1;
 
-        mService.handleSetAddressedPlayerRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, status);
+        mService.handleSetAddressedPlayerRsp(mRemoteDevice, status);
 
-        verify(mStateMachine).sendMessage(
-                AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ADDRESSED_PLAYER);
+        verify(mStateMachine)
+                .sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ADDRESSED_PLAYER);
     }
 
     @Test
     public void handleAddressedPlayerChanged() {
         int id = 1;
 
-        mService.handleAddressedPlayerChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, id);
+        mService.handleAddressedPlayerChanged(mRemoteDevice, id);
 
-        verify(mStateMachine).sendMessage(
-                AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, id);
+        verify(mStateMachine)
+                .sendMessage(
+                        AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, id);
     }
 
     @Test
     public void handleNowPlayingContentChanged() {
-        mService.handleNowPlayingContentChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY);
+        mService.handleNowPlayingContentChanged(mRemoteDevice);
 
         verify(mStateMachine).nowPlayingContentChanged();
     }
 
     @Test
-    public void JniApisWithNoBehaviors_doNotCrash() {
-        mService.handlePassthroughRsp(1, 2, new byte[0]);
-        mService.handleGroupNavigationRsp(1, 2);
-        mService.getRcFeatures(new byte[0], 1);
-        mService.setPlayerAppSettingRsp(new byte[0], (byte) 0);
-    }
-
-    @Test
     public void onConnectionStateChanged_connectCase() {
         boolean remoteControlConnected = true;
         boolean browsingConnected = true; // Calls connect when any of them is true.
 
-        mService.onConnectionStateChanged(remoteControlConnected, browsingConnected,
-                REMOTE_DEVICE_ADDRESS_AS_ARRAY);
+        mService.onConnectionStateChanged(remoteControlConnected, browsingConnected, mRemoteDevice);
 
         ArgumentCaptor<StackEvent> captor = ArgumentCaptor.forClass(StackEvent.class);
         verify(mStateMachine).connect(captor.capture());
@@ -398,8 +340,7 @@
         boolean remoteControlConnected = false;
         boolean browsingConnected = false; // Calls disconnect when both of them are false.
 
-        mService.onConnectionStateChanged(
-                remoteControlConnected, browsingConnected, REMOTE_DEVICE_ADDRESS_AS_ARRAY);
+        mService.onConnectionStateChanged(remoteControlConnected, browsingConnected, mRemoteDevice);
         assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
         verify(mStateMachine).disconnect();
     }
@@ -408,7 +349,7 @@
     public void getRcPsm() {
         int psm = 1;
 
-        mService.getRcPsm(REMOTE_DEVICE_ADDRESS_AS_ARRAY, psm);
+        mService.getRcPsm(mRemoteDevice, psm);
 
         verify(mStateMachine).sendMessage(
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_RECEIVED_COVER_ART_PSM, psm);
@@ -418,7 +359,7 @@
     public void handleRegisterNotificationAbsVol() {
         byte label = 1;
 
-        mService.handleRegisterNotificationAbsVol(REMOTE_DEVICE_ADDRESS_AS_ARRAY, label);
+        mService.handleRegisterNotificationAbsVol(mRemoteDevice, label);
 
         verify(mStateMachine).sendMessage(
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION);
@@ -429,10 +370,10 @@
         byte absVol = 15;
         byte label = 1;
 
-        mService.handleSetAbsVolume(REMOTE_DEVICE_ADDRESS_AS_ARRAY, absVol, label);
+        mService.handleSetAbsVolume(mRemoteDevice, absVol, label);
 
-        verify(mStateMachine).sendMessage(
-                AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD, absVol);
+        verify(mStateMachine)
+                .sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD, absVol);
     }
 
     @Test
@@ -441,11 +382,13 @@
         int[] attrs = new int[0];
         String[] attrVals = new String[0];
 
-        mService.onTrackChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, numAttrs, attrs, attrVals);
+        mService.onTrackChanged(mRemoteDevice, numAttrs, attrs, attrVals);
 
         ArgumentCaptor<AvrcpItem> captor = ArgumentCaptor.forClass(AvrcpItem.class);
-        verify(mStateMachine).sendMessage(
-                eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED), captor.capture());
+        verify(mStateMachine)
+                .sendMessage(
+                        eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED),
+                        captor.capture());
         AvrcpItem item = captor.getValue();
         assertThat(item.getDevice().getAddress()).isEqualTo(REMOTE_DEVICE_ADDRESS);
         assertThat(item.getItemType()).isEqualTo(AvrcpItem.TYPE_MEDIA);
@@ -457,7 +400,7 @@
         int songLen = 100;
         int currSongPos = 33;
 
-        mService.onPlayPositionChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, songLen, currSongPos);
+        mService.onPlayPositionChanged(mRemoteDevice, songLen, currSongPos);
 
         verify(mStateMachine).sendMessage(
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_POS_CHANGED, songLen, currSongPos);
@@ -465,9 +408,9 @@
 
     @Test
     public void onPlayStatusChanged() {
-        byte status = AvrcpControllerService.JNI_PLAY_STATUS_REV_SEEK;
+        byte status = PlaybackStateCompat.STATE_REWINDING;
 
-        mService.onPlayStatusChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, status);
+        mService.onPlayStatusChanged(mRemoteDevice, status);
 
         verify(mStateMachine).sendMessage(
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
@@ -476,22 +419,28 @@
 
     @Test
     public void onPlayerAppSettingChanged() {
-        byte[] playerAttribRsp = new byte[] {PlayerApplicationSettings.REPEAT_STATUS,
-                PlayerApplicationSettings.JNI_REPEAT_STATUS_ALL_TRACK_REPEAT};
+        byte[] playerAttribRsp =
+                new byte[] {
+                    PlayerApplicationSettings.REPEAT_STATUS,
+                    PlayerApplicationSettings.JNI_REPEAT_STATUS_ALL_TRACK_REPEAT
+                };
 
-        mService.onPlayerAppSettingChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, playerAttribRsp, 2);
+        mService.onPlayerAppSettingChanged(mRemoteDevice, playerAttribRsp, 2);
 
-        verify(mStateMachine).sendMessage(
-                eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS),
-                any(PlayerApplicationSettings.class));
+        verify(mStateMachine)
+                .sendMessage(
+                        eq(
+                                AvrcpControllerStateMachine
+                                        .MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS),
+                        any(PlayerApplicationSettings.class));
     }
 
     @Test
     public void onAvailablePlayerChanged() {
-        mService.onAvailablePlayerChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY);
+        mService.onAvailablePlayerChanged(mRemoteDevice);
 
-        verify(mStateMachine).sendMessage(
-                AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);
+        verify(mStateMachine)
+                .sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);
     }
 
     @Test
@@ -499,27 +448,28 @@
         int status = 2;
         AvrcpItem[] items = new AvrcpItem[] {mock(AvrcpItem.class)};
 
-        mService.handleGetFolderItemsRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, status, items);
+        mService.handleGetFolderItemsRsp(mRemoteDevice, status, items);
 
-        verify(mStateMachine).sendMessage(
-                eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS),
-                eq(new ArrayList<>(Arrays.asList(items))));
+        verify(mStateMachine)
+                .sendMessage(
+                        eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS),
+                        eq(new ArrayList<>(Arrays.asList(items))));
     }
 
     @Test
     public void handleGetPlayerItemsRsp() {
-        AvrcpPlayer[] items = new AvrcpPlayer[] {mock(AvrcpPlayer.class)};
+        List<AvrcpPlayer> items = List.of(mock(AvrcpPlayer.class));
 
-        mService.handleGetPlayerItemsRsp(REMOTE_DEVICE_ADDRESS_AS_ARRAY, items);
+        mService.handleGetPlayerItemsRsp(mRemoteDevice, items);
 
         verify(mStateMachine).sendMessage(
                 eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS),
-                eq(new ArrayList<>(Arrays.asList(items))));
+                eq(items));
     }
 
     @Test
     public void dump_doesNotCrash() {
-        mService.getRcPsm(REMOTE_DEVICE_ADDRESS_AS_ARRAY, 1);
+        mService.getRcPsm(mRemoteDevice, 1);
         mService.dump(new StringBuilder());
     }
 
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
index 2fa6849..50bc492 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
@@ -43,6 +43,7 @@
 import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.a2dpsink.A2dpSinkService;
+import com.android.bluetooth.a2dpsink.A2dpSinkNativeInterface;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
@@ -81,6 +82,8 @@
     @Mock private Resources mMockResources;
     private ArgumentCaptor<Intent> mIntentArgument = ArgumentCaptor.forClass(Intent.class);
     @Mock private AvrcpControllerService mAvrcpControllerService;
+    @Mock private AvrcpControllerNativeInterface mNativeInterface;
+    @Mock private A2dpSinkNativeInterface mA2dpSinkNativeInterface;
     @Mock private AvrcpCoverArtManager mCoverArtManager;
 
     private byte[] mTestAddress = new byte[]{01, 01, 01, 01, 01, 01};
@@ -100,6 +103,7 @@
         doReturn(mDatabaseManager).when(mA2dpAdapterService).getDatabase();
         doReturn(true).when(mA2dpAdapterService).isStartedProfile(anyString());
         TestUtils.setAdapterService(mA2dpAdapterService);
+        A2dpSinkNativeInterface.setInstance(mA2dpSinkNativeInterface);
         TestUtils.startService(mA2dpServiceRule, A2dpSinkService.class);
         A2dpSinkService.setA2dpSinkService(mA2dpSinkService);
         TestUtils.clearAdapterService(mA2dpAdapterService);
@@ -107,6 +111,7 @@
         // Start an AvrcpControllerService to get a real BluetoothMediaBrowserService up
         doReturn(true).when(mAvrcpAdapterService).isStartedProfile(anyString());
         TestUtils.setAdapterService(mAvrcpAdapterService);
+        AvrcpControllerNativeInterface.setInstance(mNativeInterface);
         TestUtils.startService(mAvrcpServiceRule, AvrcpControllerService.class);
 
         // Mock an AvrcpControllerService to give to all state machines
@@ -140,6 +145,8 @@
     public void tearDown() throws Exception {
         destroyStateMachine(mAvrcpStateMachine);
         TestUtils.clearAdapterService(mAvrcpAdapterService);
+        A2dpSinkNativeInterface.setInstance(null);
+        AvrcpControllerNativeInterface.setInstance(null);
     }
 
     /**
@@ -147,7 +154,7 @@
      */
     private AvrcpControllerStateMachine makeStateMachine(BluetoothDevice device) {
         AvrcpControllerStateMachine sm =
-                 new AvrcpControllerStateMachine(device, mAvrcpControllerService);
+                new AvrcpControllerStateMachine(device, mAvrcpControllerService, mNativeInterface);
         sm.start();
         return sm;
     }
@@ -159,13 +166,13 @@
         if (sm == null || sm.getState() == BluetoothProfile.STATE_DISCONNECTED) return;
 
         sm.disconnect();
-        TestUtils.waitForLooperToFinishScheduledTask(sm.getHandler().getLooper());
+        TestUtils.waitForLooperToBeIdle(sm.getHandler().getLooper());
 
         // is disconnected
         Assert.assertEquals(sm.getState(), BluetoothProfile.STATE_DISCONNECTED);
 
         // told mAvrcpControllerService to remove it
-        // verify(mAvrcpControllerService).removeStateMachine(eq(sm));
+        verify(mAvrcpControllerService).removeStateMachine(eq(sm));
     }
 
     /**
@@ -482,12 +489,16 @@
 
         //Play
         transportControls.play();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_DOWN));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_UP));
     }
 
     /**
@@ -501,12 +512,16 @@
 
         //Pause
         transportControls.pause();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_UP));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_UP));
     }
 
     /**
@@ -520,12 +535,16 @@
 
         //Stop
         transportControls.stop();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP), eq(KEY_DOWN));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP), eq(KEY_UP));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP),
+                        eq(KEY_UP));
     }
 
     /**
@@ -539,13 +558,16 @@
 
         //Next
         transportControls.skipToNext();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD),
-                eq(KEY_DOWN));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD), eq(KEY_UP));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD),
+                        eq(KEY_UP));
     }
 
     /**
@@ -559,13 +581,16 @@
 
         //Previous
         transportControls.skipToPrevious();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD),
-                eq(KEY_DOWN));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD), eq(KEY_UP));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD),
+                        eq(KEY_UP));
     }
 
     /**
@@ -580,14 +605,18 @@
 
         //FastForward
         transportControls.fastForward();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FF), eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_FF),
+                        eq(KEY_DOWN));
         //Finish FastForwarding
         transportControls.play();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FF), eq(KEY_UP));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_FF),
+                        eq(KEY_UP));
     }
 
     /**
@@ -601,14 +630,18 @@
 
         //Rewind
         transportControls.rewind();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_REWIND), eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_REWIND),
+                        eq(KEY_DOWN));
         //Finish Rewinding
         transportControls.play();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_REWIND), eq(KEY_UP));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_REWIND),
+                        eq(KEY_UP));
     }
 
     /**
@@ -625,15 +658,13 @@
 
         //Play an invalid item below start
         transportControls.skipToQueueItem(minSize - 1);
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).playItemNative(
-                eq(mTestAddress), eq(scope), anyLong(), anyInt());
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0))
+                .playItem(eq(mTestAddress), eq(scope), anyLong(), anyInt());
 
         //Play an invalid item beyond end
         transportControls.skipToQueueItem(maxSize + 1);
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).playItemNative(
-                eq(mTestAddress), eq(scope), anyLong(), anyInt());
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0))
+                .playItem(eq(mTestAddress), eq(scope), anyLong(), anyInt());
     }
 
     /**
@@ -650,9 +681,9 @@
 
         //Shuffle
         transportControls.setShuffleMode(1);
-        verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
-                .setPlayerApplicationSettingValuesNative(
-                eq(mTestAddress), eq((byte) 1), eq(shuffleSetting), eq(shuffleMode));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .setPlayerApplicationSettingValues(
+                        eq(mTestAddress), eq((byte) 1), eq(shuffleSetting), eq(shuffleMode));
     }
 
     /**
@@ -669,9 +700,9 @@
 
         //Shuffle
         transportControls.setRepeatMode(2);
-        verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
-                .setPlayerApplicationSettingValuesNative(
-                eq(mTestAddress), eq((byte) 1), eq(repeatSetting), eq(repeatMode));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .setPlayerApplicationSettingValues(
+                        eq(mTestAddress), eq((byte) 1), eq(repeatSetting), eq(repeatMode));
     }
 
     /**
@@ -694,9 +725,8 @@
         //Request fetch the list of players
         BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
         mAvrcpStateMachine.requestContents(results);
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
-                eq(0), eq(19));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getPlayerList(eq(mTestAddress), eq(0), eq(19));
 
         //Provide back a player object
         byte[] playerFeatures =
@@ -718,13 +748,11 @@
         BrowseTree.BrowseNode playerOneNode = mAvrcpStateMachine.findNode(
                 results.getChildren().get(0).getID());
         mAvrcpStateMachine.requestContents(playerOneNode);
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).setBrowsedPlayerNative(
-                eq(mTestAddress), eq(1));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .setBrowsedPlayer(eq(mTestAddress), eq(1));
         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH, 5);
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getFolderListNative(eq(mTestAddress),
-                eq(0), eq(4));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getFolderList(eq(mTestAddress), eq(0), eq(4));
     }
 
     /**
@@ -743,9 +771,8 @@
 
         // Verify we've uncached our browse root and made the call to fetch new players
         Assert.assertFalse(mAvrcpStateMachine.findNode(rootName).isCached());
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
-                eq(0), eq(19));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getPlayerList(eq(mTestAddress), eq(0), eq(19));
     }
 
     /**
@@ -766,6 +793,7 @@
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
 
         // Send an available players have changed event
         mAvrcpStateMachine.sendMessage(
@@ -773,9 +801,8 @@
 
         // Verify we've uncached our browse root and made the call to fetch new players
         Assert.assertFalse(mAvrcpStateMachine.findNode(rootName).isCached());
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
-                eq(0), eq(19));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getPlayerList(eq(mTestAddress), eq(0), eq(19));
 
         // Send available players set that contains our addressed player
         byte[] playerFeatures =
@@ -803,15 +830,12 @@
 
         // Verify we request metadata, playback state and now playing list
         assertNowPlayingList(new ArrayList<AvrcpItem>());
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getCurrentMetadataNative(
-                eq(mTestAddress));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlaybackStateNative(
-                eq(mTestAddress));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getCurrentMetadata(eq(mTestAddress));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getPlaybackState(eq(mTestAddress));
     }
 
     /**
@@ -829,9 +853,8 @@
 
         // Verify we've uncached our browse root and made the call to fetch new players
         Assert.assertFalse(mAvrcpStateMachine.findNode(rootName).isCached());
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
-                eq(0), eq(19));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getPlayerList(eq(mTestAddress), eq(0), eq(19));
 
         // Send available players set that does not contain the addressed player
         byte[] playerFeatures =
@@ -860,13 +883,12 @@
 
         // Verify we do not request metadata, playback state and now playing list because we're
         // sure the addressed player and metadata we have isn't impacted by the new players
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).getNowPlayingListNative(
-                any(), anyInt(), anyInt());
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).getCurrentMetadataNative(any());
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).getPlaybackStateNative(any());
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0))
+                .getNowPlayingList(any(), anyInt(), anyInt());
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0))
+                .getCurrentMetadata(any());
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0))
+                .getPlaybackState(any());
     }
 
     /**
@@ -885,9 +907,8 @@
         //Request fetch the list of players
         BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
         mAvrcpStateMachine.requestContents(results);
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
-                eq(0), eq(19));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getPlayerList(eq(mTestAddress), eq(0), eq(19));
 
         //Provide back two player objects, IDs 1 and 2
         byte[] playerFeatures =
@@ -906,6 +927,7 @@
         nowPlayingList.add(makeNowPlayingItem(2, "Song 2"));
         setNowPlayingList(nowPlayingList);
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
 
         //Change players and verify that BT attempts to update the results
         mAvrcpStateMachine.sendMessage(
@@ -920,18 +942,15 @@
         //Make sure the Now Playing list is now cleared
         assertNowPlayingList(new ArrayList<AvrcpItem>());
 
-        //Verify that a player change to a player with Now Playing support causes a refresh.
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        // Verify that a player change to a player with Now Playing support causes a refresh.
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
 
-        //Verify we request metadata and playback state
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getCurrentMetadataNative(
-                eq(mTestAddress));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlaybackStateNative(
-                eq(mTestAddress));
+        // Verify we request metadata and playback state
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getCurrentMetadata(eq(mTestAddress));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getPlaybackState(eq(mTestAddress));
     }
 
     /**
@@ -974,12 +993,10 @@
 
         //Make sure the Now Playing list is now cleared and we requested metadata
         assertNowPlayingList(new ArrayList<AvrcpItem>());
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getCurrentMetadataNative(
-                eq(mTestAddress));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlaybackStateNative(
-                eq(mTestAddress));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getCurrentMetadata(eq(mTestAddress));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getPlaybackState(eq(mTestAddress));
     }
 
     /**
@@ -1028,6 +1045,7 @@
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
 
         // Send an addressed player changed to the same player ID
         mAvrcpStateMachine.sendMessage(
@@ -1036,15 +1054,12 @@
 
         // Verify we make no assumptions about the player ID and still fetch metadata, play status
         // and now playing list (since player 1 supports it)
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getCurrentMetadataNative(
-                eq(mTestAddress));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlaybackStateNative(
-                eq(mTestAddress));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getCurrentMetadata(eq(mTestAddress));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getPlaybackState(eq(mTestAddress));
     }
 
     /**
@@ -1054,9 +1069,8 @@
     public void testNowPlaying() {
         setUpConnectedState(true, true);
         mAvrcpStateMachine.nowPlayingContentChanged();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
     }
 
     /**
@@ -1079,12 +1093,16 @@
         MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
         transportControls.play();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_DOWN));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_UP));
     }
 
     /**
@@ -1095,8 +1113,8 @@
         setUpConnectedState(true, true);
         mAvrcpStateMachine.sendMessage(
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION);
-        verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
-                .sendRegisterAbsVolRspNative(any(), anyByte(), eq(127), anyInt());
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendRegisterAbsVolRsp(any(), anyByte(), eq(127), anyInt());
     }
 
     /**
@@ -1113,9 +1131,11 @@
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
                 PlaybackStateCompat.STATE_PLAYING);
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
     }
 
@@ -1148,9 +1168,11 @@
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
                 PlaybackStateCompat.STATE_PLAYING);
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
     }
 
@@ -1168,8 +1190,11 @@
                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
                 PlaybackStateCompat.STATE_PLAYING);
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
     }
 
@@ -1262,9 +1287,11 @@
         // becoming inactive
         setActiveDevice(null);
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
         Assert.assertFalse(mAvrcpStateMachine.isActive());
     }
 
@@ -1370,15 +1397,18 @@
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
         Assert.assertFalse(mAvrcpStateMachine.isActive());
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
 
         // Now that we're inactive, receive a playback status of playing
         setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
 
         // Verify we send a pause, never request audio focus, and the playback state on
         // BluetoothMediaBrowserService never updates.
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
         Assert.assertEquals(PlaybackStateCompat.STATE_ERROR,
                 BluetoothMediaBrowserService.getPlaybackState().getState());
@@ -1396,6 +1426,7 @@
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
         Assert.assertFalse(mAvrcpStateMachine.isActive());
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
 
         // Now that we're inactive, receive a play position change
         setPlaybackPosition(1, 10);
@@ -1453,10 +1484,16 @@
         setUpConnectedState(true, true);
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
-        verify(mAvrcpControllerService,never()).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_UP));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_UP));
     }
 
     /**
@@ -1473,10 +1510,16 @@
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
 
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, times(1)).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
-        verify(mAvrcpControllerService, times(1)).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_UP));
+        verify(mNativeInterface, times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_UP));
     }
 
     /**
@@ -1491,10 +1534,16 @@
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
 
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
-        verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_UP));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_UP));
     }
 
     /**
@@ -1508,10 +1557,16 @@
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_LOSS);
 
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, times(1)).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
-        verify(mAvrcpControllerService, times(1)).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_UP));
+        verify(mNativeInterface, times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_UP));
     }
 
     /**
@@ -1525,10 +1580,16 @@
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_LOSS);
 
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, times(0)).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
-        verify(mAvrcpControllerService, times(0)).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_UP));
+        verify(mNativeInterface, times(0))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, times(0))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_UP));
     }
 
     /**
@@ -1539,14 +1600,21 @@
     public void testOnAudioFocusGainFromTransientLossWhilePlaying_playSent() {
         testOnAudioFocusTransientLossWhilePlaying_pauseSent();
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
         setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
 
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, times(1)).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_DOWN));
-        verify(mAvrcpControllerService, times(1)).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP));
+        verify(mNativeInterface, times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_UP));
     }
 
     /**
@@ -1556,26 +1624,37 @@
     public void testOnAudioFocusGainFromTransientLossWhilePlayingWithPause_playNotSent() {
         testOnAudioFocusTransientLossWhilePlaying_pauseSent();
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
         setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
 
         MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
         transportControls.pause();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_UP));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
+                        eq(KEY_UP));
 
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
 
-        verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_DOWN));
-        verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_UP));
     }
 
     /**
@@ -1586,26 +1665,37 @@
     public void testOnAudioFocusGainFromTransientLossWithStop_playNotSent() {
         testOnAudioFocusTransientLossWhilePlaying_pauseSent();
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
         setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
 
         MediaControllerCompat.TransportControls transportControls =
                 BluetoothMediaBrowserService.getTransportControls();
         transportControls.stop();
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP), eq(KEY_DOWN));
-        verify(mAvrcpControllerService,
-                timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
-                eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP), eq(KEY_UP));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP),
+                        eq(KEY_UP));
 
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
 
-        verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_DOWN));
-        verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_UP));
     }
 
     /**
@@ -1616,13 +1706,20 @@
     public void testOnAudioFocusGainFromTransientLossWhilePaused_playNotSent() {
         testOnAudioFocusTransientLossWhilePaused_pauseNotSent();
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
 
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_DOWN));
-        verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(eq(mTestAddress),
-                eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_DOWN));
+        verify(mNativeInterface, never())
+                .sendPassThroughCommand(
+                        eq(mTestAddress),
+                        eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
+                        eq(KEY_UP));
     }
 
     /**
@@ -1654,13 +1751,11 @@
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
 
         // Verify download attempt and send some elements over, verify next set is requested
-        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, times(1)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
                 new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(20), eq(39));
+        verify(mNativeInterface, times(1)).getNowPlayingList(eq(mTestAddress), eq(20), eq(39));
 
         // Force a now playing content invalidation and verify attempted download
         mAvrcpStateMachine.nowPlayingContentChanged();
@@ -1672,8 +1767,7 @@
                 new ArrayList<AvrcpItem>(nowPlayingList.subList(20, 25)));
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
 
-        verify(mAvrcpControllerService, times(2)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, times(2)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
 
         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
                 new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(0, 20)));
@@ -1720,8 +1814,7 @@
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
 
         // Verify download attempt and send some elements over, verify next set is requested
-        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, times(1)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
         mAvrcpStateMachine.nowPlayingContentChanged();
 
         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
@@ -1730,8 +1823,7 @@
 
         // Receiving the previous members should cause our fetch process to realize we're aborted
         // and a new (second) request should be triggered for the list from the beginning
-        verify(mAvrcpControllerService, times(2)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, times(2)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
 
         // Send whole list
         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
@@ -1779,13 +1871,11 @@
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
 
         // Verify download attempt and send some elements over, verify next set is requested
-        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, times(1)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
                 new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(20), eq(39));
+        verify(mNativeInterface, times(1)).getNowPlayingList(eq(mTestAddress), eq(20), eq(39));
 
         // Force a now playing content invalidation due to addressed player change
         mAvrcpStateMachine.sendMessage(
@@ -1798,8 +1888,7 @@
                 new ArrayList<AvrcpItem>(nowPlayingList.subList(20, 25)));
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
 
-        verify(mAvrcpControllerService, times(2)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, times(2)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
 
         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
                 new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(0, 20)));
@@ -1846,8 +1935,7 @@
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
 
         // Verify download attempt and send some elements over, verify next set is requested
-        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, times(1)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
 
         // Force a now playing content invalidation due to addressed player change, happening
         // before we've received any items from the remote device.
@@ -1860,8 +1948,7 @@
                 new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
 
-        verify(mAvrcpControllerService, times(2)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, times(2)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
 
         // Send requested items, they're likely from the new list at this point, but it shouldn't
         // matter what they are because we should toss them out and restart our download next.
@@ -1895,6 +1982,7 @@
         nowPlayingList.add(makeNowPlayingItem(1, "Song 1"));
         setNowPlayingList(nowPlayingList);
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
 
         // Invalidate the contents by doing a new fetch
         BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
@@ -1902,8 +1990,7 @@
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
 
         // Request for new contents should be sent
-        verify(mAvrcpControllerService, times(1)).getNowPlayingListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, times(1)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
         Assert.assertFalse(nowPlaying.isCached());
 
         // Send timeout on our own instead of waiting 10 seconds
@@ -1932,9 +2019,11 @@
         setUpConnectedState(true, true);
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
         mAvrcpStateMachine.requestContents(null);
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
         verifyNoMoreInteractions(mAvrcpControllerService);
+        verifyNoMoreInteractions(mNativeInterface);
     }
 
     /**
@@ -1947,10 +2036,12 @@
         setUpConnectedState(true, false);
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
         BrowseTree.BrowseNode deviceRoot = mAvrcpStateMachine.findNode(rootName);
         mAvrcpStateMachine.requestContents(deviceRoot);
         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
         verifyNoMoreInteractions(mAvrcpControllerService);
+        verifyNoMoreInteractions(mNativeInterface);
     }
 
     /**
@@ -1963,12 +2054,12 @@
         setUpConnectedState(true, false);
         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
         clearInvocations(mAvrcpControllerService);
+        clearInvocations(mNativeInterface);
         BrowseTree.BrowseNode deviceRoot = mAvrcpStateMachine.findNode(rootName);
         mAvrcpStateMachine.requestContents(deviceRoot);
         // issues a player list fetch
         mAvrcpStateMachine.connect(StackEvent.connectionStateChanged(true, true));
         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
-        verify(mAvrcpControllerService, times(1)).getPlayerListNative(
-                eq(mTestAddress), eq(0), eq(19));
+        verify(mNativeInterface, times(1)).getPlayerList(eq(mTestAddress), eq(0), eq(19));
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtProviderTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtProviderTest.java
index f650390..28a8dd6 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtProviderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpCoverArtProviderTest.java
@@ -57,16 +57,16 @@
 
     @Rule
     public final ServiceTestRule mServiceRule = new ServiceTestRule();
-    @Mock
-    private Uri mUri;
-    @Mock
-    private AdapterService mAdapterService;
+    @Mock private Uri mUri;
+    @Mock private AdapterService mAdapterService;
+    @Mock private AvrcpControllerNativeInterface mNativeInterface;
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         TestUtils.setAdapterService(mAdapterService);
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
+        AvrcpControllerNativeInterface.setInstance(mNativeInterface);
         TestUtils.startService(mServiceRule, AvrcpControllerService.class);
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mTestDevice = mAdapter.getRemoteDevice(mTestAddress);
@@ -76,6 +76,7 @@
     @After
     public void tearDown() throws Exception {
         TestUtils.stopService(mServiceRule, AvrcpControllerService.class);
+        AvrcpControllerNativeInterface.setInstance(null);
         TestUtils.clearAdapterService(mAdapterService);
     }
 
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 3164fec..71455a0 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
@@ -39,7 +39,6 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothLeAudioCodecConfigMetadata;
 import android.bluetooth.BluetoothLeAudioContentMetadata;
-import android.bluetooth.BluetoothLeBroadcast;
 import android.bluetooth.BluetoothLeBroadcastAssistant;
 import android.bluetooth.BluetoothLeBroadcastChannel;
 import android.bluetooth.BluetoothLeBroadcastMetadata;
@@ -50,6 +49,8 @@
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetoothLeBroadcastAssistantCallback;
 import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanResult;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -1040,4 +1041,106 @@
         assertThat(devices.contains(mCurrentDevice)).isTrue();
         assertThat(devices.contains(mCurrentDevice1)).isTrue();
     }
+
+    @Test
+    public void testActiveSyncedSource_AddRemoveGet() {
+        prepareConnectedDeviceGroup();
+        assertThat(mStateMachines.size()).isEqualTo(2);
+
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isEqualTo(null);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isEqualTo(null);
+
+        BluetoothDevice testDevice =
+                mBluetoothAdapter.getRemoteLeDevice(
+                        TEST_MAC_ADDRESS, BluetoothDevice.ADDRESS_TYPE_RANDOM);
+        // Verify add active synced source
+        mBassClientService.addActiveSyncedSource(mCurrentDevice, testDevice);
+        mBassClientService.addActiveSyncedSource(mCurrentDevice1, testDevice);
+        // Verify duplicated source won't be added
+        mBassClientService.addActiveSyncedSource(mCurrentDevice, testDevice);
+        mBassClientService.addActiveSyncedSource(mCurrentDevice1, testDevice);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNotEqualTo(null);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNotEqualTo(null);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice).size()).isEqualTo(1);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1).size()).isEqualTo(1);
+
+        // Verify remove active synced source
+        mBassClientService.removeActiveSyncedSource(mCurrentDevice, testDevice);
+        mBassClientService.removeActiveSyncedSource(mCurrentDevice1, testDevice);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isEqualTo(null);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isEqualTo(null);
+    }
+
+    @Test
+    public void testSelectSource_invalidActiveSource() {
+        byte[] scanRecord = new byte[]{
+                0x02, 0x01, 0x1a, // advertising flags
+                0x05, 0x02, 0x52, 0x18, 0x0a, 0x11, // 16 bit service uuids
+                0x04, 0x09, 0x50, 0x65, 0x64, // name
+                0x02, 0x0A, (byte) 0xec, // tx power level
+                0x05, 0x30, 0x54, 0x65, 0x73, 0x74, // broadcast name: Test
+                0x06, 0x16, 0x52, 0x18, 0x50, 0x64, 0x65, // service data
+                0x08, 0x16, 0x56, 0x18, 0x07, 0x03, 0x06, 0x07, 0x08,
+                // service data - public broadcast,
+                // feature - 0x7, metadata len - 0x3, metadata - 0x6, 0x7, 0x8
+                0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
+                0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
+        };
+        ScanRecord record = ScanRecord.parseFromBytes(scanRecord);
+
+        prepareConnectedDeviceGroup();
+        assertThat(mStateMachines.size()).isEqualTo(2);
+
+        BluetoothDevice testDevice = mBluetoothAdapter.getRemoteLeDevice(
+                TEST_MAC_ADDRESS, BluetoothDevice.ADDRESS_TYPE_RANDOM);
+        BluetoothDevice testDevice1 = mBluetoothAdapter.getRemoteLeDevice(
+                "00:11:22:33:44:66", BluetoothDevice.ADDRESS_TYPE_RANDOM);
+        BluetoothDevice testDevice2 = mBluetoothAdapter.getRemoteLeDevice(
+                "00:11:22:33:44:77", BluetoothDevice.ADDRESS_TYPE_RANDOM);
+        BluetoothDevice testDevice3 = mBluetoothAdapter.getRemoteLeDevice(
+                "00:11:22:33:44:88", BluetoothDevice.ADDRESS_TYPE_RANDOM);
+        // Verify add active synced source
+        mBassClientService.addActiveSyncedSource(mCurrentDevice, testDevice);
+        mBassClientService.addActiveSyncedSource(mCurrentDevice1, testDevice);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNotEqualTo(null);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNotEqualTo(null);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice).size()).isEqualTo(1);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1).size()).isEqualTo(1);
+
+        // Verify selectSource with synced device should not proceed
+        ScanResult scanResult = new ScanResult(testDevice, 0, 0, 0, 0, 0, 0, 0, record, 0);
+        mBassClientService.selectSource(mCurrentDevice, scanResult, false);
+        mBassClientService.selectSource(mCurrentDevice1, scanResult, false);
+        for (BassClientStateMachine sm : mStateMachines.values()) {
+            verify(sm, never()).sendMessage(any());
+        }
+
+        // Verify selectSource with max synced device should not proceed
+        mBassClientService.addActiveSyncedSource(mCurrentDevice, testDevice1);
+        mBassClientService.addActiveSyncedSource(mCurrentDevice1, testDevice1);
+        mBassClientService.addActiveSyncedSource(mCurrentDevice, testDevice2);
+        mBassClientService.addActiveSyncedSource(mCurrentDevice1, testDevice2);
+        mBassClientService.addActiveSyncedSource(mCurrentDevice, testDevice3);
+        mBassClientService.addActiveSyncedSource(mCurrentDevice1, testDevice3);
+
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isNotEqualTo(null);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isNotEqualTo(null);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice).size()).isEqualTo(4);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1).size()).isEqualTo(4);
+
+        BluetoothDevice testDevice4 = mBluetoothAdapter.getRemoteLeDevice(
+                "00:01:02:03:04:05", BluetoothDevice.ADDRESS_TYPE_RANDOM);
+        ScanResult scanResult1 = new ScanResult(testDevice4, 0, 0, 0, 0, 0, 0, 0, record, 0);
+        mBassClientService.selectSource(mCurrentDevice, scanResult1, false);
+        mBassClientService.selectSource(mCurrentDevice1, scanResult1, false);
+        for (BassClientStateMachine sm : mStateMachines.values()) {
+            verify(sm, never()).sendMessage(any());
+        }
+
+        // Verify remove all active synced source
+        mBassClientService.removeActiveSyncedSource(mCurrentDevice, null);
+        mBassClientService.removeActiveSyncedSource(mCurrentDevice1, null);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice)).isEqualTo(null);
+        assertThat(mBassClientService.getActiveSyncedSources(mCurrentDevice1)).isEqualTo(null);
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java
index 7e0aca0..fe4ad4e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java
@@ -275,6 +275,9 @@
         allowConnection(true);
         allowConnectGatt(true);
 
+        // need this to ensure expected mock behavior for getActiveSyncedSource
+        when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null);
+
         assertThat(mBassClientStateMachine.getCurrentState())
                 .isInstanceOf(BassClientStateMachine.Disconnected.class);
 
@@ -438,7 +441,7 @@
     }
 
     @Test
-    public void parseScanRecord_withoutBaseData_makesNoStopScanOffloadFalse() {
+    public void parseScanRecord_withoutBaseData_callCancelActiveSync() {
         byte[] scanRecord = new byte[]{
                 0x02, 0x01, 0x1a, // advertising flags
                 0x05, 0x02, 0x0b, 0x11, 0x0a, 0x11, // 16 bit service uuids
@@ -448,10 +451,13 @@
                 0x05, (byte) 0xff, (byte) 0xe0, 0x00, 0x02, 0x15, // manufacturer specific data
                 0x03, 0x50, 0x01, 0x02, // an unknown data type won't cause trouble
         };
+        // need this to ensure expected mock behavior for getActiveSyncedSource
+        when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null);
+
         ScanRecord data = ScanRecord.parseFromBytes(scanRecord);
-        mBassClientStateMachine.mNoStopScanOffload = true;
         mBassClientStateMachine.parseScanRecord(0, data);
-        assertThat(mBassClientStateMachine.mNoStopScanOffload).isFalse();
+        // verify getActiveSyncedSource got called in CancelActiveSync
+        verify(mBassClientService).getActiveSyncedSources(any());
     }
 
     @Test
@@ -867,6 +873,9 @@
     public void sendOtherMessages_inDisconnectedState_doesNotChangeState() {
         initToDisconnectedState();
 
+        // need this to ensure expected mock behavior for getActiveSyncedSource
+        when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null);
+
         mBassClientStateMachine.sendMessage(PSYNC_ACTIVE_TIMEOUT);
         TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
         verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
@@ -977,6 +986,9 @@
         initToConnectedState();
 
         mBassClientStateMachine.mBluetoothGatt = null;
+        // need this to ensure expected mock behavior for getActiveSyncedSource
+        when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null);
+
         mBassClientStateMachine.sendMessage(DISCONNECT);
         TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
         verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
@@ -997,6 +1009,10 @@
 
         Message connectedMsg = mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED);
         connectedMsg.obj = BluetoothProfile.STATE_CONNECTED;
+
+        // need this to ensure expected mock behavior for getActiveSyncedSource
+        when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null);
+
         mBassClientStateMachine.sendMessage(connectedMsg);
         TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
         verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
@@ -1076,11 +1092,13 @@
     @Test
     public void sendPsyncActiveMessage_inConnectedState() {
         initToConnectedState();
+        // need this to ensure expected mock behavior for getActiveSyncedSource
+        when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null);
 
-        mBassClientStateMachine.mNoStopScanOffload = true;
         mBassClientStateMachine.sendMessage(PSYNC_ACTIVE_TIMEOUT);
         TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
-        assertThat(mBassClientStateMachine.mNoStopScanOffload).isFalse();
+        // verify getActiveSyncedSource got called in CancelActiveSync
+        verify(mBassClientService).getActiveSyncedSources(any());
     }
 
     @Test
@@ -1130,6 +1148,8 @@
         when(mBassClientService.getCallbacks()).thenReturn(callbacks);
 
         BluetoothLeBroadcastMetadata metadata = createBroadcastMetadata();
+        // verify local broadcast doesn't require active synced source
+        when(mBassClientService.isLocalBroadcast(any())).thenReturn(true);
         mBassClientStateMachine.sendMessage(ADD_BCAST_SOURCE, metadata);
         TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
 
@@ -1369,6 +1389,9 @@
         // Mock instance of btGatt was created in initToConnectedProcessingState().
         BassClientStateMachine.BluetoothGattTestableWrapper btGatt =
                 mBassClientStateMachine.mBluetoothGatt;
+        // need this to ensure expected mock behavior for getActiveSyncedSource
+        when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null);
+
         mBassClientStateMachine.mBluetoothGatt = null;
         mBassClientStateMachine.sendMessage(DISCONNECT);
         TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
@@ -1390,6 +1413,9 @@
                 mBassClientStateMachine.obtainMessage(CONNECTION_STATE_CHANGED);
         msgToConnectedState.obj = BluetoothProfile.STATE_CONNECTED;
 
+        // need this to ensure expected mock behavior for getActiveSyncedSource
+        when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null);
+
         mBassClientStateMachine.sendMessage(msgToConnectedState);
         TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
         verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
@@ -1414,15 +1440,17 @@
         initToConnectedProcessingState();
         BassClientService.Callbacks callbacks = Mockito.mock(BassClientService.Callbacks.class);
         when(mBassClientService.getCallbacks()).thenReturn(callbacks);
+        // need this to ensure expected mock behavior for getActiveSyncedSource
+        when(mBassClientService.getActiveSyncedSources(any())).thenReturn(null);
 
         // Test sendPendingCallbacks(START_SCAN_OFFLOAD, ERROR_UNKNOWN)
         mBassClientStateMachine.mPendingOperation = START_SCAN_OFFLOAD;
-        mBassClientStateMachine.mNoStopScanOffload = true;
         mBassClientStateMachine.mAutoTriggered = false;
         sendMessageAndVerifyTransition(
                 mBassClientStateMachine.obtainMessage(GATT_TXN_PROCESSED, GATT_FAILURE),
                 BassClientStateMachine.Connected.class);
-        assertThat(mBassClientStateMachine.mNoStopScanOffload).isFalse();
+        // verify getActiveSyncedSource got called in CancelActiveSync
+        verify(mBassClientService).getActiveSyncedSources(any());
 
         // Test sendPendingCallbacks(START_SCAN_OFFLOAD, ERROR_UNKNOWN)
         moveConnectedStateToConnectedProcessingState();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/bass_client/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
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 1b444ce..f949f87 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
@@ -63,8 +63,9 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Objects;
+import java.util.Map;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -148,21 +149,6 @@
         when(mHearingAidService.getHiSyncId(mHearingAidDevice)).thenReturn(mHearingAidHiSyncId);
         when(mHearingAidService.getConnectedPeerDevices(mHearingAidHiSyncId))
                 .thenReturn(connectedHearingAidDevices);
-
-        when(mA2dpService.getFallbackDevice()).thenAnswer(invocation -> {
-            if (!mDeviceConnectionStack.isEmpty() && Objects.equals(mA2dpDevice,
-                    mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1))) {
-                return mA2dpDevice;
-            }
-            return null;
-        });
-        when(mHeadsetService.getFallbackDevice()).thenAnswer(invocation -> {
-            if (!mDeviceConnectionStack.isEmpty() && Objects.equals(mHeadsetDevice,
-                    mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1))) {
-                return mHeadsetDevice;
-            }
-            return null;
-        });
     }
 
     @After
@@ -1131,6 +1117,49 @@
         verify(mHearingAidService, timeout(TIMEOUT_MS)).removeActiveDevice(false);
     }
 
+    @Test
+    public void testGetFallbackCandidates() {
+        BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
+        BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
+
+        // No connected device
+        Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().isEmpty());
+
+        // One connected device
+        headsetConnected(deviceA, true);
+        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
+        Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceA));
+
+        // Two connected devices
+        headsetConnected(deviceB, false);
+        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
+        Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceA));
+        Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceB));
+    }
+
+    @Test
+    public void testGetFhpFallbackCandidates_HasWatchDevice() {
+        BluetoothDevice deviceWatch = TestUtils.getTestDevice(mAdapter, 0);
+        BluetoothDevice deviceRegular = TestUtils.getTestDevice(mAdapter, 1);
+
+        // Make deviceWatch a watch
+        mDatabaseManager.setCustomMeta(
+                deviceWatch,
+                BluetoothDevice.METADATA_DEVICE_TYPE,
+                BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
+
+        // Has a connected watch device
+        headsetConnected(deviceWatch, false);
+        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
+        Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().isEmpty());
+
+        // Two connected devices with one watch
+        headsetConnected(deviceRegular, true);
+        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
+        Assert.assertFalse(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceWatch));
+        Assert.assertTrue(mActiveDeviceManager.getHfpFallbackCandidates().contains(deviceRegular));
+    }
+
     /**
      * Helper to indicate A2dp connected for a device.
      */
@@ -1145,8 +1174,11 @@
         mDeviceConnectionStack.add(device);
         mMostRecentDevice = device;
 
-        mActiveDeviceManager.a2dpConnectionStateChanged(
-                device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.A2DP,
+                device,
+                BluetoothProfile.STATE_DISCONNECTED,
+                BluetoothProfile.STATE_CONNECTED);
     }
 
     /**
@@ -1157,8 +1189,11 @@
         mMostRecentDevice = (mDeviceConnectionStack.size() > 0)
                 ? mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1) : null;
 
-        mActiveDeviceManager.a2dpConnectionStateChanged(
-                device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.A2DP,
+                device,
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_DISCONNECTED);
     }
 
     /** Helper to indicate A2dp active device changed for a device. */
@@ -1167,7 +1202,7 @@
         mDeviceConnectionStack.add(device);
         mMostRecentDevice = device;
 
-        mActiveDeviceManager.a2dpActiveStateChanged(device);
+        mActiveDeviceManager.profileActiveDeviceChanged(BluetoothProfile.A2DP, device);
     }
 
     /** Helper to indicate Headset connected for a device. */
@@ -1182,8 +1217,11 @@
         mDeviceConnectionStack.add(device);
         mMostRecentDevice = device;
 
-        mActiveDeviceManager.hfpConnectionStateChanged(
-                device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.HEADSET,
+                device,
+                BluetoothProfile.STATE_DISCONNECTED,
+                BluetoothProfile.STATE_CONNECTED);
     }
 
     /** Helper to indicate Headset disconnected for a device. */
@@ -1194,8 +1232,11 @@
                         ? mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1)
                         : null;
 
-        mActiveDeviceManager.hfpConnectionStateChanged(
-                device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.HEADSET,
+                device,
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_DISCONNECTED);
     }
 
     /** Helper to indicate Headset active device changed for a device. */
@@ -1204,7 +1245,7 @@
         mDeviceConnectionStack.add(device);
         mMostRecentDevice = device;
 
-        mActiveDeviceManager.hfpActiveStateChanged(device);
+        mActiveDeviceManager.profileActiveDeviceChanged(BluetoothProfile.HEADSET, device);
     }
 
     /**
@@ -1214,8 +1255,11 @@
         mDeviceConnectionStack.add(device);
         mMostRecentDevice = device;
 
-        mActiveDeviceManager.hearingAidConnectionStateChanged(
-                device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.HEARING_AID,
+                device,
+                BluetoothProfile.STATE_DISCONNECTED,
+                BluetoothProfile.STATE_CONNECTED);
     }
 
     /**
@@ -1226,8 +1270,11 @@
         mMostRecentDevice = (mDeviceConnectionStack.size() > 0)
                 ? mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1) : null;
 
-        mActiveDeviceManager.hearingAidConnectionStateChanged(
-                device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.HEARING_AID,
+                device,
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_DISCONNECTED);
     }
 
     /**
@@ -1238,7 +1285,7 @@
         mDeviceConnectionStack.add(device);
         mMostRecentDevice = device;
 
-        mActiveDeviceManager.hearingAidActiveStateChanged(device);
+        mActiveDeviceManager.profileActiveDeviceChanged(BluetoothProfile.HEARING_AID, device);
     }
 
     /**
@@ -1247,8 +1294,11 @@
     private void leAudioConnected(BluetoothDevice device) {
         mMostRecentDevice = device;
 
-        mActiveDeviceManager.leAudioConnectionStateChanged(
-                device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.LE_AUDIO,
+                device,
+                BluetoothProfile.STATE_DISCONNECTED,
+                BluetoothProfile.STATE_CONNECTED);
     }
 
     /**
@@ -1259,8 +1309,11 @@
         mMostRecentDevice = (mDeviceConnectionStack.size() > 0)
                 ? mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1) : null;
 
-        mActiveDeviceManager.leAudioConnectionStateChanged(
-                device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.LE_AUDIO,
+                device,
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_DISCONNECTED);
     }
 
     /**
@@ -1271,7 +1324,7 @@
         mDeviceConnectionStack.add(device);
         mMostRecentDevice = device;
 
-        mActiveDeviceManager.leAudioActiveStateChanged(device);
+        mActiveDeviceManager.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, device);
     }
 
     /**
@@ -1281,8 +1334,11 @@
         mDeviceConnectionStack.add(device);
         mMostRecentDevice = device;
 
-        mActiveDeviceManager.hapConnectionStateChanged(
-                device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.HAP_CLIENT,
+                device,
+                BluetoothProfile.STATE_DISCONNECTED,
+                BluetoothProfile.STATE_CONNECTED);
     }
 
     /** Helper to indicate LE Hearing Aid disconnected for a device. */
@@ -1291,12 +1347,16 @@
         mMostRecentDevice = (mDeviceConnectionStack.size() > 0)
                 ? mDeviceConnectionStack.get(mDeviceConnectionStack.size() - 1) : null;
 
-        mActiveDeviceManager.hapConnectionStateChanged(
-                device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
+        mActiveDeviceManager.profileConnectionStateChanged(
+                BluetoothProfile.HAP_CLIENT,
+                device,
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_DISCONNECTED);
     }
 
     private class TestDatabaseManager extends DatabaseManager {
         ArrayMap<BluetoothDevice, SparseIntArray> mProfileConnectionPolicy;
+        final Map<String, Map<Integer, byte[]>> mMetadataCache = new HashMap<>();
 
         TestDatabaseManager(AdapterService service) {
             super(service);
@@ -1345,5 +1405,25 @@
             }
             return policy.get(profile, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
         }
+
+        @Override
+        public boolean setCustomMeta(BluetoothDevice device, int key, byte[] newValue) {
+            Map<Integer, byte[]> metadata = mMetadataCache.get(device.getAddress());
+            if (metadata == null) {
+                metadata = new ArrayMap<>();
+                mMetadataCache.put(device.getAddress(), metadata);
+            }
+            metadata.put(key, newValue);
+            return true;
+        }
+
+        @Override
+        public byte[] getCustomMeta(BluetoothDevice device, int key) {
+            Map<Integer, byte[]> metadata = mMetadataCache.get(device.getAddress());
+            if (metadata == null) {
+                return null;
+            }
+            return metadata.get(key);
+        }
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterPropertiesTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterPropertiesTest.java
index ece3203..436151b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterPropertiesTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterPropertiesTest.java
@@ -19,6 +19,7 @@
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -37,6 +38,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -53,12 +55,14 @@
     private Context mTargetContext;
 
     @Mock private AdapterService mAdapterService;
+    @Mock private AdapterNativeInterface mNativeInterface;
 
     @Before
     public void setUp() throws Exception {
         mTargetContext = InstrumentationRegistry.getTargetContext();
 
         MockitoAnnotations.initMocks(this);
+        doReturn(mNativeInterface).when(mAdapterService).getNative();
         mHandlerThread = new HandlerThread("RemoteDevicesTestHandlerThread");
         mHandlerThread.start();
 
@@ -73,7 +77,7 @@
         when(mAdapterService.getIdentityAddress(
                         Utils.getAddressStringFromByte(TEST_BT_ADDR_BYTES_2)))
                 .thenReturn(Utils.getAddressStringFromByte(TEST_BT_ADDR_BYTES));
-        when(mAdapterService.removeBondNative(any(byte[].class))).thenReturn(true);
+        when(mNativeInterface.removeBond(any(byte[].class))).thenReturn(true);
 
         mRemoteDevices = new RemoteDevices(mAdapterService, mHandlerThread.getLooper());
         verify(mAdapterService, times(1)).getSystemService(Context.BLUETOOTH_SERVICE);
@@ -108,4 +112,50 @@
         assertThat(mAdapterProperties.getBondedDevices()[0].getAddress())
                 .isEqualTo(Utils.getAddressStringFromByte(TEST_BT_ADDR_BYTES_2));
     }
+
+    @Test
+    public void setName_shortName_isEqual() {
+        StringBuilder builder = new StringBuilder();
+        String stringName = "Wonderful Bluetooth Name Using utf8";
+        builder.append(stringName);
+        builder.append(Character.toChars(0x20AC));
+
+        String initial = builder.toString();
+
+        final ArgumentCaptor<byte[]> argumentName = ArgumentCaptor.forClass(byte[].class);
+
+        mAdapterProperties.setName(initial);
+        verify(mNativeInterface)
+                .setAdapterProperty(
+                        eq(AbstractionLayer.BT_PROPERTY_BDNAME), argumentName.capture());
+
+        assertThat(argumentName.getValue()).isEqualTo(initial.getBytes());
+    }
+
+    @Test
+    public void setName_tooLongName_isTruncated() {
+        StringBuilder builder = new StringBuilder();
+        String stringName = "Wonderful Bluetooth Name Using utf8 ... But this name is too long";
+        builder.append(stringName);
+
+        int n = 300;
+        for (int i = 0; i < 2 * n; i++) {
+            builder.append(Character.toChars(0x20AC));
+        }
+
+        String initial = builder.toString();
+
+        final ArgumentCaptor<byte[]> argumentName = ArgumentCaptor.forClass(byte[].class);
+
+        mAdapterProperties.setName(initial);
+        verify(mNativeInterface)
+                .setAdapterProperty(
+                        eq(AbstractionLayer.BT_PROPERTY_BDNAME), argumentName.capture());
+
+        byte[] name = argumentName.getValue();
+
+        assertThat(name.length).isLessThan(initial.getBytes().length);
+
+        assertThat(initial).startsWith(new String(name));
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java
index 56b7537..1794e38 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java
@@ -60,11 +60,16 @@
 
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreNativeInterface;
+import com.android.bluetooth.gatt.AdvertiseManagerNativeInterface;
+import com.android.bluetooth.gatt.DistanceMeasurementNativeInterface;
+import com.android.bluetooth.gatt.GattNativeInterface;
+import com.android.bluetooth.gatt.PeriodicScanNativeInterface;
+import com.android.bluetooth.sdp.SdpManagerNativeInterface;
 import com.android.internal.app.IBatteryStats;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -91,6 +96,14 @@
     private @Mock Binder mBinder;
     private @Mock android.app.Application mApplication;
     private @Mock MetricsLogger mMockMetricsLogger;
+    private @Mock AdapterNativeInterface mNativeInterface;
+    private @Mock BluetoothKeystoreNativeInterface mKeystoreNativeInterface;
+    private @Mock BluetoothQualityReportNativeInterface mQualityNativeInterface;
+    private @Mock SdpManagerNativeInterface mSdpNativeInterface;
+    private @Mock AdvertiseManagerNativeInterface mAdvertiseNativeInterface;
+    private @Mock DistanceMeasurementNativeInterface mDistanceNativeInterface;
+    private @Mock GattNativeInterface mGattNativeInterface;
+    private @Mock PeriodicScanNativeInterface mPeriodicNativeInterface;
 
     // SystemService that are not mocked
     private BluetoothManager mBluetoothManager;
@@ -116,11 +129,6 @@
     private int mForegroundUserId;
     private TestLooper mLooper;
 
-    @BeforeClass
-    public static void setupClass() {
-        AdapterServiceTest.setupClass();
-    }
-
     <T> void mockGetSystemService(String serviceName, Class<T> serviceClass, T mockService) {
         when(mMockContext.getSystemService(eq(serviceName))).thenReturn(mockService);
         when(mMockContext.getSystemServiceName(eq(serviceClass))).thenReturn(serviceName);
@@ -139,6 +147,14 @@
 
         mLooper = new TestLooper();
         Handler handler = new Handler(mLooper.getLooper());
+        AdapterNativeInterface.setInstance(mNativeInterface);
+        BluetoothKeystoreNativeInterface.setInstance(mKeystoreNativeInterface);
+        BluetoothQualityReportNativeInterface.setInstance(mQualityNativeInterface);
+        SdpManagerNativeInterface.setInstance(mSdpNativeInterface);
+        AdvertiseManagerNativeInterface.setInstance(mAdvertiseNativeInterface);
+        DistanceMeasurementNativeInterface.setInstance(mDistanceNativeInterface);
+        GattNativeInterface.setInstance(mGattNativeInterface);
+        PeriodicScanNativeInterface.setInstance(mPeriodicNativeInterface);
 
         // Post the creation of AdapterService since it rely on Looper.myLooper()
         handler.post(() -> mAdapterService = new AdapterService(mLooper.getLooper()));
@@ -259,6 +275,14 @@
         Utils.setForegroundUserId(mForegroundUserId);
 
         mAdapterService.cleanup();
+        AdapterNativeInterface.setInstance(null);
+        BluetoothKeystoreNativeInterface.setInstance(null);
+        BluetoothQualityReportNativeInterface.setInstance(null);
+        SdpManagerNativeInterface.setInstance(null);
+        AdvertiseManagerNativeInterface.setInstance(null);
+        DistanceMeasurementNativeInterface.setInstance(null);
+        GattNativeInterface.setInstance(null);
+        PeriodicScanNativeInterface.setInstance(null);
     }
 
     void doEnable() {
@@ -268,7 +292,8 @@
                 mAdapterService,
                 mMockContext,
                 false,
-                List.of(mMockService, mMockService2));
+                List.of(mMockService, mMockService2),
+                mNativeInterface);
     }
 
     /**
@@ -317,6 +342,7 @@
      * native layer
      */
     @Test
+    @Ignore("b/296127545: BluetoothInstrumentationTests should not call native")
     public void testObfuscateBluetoothAddress_FactoryResetAndReloadNativeLayer()
             throws PackageManager.NameNotFoundException {
         byte[] metricsSalt1 = AdapterServiceTest.getMetricsSalt(mAdapterConfig);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java
index 11f5cb6..12dac99 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java
@@ -59,11 +59,13 @@
 
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreNativeInterface;
+import com.android.bluetooth.sdp.SdpManagerNativeInterface;
 import com.android.internal.app.IBatteryStats;
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -85,6 +87,10 @@
     private @Mock Binder mBinder;
     private @Mock android.app.Application mApplication;
     private @Mock MetricsLogger mMockMetricsLogger;
+    private @Mock AdapterNativeInterface mNativeInterface;
+    private @Mock BluetoothKeystoreNativeInterface mKeystoreNativeInterface;
+    private @Mock BluetoothQualityReportNativeInterface mQualityNativeInterface;
+    private @Mock SdpManagerNativeInterface mSdpNativeInterface;
 
     // Mocked SystemService
     private @Mock AlarmManager mMockAlarmManager;
@@ -110,11 +116,6 @@
     private int mForegroundUserId;
     private TestLooper mLooper;
 
-    @BeforeClass
-    public static void setupClass() {
-        AdapterServiceTest.setupClass();
-    }
-
     <T> void mockGetSystemService(String serviceName, Class<T> serviceClass, T mockService) {
         when(mMockContext.getSystemService(eq(serviceName))).thenReturn(mockService);
         when(mMockContext.getSystemServiceName(eq(serviceClass))).thenReturn(serviceName);
@@ -127,6 +128,10 @@
 
         mLooper = new TestLooper();
         Handler handler = new Handler(mLooper.getLooper());
+        AdapterNativeInterface.setInstance(mNativeInterface);
+        BluetoothKeystoreNativeInterface.setInstance(mKeystoreNativeInterface);
+        BluetoothQualityReportNativeInterface.setInstance(mQualityNativeInterface);
+        SdpManagerNativeInterface.setInstance(mSdpNativeInterface);
 
         // Post the creation of AdapterService since it rely on Looper.myLooper()
         handler.post(() -> mAdapterService = new AdapterService(mLooper.getLooper()));
@@ -230,7 +235,7 @@
 
         mLooper.dispatchAll();
 
-        mAdapterService.registerCallback(mIBluetoothCallback);
+        mAdapterService.registerRemoteCallback(mIBluetoothCallback);
 
         mAdapterConfig = TestUtils.readAdapterConfig();
         assertThat(mAdapterConfig).isNotNull();
@@ -243,17 +248,22 @@
         // Restores the foregroundUserId to the ID prior to the test setup
         Utils.setForegroundUserId(mForegroundUserId);
 
-        mAdapterService.unregisterCallback(mIBluetoothCallback);
         mAdapterService.cleanup();
+        mAdapterService.unregisterRemoteCallback(mIBluetoothCallback);
+        AdapterNativeInterface.setInstance(null);
+        BluetoothKeystoreNativeInterface.setInstance(null);
+        BluetoothQualityReportNativeInterface.setInstance(null);
+        SdpManagerNativeInterface.setInstance(null);
     }
 
     /**
-     * Test: Check if obfuscated Bluetooth address stays the same after re-initializing
-     *       {@link AdapterService}
+     * Test: Check if obfuscated Bluetooth address stays the same after re-initializing {@link
+     * AdapterService}
      */
     @Test
-    public void testObfuscateBluetoothAddress_PersistentBetweenAdapterServiceInitialization() throws
-            PackageManager.NameNotFoundException {
+    @Ignore("b/296127545: This is a native test")
+    public void testObfuscateBluetoothAddress_PersistentBetweenAdapterServiceInitialization()
+            throws PackageManager.NameNotFoundException {
         // Sleep needed to ensure the metrics are valid in both native and java (b/267528843)
         try {
             Thread.sleep(1_000);
@@ -283,13 +293,11 @@
         assertThat(obfuscatedAddress2).isEqualTo(obfuscatedAddress1);
     }
 
-    /**
-     * Test: Check if id gotten stays the same after re-initializing
-     *       {@link AdapterService}
-     */
+    /** Test: Check if id gotten stays the same after re-initializing {@link AdapterService} */
     @Test
-    public void testgetMetricId_PersistentBetweenAdapterServiceInitialization() throws
-            PackageManager.NameNotFoundException {
+    @Ignore("b/296127545: This is a native test")
+    public void testgetMetricId_PersistentBetweenAdapterServiceInitialization()
+            throws PackageManager.NameNotFoundException {
         assertThat(mAdapterService.getState()).isEqualTo(STATE_OFF);
         BluetoothDevice device = TestUtils.getTestDevice(BluetoothAdapter.getDefaultAdapter(), 0);
         int initialMetricId = mAdapterService.getMetricId(device);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
index 0df1b42..ff715a0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
@@ -16,7 +16,6 @@
 
 package com.android.bluetooth.btservice;
 
-import static android.Manifest.permission.BLUETOOTH_SCAN;
 import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON;
 import static android.bluetooth.BluetoothAdapter.STATE_BLE_TURNING_OFF;
 import static android.bluetooth.BluetoothAdapter.STATE_BLE_TURNING_ON;
@@ -50,7 +49,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.Process;
@@ -78,8 +76,13 @@
 import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
 import com.android.bluetooth.bas.BatteryService;
 import com.android.bluetooth.bass_client.BassClientService;
+import com.android.bluetooth.btservice.bluetoothkeystore.BluetoothKeystoreNativeInterface;
 import com.android.bluetooth.csip.CsipSetCoordinatorService;
+import com.android.bluetooth.gatt.AdvertiseManagerNativeInterface;
+import com.android.bluetooth.gatt.DistanceMeasurementNativeInterface;
+import com.android.bluetooth.gatt.GattNativeInterface;
 import com.android.bluetooth.gatt.GattService;
+import com.android.bluetooth.gatt.PeriodicScanNativeInterface;
 import com.android.bluetooth.hap.HapClientService;
 import com.android.bluetooth.hearingaid.HearingAidService;
 import com.android.bluetooth.hfp.HeadsetService;
@@ -95,6 +98,7 @@
 import com.android.bluetooth.pbap.BluetoothPbapService;
 import com.android.bluetooth.pbapclient.PbapClientService;
 import com.android.bluetooth.sap.SapService;
+import com.android.bluetooth.sdp.SdpManagerNativeInterface;
 import com.android.bluetooth.tbs.TbsService;
 import com.android.bluetooth.vc.VolumeControlService;
 import com.android.internal.app.IBatteryStats;
@@ -103,7 +107,7 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -115,7 +119,6 @@
 import java.security.NoSuchAlgorithmException;
 import java.util.HashMap;
 import java.util.List;
-import java.util.stream.IntStream;
 
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
@@ -143,6 +146,15 @@
     private @Mock Binder mBinder;
     private @Mock android.app.Application mApplication;
     private @Mock MetricsLogger mMockMetricsLogger;
+    private @Mock AdapterNativeInterface mNativeInterface;
+    private @Mock BluetoothKeystoreNativeInterface mKeystoreNativeInterface;
+    private @Mock BluetoothQualityReportNativeInterface mQualityNativeInterface;
+    private @Mock SdpManagerNativeInterface mSdpNativeInterface;
+    private @Mock AdvertiseManagerNativeInterface mAdvertiseNativeInterface;
+    private @Mock DistanceMeasurementNativeInterface mDistanceNativeInterface;
+    private @Mock GattNativeInterface mGattNativeInterface;
+    private @Mock PeriodicScanNativeInterface mPeriodicNativeInterface;
+    private @Mock JniCallbacks mJniCallbacks;
 
     // SystemService that are not mocked
     private BluetoothManager mBluetoothManager;
@@ -166,7 +178,6 @@
 
     private PackageManager mMockPackageManager;
     private MockContentResolver mMockContentResolver;
-    private HashMap<String, HashMap<String, String>> mAdapterConfig;
     private int mForegroundUserId;
     private TestLooper mLooper;
 
@@ -200,23 +211,6 @@
         Config.setProfileEnabled(VolumeControlService.class, false);
     }
 
-    @BeforeClass
-    public static void setupClass() {
-        Log.e(TAG, "setupClass");
-        // Bring native layer up and down to make sure config files are properly loaded
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-        assertThat(Looper.myLooper()).isNotNull();
-        AdapterService adapterService = new AdapterService(Looper.myLooper());
-        adapterService.initNative(false /* is_restricted */, false /* is_common_criteria_mode */,
-                0 /* config_compare_result */, new String[0], false, "");
-        adapterService.cleanupNative();
-        HashMap<String, HashMap<String, String>> adapterConfig = TestUtils.readAdapterConfig();
-        assertThat(adapterConfig).isNotNull();
-        assertThat(getMetricsSalt(adapterConfig)).isNotNull();
-    }
-
     <T> void mockGetSystemService(String serviceName, Class<T> serviceClass, T mockService) {
         TestUtils.mockGetSystemService(mMockContext, serviceName, serviceClass, mockService);
     }
@@ -233,6 +227,17 @@
         mLooper = new TestLooper();
         Handler handler = new Handler(mLooper.getLooper());
 
+        doReturn(mJniCallbacks).when(mNativeInterface).getCallbacks();
+
+        AdapterNativeInterface.setInstance(mNativeInterface);
+        BluetoothKeystoreNativeInterface.setInstance(mKeystoreNativeInterface);
+        BluetoothQualityReportNativeInterface.setInstance(mQualityNativeInterface);
+        SdpManagerNativeInterface.setInstance(mSdpNativeInterface);
+        AdvertiseManagerNativeInterface.setInstance(mAdvertiseNativeInterface);
+        DistanceMeasurementNativeInterface.setInstance(mDistanceNativeInterface);
+        GattNativeInterface.setInstance(mGattNativeInterface);
+        PeriodicScanNativeInterface.setInstance(mPeriodicNativeInterface);
+
         // Post the creation of AdapterService since it rely on Looper.myLooper()
         handler.post(() -> mAdapterService = new AdapterService(mLooper.getLooper()));
         assertThat(mLooper.dispatchAll()).isEqualTo(1);
@@ -337,10 +342,7 @@
 
         mLooper.dispatchAll();
 
-        mAdapterService.registerCallback(mIBluetoothCallback);
-
-        mAdapterConfig = TestUtils.readAdapterConfig();
-        assertThat(mAdapterConfig).isNotNull();
+        mAdapterService.registerRemoteCallback(mIBluetoothCallback);
     }
 
     @After
@@ -350,17 +352,21 @@
         // Restores the foregroundUserId to the ID prior to the test setup
         Utils.setForegroundUserId(mForegroundUserId);
 
+        assertThat(mLooper.nextMessage()).isNull();
         mAdapterService.cleanup();
-        mAdapterService.unregisterCallback(mIBluetoothCallback);
+        mAdapterService.unregisterRemoteCallback(mIBluetoothCallback);
+        AdapterNativeInterface.setInstance(null);
+        BluetoothKeystoreNativeInterface.setInstance(null);
+        BluetoothQualityReportNativeInterface.setInstance(null);
+        SdpManagerNativeInterface.setInstance(null);
+        AdvertiseManagerNativeInterface.setInstance(null);
+        DistanceMeasurementNativeInterface.setInstance(null);
+        GattNativeInterface.setInstance(null);
+        PeriodicScanNativeInterface.setInstance(null);
     }
 
-    /**
-     * Dispatch all the message on the Loopper and check that the `what` is expected
-     *
-     * @param what list of message that are expected to be run by the handler
-     */
     private void syncHandler(int... what) {
-        syncHandler(mLooper, what);
+        TestUtils.syncHandler(mLooper, what);
     }
 
     private void dropNextMessage(int what) {
@@ -370,20 +376,6 @@
         Log.d(TAG, "Message dropped on purpose: " + msg);
     }
 
-    private static void syncHandler(TestLooper looper, int... what) {
-        IntStream.of(what)
-                .forEach(
-                        w -> {
-                            Message msg = looper.nextMessage();
-                            assertThat(msg).isNotNull();
-                            assertWithMessage("Not the expected Message:\n" + msg)
-                                    .that(msg.what)
-                                    .isEqualTo(w);
-                            Log.d(TAG, "Processing message: " + msg);
-                            msg.getTarget().dispatchMessage(msg);
-                        });
-    }
-
     private void verifyStateChange(int prevState, int currState) {
         try {
             verify(mIBluetoothCallback).onBluetoothStateChange(prevState, currState);
@@ -423,20 +415,20 @@
             ProfileService gattService,
             AdapterService adapter,
             Context ctx,
-            IBluetoothCallback callback) {
+            IBluetoothCallback callback,
+            AdapterNativeInterface nativeInterface) {
         adapter.enable(false);
-        syncHandler(looper, AdapterState.BLE_TURN_ON);
+        TestUtils.syncHandler(looper, AdapterState.BLE_TURN_ON);
         verifyStateChange(callback, STATE_OFF, STATE_BLE_TURNING_ON);
 
-        syncHandler(looper, MESSAGE_PROFILE_SERVICE_REGISTERED);
+        TestUtils.syncHandler(looper, MESSAGE_PROFILE_SERVICE_REGISTERED);
 
-        syncHandler(looper, MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
+        TestUtils.syncHandler(looper, MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
 
-        // Native loop is not in TestLooper and it will send a event later
-        looper.startAutoDispatch();
-        verifyStateChange(callback, STATE_BLE_TURNING_ON, STATE_BLE_ON, NATIVE_INIT_MS);
-        looper.stopAutoDispatch(); // stop autoDispatch ASAP
-
+        verify(nativeInterface).enable();
+        adapter.stateChangeCallback(AbstractionLayer.BT_STATE_ON);
+        TestUtils.syncHandler(looper, AdapterState.BLE_STARTED);
+        verifyStateChange(callback, STATE_BLE_TURNING_ON, STATE_BLE_ON);
         assertThat(adapter.getState()).isEqualTo(STATE_BLE_ON);
     }
 
@@ -448,7 +440,7 @@
             boolean onlyGatt,
             List<ProfileService> services) {
         adapter.disable();
-        syncHandler(looper, AdapterState.USER_TURN_OFF);
+        TestUtils.syncHandler(looper, AdapterState.USER_TURN_OFF);
         verifyStateChange(callback, STATE_ON, STATE_TURNING_OFF);
 
         if (!onlyGatt) {
@@ -457,11 +449,11 @@
 
             for (ProfileService service : services) {
                 adapter.onProfileServiceStateChanged(service, STATE_OFF);
-                syncHandler(looper, MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
+                TestUtils.syncHandler(looper, MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
             }
         }
 
-        syncHandler(looper, AdapterState.BREDR_STOPPED);
+        TestUtils.syncHandler(looper, AdapterState.BREDR_STOPPED);
         verifyStateChange(callback, STATE_TURNING_OFF, STATE_BLE_ON);
 
         assertThat(adapter.getState()).isEqualTo(STATE_BLE_ON);
@@ -474,7 +466,8 @@
                 mAdapterService,
                 mMockContext,
                 onlyGatt,
-                List.of(mMockService, mMockService2));
+                List.of(mMockService, mMockService2),
+                mNativeInterface);
     }
     // Method is re-used in other AdapterService*Test
     static void doEnable(
@@ -483,20 +476,21 @@
             AdapterService adapter,
             Context ctx,
             boolean onlyGatt,
-            List<ProfileService> services) {
+            List<ProfileService> services,
+            AdapterNativeInterface nativeInterface) {
         Log.e(TAG, "doEnable() start");
 
         IBluetoothCallback callback = mock(IBluetoothCallback.class);
         Binder binder = mock(Binder.class);
         doReturn(binder).when(callback).asBinder();
-        adapter.registerCallback(callback);
+        adapter.registerRemoteCallback(callback);
 
         assertThat(adapter.getState()).isEqualTo(STATE_OFF);
 
-        offToBleOn(looper, gattService, adapter, ctx, callback);
+        offToBleOn(looper, gattService, adapter, ctx, callback, nativeInterface);
 
         adapter.startBrEdr();
-        syncHandler(looper, AdapterState.USER_TURN_ON);
+        TestUtils.syncHandler(looper, AdapterState.USER_TURN_ON);
         verifyStateChange(callback, STATE_BLE_ON, STATE_TURNING_ON);
 
         if (!onlyGatt) {
@@ -505,20 +499,20 @@
 
             for (ProfileService service : services) {
                 adapter.addProfile(service);
-                syncHandler(looper, MESSAGE_PROFILE_SERVICE_REGISTERED);
+                TestUtils.syncHandler(looper, MESSAGE_PROFILE_SERVICE_REGISTERED);
             }
             // Keep in 2 separate loop to first add the services and then eventually trigger the
             // ON transition during the callback
             for (ProfileService service : services) {
                 adapter.onProfileServiceStateChanged(service, STATE_ON);
-                syncHandler(looper, MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
+                TestUtils.syncHandler(looper, MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
             }
         }
-        syncHandler(looper, AdapterState.BREDR_STARTED);
+        TestUtils.syncHandler(looper, AdapterState.BREDR_STARTED);
         verifyStateChange(callback, STATE_TURNING_ON, STATE_ON);
 
         assertThat(adapter.getState()).isEqualTo(STATE_ON);
-        adapter.unregisterCallback(callback);
+        adapter.unregisterRemoteCallback(callback);
         Log.e(TAG, "doEnable() complete success");
     }
 
@@ -529,7 +523,8 @@
                 mAdapterService,
                 mMockContext,
                 onlyGatt,
-                List.of(mMockService, mMockService2));
+                List.of(mMockService, mMockService2),
+                mNativeInterface);
     }
 
     private static void doDisable(
@@ -538,30 +533,32 @@
             AdapterService adapter,
             Context ctx,
             boolean onlyGatt,
-            List<ProfileService> services) {
+            List<ProfileService> services,
+            AdapterNativeInterface nativeInterface) {
         Log.e(TAG, "doDisable() start");
         IBluetoothCallback callback = mock(IBluetoothCallback.class);
         Binder binder = mock(Binder.class);
         doReturn(binder).when(callback).asBinder();
-        adapter.registerCallback(callback);
+        adapter.registerRemoteCallback(callback);
 
         assertThat(adapter.getState()).isEqualTo(STATE_ON);
 
         onToBleOn(looper, adapter, ctx, callback, onlyGatt, services);
 
         adapter.stopBle();
-        syncHandler(looper, AdapterState.BLE_TURN_OFF);
+        TestUtils.syncHandler(looper, AdapterState.BLE_TURN_OFF);
         verifyStateChange(callback, STATE_BLE_ON, STATE_BLE_TURNING_OFF);
 
-        syncHandler(looper, MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
+        TestUtils.syncHandler(looper, MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
+        TestUtils.syncHandler(looper, MESSAGE_PROFILE_SERVICE_UNREGISTERED);
 
-        // Native loop is not in TestLooper and it will send a event later
-        looper.startAutoDispatch();
-        verifyStateChange(callback, STATE_BLE_TURNING_OFF, STATE_OFF, NATIVE_DISABLE_MS);
-        looper.stopAutoDispatch(); // stop autoDispatch ASAP
+        verify(nativeInterface).disable();
+        adapter.stateChangeCallback(AbstractionLayer.BT_STATE_OFF);
+        TestUtils.syncHandler(looper, AdapterState.BLE_STOPPED);
+        verifyStateChange(callback, STATE_BLE_TURNING_OFF, STATE_OFF);
 
         assertThat(adapter.getState()).isEqualTo(STATE_OFF);
-        adapter.unregisterCallback(callback);
+        adapter.unregisterRemoteCallback(callback);
         Log.e(TAG, "doDisable() complete success");
     }
 
@@ -578,9 +575,15 @@
     public void enable_isCorrectScanMode() {
         doEnable(false);
 
-        verify(mMockContext, timeout(ONE_SECOND_MS).times(2))
-                .sendBroadcast(any(), eq(BLUETOOTH_SCAN), any(Bundle.class));
+        final int expectedScanMode = BluetoothAdapter.SCAN_MODE_CONNECTABLE;
 
+        int type = AbstractionLayer.BT_PROPERTY_ADAPTER_SCAN_MODE;
+        byte[] val = Utils.intToByteArray(AdapterService.convertScanModeToHal(expectedScanMode));
+
+        verify(mNativeInterface).setAdapterProperty(eq(type), eq(val));
+        // Intercept native call and simulate its callback
+        mAdapterService.mAdapterProperties.adapterPropertyChangedCallback(
+                new int[] {type}, new byte[][] {val});
         assertThat(mAdapterService.getScanMode()).isEqualTo(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
     }
 
@@ -640,11 +643,11 @@
         syncHandler(AdapterState.BLE_START_TIMEOUT);
         assertThat(mAdapterService.getBluetoothGatt()).isNull();
 
-        // Native loop is not in TestLooper and it will send a event later
-        mLooper.startAutoDispatch();
-        verifyStateChange(STATE_BLE_TURNING_OFF, STATE_OFF, NATIVE_DISABLE_MS);
-        mLooper.stopAutoDispatch(); // stop autoDispatch ASAP
+        syncHandler(AdapterState.BLE_STOPPED);
+        syncHandler(MESSAGE_PROFILE_SERVICE_STATE_CHANGED);
+        syncHandler(MESSAGE_PROFILE_SERVICE_UNREGISTERED);
 
+        verifyStateChange(STATE_BLE_TURNING_OFF, STATE_OFF);
         assertThat(mAdapterService.getState()).isEqualTo(STATE_OFF);
     }
 
@@ -688,7 +691,13 @@
     public void testProfileStartTimeout() {
         assertThat(mAdapterService.getState()).isEqualTo(STATE_OFF);
 
-        offToBleOn(mLooper, mMockGattService, mAdapterService, mMockContext, mIBluetoothCallback);
+        offToBleOn(
+                mLooper,
+                mMockGattService,
+                mAdapterService,
+                mMockContext,
+                mIBluetoothCallback,
+                mNativeInterface);
 
         mAdapterService.startBrEdr();
         syncHandler(AdapterState.USER_TURN_ON);
@@ -782,13 +791,16 @@
         // Do not call stopBle().  The Adapter should turn itself off.
         syncHandler(AdapterState.BLE_TURN_OFF);
         verifyStateChange(STATE_BLE_ON, STATE_BLE_TURNING_OFF, CONTEXT_SWITCH_MS);
+
         syncHandler(MESSAGE_PROFILE_SERVICE_STATE_CHANGED); // stop GATT
+        syncHandler(MESSAGE_PROFILE_SERVICE_UNREGISTERED);
 
-        // Native loop is not in TestLooper and it will send a event later
-        mLooper.startAutoDispatch();
-        verifyStateChange(STATE_BLE_TURNING_OFF, STATE_OFF, NATIVE_DISABLE_MS);
-        mLooper.stopAutoDispatch(); // stop autoDispatch ASAP
+        verify(mNativeInterface).disable();
 
+        mAdapterService.stateChangeCallback(AbstractionLayer.BT_STATE_OFF);
+        syncHandler(AdapterState.BLE_STOPPED);
+
+        verifyStateChange(STATE_BLE_TURNING_OFF, STATE_OFF);
         assertThat(mAdapterService.getState()).isEqualTo(STATE_OFF);
 
         // Restore earlier setting
@@ -807,13 +819,17 @@
     }
 
     /**
-     * Test: Obfuscate Bluetooth address when Bluetooth is disabled
-     * Check whether the returned value meets expectation
+     * Test: Obfuscate Bluetooth address when Bluetooth is disabled Check whether the returned value
+     * meets expectation
      */
     @Test
+    @Ignore("b/296127545: This is a native test")
     public void testObfuscateBluetoothAddress_BluetoothDisabled() {
+        HashMap<String, HashMap<String, String>> adapterConfig = TestUtils.readAdapterConfig();
+        assertThat(adapterConfig).isNotNull();
+
         assertThat(mAdapterService.getState()).isEqualTo(STATE_OFF);
-        byte[] metricsSalt = getMetricsSalt(mAdapterConfig);
+        byte[] metricsSalt = getMetricsSalt(adapterConfig);
         assertThat(metricsSalt).isNotNull();
         BluetoothDevice device = TestUtils.getTestDevice(BluetoothAdapter.getDefaultAdapter(), 0);
         byte[] obfuscatedAddress = mAdapterService.obfuscateAddress(device);
@@ -823,15 +839,19 @@
     }
 
     /**
-     * Test: Obfuscate Bluetooth address when Bluetooth is enabled
-     * Check whether the returned value meets expectation
+     * Test: Obfuscate Bluetooth address when Bluetooth is enabled Check whether the returned value
+     * meets expectation
      */
     @Test
+    @Ignore("b/296127545: This is a native test")
     public void testObfuscateBluetoothAddress_BluetoothEnabled() {
+        HashMap<String, HashMap<String, String>> adapterConfig = TestUtils.readAdapterConfig();
+        assertThat(adapterConfig).isNotNull();
+
         assertThat(mAdapterService.getState()).isEqualTo(STATE_OFF);
         doEnable(false);
         assertThat(mAdapterService.getState()).isEqualTo(STATE_ON);
-        byte[] metricsSalt = getMetricsSalt(mAdapterConfig);
+        byte[] metricsSalt = getMetricsSalt(adapterConfig);
         assertThat(metricsSalt).isNotNull();
         BluetoothDevice device = TestUtils.getTestDevice(BluetoothAdapter.getDefaultAdapter(), 0);
         byte[] obfuscatedAddress = mAdapterService.obfuscateAddress(device);
@@ -840,13 +860,15 @@
         assertThat(obfuscateInJava(metricsSalt, device)).isEqualTo(obfuscatedAddress);
     }
 
-    /**
-     * Test: Check if obfuscated Bluetooth address stays the same after toggling Bluetooth
-     */
+    /** Test: Check if obfuscated Bluetooth address stays the same after toggling Bluetooth */
     @Test
+    @Ignore("b/296127545: This is a native test")
     public void testObfuscateBluetoothAddress_PersistentBetweenToggle() {
+        HashMap<String, HashMap<String, String>> adapterConfig = TestUtils.readAdapterConfig();
+        assertThat(adapterConfig).isNotNull();
+
         assertThat(mAdapterService.getState()).isEqualTo(STATE_OFF);
-        byte[] metricsSalt = getMetricsSalt(mAdapterConfig);
+        byte[] metricsSalt = getMetricsSalt(adapterConfig);
         assertThat(metricsSalt).isNotNull();
         BluetoothDevice device = TestUtils.getTestDevice(BluetoothAdapter.getDefaultAdapter(), 0);
         byte[] obfuscatedAddress1 = mAdapterService.obfuscateAddress(device);
@@ -937,21 +959,19 @@
     }
 
     /**
-     * Test: Get id when Bluetooth is disabled
-     * Check whether the returned value meets expectation
+     * Test: Get id when Bluetooth is disabled Check whether the returned value meets expectation
      */
     @Test
+    @Ignore("b/296127545: This is a native test")
     public void testGetMetricId_BluetoothDisabled() {
         assertThat(mAdapterService.getState()).isEqualTo(STATE_OFF);
         BluetoothDevice device = TestUtils.getTestDevice(BluetoothAdapter.getDefaultAdapter(), 0);
         assertThat(mAdapterService.getMetricId(device)).isGreaterThan(0);
     }
 
-    /**
-     * Test: Get id when Bluetooth is enabled
-     * Check whether the returned value meets expectation
-     */
+    /** Test: Get id when Bluetooth is enabled Check whether the returned value meets expectation */
     @Test
+    @Ignore("b/296127545: This is a native test")
     public void testGetMetricId_BluetoothEnabled() {
         assertThat(mAdapterService.getState()).isEqualTo(STATE_OFF);
         doEnable(false);
@@ -960,10 +980,9 @@
         assertThat(mAdapterService.getMetricId(device)).isGreaterThan(0);
     }
 
-    /**
-     * Test: Check if id gotten stays the same after toggling Bluetooth
-     */
+    /** Test: Check if id gotten stays the same after toggling Bluetooth */
     @Test
+    @Ignore("b/296127545: This is a native test")
     public void testGetMetricId_PersistentBetweenToggle() {
         assertThat(mAdapterService.getState()).isEqualTo(STATE_OFF);
         BluetoothDevice device = TestUtils.getTestDevice(BluetoothAdapter.getDefaultAdapter(), 0);
@@ -988,7 +1007,8 @@
 
         mAdapterService.dump(fd, writer, new String[]{});
         mAdapterService.dump(fd, writer, new String[]{"set-test-mode", "enabled"});
-        mAdapterService.dump(fd, writer, new String[]{"--proto-bin"});
-        mAdapterService.dump(fd, writer, new String[]{"random", "arguments"});
+        doReturn(new byte[0]).when(mNativeInterface).dumpMetrics();
+        mAdapterService.dump(fd, writer, new String[] {"--proto-bin"});
+        mAdapterService.dump(fd, writer, new String[] {"random", "arguments"});
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
index ec52113..91aee37 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java
@@ -70,12 +70,14 @@
     private int mVerifyCount = 0;
 
     @Mock private AdapterService mAdapterService;
+    @Mock private AdapterNativeInterface mNativeInterface;
 
     @Before
     public void setUp() throws Exception {
         mTargetContext = InstrumentationRegistry.getTargetContext();
         MockitoAnnotations.initMocks(this);
         TestUtils.setAdapterService(mAdapterService);
+        doReturn(mNativeInterface).when(mAdapterService).getNative();
         mHandlerThread = new HandlerThread("BondStateMachineTestHandlerThread");
         mHandlerThread.start();
 
@@ -114,9 +116,10 @@
         deviceProperties1.mBondState = BOND_BONDED;
         deviceProperties2.mBondState = BOND_BONDED;
 
-        doReturn(true).when(mAdapterService).removeBondNative(any(byte[].class));
-        doReturn(true).when(mAdapterService).createBondNative(any(byte[].class),
-                eq(BluetoothDevice.ADDRESS_TYPE_PUBLIC), anyInt());
+        doReturn(true).when(mNativeInterface).removeBond(any(byte[].class));
+        doReturn(true)
+                .when(mNativeInterface)
+                .createBond(any(byte[].class), eq(BluetoothDevice.ADDRESS_TYPE_PUBLIC), anyInt());
 
         // The removeBond() request for a bonded device should invoke the removeBondNative() call.
         Message removeBondMsg1 = mBondStateMachine.obtainMessage(BondStateMachine.REMOVE_BOND);
@@ -128,8 +131,8 @@
         mBondStateMachine.sendMessage(removeBondMsg2);
         TestUtils.waitForLooperToFinishScheduledTask(mBondStateMachine.getHandler().getLooper());
 
-        verify(mAdapterService, times(1)).removeBondNative(eq(TEST_BT_ADDR_BYTES));
-        verify(mAdapterService, times(1)).removeBondNative(eq(TEST_BT_ADDR_BYTES_2));
+        verify(mNativeInterface, times(1)).removeBond(eq(TEST_BT_ADDR_BYTES));
+        verify(mNativeInterface, times(1)).removeBond(eq(TEST_BT_ADDR_BYTES_2));
 
         mBondStateMachine.bondStateChangeCallback(AbstractionLayer.BT_STATUS_SUCCESS,
                 TEST_BT_ADDR_BYTES, BOND_NONE, 0);
@@ -147,10 +150,14 @@
         mBondStateMachine.sendMessage(createBondMsg2);
         TestUtils.waitForLooperToFinishScheduledTask(mBondStateMachine.getHandler().getLooper());
 
-        verify(mAdapterService, times(1)).createBondNative(eq(TEST_BT_ADDR_BYTES),
-                eq(BluetoothDevice.ADDRESS_TYPE_PUBLIC), anyInt());
-        verify(mAdapterService, times(1)).createBondNative(eq(TEST_BT_ADDR_BYTES_2),
-                eq(BluetoothDevice.ADDRESS_TYPE_PUBLIC), anyInt());
+        verify(mNativeInterface, times(1))
+                .createBond(
+                        eq(TEST_BT_ADDR_BYTES), eq(BluetoothDevice.ADDRESS_TYPE_PUBLIC), anyInt());
+        verify(mNativeInterface, times(1))
+                .createBond(
+                        eq(TEST_BT_ADDR_BYTES_2),
+                        eq(BluetoothDevice.ADDRESS_TYPE_PUBLIC),
+                        anyInt());
     }
 
     @Test
@@ -174,10 +181,14 @@
         mBondStateMachine.sendMessage(createBondMsg2);
         TestUtils.waitForLooperToFinishScheduledTask(mBondStateMachine.getHandler().getLooper());
 
-        verify(mAdapterService, times(1)).createBondNative(eq(TEST_BT_ADDR_BYTES),
-                eq(BluetoothDevice.ADDRESS_TYPE_PUBLIC), anyInt());
-        verify(mAdapterService, times(1)).createBondNative(eq(TEST_BT_ADDR_BYTES_2),
-                eq(BluetoothDevice.ADDRESS_TYPE_RANDOM), anyInt());
+        verify(mNativeInterface, times(1))
+                .createBond(
+                        eq(TEST_BT_ADDR_BYTES), eq(BluetoothDevice.ADDRESS_TYPE_PUBLIC), anyInt());
+        verify(mNativeInterface, times(1))
+                .createBond(
+                        eq(TEST_BT_ADDR_BYTES_2),
+                        eq(BluetoothDevice.ADDRESS_TYPE_RANDOM),
+                        anyInt());
     }
 
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java
index ce04cd6..481c0ca 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/PhonePolicyTest.java
@@ -468,6 +468,10 @@
         when(mA2dpService.getConnectionState(bondedDevices[0])).thenReturn(
                 BluetoothProfile.STATE_DISCONNECTED);
 
+        // ACL is connected, lets simulate this.
+        when(mAdapterService.getConnectionState(bondedDevices[0]))
+                .thenReturn(BluetoothProfile.STATE_CONNECTED);
+
         // We send a connection successful for one profile since the re-connect *only* works if we
         // have already connected successfully over one of the profiles
         updateProfileConnectionStateHelper(bondedDevices[0], BluetoothProfile.HEADSET,
@@ -479,6 +483,54 @@
     }
 
     /**
+     * Test that connectOtherProfile will not trigger any actions when ACL is disconnected. This is
+     * to add robustness to the connection mechanism
+     */
+    @Test
+    public void testConnectOtherProfileWhileDeviceIsDisconnected() {
+        // Return a list of bonded devices (just one)
+        BluetoothDevice[] bondedDevices = new BluetoothDevice[1];
+        bondedDevices[0] = getTestDevice(mAdapter, 0);
+        when(mAdapterService.getBondedDevices()).thenReturn(bondedDevices);
+
+        // Return PRIORITY_AUTO_CONNECT over HFP and A2DP. This would imply that the profiles are
+        // auto-connectable.
+        when(mHeadsetService.getConnectionPolicy(bondedDevices[0]))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        when(mA2dpService.getConnectionPolicy(bondedDevices[0]))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+
+        when(mAdapterService.getState()).thenReturn(BluetoothAdapter.STATE_ON);
+
+        // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP
+        // To enable that we need to make sure that HeadsetService returns the device as list of
+        // connected devices
+        ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>();
+        hsConnectedDevices.add(bondedDevices[0]);
+        when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices);
+        // Also the A2DP should say that it's not connected for same device
+        when(mA2dpService.getConnectionState(bondedDevices[0]))
+                .thenReturn(BluetoothProfile.STATE_DISCONNECTED);
+
+        // ACL is disconnected just after HEADSET profile got connected and connectOtherProfile
+        // was scheduled. Lets simulate this.
+        when(mAdapterService.getConnectionState(bondedDevices[0]))
+                .thenReturn(BluetoothProfile.STATE_DISCONNECTED);
+
+        // We send a connection successful for one profile since the re-connect *only* works if we
+        // have already connected successfully over one of the profiles
+        updateProfileConnectionStateHelper(
+                bondedDevices[0],
+                BluetoothProfile.HEADSET,
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_DISCONNECTED);
+
+        // Check that there will be no A2DP connect
+        verify(mA2dpService, after(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS).never())
+                .connect(eq(bondedDevices[0]));
+    }
+
+    /**
      * Test that we will try to re-connect to a profile on a device next time if a previous attempt
      * failed partially. This will make sure the connection mechanism still works at next try while
      * the previous attempt is some profiles connected on a device but some not.
@@ -486,7 +538,13 @@
     @Test
     public void testReconnectOnPartialConnect_PreviousPartialFail() {
         List<BluetoothDevice> connectionOrder = new ArrayList<>();
-        connectionOrder.add(getTestDevice(mAdapter, 0));
+        BluetoothDevice testDevice = getTestDevice(mAdapter, 0);
+        connectionOrder.add(testDevice);
+
+        // ACL is connected, lets simulate this.
+        when(mAdapterService.getConnectionState(testDevice))
+                .thenReturn(BluetoothProfile.STATE_CONNECTED);
+
         when(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).thenReturn(
                 connectionOrder.get(0));
 
@@ -573,6 +631,10 @@
             BluetoothDevice testDevice = getTestDevice(mAdapter, i);
             testDevices[i] = testDevice;
 
+            // ACL is connected, lets simulate this.
+            when(mAdapterService.getConnectionState(testDevice))
+                    .thenReturn(BluetoothProfile.STATE_CONNECTED);
+
             // Return PRIORITY_AUTO_CONNECT over HFP and A2DP. This would imply that the profiles
             // are auto-connectable.
             when(mHeadsetService.getConnectionPolicy(testDevice)).thenReturn(
@@ -641,6 +703,10 @@
             BluetoothDevice testDevice = getTestDevice(mAdapter, i);
             testDevices[i] = testDevice;
 
+            // ACL is connected, lets simulate this.
+            when(mAdapterService.getConnectionState(testDevices[i]))
+                    .thenReturn(BluetoothProfile.STATE_CONNECTED);
+
             // Connect HFP and A2DP for each device as appropriate.
             // Return PRIORITY_AUTO_CONNECT only for testDevices[0]
             if (i == 0) {
@@ -854,6 +920,12 @@
         when(mHeadsetService.getConnectionState(bondedDevices[1])).thenReturn(
                 BluetoothProfile.STATE_CONNECTED);
 
+        // ACL is connected for both devices.
+        when(mAdapterService.getConnectionState(bondedDevices[0]))
+                .thenReturn(BluetoothProfile.STATE_CONNECTED);
+        when(mAdapterService.getConnectionState(bondedDevices[1]))
+                .thenReturn(BluetoothProfile.STATE_CONNECTED);
+
         // We send a connection successful for one profile since the re-connect *only* works if we
         // have already connected successfully over one of the profiles
         Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
@@ -906,14 +978,18 @@
         when(mA2dpService.getConnectionState(bondedDevices[1])).thenReturn(
                 BluetoothProfile.STATE_DISCONNECTED);
 
+        // ACL is connected, lets simulate this.
+        when(mAdapterService.getConnectionState(bondedDevices[1]))
+                .thenReturn(BluetoothProfile.STATE_CONNECTED);
+
         // We send a connection successful for one profile since the re-connect *only* works if we
         // have already connected successfully over one of the profiles
         updateProfileConnectionStateHelper(bondedDevices[1], BluetoothProfile.HEADSET,
                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
 
-        // Check that we don't get any calls to reconnect
-        verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS)).connect(
-                eq(bondedDevices[1]));
+        // Check that we do get A2DP call to reconnect, because HEADSET just got connected
+        verify(mA2dpService, timeout(CONNECT_OTHER_PROFILES_TIMEOUT_WAIT_MILLIS))
+                .connect(eq(bondedDevices[1]));
     }
 
     /**
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
index 15721bb..79cd8fd 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/ProfileServiceTest.java
@@ -17,7 +17,6 @@
 package com.android.bluetooth.btservice;
 
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -35,8 +34,15 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.bluetooth.TestUtils;
+import com.android.bluetooth.a2dp.A2dpNativeInterface;
+import com.android.bluetooth.avrcp.AvrcpNativeInterface;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.bluetooth.gatt.GattService;
+import com.android.bluetooth.hearingaid.HearingAidNativeInterface;
+import com.android.bluetooth.hfp.HeadsetNativeInterface;
+import com.android.bluetooth.hid.HidDeviceNativeInterface;
+import com.android.bluetooth.hid.HidHostNativeInterface;
+import com.android.bluetooth.pan.PanNativeInterface;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -64,6 +70,7 @@
     @Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule();
     @Mock private AdapterService mMockAdapterService;
     @Mock private DatabaseManager mDatabaseManager;
+    @Mock private ActiveDeviceManager mActiveDeviceManager;
     @Mock private LocationManager mLocationManager;
 
     private Class[] mProfiles;
@@ -76,12 +83,20 @@
             mStartedProfileMap.put(profile.getSimpleName(), false);
         }
         Intent startIntent = new Intent(InstrumentationRegistry.getTargetContext(), profile);
-        startIntent.putExtra(AdapterService.EXTRA_ACTION,
-                AdapterService.ACTION_SERVICE_STATE_CHANGED);
+        startIntent.putExtra(
+                AdapterService.EXTRA_ACTION, AdapterService.ACTION_SERVICE_STATE_CHANGED);
         startIntent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
         mServiceTestRule.startService(startIntent);
     }
 
+    @Mock private A2dpNativeInterface mA2dpNativeInterface;
+    @Mock private AvrcpNativeInterface mAvrcpNativeInterface;
+    @Mock private HeadsetNativeInterface mHeadsetNativeInterface;
+    @Mock private HearingAidNativeInterface mHearingAidNativeInterface;
+    @Mock private HidDeviceNativeInterface mHidDeviceNativeInterface;
+    @Mock private HidHostNativeInterface mHidHostNativeInterface;
+    @Mock private PanNativeInterface mPanNativeInterface;
+
     private void setAllProfilesState(int state, int invocationNumber) throws TimeoutException {
         int profileCount = mProfiles.length;
         for (Class profile : mProfiles) {
@@ -134,48 +149,44 @@
             }
         });
         doReturn(mDatabaseManager).when(mMockAdapterService).getDatabase();
+        doReturn(mActiveDeviceManager).when(mMockAdapterService).getActiveDeviceManager();
 
         when(mMockAdapterService.getSystemService(Context.LOCATION_SERVICE))
                 .thenReturn(mLocationManager);
         when(mMockAdapterService.getSystemServiceName(LocationManager.class))
                 .thenReturn(Context.LOCATION_SERVICE);
 
-        // Despite calling on the Mock of adapterService, mockito cannot handle native method and
-        // will call the real method instead, allowing to initialize the native library
-        // when(mMockAdapterService.initNative(anyBoolean(), anyBoolean(), anyInt(), any(),
-        // anyBoolean(), anyString())).thenCallRealMethod();
-        doCallRealMethod()
-                .when(mMockAdapterService)
-                .initNative(anyBoolean(), anyBoolean(), anyInt(), any(), anyBoolean(), anyString());
-        doCallRealMethod().when(mMockAdapterService).enableNative();
-        doCallRealMethod().when(mMockAdapterService).disableNative();
-        doCallRealMethod().when(mMockAdapterService).cleanupNative();
-
         mProfiles = Config.getSupportedProfiles();
         TestUtils.setAdapterService(mMockAdapterService);
 
         Assert.assertNotNull(AdapterService.getAdapterService());
 
-        mMockAdapterService.initNative(false /* is_restricted */,
-                false /* is_common_criteria_mode */, 0 /* config_compare_result */,
-                new String[0], false, "");
-        mMockAdapterService.enableNative();
+        A2dpNativeInterface.setInstance(mA2dpNativeInterface);
+        AvrcpNativeInterface.setInstance(mAvrcpNativeInterface);
+        HeadsetNativeInterface.setInstance(mHeadsetNativeInterface);
+        HearingAidNativeInterface.setInstance(mHearingAidNativeInterface);
+        HidDeviceNativeInterface.setInstance(mHidDeviceNativeInterface);
+        HidHostNativeInterface.setInstance(mHidHostNativeInterface);
+        PanNativeInterface.setInstance(mPanNativeInterface);
     }
 
     @After
     public void tearDown()
             throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
-        mMockAdapterService.disableNative();
-        mMockAdapterService.cleanupNative();
-
         TestUtils.clearAdapterService(mMockAdapterService);
         mMockAdapterService = null;
         mProfiles = null;
+        A2dpNativeInterface.setInstance(null);
+        AvrcpNativeInterface.setInstance(null);
+        HeadsetNativeInterface.setInstance(null);
+        HearingAidNativeInterface.setInstance(null);
+        HidDeviceNativeInterface.setInstance(null);
+        HidHostNativeInterface.setInstance(null);
+        PanNativeInterface.setInstance(null);
     }
 
     /**
-     * Test: Start the Bluetooth services that are configured.
-     * Verify that the same services start.
+     * Test: Start the Bluetooth services that are configured. Verify that the same services start.
      */
     @Test
     public void testEnableDisable() throws TimeoutException {
@@ -184,8 +195,7 @@
     }
 
     /**
-     * Test: Start the Bluetooth services that are configured twice.
-     * Verify that the services start.
+     * Test: Start the Bluetooth services that are configured twice. Verify that the services start.
      */
     @Test
     public void testEnableDisableTwice() throws TimeoutException {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java
index 91b9e5c..0045e71 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/RemoteDevicesTest.java
@@ -8,7 +8,6 @@
 import android.bluetooth.BluetoothAssignedNumbers;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothHeadsetClient;
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothSinkAudioPolicy;
@@ -255,8 +254,9 @@
         // Verify that resetting battery level changes it back to BluetoothDevice
         // .BATTERY_LEVEL_UNKNOWN
         mRemoteDevices.onHeadsetConnectionStateChanged(
-                getHeadsetConnectionStateChangedIntent(mDevice1,
-                        BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_DISCONNECTED));
+                mDevice1,
+                BluetoothProfile.STATE_DISCONNECTING,
+                BluetoothProfile.STATE_DISCONNECTED);
         // Verify BATTERY_LEVEL_CHANGED intent is sent after first reset
         verify(mAdapterService, times(2)).sendBroadcast(mIntentArgument.capture(),
                 mStringArgument.capture(), any(Bundle.class));
@@ -302,8 +302,9 @@
 
         // Verify that battery level is not reset
         mRemoteDevices.onHeadsetConnectionStateChanged(
-                getHeadsetConnectionStateChangedIntent(mDevice1,
-                        BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_DISCONNECTED));
+                mDevice1,
+                BluetoothProfile.STATE_DISCONNECTING,
+                BluetoothProfile.STATE_DISCONNECTED);
 
         Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1));
         Assert.assertEquals(batteryLevel,
@@ -373,8 +374,8 @@
         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
 
         // Verify that ACTION_HF_INDICATORS_VALUE_CHANGED intent updates battery level
-        mRemoteDevices.onHfIndicatorValueChanged(getHfIndicatorIntent(mDevice1, batteryLevel,
-                HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS));
+        mRemoteDevices.onHfIndicatorValueChanged(
+                mDevice1, HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS, batteryLevel);
         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
                 any(Bundle.class));
         verifyBatteryLevelChangedIntent(mDevice1, batteryLevel, mIntentArgument);
@@ -389,7 +390,7 @@
         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
 
         // Verify that ACTION_HF_INDICATORS_VALUE_CHANGED intent updates battery level
-        mRemoteDevices.onHfIndicatorValueChanged(getHfIndicatorIntent(mDevice1, batteryLevel, 3));
+        mRemoteDevices.onHfIndicatorValueChanged(mDevice1, batteryLevel, 3);
         verify(mAdapterService, never()).sendBroadcast(any(), anyString());
         // Verify that device property is still null after invalid update
         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
@@ -401,10 +402,12 @@
         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
 
         // Verify that correct ACTION_VENDOR_SPECIFIC_HEADSET_EVENT updates battery level
-        mRemoteDevices.onVendorSpecificHeadsetEvent(getVendorSpecificHeadsetEventIntent(
+        mRemoteDevices.onVendorSpecificHeadsetEvent(
+                mDevice1,
                 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT,
-                BluetoothAssignedNumbers.PLANTRONICS, BluetoothHeadset.AT_CMD_TYPE_SET,
-                getXEventArray(3, 8), mDevice1));
+                BluetoothAssignedNumbers.PLANTRONICS,
+                BluetoothHeadset.AT_CMD_TYPE_SET,
+                getXEventArray(3, 8));
         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
                 any(Bundle.class));
         verifyBatteryLevelChangedIntent(mDevice1, 42, mIntentArgument);
@@ -417,17 +420,20 @@
         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
 
         // Verify that correct ACTION_VENDOR_SPECIFIC_HEADSET_EVENT updates battery level
-        mRemoteDevices.onVendorSpecificHeadsetEvent(getVendorSpecificHeadsetEventIntent(
+        mRemoteDevices.onVendorSpecificHeadsetEvent(
+                mDevice1,
                 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV,
-                BluetoothAssignedNumbers.APPLE, BluetoothHeadset.AT_CMD_TYPE_SET, new Object[]{
-                        3,
-                        BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
-                        5,
-                        2,
-                        1,
-                        3,
-                        10
-                }, mDevice1));
+                BluetoothAssignedNumbers.APPLE,
+                BluetoothHeadset.AT_CMD_TYPE_SET,
+                new Object[] {
+                    3,
+                    BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL,
+                    5,
+                    2,
+                    1,
+                    3,
+                    10
+                });
         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
                 any(Bundle.class));
         verifyBatteryLevelChangedIntent(mDevice1, 60, mIntentArgument);
@@ -522,8 +528,9 @@
         // Verify that resetting battery level changes it back to BluetoothDevice
         // .BATTERY_LEVEL_UNKNOWN
         mRemoteDevices.onHeadsetClientConnectionStateChanged(
-                getHeadsetClientConnectionStateChangedIntent(mDevice1,
-                        BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_DISCONNECTED));
+                mDevice1,
+                BluetoothProfile.STATE_DISCONNECTING,
+                BluetoothProfile.STATE_DISCONNECTED);
 
         // Verify BATTERY_LEVEL_CHANGED intent is sent after first reset
         verify(mAdapterService, times(2)).sendBroadcast(mIntentArgument.capture(),
@@ -571,8 +578,9 @@
 
         // Verify that battery level is not reset.
         mRemoteDevices.onHeadsetClientConnectionStateChanged(
-                getHeadsetClientConnectionStateChangedIntent(mDevice1,
-                        BluetoothProfile.STATE_DISCONNECTING, BluetoothProfile.STATE_DISCONNECTED));
+                mDevice1,
+                BluetoothProfile.STATE_DISCONNECTING,
+                BluetoothProfile.STATE_DISCONNECTED);
 
         Assert.assertNotNull(mRemoteDevices.getDeviceProperties(mDevice1));
         Assert.assertEquals(batteryLevel,
@@ -667,15 +675,14 @@
     }
 
     @Test
-    public void testAGIndicatorParser_testCorrectValue() {
+    public void testAgBatteryLevelIndicator_testCorrectValue() {
         int batteryLevel = 3;
 
         // Verify that device property is null initially
         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
 
         // Verify that ACTION_AG_EVENT intent updates battery level
-        mRemoteDevices.onAgIndicatorValueChanged(
-                getAgIndicatorIntent(mDevice1, null, null, null, new Integer(batteryLevel), null));
+        mRemoteDevices.onAgBatteryLevelChanged(mDevice1, batteryLevel);
         verify(mAdapterService).sendBroadcast(mIntentArgument.capture(), mStringArgument.capture(),
                 any(Bundle.class));
         verifyBatteryLevelChangedIntent(mDevice1,
@@ -684,21 +691,6 @@
     }
 
     @Test
-    public void testAgIndicatorParser_testWrongIndicatorId() {
-        int batteryLevel = 10;
-
-        // Verify that device property is null initially
-        Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
-
-        // Verify that ACTION_AG_EVENT intent updates battery level
-        mRemoteDevices.onAgIndicatorValueChanged(
-                getAgIndicatorIntent(mDevice1, new Integer(1), null, null, null, null));
-        verify(mAdapterService, never()).sendBroadcast(any(), anyString());
-        // Verify that device property is still null after invalid update
-        Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
-    }
-
-    @Test
     public void testSetgetHfAudioPolicyForRemoteAg() {
         // Verify that device property is null initially
         Assert.assertNull(mRemoteDevices.getDeviceProperties(mDevice1));
@@ -765,37 +757,6 @@
                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, intent.getFlags());
     }
 
-    private static Intent getHeadsetConnectionStateChangedIntent(BluetoothDevice device,
-            int oldState, int newState) {
-        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, oldState);
-        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
-        return intent;
-    }
-
-    private static Intent getHfIndicatorIntent(BluetoothDevice device, int batteryLevel,
-            int indicatorId) {
-        Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-        intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, indicatorId);
-        intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, batteryLevel);
-        return intent;
-    }
-
-    private static Intent getVendorSpecificHeadsetEventIntent(String command, int companyId,
-            int commandType, Object[] arguments, BluetoothDevice device) {
-        Intent intent = new Intent(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
-        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD, command);
-        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, commandType);
-        // assert: all elements of args are Serializable
-        intent.putExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS, arguments);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-        intent.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "."
-                + Integer.toString(companyId));
-        return intent;
-    }
-
     private static Object[] getXEventArray(int batteryLevel, int numLevels) {
         ArrayList<Object> list = new ArrayList<>();
         list.add(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL);
@@ -806,40 +767,6 @@
         return list.toArray();
     }
 
-    private static Intent getHeadsetClientConnectionStateChangedIntent(BluetoothDevice device,
-            int oldState, int newState) {
-        Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, oldState);
-        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
-        return intent;
-    }
-
-    private static Intent getAgIndicatorIntent(BluetoothDevice device, Integer networkStatus,
-            Integer networkStrength, Integer roaming, Integer batteryLevel, String operatorName) {
-        Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AG_EVENT);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-
-        if (networkStatus != null) {
-            intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_STATUS, networkStatus.intValue());
-        }
-        if (networkStrength != null) {
-            intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_SIGNAL_STRENGTH,
-                    networkStrength.intValue());
-        }
-        if (roaming != null) {
-            intent.putExtra(BluetoothHeadsetClient.EXTRA_NETWORK_ROAMING, roaming.intValue());
-        }
-        if (batteryLevel != null) {
-            intent.putExtra(BluetoothHeadsetClient.EXTRA_BATTERY_LEVEL, batteryLevel.intValue());
-        }
-        if (operatorName != null) {
-            intent.putExtra(BluetoothHeadsetClient.EXTRA_OPERATOR_NAME, operatorName);
-        }
-
-        return intent;
-    }
-
     private static BatteryService setBatteryServiceForTesting(BluetoothDevice device) {
         BatteryService newService = mock(BatteryService.class);
         when(newService.getConnectionState(device))
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java
index b900305..23aac39 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/SilenceDeviceManagerTest.java
@@ -17,12 +17,11 @@
 package com.android.bluetooth.btservice;
 
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
+
 import static org.mockito.Mockito.*;
 
-import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.Intent;
@@ -172,11 +171,8 @@
      * Helper to indicate A2dp connected for a device.
      */
     private void a2dpConnected(BluetoothDevice device) {
-        Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED);
-        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
-        mSilenceDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+        mSilenceDeviceManager.a2dpConnectionStateChanged(
+                device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
         TestUtils.waitForLooperToFinishScheduledTask(mLooper);
     }
 
@@ -184,11 +180,8 @@
      * Helper to indicate A2dp disconnected for a device.
      */
     private void a2dpDisconnected(BluetoothDevice device) {
-        Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED);
-        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
-        mSilenceDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+        mSilenceDeviceManager.a2dpConnectionStateChanged(
+                device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
         TestUtils.waitForLooperToFinishScheduledTask(mLooper);
     }
 
@@ -196,11 +189,8 @@
      * Helper to indicate Headset connected for a device.
      */
     private void headsetConnected(BluetoothDevice device) {
-        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED);
-        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
-        mSilenceDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+        mSilenceDeviceManager.hfpConnectionStateChanged(
+                device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
         TestUtils.waitForLooperToFinishScheduledTask(mLooper);
     }
 
@@ -208,12 +198,8 @@
      * Helper to indicate Headset disconnected for a device.
      */
     private void headsetDisconnected(BluetoothDevice device) {
-        Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
-        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED);
-        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
-        mSilenceDeviceManager.getBroadcastReceiver().onReceive(mContext, intent);
+        mSilenceDeviceManager.hfpConnectionStateChanged(
+                device, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
         TestUtils.waitForLooperToFinishScheduledTask(mLooper);
     }
 }
-
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionServiceTest.java
deleted file mode 100644
index 8a4db9a..0000000
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/activityAttribution/ActivityAttributionServiceTest.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.bluetooth.btservice.activityattribution;
-
-import android.os.Binder;
-import android.os.Process;
-
-import org.junit.Assume;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public final class ActivityAttributionServiceTest {
-    private ActivityAttributionService mActivityAttributionService;
-
-    @Before
-    public void setUp() {
-        Assume.assumeTrue("Ignore test when the user is not primary.", isPrimaryUser());
-        mActivityAttributionService = new ActivityAttributionService();
-    }
-
-    private boolean isPrimaryUser() {
-        return Binder.getCallingUid() == Process.BLUETOOTH_UID;
-    }
-
-    @Test
-    public void testSetUpAndTearDown() {}
-
-    @Test
-    public void testNotifyActivityAttributionInfo() {
-        mActivityAttributionService.notifyActivityAttributionInfo(1, "myApp", "address1");
-    }
-}
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 8bbc4e4..b63763c 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
@@ -103,8 +103,8 @@
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
 
+        CsipSetCoordinatorNativeInterface.setInstance(mCsipSetCoordinatorNativeInterface);
         startService();
-        mService.mCsipSetCoordinatorNativeInterface = mCsipSetCoordinatorNativeInterface;
         mService.mServiceFactory = mServiceFactory;
         when(mServiceFactory.getLeAudioService()).thenReturn(mLeAudioService);
 
@@ -158,6 +158,7 @@
         }
 
         stopService();
+        CsipSetCoordinatorNativeInterface.setInstance(null);
         mTargetContext.unregisterReceiver(mCsipSetCoordinatorIntentReceiver);
         TestUtils.clearAdapterService(mAdapterService);
         mIntentQueue.clear();
@@ -167,7 +168,6 @@
         TestUtils.startService(mServiceRule, CsipSetCoordinatorService.class);
         mService = CsipSetCoordinatorService.getCsipSetCoordinatorService();
         Assert.assertNotNull(mService);
-        verify(mAdapterService).notifyActivityAttributionInfo(any(), any());
     }
 
     private void stopService() throws TimeoutException {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/csip/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/csip/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AppScanStatsTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AppScanStatsTest.java
index b1bed3d..3b6c385 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/AppScanStatsTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AppScanStatsTest.java
@@ -17,21 +17,21 @@
 package com.android.bluetooth.gatt;
 
 import static com.google.common.truth.Truth.assertThat;
-
+import static org.mockito.Mockito.mock;
 
 import android.bluetooth.le.ScanFilter;
 import android.bluetooth.le.ScanSettings;
 import android.content.Context;
-import android.location.LocationManager;
+import android.os.BatteryStatsManager;
 import android.os.WorkSource;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.internal.app.IBatteryStats;
 
 import org.junit.After;
 import org.junit.Before;
@@ -51,16 +51,19 @@
 @RunWith(AndroidJUnit4.class)
 public class AppScanStatsTest {
 
-    private GattService mService;
-
     @Rule
     public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
     @Mock
     private ContextMap map;
 
-    @Mock
-    private AdapterService mAdapterService;
+    @Mock private GattService mMockGatt;
+    @Mock private AdapterService mAdapterService;
+
+    // BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
+    // underlying binder calls.
+    final BatteryStatsManager mBatteryStatsManager =
+            new BatteryStatsManager(mock(IBatteryStats.class));
 
     @Before
     public void setUp() throws Exception {
@@ -69,17 +72,14 @@
         TestUtils.setAdapterService(mAdapterService);
 
         TestUtils.mockGetSystemService(
-                mAdapterService, Context.LOCATION_SERVICE, LocationManager.class);
-
-        mService = new GattService(InstrumentationRegistry.getTargetContext());
-        mService.start();
+                mMockGatt,
+                Context.BATTERY_STATS_SERVICE,
+                BatteryStatsManager.class,
+                mBatteryStatsManager);
     }
 
     @After
     public void tearDown() throws Exception {
-        mService.stop();
-        mService = null;
-
         TestUtils.clearAdapterService(mAdapterService);
     }
 
@@ -88,10 +88,10 @@
         String name = "appName";
         WorkSource source = null;
 
-        AppScanStats appScanStats = new AppScanStats(name, source, map, mService);
+        AppScanStats appScanStats = new AppScanStats(name, source, map, mMockGatt);
 
         assertThat(appScanStats.mContextMap).isEqualTo(map);
-        assertThat(appScanStats.mGattService).isEqualTo(mService);
+        assertThat(appScanStats.mGattService).isEqualTo(mMockGatt);
 
         assertThat(appScanStats.isScanning()).isEqualTo(false);
     }
@@ -101,7 +101,7 @@
         String name = "appName";
         WorkSource source = null;
 
-        AppScanStats appScanStats = new AppScanStats(name, source, map, mService);
+        AppScanStats appScanStats = new AppScanStats(name, source, map, mMockGatt);
 
         ScanSettings settings = new ScanSettings.Builder().build();
         List<ScanFilter> filters = new ArrayList<>();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java
index e391a38..6d7ba9c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/ContextMapTest.java
@@ -18,17 +18,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
 import android.bluetooth.le.AdvertiseData;
 import android.bluetooth.le.AdvertisingSetParameters;
 import android.bluetooth.le.PeriodicAdvertisingParameters;
-import android.content.Context;
-import android.location.LocationManager;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
@@ -54,17 +53,15 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ContextMapTest {
-
-    private GattService mService;
+    private static final String APP_NAME = "com.android.what.a.name";
 
     @Rule
     public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
-    @Mock
-    private AdapterService mAdapterService;
-
-    @Mock
-    private AppAdvertiseStats appAdvertiseStats;
+    @Mock private AdapterService mAdapterService;
+    @Mock private AppAdvertiseStats appAdvertiseStats;
+    @Mock private GattService mMockGatt;
+    @Mock private PackageManager mMockPackageManager;
 
     @Spy
     private BluetoothMethodProxy mMapMethodProxy = BluetoothMethodProxy.getInstance();
@@ -76,20 +73,14 @@
 
         TestUtils.setAdapterService(mAdapterService);
 
-        TestUtils.mockGetSystemService(
-                mAdapterService, Context.LOCATION_SERVICE, LocationManager.class);
-
-        mService = new GattService(InstrumentationRegistry.getTargetContext());
-        mService.start();
+        doReturn(mMockPackageManager).when(mMockGatt).getPackageManager();
+        doReturn(APP_NAME).when(mMockPackageManager).getNameForUid(anyInt());
     }
 
     @After
     public void tearDown() throws Exception {
         BluetoothMethodProxy.setInstanceForTesting(null);
 
-        mService.stop();
-        mService = null;
-
         TestUtils.clearAdapterService(mAdapterService);
     }
 
@@ -98,18 +89,17 @@
         ContextMap contextMap = new ContextMap<>();
 
         int id = 12345;
-        contextMap.add(id, null, mService);
+        contextMap.add(id, null, mMockGatt);
 
-        contextMap.add(UUID.randomUUID(), null, null, null, mService);
+        contextMap.add(UUID.randomUUID(), null, null, null, mMockGatt);
 
         int appUid = Binder.getCallingUid();
-        String appName = mService.getPackageManager().getNameForUid(appUid);
 
         ContextMap.App contextMapById = contextMap.getById(appUid);
-        assertThat(contextMapById.name).isEqualTo(appName);
+        assertThat(contextMapById.name).isEqualTo(APP_NAME);
 
-        ContextMap.App contextMapByName = contextMap.getByName(appName);
-        assertThat(contextMapByName.name).isEqualTo(appName);
+        ContextMap.App contextMapByName = contextMap.getByName(APP_NAME);
+        assertThat(contextMapByName.name).isEqualTo(APP_NAME);
     }
 
     @Test
@@ -118,11 +108,11 @@
 
         int appUid = Binder.getCallingUid();
         int id = 12345;
-        String appName = mService.getPackageManager().getNameForUid(appUid);
-        doReturn(appAdvertiseStats).when(mMapMethodProxy)
-                .createAppAdvertiseStats(appUid, id, appName, contextMap, mService);
+        doReturn(appAdvertiseStats)
+                .when(mMapMethodProxy)
+                .createAppAdvertiseStats(appUid, id, APP_NAME, contextMap, mMockGatt);
 
-        contextMap.add(id, null, mService);
+        contextMap.add(id, null, mMockGatt);
 
         int duration = 60;
         int maxExtAdvEvents = 100;
@@ -177,14 +167,14 @@
         ContextMap contextMap = new ContextMap<>();
 
         int id = 12345;
-        contextMap.add(id, null, mService);
+        contextMap.add(id, null, mMockGatt);
 
-        contextMap.add(UUID.randomUUID(), null, null, null, mService);
+        contextMap.add(UUID.randomUUID(), null, null, null, mMockGatt);
 
         contextMap.recordAdvertiseStop(id);
 
         int idSecond = 54321;
-        contextMap.add(idSecond, null, mService);
+        contextMap.add(idSecond, null, mMockGatt);
 
         contextMap.dump(sb);
 
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java
index 9472b30..f1d9d34 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementManagerTest.java
@@ -20,7 +20,6 @@
 import static org.mockito.Mockito.after;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -30,9 +29,7 @@
 import android.bluetooth.le.DistanceMeasurementMethod;
 import android.bluetooth.le.DistanceMeasurementParams;
 import android.bluetooth.le.DistanceMeasurementResult;
-import android.bluetooth.le.DistanceMeasurementSession;
 import android.bluetooth.le.IDistanceMeasurementCallback;
-import android.os.HandlerThread;
 import android.os.RemoteException;
 
 import androidx.test.filters.SmallTest;
@@ -70,10 +67,9 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         doReturn(IDENTITY_ADDRESS).when(mAdapterService).getIdentityAddress(IDENTITY_ADDRESS);
+        DistanceMeasurementNativeInterface.setInstance(mDistanceMeasurementNativeInterface);
         mDistanceMeasurementManager = new DistanceMeasurementManager(mAdapterService);
         mDistanceMeasurementManager.start();
-        mDistanceMeasurementManager.mDistanceMeasurementNativeInterface =
-                mDistanceMeasurementNativeInterface;
         mUuid = UUID.randomUUID();
         mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(IDENTITY_ADDRESS);
     }
@@ -81,6 +77,7 @@
     @After
     public void tearDown() throws Exception {
         mDistanceMeasurementManager.cleanup();
+        DistanceMeasurementNativeInterface.setInstance(null);
     }
 
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
index 7d27c29..fbee565 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
@@ -94,6 +94,7 @@
     @Mock private Set<String> mReliableQueue;
     @Mock private GattService.ServerMap mServerMap;
     @Mock private DistanceMeasurementManager mDistanceMeasurementManager;
+    @Mock private AdvertiseManagerNativeInterface mAdvertiseManagerNativeInterface;
 
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
@@ -138,6 +139,7 @@
         mBtCompanionManager = new CompanionManager(mAdapterService, null);
         doReturn(mBtCompanionManager).when(mAdapterService).getCompanionManager();
 
+        AdvertiseManagerNativeInterface.setInstance(mAdvertiseManagerNativeInterface);
         mService = new GattService(InstrumentationRegistry.getTargetContext());
         mService.start();
 
@@ -151,6 +153,7 @@
     public void tearDown() throws Exception {
         mService.stop();
         mService = null;
+        AdvertiseManagerNativeInterface.setInstance(null);
 
         TestUtils.clearAdapterService(mAdapterService);
         GattObjectsFactory.setInstanceForTesting(null);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java
index 42d26f7..dc5e5e7 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/ScanManagerTest.java
@@ -33,20 +33,28 @@
 import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.AlarmManager;
 import android.bluetooth.BluetoothProtoEnums;
 import android.bluetooth.le.ScanFilter;
 import android.bluetooth.le.ScanSettings;
 import android.content.Context;
+import android.hardware.display.DisplayManager;
 import android.location.LocationManager;
+import android.os.BatteryStatsManager;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.provider.Settings;
+import android.test.mock.MockContentProvider;
+import android.test.mock.MockContentResolver;
 import android.util.Log;
 import android.util.SparseIntArray;
 
@@ -59,6 +67,7 @@
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.BluetoothAdapterProxy;
 import com.android.bluetooth.btservice.MetricsLogger;
+import com.android.internal.app.IBatteryStats;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -94,14 +103,19 @@
     private static final int DELAY_SCAN_DOWNGRADE_DURATION_MS = 100;
 
     private Context mTargetContext;
-    private GattService mService;
     private ScanManager mScanManager;
     private Handler mHandler;
     private CountDownLatch mLatch;
     private long mScanReportDelay;
 
+    // BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
+    // underlying binder calls.
+    final BatteryStatsManager mBatteryStatsManager =
+            new BatteryStatsManager(mock(IBatteryStats.class));
+
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
     @Mock private AdapterService mAdapterService;
+    @Mock private GattService mMockGattService;
     @Mock private BluetoothAdapterProxy mBluetoothAdapterProxy;
     @Mock private LocationManager mLocationManager;
     @Spy private GattObjectsFactory mFactory = GattObjectsFactory.getInstance();
@@ -109,6 +123,7 @@
     @Mock private ScanNativeInterface mScanNativeInterface;
     @Mock private MetricsLogger  mMetricsLogger;
 
+    private MockContentResolver mMockContentResolver;
     @Captor ArgumentCaptor<Long> mScanDurationCaptor;
 
     @Before
@@ -117,8 +132,8 @@
         MockitoAnnotations.initMocks(this);
 
         TestUtils.setAdapterService(mAdapterService);
-        when(mAdapterService.getScanTimeoutMillis()).
-                thenReturn((long) DELAY_DEFAULT_SCAN_TIMEOUT_MS);
+        when(mAdapterService.getScanTimeoutMillis())
+                .thenReturn((long) DELAY_DEFAULT_SCAN_TIMEOUT_MS);
         when(mAdapterService.getNumOfOffloadedScanFilterSupported())
                 .thenReturn(DEFAULT_NUM_OFFLOAD_SCAN_FILTER);
         when(mAdapterService.getOffloadedScanResultStorage())
@@ -128,6 +143,28 @@
                 mAdapterService, Context.LOCATION_SERVICE, LocationManager.class, mLocationManager);
         doReturn(true).when(mLocationManager).isLocationEnabled();
 
+        TestUtils.mockGetSystemService(
+                mMockGattService,
+                Context.DISPLAY_SERVICE,
+                DisplayManager.class,
+                mTargetContext.getSystemService(DisplayManager.class));
+        TestUtils.mockGetSystemService(
+                mMockGattService,
+                Context.BATTERY_STATS_SERVICE,
+                BatteryStatsManager.class,
+                mBatteryStatsManager);
+        TestUtils.mockGetSystemService(mMockGattService, Context.ALARM_SERVICE, AlarmManager.class);
+
+        mMockContentResolver = new MockContentResolver(mTargetContext);
+        mMockContentResolver.addProvider(
+                Settings.AUTHORITY,
+                new MockContentProvider() {
+                    @Override
+                    public Bundle call(String method, String request, Bundle args) {
+                        return Bundle.EMPTY;
+                    }
+                });
+        doReturn(mMockContentResolver).when(mMockGattService).getContentResolver();
         BluetoothAdapterProxy.setInstanceForTesting(mBluetoothAdapterProxy);
         // Needed to mock Native call/callback when hw offload scan filter is enabled
         when(mBluetoothAdapterProxy.isOffloadedScanFilteringSupported()).thenReturn(true);
@@ -140,11 +177,11 @@
 
         MetricsLogger.setInstanceForTesting(mMetricsLogger);
 
-        mService = new GattService(InstrumentationRegistry.getTargetContext());
-        mService.start();
+        doReturn(mTargetContext.getUser()).when(mMockGattService).getUser();
+        doReturn(mTargetContext.getPackageName()).when(mMockGattService).getPackageName();
 
-        mScanManager = mService.getScanManager();
-        assertThat(mScanManager).isNotNull();
+        mScanManager = new ScanManager(mMockGattService, mAdapterService, mBluetoothAdapterProxy);
+        mScanManager.start();
 
         mHandler = mScanManager.getClientHandler();
         assertThat(mHandler).isNotNull();
@@ -157,9 +194,6 @@
 
     @After
     public void tearDown() throws Exception {
-        mService.stop();
-        mService = null;
-
         TestUtils.clearAdapterService(mAdapterService);
         BluetoothAdapterProxy.setInstanceForTesting(null);
         GattObjectsFactory.setInstanceForTesting(null);
@@ -196,7 +230,7 @@
         ScanSettings scanSettings = createScanSettings(scanMode, isBatch, isAutoBatch);
 
         ScanClient client = new ScanClient(id, scanSettings, scanFilterList);
-        client.stats = new AppScanStats("Test", null, null, mService);
+        client.stats = new AppScanStats("Test", null, null, mMockGattService);
         client.stats.recordScanStart(scanSettings, scanFilterList, isFiltered, false, id);
         return client;
     }
@@ -577,12 +611,12 @@
             assertThat(client.stats.isScanTimeout(client.scannerId)).isTrue();
             // Turn off screen
             sendMessageWaitForProcessed(createScreenOnOffMessage(false));
-            assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode);
-            // Turn on screen
-            sendMessageWaitForProcessed(createScreenOnOffMessage(true));
-            assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode);
+            assertThat(client.settings.getScanMode()).isEqualTo(SCAN_MODE_SCREEN_OFF);
             // Set as backgournd app
             sendMessageWaitForProcessed(createImportanceMessage(false));
+            assertThat(client.settings.getScanMode()).isEqualTo(SCAN_MODE_SCREEN_OFF);
+            // Turn on screen
+            sendMessageWaitForProcessed(createScreenOnOffMessage(true));
             assertThat(client.settings.getScanMode()).isEqualTo(expectedScanMode);
             // Set as foreground app
             sendMessageWaitForProcessed(createImportanceMessage(true));
@@ -1149,8 +1183,10 @@
         sendMessageWaitForProcessed(createScreenOnOffMessage(true));
         verify(mMetricsLogger, atLeastOnce()).cacheCount(
                 eq(BluetoothProtoEnums.LE_SCAN_RADIO_DURATION_REGULAR), anyLong());
-        verify(mMetricsLogger, never()).cacheCount(
-                eq(BluetoothProtoEnums.LE_SCAN_RADIO_DURATION_REGULAR_SCREEN_ON), anyLong());
+        verify(mMetricsLogger, atMost(1))
+                .cacheCount(
+                        eq(BluetoothProtoEnums.LE_SCAN_RADIO_DURATION_REGULAR_SCREEN_ON),
+                        anyLong());
         verify(mMetricsLogger, atLeastOnce()).cacheCount(
                 eq(BluetoothProtoEnums.LE_SCAN_RADIO_DURATION_REGULAR_SCREEN_OFF), anyLong());
         Mockito.clearInvocations(mMetricsLogger);
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 32e6435..898a05d 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
@@ -129,8 +129,8 @@
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mAttributionSource = mAdapter.getAttributionSource();
 
+        HapClientNativeInterface.setInstance(mNativeInterface);
         startService();
-        mService.mHapClientNativeInterface = mNativeInterface;
         mService.mFactory = mServiceFactory;
         doReturn(mCsipService).when(mServiceFactory).getCsipSetCoordinatorService();
         mServiceBinder = (HapClientService.BluetoothHapClientBinder) mService.initBinder();
@@ -217,6 +217,7 @@
         mService.mCallbacks.unregister(mCallback);
 
         stopService();
+        HapClientNativeInterface.setInstance(null);
 
         if (mHasIntentReceiver != null) {
             mTargetContext.unregisterReceiver(mHasIntentReceiver);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
index d6e01ee..28fef1c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
@@ -104,8 +104,8 @@
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
+        HearingAidNativeInterface.setInstance(mNativeInterface);
         startService();
-        mService.mHearingAidNativeInterface = mNativeInterface;
         mService.mAudioManager = mAudioManager;
         mServiceBinder = (HearingAidService.BluetoothHearingAidBinder) mService.initBinder();
         mServiceBinder.mIsTesting = true;
@@ -137,6 +137,7 @@
     @After
     public void tearDown() throws Exception {
         stopService();
+        HearingAidNativeInterface.setInstance(null);
         mTargetContext.unregisterReceiver(mHearingAidIntentReceiver);
         mHearingAidIntentReceiver.clear();
         mDeviceQueueMap.clear();
@@ -148,7 +149,6 @@
         TestUtils.startService(mServiceRule, HearingAidService.class);
         mService = HearingAidService.getHearingAidService();
         Assert.assertNotNull(mService);
-        verify(mAdapterService).notifyActivityAttributionInfo(any(), any());
     }
 
     private void stopService() throws TimeoutException {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/AtPhonebookTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/AtPhonebookTest.java
index ac30e35..50680ef 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/AtPhonebookTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/AtPhonebookTest.java
@@ -22,7 +22,6 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -58,9 +57,9 @@
     private BluetoothAdapter mAdapter;
     private BluetoothDevice mTestDevice;
 
-    @Mock
-    private AdapterService mAdapterService;
-    private HeadsetNativeInterface mNativeInterface;
+    @Mock private AdapterService mAdapterService;
+    @Mock private HeadsetNativeInterface mNativeInterface;
+
     private AtPhonebook mAtPhonebook;
     @Spy
     private BluetoothMethodProxy mHfpMethodProxy = BluetoothMethodProxy.getInstance();
@@ -75,7 +74,6 @@
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
         // Spy on native interface
-        mNativeInterface = spy(HeadsetNativeInterface.getInstance());
         mAtPhonebook = new AtPhonebook(mTargetContext, mNativeInterface);
     }
 
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java
index 185a7aa..d9a5c98 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java
@@ -41,7 +41,6 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.telecom.PhoneAccount;
-import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.espresso.intent.Intents;
@@ -53,13 +52,14 @@
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.ActiveDeviceManager;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.RemoteDevices;
+import com.android.bluetooth.btservice.SilenceDeviceManager;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -84,7 +84,7 @@
 @RunWith(AndroidJUnit4.class)
 public class HeadsetServiceAndStateMachineTest {
     private static final String TAG = "HeadsetServiceAndStateMachineTest";
-    private static final int ASYNC_CALL_TIMEOUT_MILLIS = 250;
+    private static final int ASYNC_CALL_TIMEOUT_MILLIS = 500;
     private static final int START_VR_TIMEOUT_MILLIS = 1000;
     private static final int START_VR_TIMEOUT_WAIT_MILLIS = START_VR_TIMEOUT_MILLIS * 3 / 2;
     private static final int STATE_CHANGE_TIMEOUT_MILLIS = 1000;
@@ -98,9 +98,6 @@
     private Context mTargetContext;
     private HeadsetService mHeadsetService;
     private BluetoothAdapter mAdapter;
-    private HeadsetNativeInterface mNativeInterface;
-    private ArgumentCaptor<HeadsetStateMachine> mStateMachineArgument =
-            ArgumentCaptor.forClass(HeadsetStateMachine.class);
     private HashSet<BluetoothDevice> mBondedDevices = new HashSet<>();
     private final BlockingQueue<Intent> mConnectionStateChangedQueue = new LinkedBlockingQueue<>();
     private final BlockingQueue<Intent> mActiveDeviceChangedQueue = new LinkedBlockingQueue<>();
@@ -109,6 +106,8 @@
     private int mOriginalVrTimeoutMs = 5000;
     private PowerManager.WakeLock mVoiceRecognitionWakeLock;
 
+    @Mock private HeadsetNativeInterface mNativeInterface;
+
     private class HeadsetIntentReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -152,10 +151,12 @@
     @Spy private HeadsetObjectsFactory mObjectsFactory = HeadsetObjectsFactory.getInstance();
     @Mock private AdapterService mAdapterService;
     @Mock private ActiveDeviceManager mActiveDeviceManager;
+    @Mock private SilenceDeviceManager mSilenceDeviceManager;
     @Mock private DatabaseManager mDatabaseManager;
     @Mock private HeadsetSystemInterface mSystemInterface;
     @Mock private AudioManager mAudioManager;
     @Mock private HeadsetPhoneState mPhoneState;
+    @Mock private RemoteDevices mRemoteDevices;
 
     @Before
     public void setUp() throws Exception {
@@ -187,6 +188,8 @@
         doReturn(new BluetoothSinkAudioPolicy.Builder().build()).when(mAdapterService)
                 .getRequestedAudioPolicyAsSink(any(BluetoothDevice.class));
         doReturn(mActiveDeviceManager).when(mAdapterService).getActiveDeviceManager();
+        doReturn(mSilenceDeviceManager).when(mAdapterService).getSilenceDeviceManager();
+        doReturn(mRemoteDevices).when(mAdapterService).getRemoteDevices();
         // Mock system interface
         doNothing().when(mSystemInterface).stop();
         doReturn(mPhoneState).when(mSystemInterface).getHeadsetPhoneState();
@@ -196,9 +199,6 @@
         doReturn(mVoiceRecognitionWakeLock).when(mSystemInterface).getVoiceRecognitionWakeLock();
         doReturn(true).when(mSystemInterface).isCallIdle();
         // Mock methods in HeadsetNativeInterface
-        mNativeInterface = spy(HeadsetNativeInterface.getInstance());
-        doNothing().when(mNativeInterface).init(anyInt(), anyBoolean());
-        doNothing().when(mNativeInterface).cleanup();
         doReturn(true).when(mNativeInterface).connectHfp(any(BluetoothDevice.class));
         doReturn(true).when(mNativeInterface).disconnectHfp(any(BluetoothDevice.class));
         doReturn(true).when(mNativeInterface).connectAudio(any(BluetoothDevice.class));
@@ -350,9 +350,11 @@
         unbondIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
         InstrumentationRegistry.getTargetContext().sendBroadcast(unbondIntent);
         // Check that the state machine is actually destroyed
-        verify(mObjectsFactory, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).destroyStateMachine(
-                mStateMachineArgument.capture());
-        Assert.assertEquals(device, mStateMachineArgument.getValue().getDevice());
+        ArgumentCaptor<HeadsetStateMachine> stateMachineArgument =
+                ArgumentCaptor.forClass(HeadsetStateMachine.class);
+        verify(mObjectsFactory, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .destroyStateMachine(stateMachineArgument.capture());
+        Assert.assertEquals(device, stateMachineArgument.getValue().getDevice());
     }
 
     /**
@@ -407,10 +409,11 @@
         waitAndVerifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
                 BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
         // Check that the state machine is destroyed after another async call
-        verify(mObjectsFactory, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).destroyStateMachine(
-                mStateMachineArgument.capture());
-        Assert.assertEquals(device, mStateMachineArgument.getValue().getDevice());
-
+        ArgumentCaptor<HeadsetStateMachine> stateMachineArgument =
+                ArgumentCaptor.forClass(HeadsetStateMachine.class);
+        verify(mObjectsFactory, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .destroyStateMachine(stateMachineArgument.capture());
+        Assert.assertEquals(device, stateMachineArgument.getValue().getDevice());
     }
 
     /**
@@ -473,7 +476,9 @@
         BluetoothDevice activeDevice = connectedDevices.get(MAX_HEADSET_CONNECTIONS / 2);
         Assert.assertTrue(mHeadsetService.setActiveDevice(activeDevice));
         verify(mNativeInterface).setActiveDevice(activeDevice);
-        verify(mActiveDeviceManager).hfpActiveStateChanged(activeDevice);
+        verify(mActiveDeviceManager)
+                .profileActiveDeviceChanged(BluetoothProfile.HEADSET, activeDevice);
+        verify(mSilenceDeviceManager).hfpActiveDeviceChanged(activeDevice);
         Assert.assertEquals(activeDevice, mHeadsetService.getActiveDevice());
         // Start virtual call
         Assert.assertTrue(mHeadsetService.startScoUsingVirtualVoiceCall());
@@ -670,7 +675,7 @@
         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
         verify(mNativeInterface).setActiveDevice(device);
         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
-        verify(mNativeInterface).sendBsir(eq(device), eq(true));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(eq(device), eq(true));
         // Start voice recognition
         startVoiceRecognitionFromHf(device);
     }
@@ -694,7 +699,7 @@
         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
         verify(mNativeInterface).setActiveDevice(device);
         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
-        verify(mNativeInterface).sendBsir(eq(device), eq(true));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(eq(device), eq(true));
         // Start voice recognition
         startVoiceRecognitionFromHf(device);
         // Stop voice recognition
@@ -729,7 +734,7 @@
         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
         verify(mNativeInterface).setActiveDevice(device);
         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
-        verify(mNativeInterface).sendBsir(eq(device), eq(true));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(eq(device), eq(true));
         // Start voice recognition
         HeadsetStackEvent startVrEvent =
                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
@@ -762,7 +767,7 @@
         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
         verify(mNativeInterface).setActiveDevice(device);
         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
-        verify(mNativeInterface).sendBsir(eq(device), eq(true));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(eq(device), eq(true));
         // Start voice recognition
         HeadsetStackEvent startVrEvent =
                 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED,
@@ -795,7 +800,7 @@
         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
         verify(mNativeInterface).setActiveDevice(device);
         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
-        verify(mNativeInterface).sendBsir(eq(device), eq(true));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(eq(device), eq(true));
         // Start voice recognition
         startVoiceRecognitionFromAg();
     }
@@ -869,7 +874,7 @@
         Assert.assertTrue(mHeadsetService.setActiveDevice(device));
         verify(mNativeInterface).setActiveDevice(device);
         Assert.assertEquals(device, mHeadsetService.getActiveDevice());
-        verify(mNativeInterface).sendBsir(eq(device), eq(true));
+        verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS)).sendBsir(eq(device), eq(true));
         // Start voice recognition
         startVoiceRecognitionFromAg();
         // Stop voice recognition
@@ -919,9 +924,12 @@
         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
         connectTestDevice(deviceB);
         InOrder inOrder = inOrder(mNativeInterface);
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceA), eq(true));
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceB), eq(false));
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceA), eq(false));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceA), eq(true));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceB), eq(false));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceA), eq(false));
         // Set active device to device B
         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
         verify(mNativeInterface).setActiveDevice(deviceB);
@@ -969,14 +977,17 @@
     @Test
     public void testVoiceRecognition_MultiHfInitiatedSwitchActiveDeviceReplyWrongHfSuccess() {
         // Connect two devices
+        InOrder inOrder = inOrder(mNativeInterface);
         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
         connectTestDevice(deviceA);
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceA), eq(true));
         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
         connectTestDevice(deviceB);
-        InOrder inOrder = inOrder(mNativeInterface);
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceA), eq(true));
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceB), eq(false));
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceA), eq(false));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceB), eq(false));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceA), eq(false));
         // Set active device to device B
         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
         verify(mNativeInterface).setActiveDevice(deviceB);
@@ -1022,7 +1033,6 @@
      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
      */
     @Test
-    @Ignore("b/271351629")
     public void testVoiceRecognition_MultiAgInitiatedSuccess() {
         // Connect two devices
         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
@@ -1030,9 +1040,12 @@
         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
         connectTestDevice(deviceB);
         InOrder inOrder = inOrder(mNativeInterface);
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceA), eq(true));
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceB), eq(false));
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceA), eq(false));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceA), eq(true));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceB), eq(false));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceA), eq(false));
         // Set active device to device B
         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
         verify(mNativeInterface).setActiveDevice(deviceB);
@@ -1064,7 +1077,6 @@
      * Reference: Section 4.25, Page 64/144 of HFP 1.7.1 specification
      */
     @Test
-    @Ignore("b/271351629")
     public void testVoiceRecognition_MultiAgInitiatedDeviceNotActive() {
         // Connect two devices
         BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
@@ -1072,9 +1084,12 @@
         BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
         connectTestDevice(deviceB);
         InOrder inOrder = inOrder(mNativeInterface);
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceA), eq(true));
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceB), eq(false));
-        inOrder.verify(mNativeInterface).sendBsir(eq(deviceA), eq(false));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceA), eq(true));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceB), eq(false));
+        inOrder.verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
+                .sendBsir(eq(deviceA), eq(false));
         // Set active device to device B
         Assert.assertTrue(mHeadsetService.setActiveDevice(deviceB));
         verify(mNativeInterface).setActiveDevice(deviceB);
@@ -1180,6 +1195,12 @@
                 mHeadsetService.getStateMachinesThreadLooper(), mHeadsetService, mAdapterService,
                 mNativeInterface, mSystemInterface);
         verify(mActiveDeviceManager, timeout(STATE_CHANGE_TIMEOUT_MILLIS))
+                .profileConnectionStateChanged(
+                        BluetoothProfile.HEADSET,
+                        device,
+                        BluetoothProfile.STATE_DISCONNECTED,
+                        BluetoothProfile.STATE_CONNECTING);
+        verify(mSilenceDeviceManager, timeout(STATE_CHANGE_TIMEOUT_MILLIS))
                 .hfpConnectionStateChanged(
                         device,
                         BluetoothProfile.STATE_DISCONNECTED,
@@ -1195,6 +1216,12 @@
                         HeadsetHalConstants.CONNECTION_STATE_SLC_CONNECTED, device);
         mHeadsetService.messageFromNative(slcConnectedEvent);
         verify(mActiveDeviceManager, timeout(STATE_CHANGE_TIMEOUT_MILLIS))
+                .profileConnectionStateChanged(
+                        BluetoothProfile.HEADSET,
+                        device,
+                        BluetoothProfile.STATE_CONNECTING,
+                        BluetoothProfile.STATE_CONNECTED);
+        verify(mSilenceDeviceManager, timeout(STATE_CHANGE_TIMEOUT_MILLIS))
                 .hfpConnectionStateChanged(
                         device,
                         BluetoothProfile.STATE_CONNECTING,
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java
index d025b09..d18b6df 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -54,6 +53,8 @@
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.ActiveDeviceManager;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.RemoteDevices;
+import com.android.bluetooth.btservice.SilenceDeviceManager;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.hamcrest.Matchers;
@@ -87,7 +88,6 @@
     private Context mTargetContext;
     private HeadsetService mHeadsetService;
     private BluetoothAdapter mAdapter;
-    private HeadsetNativeInterface mNativeInterface;
     private BluetoothDevice mCurrentDevice;
     private final HashMap<BluetoothDevice, HeadsetStateMachine> mStateMachines = new HashMap<>();
 
@@ -96,10 +96,13 @@
     @Spy private HeadsetObjectsFactory mObjectsFactory = HeadsetObjectsFactory.getInstance();
     @Mock private AdapterService mAdapterService;
     @Mock private ActiveDeviceManager mActiveDeviceManager;
+    @Mock private SilenceDeviceManager mSilenceDeviceManager;
     @Mock private DatabaseManager mDatabaseManager;
     @Mock private HeadsetSystemInterface mSystemInterface;
+    @Mock private HeadsetNativeInterface mNativeInterface;
     @Mock private AudioManager mAudioManager;
     @Mock private HeadsetPhoneState mPhoneState;
+    @Mock private RemoteDevices mRemoteDevices;
 
     @Before
     public void setUp() throws Exception {
@@ -124,8 +127,10 @@
         doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService)
                 .getBondState(any(BluetoothDevice.class));
         doReturn(mActiveDeviceManager).when(mAdapterService).getActiveDeviceManager();
+        doReturn(mSilenceDeviceManager).when(mAdapterService).getSilenceDeviceManager();
         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
+        doReturn(mRemoteDevices).when(mAdapterService).getRemoteDevices();
         doAnswer(invocation -> {
             Set<BluetoothDevice> keys = mStateMachines.keySet();
             return keys.toArray(new BluetoothDevice[keys.size()]);
@@ -138,7 +143,6 @@
         when(mSystemInterface.getAudioManager()).thenReturn(mAudioManager);
         when(mSystemInterface.isCallIdle()).thenReturn(true, false, true, false);
         // Mock methods in HeadsetNativeInterface
-        mNativeInterface = spy(HeadsetNativeInterface.getInstance());
         doNothing().when(mNativeInterface).init(anyInt(), anyBoolean());
         doNothing().when(mNativeInterface).cleanup();
         doReturn(true).when(mNativeInterface).connectHfp(any(BluetoothDevice.class));
@@ -158,10 +162,10 @@
         }).when(mObjectsFactory).makeStateMachine(any(), any(), any(), any(), any(), any());
         doReturn(mSystemInterface).when(mObjectsFactory).makeSystemInterface(any());
         doReturn(mNativeInterface).when(mObjectsFactory).getNativeInterface();
+        HeadsetNativeInterface.setInstance(mNativeInterface);
         TestUtils.startService(mServiceRule, HeadsetService.class);
         mHeadsetService = HeadsetService.getHeadsetService();
         Assert.assertNotNull(mHeadsetService);
-        verify(mAdapterService).notifyActivityAttributionInfo(any(), any());
         verify(mObjectsFactory).makeSystemInterface(mHeadsetService);
         verify(mObjectsFactory).getNativeInterface();
         mHeadsetService.setForceScoAudio(true);
@@ -170,6 +174,7 @@
     @After
     public void tearDown() throws Exception {
         TestUtils.stopService(mServiceRule, HeadsetService.class);
+        HeadsetNativeInterface.setInstance(null);
         mHeadsetService = HeadsetService.getHeadsetService();
         Assert.assertNull(mHeadsetService);
         mStateMachines.clear();
@@ -1017,52 +1022,6 @@
     }
 
     @Test
-    public void testGetFallbackCandidates() {
-        BluetoothDevice deviceA = TestUtils.getTestDevice(mAdapter, 0);
-        BluetoothDevice deviceB = TestUtils.getTestDevice(mAdapter, 1);
-        when(mDatabaseManager.getCustomMeta(any(BluetoothDevice.class),
-                any(Integer.class))).thenReturn(null);
-
-        // No connected device
-        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager).isEmpty());
-
-        // One connected device
-        addConnectedDeviceHelper(deviceA);
-        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager)
-                .contains(deviceA));
-
-        // Two connected devices
-        addConnectedDeviceHelper(deviceB);
-        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager)
-                .contains(deviceA));
-        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager)
-                .contains(deviceB));
-    }
-
-    @Test
-    public void testGetFallbackCandidates_HasWatchDevice() {
-        BluetoothDevice deviceWatch = TestUtils.getTestDevice(mAdapter, 0);
-        BluetoothDevice deviceRegular = TestUtils.getTestDevice(mAdapter, 1);
-
-        // Make deviceWatch a watch
-        when(mDatabaseManager.getCustomMeta(deviceWatch, BluetoothDevice.METADATA_DEVICE_TYPE))
-                .thenReturn(BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
-        when(mDatabaseManager.getCustomMeta(deviceRegular, BluetoothDevice.METADATA_DEVICE_TYPE))
-                .thenReturn(null);
-
-        // Has a connected watch device
-        addConnectedDeviceHelper(deviceWatch);
-        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager).isEmpty());
-
-        // Two connected devices with one watch
-        addConnectedDeviceHelper(deviceRegular);
-        Assert.assertFalse(mHeadsetService.getFallbackCandidates(mDatabaseManager)
-                .contains(deviceWatch));
-        Assert.assertTrue(mHeadsetService.getFallbackCandidates(mDatabaseManager)
-                .contains(deviceRegular));
-    }
-
-    @Test
     public void testConnectDeviceNotAllowedInbandRingPolicy_InbandRingStatus() {
         when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class),
                 eq(BluetoothProfile.HEADSET)))
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
index ada8bcc..2cac8d3 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
@@ -51,6 +51,8 @@
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.ActiveDeviceManager;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.RemoteDevices;
+import com.android.bluetooth.btservice.SilenceDeviceManager;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.hamcrest.core.IsInstanceOf;
@@ -87,6 +89,7 @@
 
     @Mock private AdapterService mAdapterService;
     @Mock private ActiveDeviceManager mActiveDeviceManager;
+    @Mock private SilenceDeviceManager mSilenceDeviceManager;
     @Mock private DatabaseManager mDatabaseManager;
     @Mock private HeadsetService mHeadsetService;
     @Mock private HeadsetSystemInterface mSystemInterface;
@@ -94,7 +97,8 @@
     @Mock private HeadsetPhoneState mPhoneState;
     @Mock private Intent mIntent;
     private MockContentResolver mMockContentResolver;
-    private HeadsetNativeInterface mNativeInterface;
+    @Mock private HeadsetNativeInterface mNativeInterface;
+    @Mock private RemoteDevices mRemoteDevices;
 
     @Before
     public void setUp() throws Exception {
@@ -114,9 +118,9 @@
         doReturn(true).when(mDatabaseManager).setAudioPolicyMetadata(anyObject(), anyObject());
         // Get an active device manager
         doReturn(mActiveDeviceManager).when(mAdapterService).getActiveDeviceManager();
-        // Spy on native interface
-        mNativeInterface = spy(HeadsetNativeInterface.getInstance());
-        doNothing().when(mNativeInterface).init(anyInt(), anyBoolean());
+        // Get a silence device manager
+        doReturn(mSilenceDeviceManager).when(mAdapterService).getSilenceDeviceManager();
+        doReturn(mRemoteDevices).when(mAdapterService).getRemoteDevices();
         doReturn(true).when(mNativeInterface).connectHfp(mTestDevice);
         doReturn(true).when(mNativeInterface).disconnectHfp(mTestDevice);
         doReturn(true).when(mNativeInterface).connectAudio(mTestDevice);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
index 8f8d547..ace68f8 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
@@ -26,9 +26,7 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothSinkAudioPolicy;
-import android.bluetooth.BluetoothManager;
 import android.content.Context;
 import android.content.Intent;
 import android.os.BatteryManager;
@@ -38,14 +36,13 @@
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.RemoteDevices;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
 import org.junit.Assert;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -67,16 +64,20 @@
 
     @Mock private AdapterService mAdapterService;
     @Mock private HeadsetClientStateMachine mStateMachine;
-
+    @Mock private NativeInterface mNativeInterface;
     @Mock private DatabaseManager mDatabaseManager;
+    @Mock private RemoteDevices mRemoteDevices;
 
     @Before
     public void setUp() throws Exception {
         mTargetContext = InstrumentationRegistry.getTargetContext();
         MockitoAnnotations.initMocks(this);
+
         TestUtils.setAdapterService(mAdapterService);
         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
+        doReturn(mRemoteDevices).when(mAdapterService).getRemoteDevices();
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
+        NativeInterface.setInstance(mNativeInterface);
         TestUtils.startService(mServiceRule, HeadsetClientService.class);
         // At this point the service should have started so check NOT null
         mService = HeadsetClientService.getHeadsetClientService();
@@ -89,6 +90,7 @@
     @After
     public void tearDown() throws Exception {
         TestUtils.stopService(mServiceRule, HeadsetClientService.class);
+        NativeInterface.setInstance(null);
         mService = HeadsetClientService.getHeadsetClientService();
         Assert.assertNull(mService);
         TestUtils.clearAdapterService(mAdapterService);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
index 786d424..14e15e0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
@@ -56,6 +56,7 @@
 import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.btservice.RemoteDevices;
 import com.android.bluetooth.hfp.HeadsetService;
 
 import org.hamcrest.core.AllOf;
@@ -86,18 +87,14 @@
     private BluetoothDevice mTestDevice;
     private Context mTargetContext;
 
-    @Mock
-    private AdapterService mAdapterService;
-    @Mock
-    private Resources mMockHfpResources;
-    @Mock
-    private HeadsetService mHeadsetService;
-    @Mock
-    private HeadsetClientService mHeadsetClientService;
-    @Mock
-    private AudioManager mAudioManager;
+    @Mock private AdapterService mAdapterService;
+    @Mock private Resources mMockHfpResources;
+    @Mock private HeadsetService mHeadsetService;
+    @Mock private HeadsetClientService mHeadsetClientService;
+    @Mock private AudioManager mAudioManager;
+    @Mock private RemoteDevices mRemoteDevices;
 
-    private NativeInterface mNativeInterface;
+    @Mock private NativeInterface mNativeInterface;
 
     private static final int STANDARD_WAIT_MILLIS = 1000;
     private static final int QUERY_CURRENT_CALLS_WAIT_MILLIS = 2000;
@@ -122,7 +119,7 @@
                 .thenReturn(2000);
 
         TestUtils.setAdapterService(mAdapterService);
-        mNativeInterface = spy(NativeInterface.getInstance());
+        doReturn(mRemoteDevices).when(mAdapterService).getRemoteDevices();
         doReturn(true).when(mNativeInterface).sendAndroidAt(anyObject(), anyString());
 
         // This line must be called to make sure relevant objects are initialized properly
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/VendorCommandResponseProcessorTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/VendorCommandResponseProcessorTest.java
index c866a68..3876a4a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/VendorCommandResponseProcessorTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/VendorCommandResponseProcessorTest.java
@@ -19,7 +19,6 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothAssignedNumbers;
@@ -45,7 +44,7 @@
 
     private BluetoothAdapter mAdapter;
     private BluetoothDevice mTestDevice;
-    private NativeInterface mNativeInterface;
+    @Mock private NativeInterface mNativeInterface;
     private VendorCommandResponseProcessor mProcessor;
 
     @Mock
@@ -57,7 +56,6 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         TestUtils.setAdapterService(mAdapterService);
-        mNativeInterface = spy(NativeInterface.getInstance());
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
index 780448b..9205895 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
@@ -27,14 +27,12 @@
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
 import org.junit.Assert;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -54,6 +52,7 @@
 
     @Mock private AdapterService mAdapterService;
     @Mock private DatabaseManager mDatabaseManager;
+    @Mock private HidHostNativeInterface mNativeInterface;
 
     @Before
     public void setUp() throws Exception {
@@ -62,6 +61,7 @@
         TestUtils.setAdapterService(mAdapterService);
         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
         when(mAdapterService.isStartedProfile(anyString())).thenReturn(true);
+        HidHostNativeInterface.setInstance(mNativeInterface);
         TestUtils.startService(mServiceRule, HidHostService.class);
         mService = HidHostService.getHidHostService();
         Assert.assertNotNull(mService);
@@ -77,6 +77,7 @@
     public void tearDown() throws Exception {
         when(mAdapterService.isStartedProfile(anyString())).thenReturn(false);
         TestUtils.stopService(mServiceRule, HidHostService.class);
+        HidHostNativeInterface.setInstance(null);
         mService = HidHostService.getHidHostService();
         Assert.assertNull(mService);
         TestUtils.clearAdapterService(mAdapterService);
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 1a8e430..63248a5 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
@@ -28,7 +28,6 @@
 import android.media.AudioManager;
 import android.os.Looper;
 
-import android.os.ParcelUuid;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ServiceTestRule;
@@ -70,8 +69,8 @@
     private DatabaseManager mDatabaseManager;
     @Mock
     private AudioManager mAudioManager;
-    @Mock
-    private LeAudioBroadcasterNativeInterface mNativeInterface;
+    @Mock private LeAudioBroadcasterNativeInterface mLeAudioBroadcasterNativeInterface;
+    @Mock private LeAudioNativeInterface mLeAudioNativeInterface;
     @Mock private LeAudioTmapGattServer mTmapGattServer;
     @Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
 
@@ -179,9 +178,10 @@
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
 
+        LeAudioBroadcasterNativeInterface.setInstance(mLeAudioBroadcasterNativeInterface);
+        LeAudioNativeInterface.setInstance(mLeAudioNativeInterface);
         startService();
         mService.mAudioManager = mAudioManager;
-        mService.mLeAudioBroadcasterNativeInterface = mNativeInterface;
 
         // Set up the State Changed receiver
         IntentFilter filter = new IntentFilter();
@@ -191,7 +191,7 @@
         mTargetContext.registerReceiver(mLeAudioIntentReceiver, filter);
 
         mDevice = TestUtils.getTestDevice(mAdapter, 0);
-        when(mNativeInterface.getDevice(any(byte[].class))).thenReturn(mDevice);
+        when(mLeAudioBroadcasterNativeInterface.getDevice(any(byte[].class))).thenReturn(mDevice);
 
         mIntentQueue = new LinkedBlockingQueue<Intent>();
     }
@@ -203,6 +203,8 @@
         }
 
         stopService();
+        LeAudioBroadcasterNativeInterface.setInstance(null);
+        LeAudioNativeInterface.setInstance(null);
         mTargetContext.unregisterReceiver(mLeAudioIntentReceiver);
         TestUtils.clearAdapterService(mAdapterService);
         reset(mAudioManager);
@@ -258,11 +260,14 @@
                         .map(setting -> setting.getContentMetadata().getRawMetadata())
                         .toArray(byte[][]::new);
 
-        verify(mNativeInterface, times(1)).createBroadcast(eq(true), eq(TEST_BROADCAST_NAME),
-                eq(settings.getBroadcastCode()),
-                eq(settings.getPublicBroadcastMetadata().getRawMetadata()),
-                eq(expectedQualityArray),
-                eq(expectedDataArray));
+        verify(mLeAudioBroadcasterNativeInterface, times(1))
+                .createBroadcast(
+                        eq(true),
+                        eq(TEST_BROADCAST_NAME),
+                        eq(settings.getBroadcastCode()),
+                        eq(settings.getPublicBroadcastMetadata().getRawMetadata()),
+                        eq(expectedQualityArray),
+                        eq(expectedDataArray));
 
         // Check if broadcast is started automatically when created
         LeAudioStackEvent create_event =
@@ -272,7 +277,7 @@
         mService.messageFromNative(create_event);
 
         // Verify if broadcast is auto-started on start
-        verify(mNativeInterface, times(1)).startBroadcast(eq(broadcastId));
+        verify(mLeAudioBroadcasterNativeInterface, times(1)).startBroadcast(eq(broadcastId));
 
         // Notify initial paused state
         LeAudioStackEvent state_event =
@@ -288,14 +293,14 @@
         mService.messageFromNative(state_event);
 
         // Check if metadata is requested when the broadcast starts to stream
-        verify(mNativeInterface, times(1)).getBroadcastMetadata(eq(broadcastId));
+        verify(mLeAudioBroadcasterNativeInterface, times(1)).getBroadcastMetadata(eq(broadcastId));
         Assert.assertFalse(mOnBroadcastStartFailedCalled);
         Assert.assertTrue(mOnBroadcastStartedCalled);
     }
 
     void verifyBroadcastStopped(int broadcastId) {
         mService.stopBroadcast(broadcastId);
-        verify(mNativeInterface, times(1)).stopBroadcast(eq(broadcastId));
+        verify(mLeAudioBroadcasterNativeInterface, times(1)).stopBroadcast(eq(broadcastId));
 
         LeAudioStackEvent state_event =
                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE);
@@ -304,7 +309,7 @@
         mService.messageFromNative(state_event);
 
         // Verify if broadcast is auto-destroyed on stop
-        verify(mNativeInterface, times(1)).destroyBroadcast(eq(broadcastId));
+        verify(mLeAudioBroadcasterNativeInterface, times(1)).destroyBroadcast(eq(broadcastId));
 
         state_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_DESTROYED);
         state_event.valueInt1 = broadcastId;
@@ -367,10 +372,14 @@
         byte[][] expectedDataArray =
                 {settings.getSubgroupSettings().get(0).getContentMetadata().getRawMetadata()};
 
-        verify(mNativeInterface, times(1)).createBroadcast(eq(true), eq(TEST_BROADCAST_NAME),
-                eq(code), eq(settings.getPublicBroadcastMetadata().getRawMetadata()),
-                eq(expectedQualityArray),
-                eq(expectedDataArray));
+        verify(mLeAudioBroadcasterNativeInterface, times(1))
+                .createBroadcast(
+                        eq(true),
+                        eq(TEST_BROADCAST_NAME),
+                        eq(code),
+                        eq(settings.getPublicBroadcastMetadata().getRawMetadata()),
+                        eq(expectedQualityArray),
+                        eq(expectedDataArray));
 
         LeAudioStackEvent create_event =
                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioTmapGattServerTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioTmapGattServerTest.java
index 230bfbf..da9e348 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioTmapGattServerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioTmapGattServerTest.java
@@ -17,7 +17,7 @@
 package com.android.bluetooth.le_audio;
 
 import static android.bluetooth.BluetoothGattCharacteristic.FORMAT_UINT16;
-import static android.bluetooth.BluetoothGattCharacteristic.PERMISSION_READ_ENCRYPTED;
+import static android.bluetooth.BluetoothGattCharacteristic.PERMISSION_READ;
 import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_READ;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -86,7 +86,7 @@
                 service.getCharacteristic(LeAudioTmapGattServer.UUID_TMAP_ROLE);
         assertThat(characteristic).isNotNull();
         assertThat(characteristic.getProperties()).isEqualTo(PROPERTY_READ);
-        assertThat(characteristic.getPermissions()).isEqualTo(PERMISSION_READ_ENCRYPTED);
+        assertThat(characteristic.getPermissions()).isEqualTo(PERMISSION_READ);
 
         // verify characteristic value
         int value = characteristic.getIntValue(FORMAT_UINT16, 0);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/le_audio/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/mcp/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppBtEnableActivityTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppBtEnableActivityTest.java
index d3d1678..3a5de2a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppBtEnableActivityTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppBtEnableActivityTest.java
@@ -31,6 +31,7 @@
 import android.content.Intent;
 
 import androidx.test.core.app.ActivityScenario;
+import androidx.test.espresso.action.ViewActions;
 import androidx.test.espresso.intent.Intents;
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -77,6 +78,9 @@
                 mIntent);
         activityScenario.onActivity(
                 activity -> activity.mOppManager = mock(BluetoothOppManager.class));
+        onView(withText(mTargetContext.getText(R.string.bt_enable_ok).toString()))
+                .inRoot(isDialog())
+                .perform(ViewActions.scrollTo());
         onView(withText(mTargetContext.getText(R.string.bt_enable_ok).toString())).inRoot(
                 isDialog()).check(matches(isDisplayed())).perform(click());
         intended(hasComponent(BluetoothOppBtEnablingActivity.class.getName()));
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java
index d61d3de..202c96a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java
@@ -19,10 +19,12 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -47,6 +49,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @MediumTest
@@ -82,6 +85,18 @@
         // Try getting the Bluetooth adapter
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         Assert.assertNotNull(mAdapter);
+
+        // Wait until the initial trimDatabase operation is done.
+        verify(mBluetoothMethodProxy, timeout(3_000))
+                .contentResolverQuery(
+                        any(),
+                        eq(BluetoothShare.CONTENT_URI),
+                        eq(new String[] {BluetoothShare._ID}),
+                        any(),
+                        isNull(),
+                        eq(BluetoothShare._ID));
+
+        Mockito.clearInvocations(mBluetoothMethodProxy);
     }
 
     @After
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferHistoryTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferHistoryTest.java
index 531a29d..7bb08ca 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferHistoryTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferHistoryTest.java
@@ -32,6 +32,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.net.Uri;
 import android.view.MenuItem;
@@ -47,6 +48,7 @@
 import com.google.common.base.Objects;
 
 import org.junit.After;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -135,7 +137,11 @@
     }
 
     @Test
-    public void onCreate_withDirectionInbound_withExtraShowAllFileIsTrue_displayLiveFolder() {
+    public void onCreate_withDirectionInbound_withExtraShowAllFileIsTrue_displayLiveFolder()
+            throws Exception {
+        Assume.assumeFalse(
+                mTargetContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
+
         BluetoothOppTestUtils.setUpMockCursor(mCursor, mCursorMockDataList);
         mIntent.putExtra(Constants.EXTRA_SHOW_ALL_FILES, true);
         mIntent.putExtra("direction", BluetoothShare.DIRECTION_INBOUND);
@@ -148,6 +154,9 @@
 
     @Test
     public void onCreate_withDirectionInbound_withExtraShowAllFileIsFalse_displayInboundHistory() {
+        Assume.assumeFalse(
+                mTargetContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
+
         BluetoothOppTestUtils.setUpMockCursor(mCursor, mCursorMockDataList);
         mIntent.putExtra(Constants.EXTRA_SHOW_ALL_FILES, false);
         mIntent.putExtra("direction", BluetoothShare.DIRECTION_INBOUND);
@@ -161,6 +170,9 @@
 
     @Test
     public void onCreate_withDirectionOutbound_displayOutboundHistory() {
+        Assume.assumeFalse(
+                mTargetContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
+
         BluetoothOppTestUtils.setUpMockCursor(mCursor, mCursorMockDataList);
         mCursorMockDataList.set(1,
                 new BluetoothOppTestUtils.CursorMockData(BluetoothShare.DIRECTION, 2,
@@ -175,6 +187,7 @@
                 matches(isDisplayed()));
     }
 
+    // TODO: Check whether watch devices can pass this test
     @Ignore("b/268424815")
     @Test
     public void onOptionsItemSelected_clearAllSelected_promptWarning() {
@@ -211,4 +224,4 @@
                 argThat(arg -> Objects.equal(arg.get(BluetoothShare.VISIBILITY),
                         BluetoothShare.VISIBILITY_HIDDEN)), any(), any());
     }
-}
\ No newline at end of file
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
index 9b2bed2..02c5d0e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
@@ -41,7 +41,6 @@
 import com.android.bluetooth.pan.PanService.BluetoothPanDevice;
 
 import org.junit.After;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -63,6 +62,7 @@
 
     @Mock private AdapterService mAdapterService;
     @Mock private DatabaseManager mDatabaseManager;
+    @Mock private PanNativeInterface mNativeInterface;
     @Mock private UserManager mMockUserManager;
 
     @Before
@@ -71,8 +71,10 @@
         TestUtils.setAdapterService(mAdapterService);
         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
+        PanNativeInterface.setInstance(mNativeInterface);
         TestUtils.startService(mServiceRule, PanService.class);
         mService = PanService.getPanService();
+
         assertThat(mService).isNotNull();
         // Try getting the Bluetooth adapter
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -84,6 +86,7 @@
     @After
     public void tearDown() throws Exception {
         TestUtils.stopService(mServiceRule, PanService.class);
+        PanNativeInterface.setInstance(null);
         mService = PanService.getPanService();
         assertThat(mService).isNull();
         TestUtils.clearAdapterService(mAdapterService);
@@ -125,15 +128,15 @@
 
     @Test
     public void convertHalState() {
-        assertThat(PanService.convertHalState(PanService.CONN_STATE_CONNECTED))
+        assertThat(PanNativeInterface.convertHalState(PanNativeInterface.CONN_STATE_CONNECTED))
                 .isEqualTo(BluetoothProfile.STATE_CONNECTED);
-        assertThat(PanService.convertHalState(PanService.CONN_STATE_CONNECTING))
+        assertThat(PanNativeInterface.convertHalState(PanNativeInterface.CONN_STATE_CONNECTING))
                 .isEqualTo(BluetoothProfile.STATE_CONNECTING);
-        assertThat(PanService.convertHalState(PanService.CONN_STATE_DISCONNECTED))
+        assertThat(PanNativeInterface.convertHalState(PanNativeInterface.CONN_STATE_DISCONNECTED))
                 .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
-        assertThat(PanService.convertHalState(PanService.CONN_STATE_DISCONNECTING))
+        assertThat(PanNativeInterface.convertHalState(PanNativeInterface.CONN_STATE_DISCONNECTING))
                 .isEqualTo(BluetoothProfile.STATE_DISCONNECTING);
-        assertThat(PanService.convertHalState(-24664)) // illegal value
+        assertThat(PanNativeInterface.convertHalState(-24664)) // illegal value
                 .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
     }
 
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java
index ac68473..d4d66d7 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceTest.java
@@ -30,7 +30,6 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHeadsetClient;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.Intent;
@@ -50,7 +49,6 @@
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -366,14 +364,14 @@
     }
 
     @Test
-    public void broadcastReceiver_withActionHeadsetClientConnectionStateChanged() {
+    public void headsetClientConnectionStateChanged_hfpCallLogIsRemoved() {
         BluetoothMethodProxy methodProxy = spy(BluetoothMethodProxy.getInstance());
         BluetoothMethodProxy.setInstanceForTesting(methodProxy);
 
-        Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
-        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
-        intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED);
-        mService.mPbapBroadcastReceiver.onReceive(mService, intent);
+        mService.handleHeadsetClientConnectionStateChanged(
+                mRemoteDevice,
+                BluetoothProfile.STATE_CONNECTED,
+                BluetoothProfile.STATE_DISCONNECTED);
 
         ArgumentCaptor<Object> selectionArgsCaptor = ArgumentCaptor.forClass(Object.class);
         verify(methodProxy).contentResolverDelete(any(), eq(CallLog.Calls.CONTENT_URI), any(),
diff --git a/android/app/tests/unit/src/com/android/bluetooth/sdp/DipTest.java b/android/app/tests/unit/src/com/android/bluetooth/sdp/DipTest.java
index d4ba194..f27e158 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/sdp/DipTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/sdp/DipTest.java
@@ -22,31 +22,24 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.SdpDipRecord;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.Looper;
-import android.os.ParcelUuid;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
-import com.android.bluetooth.sdp.SdpManager;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AbstractionLayer;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.Utils;
 
 import org.junit.After;
-import org.junit.Assume;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -66,6 +59,7 @@
     private ArgumentCaptor<Bundle> mBundleArgument = ArgumentCaptor.forClass(Bundle.class);
 
     @Mock private AdapterService mAdapterService = null;
+    @Mock private SdpManagerNativeInterface mNativeInterface;
 
     @Before
     public void setUp() throws Exception {
@@ -73,6 +67,7 @@
         // Set up mocks and test assets
         MockitoAnnotations.initMocks(this);
 
+        SdpManagerNativeInterface.setInstance(mNativeInterface);
         TestUtils.setAdapterService(mAdapterService);
         doReturn("00:01:02:03:04:05").when(mAdapterService).getIdentityAddress("00:01:02:03:04:05");
 
@@ -90,6 +85,7 @@
     @After
     public void tearDown() throws Exception {
         TestUtils.clearAdapterService(mAdapterService);
+        SdpManagerNativeInterface.setInstance(null);
     }
 
     private void verifyDipSdpRecordIntent(ArgumentCaptor<Intent> intentArgument,
diff --git a/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java
index ea787aa..b3189fa 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java
@@ -76,6 +76,7 @@
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class BluetoothInCallServiceTest {
+    private static final String TAG = "BluetoothInCallServiceTest";
 
     private static final int TEST_DTMF_TONE = 0;
     private static final String TEST_ACCOUNT_ADDRESS = "//foo.com/";
@@ -554,6 +555,53 @@
     }
 
     @Test
+    public void testListCurrentCallsCallHandleChanged() throws Exception {
+        mBluetoothInCallService.mTelephonyManager = mMockTelephonyManager;
+        when(mMockTelephonyManager.getNetworkCountryIso()).thenReturn("");
+
+        ArrayList<BluetoothCall> calls = new ArrayList<>();
+        when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
+        BluetoothCall activeCall = createForegroundCall(UUID.randomUUID());
+        calls.add(activeCall);
+        mBluetoothInCallService.onCallAdded(activeCall);
+
+        when(activeCall.getState()).thenReturn(Call.STATE_ACTIVE);
+        when(activeCall.isIncoming()).thenReturn(true);
+        when(activeCall.isConference()).thenReturn(false);
+        when(activeCall.getHandle()).thenReturn(Uri.parse("tel:2135550000"));
+        Log.w(TAG, "call handle" + Uri.parse("tel:2135550000"));
+        when(activeCall.getGatewayInfo())
+                .thenReturn(new GatewayInfo(null, null, Uri.parse("tel:2135550000")));
+
+        clearInvocations(mMockBluetoothHeadset);
+        mBluetoothInCallService.listCurrentCalls();
+        verify(mMockBluetoothHeadset)
+                .clccResponse(
+                        1,
+                        1,
+                        CALL_STATE_ACTIVE,
+                        0,
+                        false,
+                        "2135550000",
+                        PhoneNumberUtils.TOA_Unknown);
+
+        // call handle changed
+        when(activeCall.getHandle()).thenReturn(Uri.parse("tel:213-555-0000"));
+        clearInvocations(mMockBluetoothHeadset);
+        Log.w(TAG, "call handle" + Uri.parse("tel:213-555-0000"));
+        mBluetoothInCallService.listCurrentCalls();
+        verify(mMockBluetoothHeadset)
+                .clccResponse(
+                        1,
+                        1,
+                        CALL_STATE_ACTIVE,
+                        0,
+                        false,
+                        "2135550000",
+                        PhoneNumberUtils.TOA_Unknown);
+    }
+
+    @Test
     public void testRingingCallClccResponse() throws Exception {
         ArrayList<BluetoothCall> calls = new ArrayList<>();
         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
@@ -771,6 +819,8 @@
     @Test
     public void testListCurrentCallsConferenceEmptyChildrenInference() throws Exception {
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_BLUETOOTH, CLCC_INFERENCE, "true", false);
+        mBluetoothInCallService.mTelephonyManager = mMockTelephonyManager;
+        when(mMockTelephonyManager.getNetworkCountryIso()).thenReturn("");
 
         ArrayList<BluetoothCall> calls = new ArrayList<>();
         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/vc/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/vc/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
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 dcee211..3b8089f 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
@@ -44,13 +44,11 @@
 import com.android.bluetooth.btservice.ServiceFactory;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.bluetooth.csip.CsipSetCoordinatorService;
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.x.com.android.modules.utils.SynchronousResultReceiver;
 
 import org.junit.After;
 import org.junit.Assert;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -121,8 +119,8 @@
         doReturn(CALL_MAX_VOL).when(mAudioManager)
                 .getStreamMaxVolume(eq(AudioManager.STREAM_VOICE_CALL));
 
+        VolumeControlNativeInterface.setInstance(mNativeInterface);
         startService();
-        mService.mVolumeControlNativeInterface = mNativeInterface;
         mService.mAudioManager = mAudioManager;
         mService.mFactory = mServiceFactory;
         mServiceBinder = (VolumeControlService.BluetoothVolumeControlBinder) mService.initBinder();
@@ -160,6 +158,7 @@
         }
 
         stopService();
+        VolumeControlNativeInterface.setInstance(null);
         mTargetContext.unregisterReceiver(mVolumeControlIntentReceiver);
         mDeviceQueueMap.clear();
         TestUtils.clearAdapterService(mAdapterService);
diff --git a/android/leaudio/Android.bp b/android/leaudio/Android.bp
index a3209cf..958556f 100644
--- a/android/leaudio/Android.bp
+++ b/android/leaudio/Android.bp
@@ -10,6 +10,7 @@
     srcs: ["app/src/main/**/*.java"],
     resource_dirs: ["app/src/main/res"],
     manifest: "app/src/main/AndroidManifest.xml",
+    min_sdk_version: "Tiramisu",
 
     static_libs: [
         "androidx-constraintlayout_constraintlayout",
diff --git a/android/leaudio/OWNERS b/android/leaudio/OWNERS
index 9f1d8db..c3c1ccc 100644
--- a/android/leaudio/OWNERS
+++ b/android/leaudio/OWNERS
@@ -1 +1 @@
-include ../app/OWNERS
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
diff --git a/android/leaudio/app/build.gradle b/android/leaudio/app/build.gradle
index f6f894d..47afb27 100644
--- a/android/leaudio/app/build.gradle
+++ b/android/leaudio/app/build.gradle
@@ -1,11 +1,11 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion = 'android-S'
+    compileSdkVersion = 'android-T'
     defaultConfig {
         applicationId "com.android.bluetooth.leaudio"
-        minSdkVersion "S"
-        targetSdkVersion "S"
+        minSdkVersion "T"
+        targetSdkVersion "T"
         versionCode 1
         versionName "1.0"
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
diff --git a/android/leaudio/app/src/main/AndroidManifest.xml b/android/leaudio/app/src/main/AndroidManifest.xml
index 120142d..96345d1 100644
--- a/android/leaudio/app/src/main/AndroidManifest.xml
+++ b/android/leaudio/app/src/main/AndroidManifest.xml
@@ -13,6 +13,7 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-sdk android:minSdkVersion="13"/>
 
     <application
         android:allowBackup="true"
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 5736a4d..e80b7c4 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
@@ -46,6 +46,7 @@
 import java.io.ByteArrayOutputStream;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 public class BroadcasterActivity extends AppCompatActivity {
@@ -400,7 +401,8 @@
 
         // Get the initial state
         mViewModel = ViewModelProviders.of(this).get(BroadcasterViewModel.class);
-        itemsAdapter.updateBroadcastsMetadata(mViewModel.getAllBroadcastMetadata());
+        final List<BluetoothLeBroadcastMetadata> metadata = mViewModel.getAllBroadcastMetadata();
+        itemsAdapter.updateBroadcastsMetadata(metadata.isEmpty() ? new ArrayList<>() : metadata);
 
         // Put a watch on updates
         mViewModel.getBroadcastUpdateMetadataLive().observe(this, audioBroadcast -> {
diff --git a/android/pandora/OWNERS b/android/pandora/OWNERS
index 142833b..a188b6c 100644
--- a/android/pandora/OWNERS
+++ b/android/pandora/OWNERS
@@ -3,4 +3,3 @@
 licorne@google.com
 uael@google.com
 charliebout@google.com
-qiaoccolato@google.com
diff --git a/android/pandora/server/Android.bp b/android/pandora/server/Android.bp
index 0e3b697..18d021a 100644
--- a/android/pandora/server/Android.bp
+++ b/android/pandora/server/Android.bp
@@ -62,6 +62,7 @@
         ":PandoraServer",
         ":mmi2grpc",
         "configs/pts_bot_tests_config.json",
+        "configs/pts_bot_tests_config_auto.json",
     ],
     test_suites: ["device-tests"],
     min_sdk_version: "Tiramisu",
@@ -74,6 +75,7 @@
         ":PandoraServer",
         ":mmi2grpc",
         "configs/pts_bot_tests_config.json",
+        "configs/pts_bot_tests_config_auto.json",
     ],
     test_suites: ["mts-bluetooth"],
     min_sdk_version: "Tiramisu",
diff --git a/android/pandora/server/configs/pts_bot_tests_config_auto.json b/android/pandora/server/configs/pts_bot_tests_config_auto.json
new file mode 100644
index 0000000..43419ff
--- /dev/null
+++ b/android/pandora/server/configs/pts_bot_tests_config_auto.json
@@ -0,0 +1,377 @@
+{
+  "ics": {
+    "TSPC_A2DP_1_2": true,
+    "TSPC_A2DP_4_1": true,
+    "TSPC_A2DP_4_10": true,
+    "TSPC_A2DP_4_13": true,
+    "TSPC_A2DP_4_14": true,
+    "TSPC_A2DP_4_2": true,
+    "TSPC_A2DP_4_3": true,
+    "TSPC_A2DP_4_4": true,
+    "TSPC_A2DP_4_5": true,
+    "TSPC_A2DP_4_6": true,
+    "TSPC_A2DP_4_7": true,
+    "TSPC_A2DP_4_8": true,
+    "TSPC_A2DP_4_9": true,
+    "TSPC_A2DP_7a_2": true,
+    "TSPC_A2DP_5_1": true,
+    "TSPC_A2DP_5_2": true,
+    "TSPC_A2DP_5_4": true,
+    "TSPC_A2DP_5a_1": true,
+    "TSPC_A2DP_5a_10": true,
+    "TSPC_A2DP_5a_11": true,
+    "TSPC_A2DP_5a_12": true,
+    "TSPC_A2DP_5a_2": true,
+    "TSPC_A2DP_5a_3": true,
+    "TSPC_A2DP_5a_4": true,
+    "TSPC_A2DP_5a_5": true,
+    "TSPC_A2DP_5a_6": true,
+    "TSPC_A2DP_5a_7": true,
+    "TSPC_A2DP_5a_8": true,
+    "TSPC_A2DP_5a_9": true,
+    "TSPC_AVRCP_1_1": true,
+    "TSPC_AVRCP_1_2": true,
+    "TSPC_AVRCP_2b_5": true,
+    "TSPC_AVRCP_2c_2": true,
+    "TSPC_AVRCP_7b_5": true,
+    "TSPC_AVRCP_7c_7": true,
+    "TSPC_AVRCP_14_1": true,
+    "TSPC_AVRCP_14_2": true,
+    "TSPC_AVRCP_14_3": true,
+    "TSPC_AVRCP_2_1": true,
+    "TSPC_AVRCP_2_11": true,
+    "TSPC_AVRCP_2_12": true,
+    "TSPC_AVRCP_2_13": true,
+    "TSPC_AVRCP_2_14": true,
+    "TSPC_AVRCP_2_15": true,
+    "TSPC_AVRCP_2_17": true,
+    "TSPC_AVRCP_2_18": true,
+    "TSPC_AVRCP_2_2": true,
+    "TSPC_AVRCP_2_20": true,
+    "TSPC_AVRCP_2_21": true,
+    "TSPC_AVRCP_2_22": true,
+    "TSPC_AVRCP_2_23": true,
+    "TSPC_AVRCP_2_24": true,
+    "TSPC_AVRCP_2_27": true,
+    "TSPC_AVRCP_2_28": true,
+    "TSPC_AVRCP_2_29": true,
+    "TSPC_AVRCP_2_3": true,
+    "TSPC_AVRCP_2_30": true,
+    "TSPC_AVRCP_2_31": true,
+    "TSPC_AVRCP_2_32": true,
+    "TSPC_AVRCP_2_33": true,
+    "TSPC_AVRCP_2_34": true,
+    "TSPC_AVRCP_2_35": true,
+    "TSPC_AVRCP_2_35b": true,
+    "TSPC_AVRCP_2_36": true,
+    "TSPC_AVRCP_2_37": true,
+    "TSPC_AVRCP_2_38": true,
+    "TSPC_AVRCP_2_4": true,
+    "TSPC_AVRCP_2_43": true,
+    "TSPC_AVRCP_2_44": true,
+    "TSPC_AVRCP_2_44b": true,
+    "TSPC_AVRCP_2_45": true,
+    "TSPC_AVRCP_2_47": true,
+    "TSPC_AVRCP_2_48": true,
+    "TSPC_AVRCP_2_52": true,
+    "TSPC_AVRCP_2_54": true,
+    "TSPC_AVRCP_2_55": true,
+    "TSPC_AVRCP_2_56": true,
+    "TSPC_AVRCP_2_7": true,
+    "TSPC_AVRCP_3_19": true,
+    "TSPC_AVRCP_3_20": true,
+    "TSPC_AVRCP_3_21": true,
+    "TSPC_AVRCP_3_23": true,
+    "TSPC_AVRCP_3_24": true,
+    "TSPC_AVRCP_3_26": true,
+    "TSPC_AVRCP_3_27": true,
+    "TSPC_AVRCP_7_2": true,
+    "TSPC_AVRCP_7_22": true,
+    "TSPC_AVRCP_7_3": true,
+    "TSPC_AVRCP_7_4": true,
+    "TSPC_AVRCP_7_5": true,
+    "TSPC_AVRCP_7_6": true,
+    "TSPC_AVRCP_7_60": true,
+    "TSPC_AVRCP_7_61": true,
+    "TSPC_AVRCP_7_62": true,
+    "TSPC_AVRCP_7_65": true,
+    "TSPC_AVRCP_7_8": true,
+    "TSPC_AVRCP_9_19": true,
+    "TSPC_AVRCP_9_20": true,
+    "TSPC_PAN_1_1": true,
+    "TSPC_PAN_1_3": true,
+    "TSPC_PAN_2_1": true,
+    "TSPC_PAN_2_17": true,
+    "TSPC_PAN_2_18": true,
+    "TSPC_PAN_2_2": true,
+    "TSPC_PAN_2_4": true,
+    "TSPC_PAN_4_1": true,
+    "TSPC_PAN_4_2": true,
+    "TSPC_AVDTP_1_2": true,
+    "TSPC_AVDTP_1_3": true,
+    "TSPC_AVDTP_1_4": true,
+    "TSPC_AVDTP_16_1": true,
+    "TSPC_AVDTP_18_1": true,
+    "TSPC_AVDTP_18_3": true,
+    "TSPC_AVDTP_2b_1": true,
+    "TSPC_AVDTP_2b_2": true,
+    "TSPC_AVDTP_2b_3": true,
+    "TSPC_AVDTP_2b_4": true,
+    "TSPC_AVDTP_3b_1": true,
+    "TSPC_AVDTP_3b_2": true,
+    "TSPC_AVDTP_4b_1": true,
+    "TSPC_AVDTP_4b_2": true,
+    "TSPC_AVDTP_4b_3": true,
+    "TSPC_AVDTP_4b_4": true,
+    "TSPC_AVDTP_4b_5": true,
+    "TSPC_AVDTP_5b_1": true,
+    "TSPC_AVDTP_5b_2": true,
+    "TSPC_AVDTP_5b_3": true,
+    "TSPC_AVDTP_5b_4": true,
+    "TSPC_AVDTP_5b_5": true,
+    "TSPC_AVDTP_6b_1": true,
+    "TSPC_AVDTP_7b_1": true,
+    "TSPC_AVDTP_10b_1": true,
+    "TSPC_AVDTP_10b_2": true,
+    "TSPC_AVDTP_10b_3": true,
+    "TSPC_AVDTP_10b_4": true,
+    "TSPC_AVDTP_10b_5": true,
+    "TSPC_AVDTP_13b_1": true,
+    "TSPC_AVDTP_8b_1": true,
+    "TSPC_AVDTP_8b_2": true,
+    "TSPC_AVDTP_8b_3": true,
+    "TSPC_AVDTP_8b_4": true,
+    "TSPC_AVDTP_9b_1": true,
+    "TSPC_AVDTP_9b_2": true,
+    "TSPC_AVDTP_15_1": true,
+    "TSPC_AVDTP_15a_2": true,
+    "TSPC_AVCTP_0_2": true,
+    "TSPC_AVCTP_1_1": true,
+    "TSPC_AVCTP_1_2": true,
+    "TSPC_AVCTP_2_10": true,
+    "TSPC_AVCTP_2_11": true,
+    "TSPC_AVCTP_2_12": true,
+    "TSPC_AVCTP_2_13": true,
+    "TSPC_AVCTP_2_14": true,
+    "TSPC_AVCTP_2_2": true,
+    "TSPC_AVCTP_2_3": true,
+    "TSPC_AVCTP_2_4": true,
+    "TSPC_AVCTP_2_5": true,
+    "TSPC_AVCTP_2_6": true,
+    "TSPC_AVCTP_2_7": true,
+    "TSPC_AVCTP_2_8": true,
+    "TSPC_AVCTP_2_9": true,
+    "TSPC_AVCTP_3_2": true,
+    "TSPC_AVCTP_3_3": true,
+    "TSPC_AVCTP_3_4": true,
+    "TSPC_AVCTP_3_5": true,
+    "TSPC_AVCTP_3_6": true,
+    "TSPC_AVCTP_3_7": true,
+    "TSPC_HFP_0b_3": true,
+    "TSPC_HFP_0d_1": true,
+    "TSPC_HFP_1_2": true,
+    "TSPC_HFP_3_1": true,
+    "TSPC_HFP_3_11": true,
+    "TSPC_HFP_3_12": true,
+    "TSPC_HFP_3_12a": true,
+    "TSPC_HFP_3_12b": true,
+    "TSPC_HFP_3_12c": true,
+    "TSPC_HFP_3_12d": true,
+    "TSPC_HFP_3_12e": true,
+    "TSPC_HFP_3_13": true,
+    "TSPC_HFP_3_14": true,
+    "TSPC_HFP_3_15": true,
+    "TSPC_HFP_3_17": true,
+    "TSPC_HFP_3_19": true,
+    "TSPC_HFP_3_20": true,
+    "TSPC_HFP_3_21a": true,
+    "TSPC_HFP_3_21b": true,
+    "TSPC_HFP_3_24": true,
+    "TSPC_HFP_3_26": true,
+    "TSPC_HFP_3_2a": true,
+    "TSPC_HFP_3_2b": true,
+    "TSPC_HFP_3_2c": true,
+    "TSPC_HFP_3_2d": true,
+    "TSPC_HFP_3_2e": true,
+    "TSPC_HFP_3_2f": true,
+    "TSPC_HFP_3_3": true,
+    "TSPC_HFP_3_3a": true,
+    "TSPC_HFP_3_3b": true,
+    "TSPC_HFP_3_3c": true,
+    "TSPC_HFP_3_4a": true,
+    "TSPC_HFP_3_4b": true,
+    "TSPC_HFP_3_5": true,
+    "TSPC_HFP_3_6": true,
+    "TSPC_HFP_3_7": true,
+    "TSPC_HFP_3_7a": true,
+    "TSPC_HFP_3_8": true,
+    "TSPC_HFP_4_1": true,
+    "TSPC_HFP_4_2": true,
+    "TSPC_HFP_4_3": true,
+    "TSPC_HFP_4_4": true,
+    "TSPC_HFP_8_1": true,
+    "TSPC_HFP_8_10": true,
+    "TSPC_HFP_8_11": true,
+    "TSPC_HFP_8_2": true,
+    "TSPC_HFP_8_3": true,
+    "TSPC_HFP_8_4": true,
+    "TSPC_HFP_8_5": true,
+    "TSPC_HFP_8_7": true,
+    "TSPC_PBAP_1_1": true,
+    "TSPC_PBAP_2_1": true,
+    "TSPC_PBAP_2_12": true,
+    "TSPC_PBAP_2_12a": true,
+    "TSPC_PBAP_2_13a": true,
+    "TSPC_PBAP_2_13b": true,
+    "TSPC_PBAP_2_13c": true,
+    "TSPC_PBAP_2_13d": true,
+    "TSPC_PBAP_2_13g": true,
+    "TSPC_PBAP_2_13h": true,
+    "TSPC_PBAP_2_3": true,
+    "TSPC_PBAP_2_4": true,
+    "TSPC_PBAP_25_1": true,
+    "TSPC_PBAP_25_2": true,
+    "TSPC_PBAP_25_3": true,
+    "TSPC_PBAP_25_4": true,
+    "TSPC_PBAP_25_6": true,
+    "TSPC_PBAP_2a_3": true,
+    "TSPC_PBAP_2b_2": true,
+    "TSPC_PBAP_3_1": true,
+    "TSPC_PBAP_5_1": true,
+    "TSPC_PBAP_5_2": true,
+    "TSPC_PBAP_6_1": true,
+    "TSPC_PBAP_6_2": true,
+    "TSPC_PBAP_6_3": true,
+    "TSPC_PBAP_6_4": true,
+    "TSPC_PBAP_6_7": true,
+    "TSPC_PBAP_7_1": true,
+    "TSPC_PBAP_7_10": true,
+    "TSPC_PBAP_7_11": true,
+    "TSPC_PBAP_7_12": true,
+    "TSPC_PBAP_7_13": true,
+    "TSPC_PBAP_7_2": true,
+    "TSPC_PBAP_7_3": true,
+    "TSPC_PBAP_7_4": true,
+    "TSPC_PBAP_7_5": true,
+    "TSPC_PBAP_7_6": true,
+    "TSPC_PBAP_7_7": true,
+    "TSPC_PBAP_7_8": true,
+    "TSPC_PBAP_7_9": true,
+    "TSPC_PBAP_8_1": true,
+    "TSPC_PBAP_8_2": true,
+    "TSPC_PBAP_8_3": true,
+    "TSPC_PBAP_8_4": true,
+    "TSPC_PBAP_8_5": true,
+    "TSPC_PBAP_8_6": true,
+    "TSPC_PBAP_8_7": true,
+    "TSPC_PBAP_8_8": true,
+    "TSPC_PBAP_24_1": true,
+    "TSPC_PBAP_24_3": true,
+    "TSPC_PROD_1_6": true,
+    "TSPC_MAP_0_6": true,
+    "TSPC_MAP_0a_5": true,
+    "TSPC_MAP_1_2": true,
+    "TSPC_MAP_7b_1": true,
+    "TSPC_MAP_7b_2": true,
+    "TSPC_MAP_7b_3": true,
+    "TSPC_MAP_2_1": true,
+    "TSPC_MAP_2_10a": true,
+    "TSPC_MAP_2_10b": true,
+    "TSPC_MAP_2_1a": true,
+    "TSPC_MAP_2_2": true,
+    "TSPC_MAP_2_2a": true,
+    "TSPC_MAP_2_2b": true,
+    "TSPC_MAP_2_2c": true,
+    "TSPC_MAP_2_2d": true,
+    "TSPC_MAP_2_2g": true,
+    "TSPC_MAP_2_3": true,
+    "TSPC_MAP_2_3a": true,
+    "TSPC_MAP_2_3b": true,
+    "TSPC_MAP_2_3c": true,
+    "TSPC_MAP_2_5": true,
+    "TSPC_MAP_2_5a": true,
+    "TSPC_MAP_2_5b": true,
+    "TSPC_MAP_2_6b": true,
+    "TSPC_MAP_2_6c": true,
+    "TSPC_MAP_2_6d": true,
+    "TSPC_MAP_2_8": true,
+    "TSPC_MAP_2_8a": true,
+    "TSPC_MAP_2_8b": true,
+    "TSPC_MAP_2_9a": true,
+    "TSPC_MAP_10_1": true,
+    "TSPC_MAP_10_10": true,
+    "TSPC_MAP_10_2": true,
+    "TSPC_MAP_10_3": true,
+    "TSPC_MAP_10_4": true,
+    "TSPC_MAP_10_5": true,
+    "TSPC_MAP_10_6": true,
+    "TSPC_MAP_10_7": true,
+    "TSPC_MAP_10_8": true,
+    "TSPC_MAP_10_9": true,
+    "TSPC_MAP_11_1": true,
+    "TSPC_MAP_11_2": true,
+    "TSPC_MAP_11_3": true,
+    "TSPC_MAP_11_4": true,
+    "TSPC_MAP_11_5": true,
+    "TSPC_MAP_11_6": true,
+    "TSPC_MAP_11_7": true,
+    "TSPC_MAP_11_8": true,
+    "TSPC_MAP_12_1": true,
+    "TSPC_MAP_12_2": true,
+    "TSPC_MAP_12_4": true,
+    "TSPC_MAP_8_1": true,
+    "TSPC_MAP_8_2": true,
+    "TSPC_MAP_8_3": true,
+    "TSPC_MAP_8_4": true,
+    "TSPC_MAP_8_5": true,
+    "TSPC_MAP_8_6": true,
+    "TSPC_MAP_9_1": true,
+    "TSPC_MAP_9_2": true,
+    "TSPC_MAP_9_3": true,
+    "TSPC_MAP_9_4": true
+  },
+  "ixit": {
+    "default": {},
+    "4.0HCI": {},
+    "A2DP": {},
+    "ATT": {},
+    "AVCTP": {},
+    "AVDTP": {},
+    "AVRCP": {
+      "TSPX_player_feature_bitmask": "0000000000B7010C0A00000000000000"
+    },
+    "BNEP": {},
+    "DID": {},
+    "GAP": {},
+    "GATT": {},
+    "GAVDP": {},
+    "HCI": {},
+    "HFP": {
+      "TSPX_phone_number": "42",
+      "TSPX_second_phone_number": "43"
+    },
+    "HID": {},
+    "HOGP": {},
+    "HSP": {},
+    "IOP": {},
+    "L2CAP": {
+      "TSPX_spsm": "0080",
+      "TSPX_psm_authentication_required": "0080",
+      "TSPX_psm_authorization_required": "0080"
+    },
+    "MAP": {},
+    "MCAP": {},
+    "OPP": {},
+    "PAN": {},
+    "PBAP": {},
+    "RFCOMM": {
+      "TSPX_server_channel_iut": "7",
+      "TSPX_security_enabled": "FALSE"
+    },
+    "SAP": {},
+    "SCPP": {},
+    "SDP": {},
+    "SM": {},
+    "SPP": {}
+  }
+}
diff --git a/android/pandora/server/src/Host.kt b/android/pandora/server/src/Host.kt
index 379b58a..47f3276 100644
--- a/android/pandora/server/src/Host.kt
+++ b/android/pandora/server/src/Host.kt
@@ -31,8 +31,10 @@
 import android.bluetooth.le.AdvertiseSettings
 import android.bluetooth.le.AdvertisingSetParameters
 import android.bluetooth.le.ScanCallback
+import android.bluetooth.le.ScanFilter
 import android.bluetooth.le.ScanRecord
 import android.bluetooth.le.ScanResult
+import android.bluetooth.le.ScanSettings
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
@@ -468,7 +470,9 @@
                         }
                     }
                 val bluetoothLeScanner = bluetoothAdapter.bluetoothLeScanner
-                bluetoothLeScanner?.startScan(leScanCallback) ?: run { trySendBlocking(null) }
+                val leScanFilters: List<ScanFilter> = listOf()
+                val leScanSettings = ScanSettings.Builder().setLegacy(false).build()
+                bluetoothLeScanner?.startScan(leScanFilters, leScanSettings, leScanCallback) ?: run { trySendBlocking(null) }
                 awaitClose { bluetoothLeScanner?.stopScan(leScanCallback) }
             }
             bluetoothDevice = flow.first()
diff --git a/android/pandora/test/asha_test.py b/android/pandora/test/asha_test.py
index 83b8d45..f9ec7f7 100644
--- a/android/pandora/test/asha_test.py
+++ b/android/pandora/test/asha_test.py
@@ -86,6 +86,21 @@
 
     @avatar.asynchronous
     async def setup_test(self) -> None:
+        # TODO(b/296927588): Test should pass with a random identity address
+        # We have to update the config before the reset otherwise it'll be overridden
+        random_identity_address_type_list = [
+            "test_auto_connection(1,1)",
+            "test_advertising_advertisement_data(1,1)",
+            "test_advertising_scan_response",
+            "test_auto_connection_dual_device(1,1,0)",
+            "test_auto_connection_dual_device(1,1,1)",
+        ]
+        if self.current_test_info.name in random_identity_address_type_list:
+            self.ref_left._bumble.config.update({'server': {'identity_address_type': 'random'}})
+            self.ref_right._bumble.config.update({'server': {'identity_address_type': 'random'}})
+        else:
+            self.ref_left._bumble.config.update({'server': {'identity_address_type': 'public'}})
+            self.ref_right._bumble.config.update({'server': {'identity_address_type': 'public'}})
         await asyncio.gather(self.dut.reset(), self.ref_left.reset(), self.ref_right.reset())
 
         # ASHA hearing aid's IO capability is NO_OUTPUT_NO_INPUT
@@ -683,6 +698,9 @@
            2. The disconnected peripheral starts sending ASHA advertisements.
            3. Verify that DUT auto-connects to the peripheral.
         """
+        # This tests need to be reactivated ASAP
+        if ref_address_type == PUBLIC:
+            raise signals.TestSkip('TODO: b/296927588')
 
         advertisement_left = await self.ref_advertise_asha(
             ref_device=self.ref_left, ref_address_type=ref_address_type, ear=Ear.LEFT
@@ -865,9 +883,6 @@
         Verify that DUT sends a correct AudioControlPoint `Stop` command.
         """
 
-        # TODO(b/290204194) Re-activate this test ASAP
-        raise signals.TestSkip('TODO(b/290204194) Re-activate this test ASAP')
-
         async def ref_device_connect(ref_device: BumblePandoraDevice, ear: Ear) -> Tuple[Connection, Connection]:
             advertisement = await self.ref_advertise_asha(ref_device=ref_device, ref_address_type=RANDOM, ear=ear)
             ref = await self.dut_scan_for_asha(dut_address_type=RANDOM, ear=ear)
@@ -900,6 +915,9 @@
         logging.info(f"stop_result:{stop_result}")
         assert_is_not_none(stop_result)
 
+        # Sleep 0.5 second to mitigate flaky test first.
+        await asyncio.sleep(0.5)
+
         audio_data = await self.get_audio_data(
             ref_asha=AioAsha(self.ref_left.aio.channel), connection=ref_dut, timeout=10
         )
@@ -1064,9 +1082,6 @@
         Verify Refs cannot recevice audio data after DUT stops media streaming.
         """
 
-        # TODO(b/290204194) Re-activate this test ASAP
-        raise signals.TestSkip('TODO(b/290204194) Re-activate this test ASAP')
-
         async def ref_device_connect(ref_device: BumblePandoraDevice, ear: Ear) -> Tuple[Connection, Connection]:
             advertisement = await self.ref_advertise_asha(ref_device=ref_device, ref_address_type=RANDOM, ear=ear)
             ref = await self.dut_scan_for_asha(dut_address_type=RANDOM, ear=ear)
@@ -1119,6 +1134,9 @@
         assert_is_not_none(stop_result_left)
         assert_is_not_none(stop_result_right)
 
+        # Sleep 0.5 second to mitigate flaky test first.
+        await asyncio.sleep(0.5)
+
         (audio_data_left, audio_data_right) = await asyncio.gather(
             self.get_audio_data(ref_asha=ref_left_asha, connection=ref_left_dut, timeout=10),
             self.get_audio_data(ref_asha=ref_right_asha, connection=ref_right_dut, timeout=10),
diff --git a/android/pandora/test/hfpclient_test.py b/android/pandora/test/hfpclient_test.py
index d655590..f7bb5de 100644
--- a/android/pandora/test/hfpclient_test.py
+++ b/android/pandora/test/hfpclient_test.py
@@ -18,13 +18,14 @@
 
 from avatar import BumblePandoraDevice, PandoraDevice, PandoraDevices
 from avatar.pandora_server import AndroidPandoraServer
+from bumble.colors import color
 from bumble.core import (
     BT_GENERIC_AUDIO_SERVICE,
     BT_HANDSFREE_AUDIO_GATEWAY_SERVICE,
     BT_L2CAP_PROTOCOL_ID,
     BT_RFCOMM_PROTOCOL_ID,
 )
-from bumble.hfp import HfpProtocol
+from bumble import rfcomm
 from bumble.rfcomm import DLC, Server as RfcommServer
 from bumble.sdp import (
     SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
@@ -34,6 +35,7 @@
     DataElement,
     ServiceAttribute,
 )
+import collections
 from mobly import base_test, test_runner
 from mobly.asserts import assert_equal  # type: ignore
 from mobly.asserts import assert_in  # type: ignore
@@ -41,7 +43,7 @@
 from mobly.asserts import assert_not_in  # type: ignore
 from pandora.host_pb2 import Connection as PandoraConnection
 from pandora.security_pb2 import LEVEL2
-from typing import Dict, List, Optional, Tuple
+from typing import Dict, List, Optional, Tuple, Union
 
 SDP_PROFILE_SUPPORTED_FEATURES_ID = 0x0311
 
@@ -58,6 +60,58 @@
 HFP_VERSION_1_7 = 0x0107
 
 
+# Stub for Audio Gateway implementation
+# TODO: b/296471045
+logger = logging.getLogger(__name__)
+class HfpProtocol:
+    dlc: rfcomm.DLC
+    buffer: str
+    lines: collections.deque
+    lines_available: asyncio.Event
+
+    def __init__(self, dlc: rfcomm.DLC) -> None:
+        self.dlc = dlc
+        self.buffer = ''
+        self.lines = collections.deque()
+        self.lines_available = asyncio.Event()
+
+        dlc.sink = self.feed
+
+    def feed(self, data: Union[bytes, str]) -> None:
+        # Convert the data to a string if needed
+        if isinstance(data, bytes):
+            data = data.decode('utf-8')
+
+        logger.debug(f'<<< Data received: {data}')
+
+        # Add to the buffer and look for lines
+        self.buffer += data
+        while (separator := self.buffer.find('\r')) >= 0:
+            line = self.buffer[:separator].strip()
+            self.buffer = self.buffer[separator + 1:]
+            if len(line) > 0:
+                self.on_line(line)
+
+    def on_line(self, line: str) -> None:
+        self.lines.append(line)
+        self.lines_available.set()
+
+    def send_command_line(self, line: str) -> None:
+        logger.debug(color(f'>>> {line}', 'yellow'))
+        self.dlc.write(line + '\r')
+
+    def send_response_line(self, line: str) -> None:
+        logger.debug(color(f'>>> {line}', 'yellow'))
+        self.dlc.write('\r\n' + line + '\r\n')
+
+    async def next_line(self) -> str:
+        await self.lines_available.wait()
+        line = self.lines.popleft()
+        if not self.lines:
+            self.lines_available.clear()
+        logger.debug(color(f'<<< {line}', 'green'))
+        return line
+
 class HfpClientTest(base_test.BaseTestClass):  # type: ignore[misc]
     devices: Optional[PandoraDevices] = None
 
@@ -98,6 +152,7 @@
 
     @avatar.asynchronous
     async def setup_test(self) -> None:
+        self.ref._bumble.config.update({'server': {'identity_address_type': 'public'}})
         await asyncio.gather(self.dut.reset(), self.ref.reset())
 
     # TODO(b/286338264): Moving connecting and bonding methods to a shared util scripts
@@ -306,6 +361,7 @@
         self.send_response_line('OK')
 
 
+
 if __name__ == '__main__':
     logging.basicConfig(level=logging.DEBUG)
     test_runner.main()  # type: ignore
diff --git a/android/pandora/test/smp_test.py b/android/pandora/test/smp_test.py
index ad32123..8e5706a 100644
--- a/android/pandora/test/smp_test.py
+++ b/android/pandora/test/smp_test.py
@@ -19,6 +19,7 @@
 from avatar.aio import asynchronous
 from bumble import smp
 from bumble.hci import Address
+from bumble.pairing import PairingDelegate
 from concurrent import futures
 from contextlib import suppress
 from mobly import base_test, signals, test_runner
@@ -56,15 +57,35 @@
 
     async def handle_pairing_events(self) -> NoReturn:
         dut_pairing_stream = self.dut.aio.security.OnPairing()
+        ref_pairing_stream = self.ref.aio.security.OnPairing()
         try:
             while True:
                 dut_pairing_event = await (anext(dut_pairing_stream))
-                dut_pairing_stream.send_nowait(
-                    PairingEventAnswer(
+
+                if dut_pairing_event.method_variant() == 'passkey_entry_notification':
+                    ref_pairing_event = await (anext(ref_pairing_stream))
+
+                    assert_equal(ref_pairing_event.method_variant(), 'passkey_entry_request')
+                    assert_is_not_none(dut_pairing_event.passkey_entry_notification)
+                    assert dut_pairing_event.passkey_entry_notification is not None
+
+                    ref_ev_answer = PairingEventAnswer(
+                        event=ref_pairing_event,
+                        passkey=dut_pairing_event.passkey_entry_notification,
+                    )
+                    ref_pairing_stream.send_nowait(ref_ev_answer)
+                else:
+                    dut_pairing_stream.send_nowait(PairingEventAnswer(
                         event=dut_pairing_event,
                         confirm=True,
-                    )
-                )
+                    ))
+                    ref_pairing_event = await (anext(ref_pairing_stream))
+
+                    ref_pairing_stream.send_nowait(PairingEventAnswer(
+                        event=ref_pairing_event,
+                        confirm=True,
+                    ))
+
         finally:
             dut_pairing_stream.cancel()
 
@@ -148,6 +169,94 @@
         is_bonded = await self.dut.aio.security_storage.IsBonded(random=ref1.random)
         assert_false(is_bonded.value, "")
 
+    @asynchronous
+    async def test_mitm_sec_req_on_enc(self) -> None:
+        if isinstance(self.ref, BumblePandoraDevice):
+            io_capability = PairingDelegate.IoCapability.DISPLAY_OUTPUT_AND_KEYBOARD_INPUT
+            self.ref.server_config.io_capability = io_capability
+
+        advertisement = self.ref.aio.host.Advertise(
+            legacy=True,
+            connectable=True,
+            own_address_type=RANDOM,
+            data=DataTypes(manufacturer_specific_data=b'pause cafe'),
+        )
+
+        scan = self.dut.aio.host.Scan(own_address_type=RANDOM)
+        ref = await anext((x async for x in scan if b'pause cafe' in x.data.manufacturer_specific_data))
+        scan.cancel()
+
+        asyncio.create_task(self.handle_pairing_events())
+        (dut_ref_res, ref_dut_res) = await asyncio.gather(
+            self.dut.aio.host.ConnectLE(own_address_type=RANDOM, **ref.address_asdict()),
+            anext(aiter(advertisement)),
+        )
+
+        advertisement.cancel()
+        ref_dut, dut_ref = ref_dut_res.connection, dut_ref_res.connection
+        assert_is_not_none(dut_ref)
+        assert dut_ref
+
+        # Pair with MITM requirements
+        (secure, wait_security) = await asyncio.gather(
+            self.dut.aio.security.Secure(connection=dut_ref, le=LE_LEVEL3),
+            self.ref.aio.security.WaitSecurity(connection=ref_dut, le=LE_LEVEL3),
+        )
+
+        assert_equal(secure.result_variant(), 'success')
+        assert_equal(wait_security.result_variant(), 'success')
+
+        # Disconnect
+        await asyncio.gather(
+            self.ref.aio.host.Disconnect(connection=ref_dut),
+            self.dut.aio.host.WaitDisconnection(connection=dut_ref),
+        )
+
+        advertisement = self.ref.aio.host.Advertise(
+            legacy=True,
+            connectable=True,
+            own_address_type=RANDOM,
+            data=DataTypes(manufacturer_specific_data=b'pause cafe'),
+        )
+
+        scan = self.dut.aio.host.Scan(own_address_type=RANDOM)
+        ref = await anext((x async for x in scan if b'pause cafe' in x.data.manufacturer_specific_data))
+        scan.cancel()
+
+        (dut_ref_res, ref_dut_res) = await asyncio.gather(
+            self.dut.aio.host.ConnectLE(own_address_type=RANDOM, **ref.address_asdict()),
+            anext(aiter(advertisement)),
+        )
+        ref_dut, dut_ref = ref_dut_res.connection, dut_ref_res.connection
+
+        # Wait for the link to get encrypted
+        connection = self.ref.device.lookup_connection(int.from_bytes(ref_dut.cookie.value, 'big'))
+
+        def on_connection_encryption_change():
+            self.ref.device.smp_manager.request_pairing(connection)
+
+        connection.on('connection_encryption_change', on_connection_encryption_change)
+
+        # Fail if repairing is initiated
+        fut = asyncio.get_running_loop().create_future()
+
+        class Session(smp.Session):
+
+            def on_smp_pairing_request_command(self, command: smp.SMP_Pairing_Request_Command) -> None:
+                nonlocal fut
+                fut.set_result(False)
+
+        self.ref.device.smp_session_proxy = Session
+
+        # Pass if the link is encrypted again
+        def on_connection_encryption_key_refresh():
+            nonlocal fut
+            fut.set_result(True)
+
+        connection.on('connection_encryption_key_refresh', on_connection_encryption_key_refresh)
+
+        assert_true(await fut, "Repairing initiated")
+
 
 if __name__ == '__main__':
     logging.basicConfig(level=logging.DEBUG)
diff --git a/apex/OWNERS b/apex/OWNERS
index 9b1bfef..f05027c 100644
--- a/apex/OWNERS
+++ b/apex/OWNERS
@@ -1,6 +1,5 @@
 # Reviewers for /apex
 
 eruffieux@google.com
-rahulsabnis@google.com
 wescande@google.com
 
diff --git a/floss/build/Dockerfile b/floss/build/Dockerfile
index 4d803b8..b109a43 100644
--- a/floss/build/Dockerfile
+++ b/floss/build/Dockerfile
@@ -35,6 +35,7 @@
     libglib2.0-dev \
     libgtest-dev \
     libgmock-dev \
+    liblc3-dev \
     liblz4-tool \
     libncurses5 \
     libnss3-dev \
diff --git a/framework/OWNERS b/framework/OWNERS
index 95e6a1a..301f9e8 100644
--- a/framework/OWNERS
+++ b/framework/OWNERS
@@ -1,7 +1,4 @@
-# Bug component: 27441
-
 eruffieux@google.com
-rahulsabnis@google.com
 sattiraju@google.com
 siyuanh@google.com
 wescande@google.com
diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java
index 4d49059..90922a8 100644
--- a/framework/java/android/bluetooth/BluetoothGatt.java
+++ b/framework/java/android/bluetooth/BluetoothGatt.java
@@ -94,6 +94,8 @@
 
     private static final int WRITE_CHARACTERISTIC_MAX_RETRIES = 5;
     private static final int WRITE_CHARACTERISTIC_TIME_TO_WAIT = 10; // milliseconds
+    // Max length of an attribute value, defined in gatt_api.h
+    private static final int GATT_MAX_ATTR_LEN = 512;
 
     private List<BluetoothGattService> mServices;
 
@@ -1438,6 +1440,10 @@
         if (value == null) {
             throw new IllegalArgumentException("value must not be null");
         }
+        if (value.length > GATT_MAX_ATTR_LEN) {
+            throw new IllegalArgumentException(
+                    "notification should not be longer than max length of an attribute value");
+        }
         if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
         if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
                 && (characteristic.getProperties()
diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java
index 0391065..386165d 100644
--- a/framework/java/android/bluetooth/BluetoothGattServer.java
+++ b/framework/java/android/bluetooth/BluetoothGattServer.java
@@ -68,6 +68,8 @@
     private List<BluetoothGattService> mServices;
 
     private static final int CALLBACK_REG_TIMEOUT = 10000;
+    // Max length of an attribute value, defined in gatt_api.h
+    private static final int GATT_MAX_ATTR_LEN = 512;
 
     /**
      * Bluetooth GATT interface callbacks
@@ -829,6 +831,10 @@
         if (device == null) {
             throw new IllegalArgumentException("device must not be null");
         }
+        if (value.length > GATT_MAX_ATTR_LEN) {
+            throw new IllegalArgumentException(
+                    "notification should not be longer than max length of an attribute value");
+        }
         BluetoothGattService service = characteristic.getService();
         if (service == null) {
             throw new IllegalArgumentException("Characteristic must have a non-null service");
diff --git a/framework/java/android/bluetooth/BluetoothQualityReport.java b/framework/java/android/bluetooth/BluetoothQualityReport.java
index b74bb3b..258dd94 100644
--- a/framework/java/android/bluetooth/BluetoothQualityReport.java
+++ b/framework/java/android/bluetooth/BluetoothQualityReport.java
@@ -58,7 +58,7 @@
  */
 @SystemApi
 public final class BluetoothQualityReport implements Parcelable {
-    private static final String TAG = "BluetoothQualityReport";
+    private static final String TAG = BluetoothQualityReport.class.getSimpleName();
 
     /**
      * Quality report ID: Monitor.
@@ -525,12 +525,12 @@
      */
     @SystemApi
     public static final class Builder {
-        private String remoteAddr;
+        private String remoteAddr = "00:00:00:00:00:00";
         private int lmpVer;
         private int lmpSubVer;
         private int manufacturerId;
-        private String remoteName;
-        private BluetoothClass bluetoothClass;
+        private String remoteName = "";
+        private BluetoothClass bluetoothClass = new BluetoothClass(0);
         private byte[] rawData;
 
         /**
@@ -555,7 +555,11 @@
         @NonNull
         @SystemApi
         public Builder setRemoteAddress(@Nullable String remoteAddr) {
-            this.remoteAddr = remoteAddr;
+            if (!BluetoothAdapter.checkBluetoothAddress(remoteAddr)) {
+                Log.d(TAG, "remote address is not a valid bluetooth address: " + remoteAddr);
+            } else {
+                this.remoteAddr = remoteAddr;
+            }
             return this;
         }
 
@@ -611,7 +615,11 @@
         @NonNull
         @SystemApi
         public Builder setRemoteName(@Nullable String remoteName) {
-            this.remoteName = remoteName;
+            if (remoteName == null) {
+                Log.d(TAG, "remote name is null");
+            } else {
+                this.remoteName = remoteName;
+            }
             return this;
         }
 
@@ -625,7 +633,11 @@
         @NonNull
         @SystemApi
         public Builder setBluetoothClass(@Nullable BluetoothClass bluetoothClass) {
-            this.bluetoothClass = bluetoothClass;
+            if (bluetoothClass == null) {
+                Log.d(TAG, "remote bluetooth class is null");
+            } else {
+                this.bluetoothClass = bluetoothClass;
+            }
             return this;
         }
 
@@ -639,7 +651,6 @@
         @NonNull
         @SystemApi
         public BluetoothQualityReport build() {
-            validateBluetoothQualityReport();
             return new BluetoothQualityReport(
                     remoteAddr,
                     lmpVer,
@@ -649,18 +660,6 @@
                     bluetoothClass,
                     rawData);
         }
-
-        private void validateBluetoothQualityReport() {
-            if (!BluetoothAdapter.checkBluetoothAddress(remoteAddr)) {
-                Log.d(TAG, "remote addr is invalid");
-                remoteAddr = "00:00:00:00:00:00";
-            }
-
-            if (remoteName == null) {
-                Log.d(TAG, "remote name is null");
-                remoteName = "";
-            }
-        }
     }
 
     /**
diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java
index 6d66af8..7c3d60a 100644
--- a/framework/java/android/bluetooth/le/ScanFilter.java
+++ b/framework/java/android/bluetooth/le/ScanFilter.java
@@ -771,31 +771,33 @@
         /**
          * Set a scan filter on the remote device address with an address type and the Identity
          * Resolving Key (IRK).
-         * <p>
-         * The address passed to this API must be either a public or random static address in big
+         *
+         * <p>The address passed to this API must be either a public or random static address in big
          * endian byte order. It needs to be in the format of "01:02:03:AB:CD:EF". The device
          * address can be validated using {@link BluetoothAdapter#checkBluetoothAddress}.
-         * <p>
-         * The IRK is used to resolve a static address from a private address. The IRK must be
+         *
+         * <p>The IRK is used to resolve a static address from a private address. The IRK must be
          * provided in little endian byte order.
          *
+         * <p>Note if the device with this IRK is already bonded, calling {@link
+         * BluetoothDevice#getAddress} on the {@link ScanResult} using this filter will return the
+         * device address that was used to initiate bonding, and may not match the address passed
+         * into this method in that scenario.
+         *
          * @param deviceAddress the remote device Bluetooth address for the filter
          * @param addressType indication of the type of address
          * @param irk non-null little endian byte array representing the Identity Resolving Key
-         *
          * @throws IllegalArgumentException If the {@code deviceAddress} is invalid
          * @throws IllegalArgumentException if the {@code irk} is invalid length
          * @throws IllegalArgumentException If the {@code addressType} is an invalid length or is
-         * not PUBLIC or RANDOM STATIC
+         *     not PUBLIC or RANDOM STATIC
          * @throws NullPointerException if {@code deviceAddress} or {@code irk} is null
-         *
          * @hide
          */
         @NonNull
         @SystemApi
-        public Builder setDeviceAddress(@NonNull String deviceAddress,
-                                        @AddressType int addressType,
-                                        @NonNull byte[] irk) {
+        public Builder setDeviceAddress(
+                @NonNull String deviceAddress, @AddressType int addressType, @NonNull byte[] irk) {
             requireNonNull(irk);
             if (irk.length != LEN_IRK_OCTETS) {
                 throw new IllegalArgumentException("'irk' is invalid length!");
diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java
index f437d86..06b1856 100644
--- a/framework/java/android/bluetooth/le/ScanResult.java
+++ b/framework/java/android/bluetooth/le/ScanResult.java
@@ -204,7 +204,10 @@
     }
 
     /**
-     * Returns the remote Bluetooth device identified by the Bluetooth device address.
+     * Returns the remote Bluetooth device identified by the Bluetooth device address. If the device
+     * is bonded, calling {@link BluetoothDevice#getAddress} on the object returned by this method
+     * will return the address that was originally bonded with (either identity address or random
+     * address).
      */
     public BluetoothDevice getDevice() {
         return mDevice;
diff --git a/framework/tests/bumble/Android.bp b/framework/tests/bumble/Android.bp
index 3d7fc14..d614f1a 100644
--- a/framework/tests/bumble/Android.bp
+++ b/framework/tests/bumble/Android.bp
@@ -50,6 +50,7 @@
 
     data: [
         ":BumbleBluetoothTestsApp",
+        "bumble_config.json",
     ],
     data_native_bins: [
         "bumble_pandora_server",
diff --git a/framework/tests/bumble/AndroidTest.xml b/framework/tests/bumble/AndroidTest.xml
index cb40366..50123f8 100644
--- a/framework/tests/bumble/AndroidTest.xml
+++ b/framework/tests/bumble/AndroidTest.xml
@@ -16,7 +16,7 @@
       <option name="python-virtualenv" value="true"/>
       <option name="host-setup-command" value="adb -s $SERIAL reverse tcp:7999 tcp:7999" />
       <option name="host-background-command"
-        value="$EXTRA_FILE(host_testcases)/BumbleBluetoothTests/bumble_pandora_server"/>
+        value="$EXTRA_FILE(host_testcases)/BumbleBluetoothTests/bumble_pandora_server --config $EXTRA_FILE(host_testcases)/BumbleBluetoothTests/bumble_config.json"/>
       <option name="host-teardown-command" value="adb -s $SERIAL reverse --remove tcp:7999" />
     </target_preparer>
 
diff --git a/framework/tests/bumble/OWNERS b/framework/tests/bumble/OWNERS
index 2992b8c..be1d194 100644
--- a/framework/tests/bumble/OWNERS
+++ b/framework/tests/bumble/OWNERS
@@ -1,5 +1,5 @@
 # Bug component: 1099313
 # Project owners
 girardier@google.com
-qiaoccolato@google.com
 licorne@google.com
+charliebout@google.com
diff --git a/framework/tests/bumble/bumble_config.json b/framework/tests/bumble/bumble_config.json
new file mode 100644
index 0000000..9e26dfe
--- /dev/null
+++ b/framework/tests/bumble/bumble_config.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/framework/tests/bumble/src/android/bluetooth/LeAdvertisingTest.java b/framework/tests/bumble/src/android/bluetooth/LeAdvertisingTest.java
index be416b7..ea904c3 100644
--- a/framework/tests/bumble/src/android/bluetooth/LeAdvertisingTest.java
+++ b/framework/tests/bumble/src/android/bluetooth/LeAdvertisingTest.java
@@ -1,8 +1,7 @@
 package android.bluetooth;
 
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.Utils;
+import static android.bluetooth.Utils.factoryResetAndCreateNewChannel;
+
 import android.bluetooth.le.AdvertiseData;
 import android.bluetooth.le.AdvertisingSet;
 import android.bluetooth.le.AdvertisingSetCallback;
@@ -12,7 +11,6 @@
 
 import androidx.core.util.Pair;
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
@@ -22,7 +20,6 @@
 import io.grpc.Context.CancellableContext;
 import io.grpc.Deadline;
 import io.grpc.ManagedChannel;
-import io.grpc.okhttp.OkHttpChannelBuilder;
 import io.grpc.stub.StreamObserver;
 
 import java.util.concurrent.CompletableFuture;
@@ -45,7 +42,7 @@
 @RunWith(AndroidJUnit4.class)
 public class LeAdvertisingTest {
 
-    private static final String LOG_TAG = "LeAdvertisingTest";
+    private static final String TAG = "LeAdvertisingTest";
 
     private static final int TIMEOUT_ADVERTISING_MS = 1000;
 
@@ -63,25 +60,8 @@
 
     @Before
     public void setUp() throws Exception {
-        // FactorReset is killing the server and restart
-        // all channel created before the server restarted
-        // cannot be reused
-        ManagedChannel channel = OkHttpChannelBuilder
-              .forAddress("localhost", 7999)
-              .usePlaintext()
-              .build();
-
-        HostGrpc.HostBlockingStub stub = HostGrpc.newBlockingStub(channel);
-        stub.factoryReset(Empty.getDefaultInstance());
-
-        // terminate the channel
-        channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
-
-        // Create a new channel for all successive grpc calls
-        mChannel = OkHttpChannelBuilder
-              .forAddress("localhost", 7999)
-              .usePlaintext()
-              .build();
+        // Cleanup previous channels and create a new channel for all successive grpc calls
+        mChannel = factoryResetAndCreateNewChannel();
 
         mHostBlockingStub = HostGrpc.newBlockingStub(mChannel);
         mHostStub = HostGrpc.newStub(mChannel);
@@ -100,7 +80,7 @@
                                       .thenCompose(advAddressPair -> scanWithBumble(advAddressPair))
                                       .join();
 
-        Log.i(LOG_TAG, "scan response: " + response);
+        Log.i(TAG, "scan response: " + response);
         assertThat(response).isNotNull();
     }
 
@@ -118,29 +98,47 @@
              setOwnAddressType(AdvertisingSetParameters.ADDRESS_TYPE_RANDOM).build();
         AdvertiseData advertiseData = new AdvertiseData.Builder().build();
         AdvertiseData scanResponse = new AdvertiseData.Builder().build();
-        AdvertisingSetCallback advertisingSetCallback = new AdvertisingSetCallback() {
-            @Override
-            public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower,
-                    int status) {
-                Log.i(LOG_TAG, "onAdvertisingSetStarted " + " txPower:" + txPower
-                    + " status:" + status);
-                advertisingSet.enableAdvertising(true, TIMEOUT_ADVERTISING_MS, 0);
-            }
-            @Override
-            public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType,
-                    String address) {
-                Log.i(LOG_TAG, "onOwnAddressRead " + " addressType:" + addressType
-                    + " address:" + address);
-                future.complete(new Pair<String, Integer>(address, addressType));
-            }
-            @Override
-            public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enabled,
-                    int status) {
-                Log.i(LOG_TAG, "onAdvertisingEnabled " + " enabled:" + enabled
-                        + " status:" + status);
-                advertisingSet.getOwnAddress();
-            }
-        };
+        AdvertisingSetCallback advertisingSetCallback =
+                new AdvertisingSetCallback() {
+                    @Override
+                    public void onAdvertisingSetStarted(
+                            AdvertisingSet advertisingSet, int txPower, int status) {
+                        Log.i(
+                                TAG,
+                                "onAdvertisingSetStarted "
+                                        + " txPower:"
+                                        + txPower
+                                        + " status:"
+                                        + status);
+                        advertisingSet.enableAdvertising(true, TIMEOUT_ADVERTISING_MS, 0);
+                    }
+
+                    @Override
+                    public void onOwnAddressRead(
+                            AdvertisingSet advertisingSet, int addressType, String address) {
+                        Log.i(
+                                TAG,
+                                "onOwnAddressRead "
+                                        + " addressType:"
+                                        + addressType
+                                        + " address:"
+                                        + address);
+                        future.complete(new Pair<String, Integer>(address, addressType));
+                    }
+
+                    @Override
+                    public void onAdvertisingEnabled(
+                            AdvertisingSet advertisingSet, boolean enabled, int status) {
+                        Log.i(
+                                TAG,
+                                "onAdvertisingEnabled "
+                                        + " enabled:"
+                                        + enabled
+                                        + " status:"
+                                        + status);
+                        advertisingSet.getOwnAddress();
+                    }
+                };
         leAdvertiser.startAdvertisingSet(parameters, advertiseData, scanResponse,
           null, null, 0, 0, advertisingSetCallback);
 
@@ -156,34 +154,34 @@
         int addressType = addressPair.second;
 
         ScanRequest request = ScanRequest.newBuilder().build();
-        StreamObserver<ScanningResponse> responseObserver = new StreamObserver<ScanningResponse>(){
-            public void onNext(ScanningResponse response) {
-                String addr = "";
-                if (addressType == AdvertisingSetParameters.ADDRESS_TYPE_PUBLIC) {
-                    addr = Utils.addressStringFromByteString(response.getPublic());
-                }
-                else {
-                    addr = Utils.addressStringFromByteString(response.getRandom());
-                }
-                Log.i(LOG_TAG,"scan observer: scan response address: " + addr);
+        StreamObserver<ScanningResponse> responseObserver =
+                new StreamObserver<ScanningResponse>() {
+                    public void onNext(ScanningResponse response) {
+                        String addr = "";
+                        if (addressType == AdvertisingSetParameters.ADDRESS_TYPE_PUBLIC) {
+                            addr = Utils.addressStringFromByteString(response.getPublic());
+                        } else {
+                            addr = Utils.addressStringFromByteString(response.getRandom());
+                        }
+                        Log.i(TAG, "scan observer: scan response address: " + addr);
 
-                if (addr.equals(address)) {
-                    future.complete(response);
-                }
-            }
+                        if (addr.equals(address)) {
+                            future.complete(response);
+                        }
+                    }
 
-            @Override
-            public void onError(Throwable e) {
-                Log.e(LOG_TAG,"scan observer: on error " + e);
-                future.completeExceptionally(e);
-            }
+                    @Override
+                    public void onError(Throwable e) {
+                        Log.e(TAG, "scan observer: on error " + e);
+                        future.completeExceptionally(e);
+                    }
 
-            @Override
-            public void onCompleted() {
-                Log.i(LOG_TAG,"scan observer: on completed");
-                future.complete(null);
-            }
-        };
+                    @Override
+                    public void onCompleted() {
+                        Log.i(TAG, "scan observer: on completed");
+                        future.complete(null);
+                    }
+                };
 
         Deadline initialDeadline = Deadline.after(TIMEOUT_ADVERTISING_MS, TimeUnit.MILLISECONDS);
         withCancellation.run(() -> mHostStub.withDeadline(initialDeadline)
diff --git a/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java b/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java
new file mode 100644
index 0000000..d3ded90
--- /dev/null
+++ b/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import static android.bluetooth.Utils.factoryResetAndCreateNewChannel;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.google.protobuf.Empty;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import io.grpc.ManagedChannel;
+import io.grpc.stub.StreamObserver;
+
+import pandora.HostGrpc;
+import pandora.HostProto;
+import pandora.HostProto.AdvertiseRequest;
+import pandora.HostProto.AdvertiseResponse;
+
+@RunWith(AndroidJUnit4.class)
+public class LeScanningTest {
+    private static final String TAG = "LeScanningTest";
+    private static final int TIMEOUT_SCANNING_MS = 2000;
+
+    private static ManagedChannel mChannel;
+
+    private static HostGrpc.HostBlockingStub mHostBlockingStub;
+
+    private static HostGrpc.HostStub mHostStub;
+
+    private final String TEST_UUID_STRING = "00001805-0000-1000-8000-00805f9b34fb";
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
+                .adoptShellPermissionIdentity();
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        // Cleanup previous channels and create a new channel for all successive grpc calls
+        mChannel = factoryResetAndCreateNewChannel();
+
+        mHostBlockingStub = HostGrpc.newBlockingStub(mChannel);
+        mHostStub = HostGrpc.newStub(mChannel);
+        mHostBlockingStub.withWaitForReady().readLocalAddress(Empty.getDefaultInstance());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // terminate the channel
+        mChannel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+        InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void startBleScan_withCallbackTypeAllMatches() {
+        advertiseWithBumble(TEST_UUID_STRING);
+
+        List<ScanResult> results = startScanning(TEST_UUID_STRING,
+                ScanSettings.CALLBACK_TYPE_ALL_MATCHES).join();
+
+        assertThat(results.get(0).getScanRecord().getServiceUuids().get(0)).isEqualTo(
+                ParcelUuid.fromString(TEST_UUID_STRING));
+        assertThat(results.get(1).getScanRecord().getServiceUuids().get(0)).isEqualTo(
+                ParcelUuid.fromString(TEST_UUID_STRING));
+    }
+
+    private CompletableFuture<List<ScanResult>> startScanning(String serviceUuid,
+            int callbackType) {
+        CompletableFuture<List<ScanResult>> future = new CompletableFuture<>();
+        List<ScanResult> scanResults = new ArrayList<>();
+
+        android.content.Context context = ApplicationProvider.getApplicationContext();
+        BluetoothManager bluetoothManager = context.getSystemService(BluetoothManager.class);
+        BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
+
+        // Start scanning
+        BluetoothLeScanner leScanner = bluetoothAdapter.getBluetoothLeScanner();
+
+        ScanSettings scanSettings =
+                new ScanSettings.Builder()
+                        .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
+                        .setCallbackType(callbackType)
+                        .build();
+
+        List<ScanFilter> scanFilters = new ArrayList<>();
+        ScanFilter scanFilter = new ScanFilter.Builder()
+                .setServiceUuid(ParcelUuid.fromString(serviceUuid))
+                .build();
+        scanFilters.add(scanFilter);
+
+        ScanCallback scanCallback =
+                new ScanCallback() {
+                    @Override
+                    public void onScanResult(int callbackType, ScanResult result) {
+                        Log.i(TAG, "onScanResult " + "callbackType: " + callbackType
+                                + ", service uuids: " + result.getScanRecord().getServiceUuids());
+                        if (scanResults.size() < 2) {
+                            scanResults.add(result);
+                        } else {
+                            future.complete(scanResults);
+                        }
+                    }
+
+                    @Override
+                    public void onScanFailed(int errorCode) {
+                        Log.i(TAG, "onScanFailed " + "errorCode: " + errorCode);
+                        future.complete(null);
+                    }
+                };
+
+        leScanner.startScan(scanFilters, scanSettings, scanCallback);
+
+        // Make sure completableFuture object completes with null after some timeout
+        return future.completeOnTimeout(null, TIMEOUT_SCANNING_MS, TimeUnit.MILLISECONDS);
+    }
+
+    private void advertiseWithBumble(String serviceUuid) {
+        HostProto.DataTypes dataType = HostProto.DataTypes.newBuilder()
+                .addCompleteServiceClassUuids128(serviceUuid)
+                .build();
+
+        AdvertiseRequest request = AdvertiseRequest.newBuilder()
+                .setLegacy(true)
+                .setData(dataType)
+                .build();
+
+        StreamObserver<AdvertiseResponse> responseObserver =
+                new StreamObserver<>() {
+                    @Override
+                    public void onNext(AdvertiseResponse response) {
+                        Log.i(TAG, "advertise observer: onNext");
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+                        Log.e(TAG, "advertise observer: on error " + e);
+                    }
+
+                    @Override
+                    public void onCompleted() {
+                        Log.i(TAG, "advertise observer: on completed");
+                    }
+                };
+
+        mHostStub.advertise(request, responseObserver);
+    }
+}
diff --git a/framework/tests/bumble/src/android/bluetooth/Utils.java b/framework/tests/bumble/src/android/bluetooth/Utils.java
index 8ee7f53..ee09ebc 100644
--- a/framework/tests/bumble/src/android/bluetooth/Utils.java
+++ b/framework/tests/bumble/src/android/bluetooth/Utils.java
@@ -1,6 +1,13 @@
 package android.bluetooth;
 
 import com.google.protobuf.ByteString;
+import com.google.protobuf.Empty;
+
+import java.util.concurrent.TimeUnit;
+
+import io.grpc.ManagedChannel;
+import io.grpc.okhttp.OkHttpChannelBuilder;
+import pandora.HostGrpc;
 
 public final class Utils {
     public static String addressStringFromByteString(ByteString bs) {
@@ -13,4 +20,22 @@
         }
         return refAddrBuilder.toString();
     }
+
+    public static ManagedChannel factoryResetAndCreateNewChannel() throws InterruptedException {
+        // FactoryReset is killing the server and restarting all channels created before the server
+        // restarted that cannot be reused
+        ManagedChannel channel = OkHttpChannelBuilder
+                .forAddress("localhost", 7999)
+                .usePlaintext()
+                .build();
+
+        HostGrpc.HostBlockingStub stub = HostGrpc.newBlockingStub(channel);
+        stub.factoryReset(Empty.getDefaultInstance());
+
+        // terminate the channel
+        channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
+
+        // return new channel for future use
+        return OkHttpChannelBuilder.forAddress("localhost", 7999).usePlaintext().build();
+    }
 }
diff --git a/system/Android.mk b/system/Android.mk
index 4d35c3c..5ef8ba5 100644
--- a/system/Android.mk
+++ b/system/Android.mk
@@ -19,7 +19,10 @@
 	$(HOST_OUT_EXECUTABLES)/root-canal
 
 LOCAL_host_python_hci_packets_library := \
-	$(SOONG_OUT_DIR)/.intermediates/packages/modules/Bluetooth/system/gd/gd_hci_packets_python3_gen/gen/hci_packets.py
+	$(SOONG_OUT_DIR)/.intermediates/packages/modules/Bluetooth/system/pdl/hci/gd_hci_packets_python3_gen/gen/hci_packets.py
+
+LOCAL_host_python_smp_packets_library := \
+	$(SOONG_OUT_DIR)/.intermediates/packages/modules/Bluetooth/system/pdl/security/gd_smp_packets_python3_gen/gen/smp_packets.py
 
 LOCAL_host_python_extension_libraries := \
 	$(HOST_OUT_SHARED_LIBRARIES)/bluetooth_packets_python3.so
@@ -94,16 +97,19 @@
 $(bluetooth_cert_src_and_bin_zip): PRIVATE_host_libraries := $(LOCAL_host_libraries)
 $(bluetooth_cert_src_and_bin_zip): PRIVATE_host_python_extension_libraries := $(LOCAL_host_python_extension_libraries)
 $(bluetooth_cert_src_and_bin_zip): PRIVATE_host_python_hci_packets_library := $(LOCAL_host_python_hci_packets_library)
+$(bluetooth_cert_src_and_bin_zip): PRIVATE_host_python_smp_packets_library := $(LOCAL_host_python_smp_packets_library)
 $(bluetooth_cert_src_and_bin_zip): PRIVATE_target_executables := $(LOCAL_target_executables)
 $(bluetooth_cert_src_and_bin_zip): PRIVATE_target_libraries := $(LOCAL_target_libraries)
 $(bluetooth_cert_src_and_bin_zip): $(SOONG_ZIP) $(LOCAL_cert_test_sources) \
 		$(LOCAL_host_executables) $(LOCAL_host_libraries) $(LOCAL_host_python_libraries) \
 		$(LOCAL_host_python_extension_libraries) \
 		$(LOCAL_host_python_hci_packets_library) \
+		$(LOCAL_host_python_smp_packets_library) \
 		$(LOCAL_target_executables) $(LOCAL_target_libraries)
 	$(hide) $(SOONG_ZIP) -d -o $@ \
 		-C $(PRIVATE_bluetooth_project_dir) $(addprefix -f ,$(PRIVATE_cert_test_sources)) \
 		-C $(dir $(PRIVATE_host_python_hci_packets_library)) -f $(PRIVATE_host_python_hci_packets_library) \
+		-C $(dir $(PRIVATE_host_python_smp_packets_library)) -f $(PRIVATE_host_python_smp_packets_library) \
 		-C $(HOST_OUT_EXECUTABLES) $(addprefix -f ,$(PRIVATE_host_executables)) \
 		-C $(HOST_OUT_SHARED_LIBRARIES) $(addprefix -f ,$(PRIVATE_host_python_extension_libraries)) \
 		-P lib64 \
diff --git a/system/OWNERS b/system/OWNERS
index 9f56ed3..32a1a17 100644
--- a/system/OWNERS
+++ b/system/OWNERS
@@ -1,13 +1,11 @@
 # Reviewers for /system
 
-bidsharma@google.com
 cmanton@google.com
 cncn@google.com
 eruffieux@google.com
 hallstrom@google.com
 jpawlowski@google.com
 mylesgw@google.com
-rahulsabnis@google.com
 sattiraju@google.com
 siyuanh@google.com
 wescande@google.com
diff --git a/system/audio_hal_interface/Android.bp b/system/audio_hal_interface/Android.bp
index eface26..acb92ae 100644
--- a/system/audio_hal_interface/Android.bp
+++ b/system/audio_hal_interface/Android.bp
@@ -27,6 +27,7 @@
     ],
     static_libs: [
         "libbt-common",
+        "libbt_shim_bridge",
         "libosi",
     ],
     target: {
@@ -71,6 +72,7 @@
         "com.android.btservices",
     ],
     min_sdk_version: "Tiramisu",
+    header_libs: ["libbluetooth_headers"],
 }
 
 // Bluetooth Audio client interface library unit tests for target and host
@@ -99,9 +101,11 @@
     static_libs: [
         "libbt-audio-hal-interface",
         "libbt-common",
+        "libbt_shim_bridge",
         "libchrome",
     ],
     cflags: [
         "-DBUILDCFG",
     ],
+    header_libs: ["libbluetooth_headers"],
 }
diff --git a/system/blueberry/tests/gd/security/le_security_test.py b/system/blueberry/tests/gd/security/le_security_test.py
index 824c39b..6368c30 100644
--- a/system/blueberry/tests/gd/security/le_security_test.py
+++ b/system/blueberry/tests/gd/security/le_security_test.py
@@ -14,7 +14,7 @@
 #   limitations under the License.
 
 import hci_packets as hci
-from bluetooth_packets_python3.security_packets import PairingFailedReason
+import smp_packets as smp
 from blueberry.tests.gd.cert.matchers import SecurityMatchers
 from blueberry.tests.gd.cert.metadata import metadata
 from blueberry.tests.gd.cert.py_hci import PyHci
@@ -585,7 +585,7 @@
         # 3. IUT transmits the Pairing Failed command.
         assertThat(self.dut_security.get_bond_stream()).emits(
             SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.cert_address,
-                                     int(PairingFailedReason.ENCRYPTION_KEY_SIZE)))
+                                     int(smp.PairingFailedReason.ENCRYPTION_KEY_SIZE)))
 
     @metadata(pts_test_id="SM/SLA/EKS/BI-02-C",
               pts_test_name="IUT Responder, Lower Tester Maximum Encryption Key Size < Min_Encryption_Key_Length")
@@ -622,7 +622,7 @@
         #3. IUT transmits the Pairing Failed command.
         assertThat(self.cert_security.get_bond_stream()).emits(
             SecurityMatchers.BondMsg(BondMsgType.DEVICE_BOND_FAILED, self.dut_address,
-                                     int(PairingFailedReason.ENCRYPTION_KEY_SIZE)))
+                                     int(smp.PairingFailedReason.ENCRYPTION_KEY_SIZE)))
 
     @metadata(pts_test_id="SM/MAS/SCPK/BV-01-C",
               pts_test_name="Passkey Entry, IUT Initiator, Secure Connections – Success")
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 2c687cf..afb2f71 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -27,7 +27,6 @@
         "packages/modules/Bluetooth/system/stack/btm",
         "packages/modules/Bluetooth/system/stack/include",
         "packages/modules/Bluetooth/system/udrv/include",
-        "packages/modules/Bluetooth/system/vnd/include",
     ],
     header_libs: ["libbluetooth_headers"],
     generated_headers: [
@@ -43,6 +42,7 @@
         "dm/bta_dm_api.cc",
         "dm/bta_dm_cfg.cc",
         "dm/bta_dm_ci.cc",
+        "dm/bta_dm_gatt_client.cc",
         "dm/bta_dm_main.cc",
         "dm/bta_dm_pm.cc",
     ],
@@ -103,13 +103,13 @@
         "le_audio/broadcaster/state_machine.cc",
         "le_audio/client.cc",
         "le_audio/client_parser.cc",
+        "le_audio/codec_interface.cc",
         "le_audio/codec_manager.cc",
         "le_audio/content_control_id_keeper.cc",
         "le_audio/devices.cc",
         "le_audio/hal_verifier.cc",
         "le_audio/le_audio_health_status.cc",
         "le_audio/le_audio_log_history.cc",
-        "le_audio/le_audio_set_configuration_provider.cc",
         "le_audio/le_audio_set_configuration_provider_json.cc",
         "le_audio/le_audio_types.cc",
         "le_audio/le_audio_utils.cc",
@@ -132,7 +132,10 @@
         "lib-bt-packets",
         "libbluetooth_gd",
         "libbt-bta-core",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
         "libcom.android.sysprop.bluetooth",
+        "liblc3",
     ],
     shared_libs: [
         "android.hardware.bluetooth.audio@2.1",
@@ -190,6 +193,8 @@
     ],
     static_libs: [
         "lib-bt-packets",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
         "libcom.android.sysprop.bluetooth",
     ],
     apex_available: [
@@ -244,6 +249,9 @@
         "libbt-bta",
         "libbt-bta-core",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtcore",
         "libchrome",
         "libcom.android.sysprop.bluetooth",
@@ -290,6 +298,9 @@
         "libbt-bta",
         "libbt-bta-core",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtcore",
         "libchrome",
         "libosi",
@@ -406,6 +417,9 @@
         "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtcore",
         "libbtdevice",
         "libchrome",
@@ -465,8 +479,11 @@
     ],
     static_libs: [
         "crypto_toolbox_for_tests",
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libgmock",
         "libosi",
@@ -509,8 +526,11 @@
     ],
     static_libs: [
         "crypto_toolbox_for_tests",
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libgmock",
         "libosi",
@@ -564,8 +584,12 @@
     ],
     static_libs: [
         "crypto_toolbox_for_tests",
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libgmock",
         "libosi",
@@ -650,6 +674,81 @@
 // bta unit tests for LE Audio
 // ========================================================
 cc_test {
+    name: "bluetooth_le_audio_codec_manager_test",
+    test_suites: ["device-tests"],
+    defaults: [
+        "bluetooth_gtest_x86_asan_workaround",
+        "fluoride_defaults",
+        "mts_defaults",
+    ],
+    host_supported: true,
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        android: {
+            sanitize: {
+                misc_undefined: ["bounds"],
+            },
+            whole_static_libs: [
+                "libPlatformProperties",
+            ],
+        },
+    },
+    include_dirs: [
+        "packages/modules/Bluetooth/system",
+        "packages/modules/Bluetooth/system/bta/include",
+        "packages/modules/Bluetooth/system/bta/test/common",
+        "packages/modules/Bluetooth/system/btif/include",
+        "packages/modules/Bluetooth/system/gd",
+        "packages/modules/Bluetooth/system/stack/include",
+    ],
+    srcs: [
+        ":TestCommonMockFunctions",
+        ":TestMockBtaLeAudioHalVerifier",
+        ":TestStubOsi",
+        "le_audio/codec_manager.cc",
+        "le_audio/codec_manager_test.cc",
+        "le_audio/le_audio_set_configuration_provider_json.cc",
+        "le_audio/le_audio_types.cc",
+        "test/common/btm_api_mock.cc",
+        "test/common/mock_controller.cc",
+    ],
+    data: [
+        ":audio_set_configurations_bfbs",
+        ":audio_set_configurations_json",
+        ":audio_set_scenarios_bfbs",
+        ":audio_set_scenarios_json",
+    ],
+    generated_headers: [
+        "LeAudioSetConfigSchemas_h",
+    ],
+    shared_libs: [
+        "android.hardware.bluetooth.audio@2.0",
+        "android.hardware.bluetooth.audio@2.1",
+        "libcrypto",
+        "libhidlbase",
+        "liblog", // __android_log_print
+    ],
+    static_libs: [
+        "libbluetooth-types",
+        "libbluetooth_gd",
+        "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
+        "libchrome",
+        "libevent",
+        "libflatbuffers-cpp",
+        "libgmock",
+        "libosi",
+    ],
+    sanitize: {
+        cfi: false,
+    },
+    header_libs: ["libbluetooth_headers"],
+}
+
+cc_test {
     name: "bluetooth_le_audio_test",
     test_suites: ["device-tests"],
     defaults: [
@@ -698,6 +797,7 @@
         "le_audio/le_audio_types.cc",
         "le_audio/le_audio_types_test.cc",
         "le_audio/metrics_collector_linux.cc",
+        "le_audio/mock_codec_interface.cc",
         "le_audio/mock_codec_manager.cc",
         "le_audio/mock_iso_manager.cc",
         "le_audio/state_machine.cc",
@@ -728,8 +828,11 @@
         "liblog", // __android_log_print
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
         "libflatbuffers-cpp",
@@ -739,6 +842,7 @@
     sanitize: {
         cfi: false,
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -776,6 +880,7 @@
         "le_audio/le_audio_utils.cc",
         "le_audio/metrics_collector.cc",
         "le_audio/metrics_collector_test.cc",
+        "le_audio/mock_codec_interface.cc",
         "le_audio/mock_codec_manager.cc",
         "le_audio/mock_iso_manager.cc",
         "le_audio/mock_state_machine.cc",
@@ -799,9 +904,13 @@
     ],
     static_libs: [
         "crypto_toolbox_for_tests",
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-audio-hal-interface",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
         "libflatbuffers-cpp",
@@ -863,8 +972,11 @@
     ],
     static_libs: [
         "crypto_toolbox_for_tests",
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libgmock",
         "libosi",
@@ -908,6 +1020,7 @@
         "le_audio/broadcaster/state_machine.cc",
         "le_audio/broadcaster/state_machine_test.cc",
         "le_audio/le_audio_types.cc",
+        "le_audio/mock_codec_interface.cc",
         "le_audio/mock_codec_manager.cc",
         "le_audio/mock_iso_manager.cc",
     ],
@@ -916,8 +1029,11 @@
         "liblog",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libgmock",
         "liblc3",
@@ -965,6 +1081,7 @@
         "le_audio/le_audio_types.cc",
         "le_audio/le_audio_utils.cc",
         "le_audio/metrics_collector_linux.cc",
+        "le_audio/mock_codec_interface.cc",
         "le_audio/mock_codec_manager.cc",
         "le_audio/mock_iso_manager.cc",
         "test/common/mock_controller.cc",
@@ -979,8 +1096,11 @@
         "liblog",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbt-audio-hal-interface",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
         "libgmock",
@@ -1040,8 +1160,11 @@
     ],
     static_libs: [
         "crypto_toolbox_for_tests",
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libgmock",
     ],
@@ -1102,8 +1225,12 @@
     ],
     static_libs: [
         "crypto_toolbox_for_tests",
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libcom.android.sysprop.bluetooth",
         "libevent",
diff --git a/system/bta/BUILD.gn b/system/bta/BUILD.gn
index 5b48ff3..a2b7225 100644
--- a/system/bta/BUILD.gn
+++ b/system/bta/BUILD.gn
@@ -38,6 +38,7 @@
     "dm/bta_dm_api.cc",
     "dm/bta_dm_cfg.cc",
     "dm/bta_dm_ci.cc",
+    "dm/bta_dm_gatt_client.cc",
     "dm/bta_dm_main.cc",
     "dm/bta_dm_pm.cc",
     "gatt/bta_gattc_act.cc",
diff --git a/system/bta/ag/bta_ag_cmd.cc b/system/bta/ag/bta_ag_cmd.cc
index fccbb91..175f794 100644
--- a/system/bta/ag/bta_ag_cmd.cc
+++ b/system/bta/ag/bta_ag_cmd.cc
@@ -29,7 +29,7 @@
 #include "bta/include/utl.h"
 
 #ifdef __ANDROID__
-#include "bta/le_audio/devices.h"
+#include "bta_le_audio_api.h"
 #endif
 
 #include "device/include/interop.h"
diff --git a/system/bta/ag/bta_ag_sdp.cc b/system/bta/ag/bta_ag_sdp.cc
index 6a6732f..8c1f456 100644
--- a/system/bta/ag/bta_ag_sdp.cc
+++ b/system/bta/ag/bta_ag_sdp.cc
@@ -404,7 +404,9 @@
       /* get features if HFP */
       p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
           p_rec, ATTR_ID_SUPPORTED_FEATURES);
-      if (p_attr != nullptr) {
+      if (p_attr != nullptr &&
+          SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+          SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
         /* Found attribute. Get value. */
         /* There might be race condition between SDP and BRSF.  */
         /* Do not update if we already received BRSF.           */
@@ -447,7 +449,9 @@
       /* get features if HSP */
       p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
           p_rec, ATTR_ID_REMOTE_AUDIO_VOLUME_CONTROL);
-      if (p_attr != nullptr) {
+      if (p_attr != nullptr &&
+            SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == BOOLEAN_DESC_TYPE &&
+            SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1) {
         /* Remote volume control of HSP */
         if (p_attr->attr_value.v.u8)
           p_scb->peer_features |= BTA_AG_PEER_FEAT_VOL;
diff --git a/system/bta/av/bta_av_aact.cc b/system/bta/av/bta_av_aact.cc
index ed4104b..423ad9a 100644
--- a/system/bta/av/bta_av_aact.cc
+++ b/system/bta/av/bta_av_aact.cc
@@ -1979,10 +1979,9 @@
   bool sus_evt = true;
   BT_HDR* p_buf;
 
-  APPL_TRACE_ERROR(
-      "%s: peer %s bta_handle:0x%x audio_open_cnt:%d, p_data %p start:%d",
-      __func__, ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()), p_scb->hndl,
-      bta_av_cb.audio_open_cnt, p_data, start);
+  LOG_INFO("peer %s bta_handle:0x%x audio_open_cnt:%d, p_data %p start:%d",
+           ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()), p_scb->hndl,
+           bta_av_cb.audio_open_cnt, p_data, start);
 
   bta_sys_idle(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->PeerAddress());
   BTM_unblock_role_switch_and_sniff_mode_for(p_scb->PeerAddress());
diff --git a/system/bta/av/bta_av_act.cc b/system/bta/av/bta_av_act.cc
index ab737db..a5889a2 100644
--- a/system/bta/av/bta_av_act.cc
+++ b/system/bta/av/bta_av_act.cc
@@ -1894,7 +1894,9 @@
         /* get supported categories */
         p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
             p_rec, ATTR_ID_SUPPORTED_FEATURES);
-        if (p_attr != NULL) {
+        if (p_attr != NULL &&
+            SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+            SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
           categories = p_attr->attr_value.v.u16;
           if (categories & AVRC_SUPF_CT_CAT2)
             peer_features |= (BTA_AV_FEAT_ADV_CTRL);
@@ -1960,7 +1962,9 @@
       tSDP_DISC_ATTR* p_attr =
           get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
               p_rec, ATTR_ID_SUPPORTED_FEATURES);
-      if (p_attr != NULL) {
+      if (p_attr != NULL &&
+          SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+          SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
         uint16_t categories = p_attr->attr_value.v.u16;
         /*
          * Though Absolute Volume came after in 1.4 and above, but there are
@@ -2226,9 +2230,9 @@
         tBTA_AV bta_av_data = {
             .rc_open =
                 {
-                    .peer_addr = p_scb->PeerAddress(),
                     .peer_ct_features = peer_ct_features,
                     .peer_tg_features = peer_tg_features,
+                    .peer_addr = p_scb->PeerAddress(),
                     .status = BTA_AV_FAIL_SDP,
                 },
         };
@@ -2256,9 +2260,9 @@
     }
 
     tBTA_AV bta_av_feat = {.rc_feat = {
+                               .rc_handle = rc_handle,
                                .peer_ct_features = peer_ct_features,
                                .peer_tg_features = peer_tg_features,
-                               .rc_handle = rc_handle,
                                .peer_addr = peer_addr,
                            }};
     (*p_cb->p_cback)(BTA_AV_RC_FEAT_EVT, &bta_av_feat);
diff --git a/system/bta/av/bta_av_int.h b/system/bta/av/bta_av_int.h
index 560dd52..ecaef26 100644
--- a/system/bta/av/bta_av_int.h
+++ b/system/bta/av/bta_av_int.h
@@ -27,14 +27,15 @@
 #include <cstdint>
 #include <string>
 
-#include "bta/av/bta_av_int.h"
 #include "bta/include/bta_av_api.h"
-#include "bta/include/bta_av_co.h"
 #include "bta/sys/bta_sys.h"
+#include "include/hardware/bt_av.h"
 #include "osi/include/list.h"
+#include "stack/include/a2dp_error_codes.h"
 #include "stack/include/avdt_api.h"
 #include "stack/include/bt_hdr.h"
-#include "stack/include/bt_types.h"
+#include "stack/include/hci_error_code.h"
+#include "types/hci_role.h"
 #include "types/raw_address.h"
 
 #define CASE_RETURN_TEXT(code) \
@@ -400,8 +401,8 @@
 /* data type for BTA_AV_ROLE_CHANGE_EVT */
 typedef struct {
   BT_HDR_RIGID hdr;
-  uint8_t new_role;
-  uint8_t hci_status;
+  tHCI_ROLE new_role;
+  tHCI_STATUS hci_status;
 } tBTA_AV_ROLE_RES;
 
 /* data type for BTA_AV_SDP_DISC_OK_EVT */
diff --git a/system/bta/av/bta_av_main.cc b/system/bta/av/bta_av_main.cc
index c71015c..768fbca 100644
--- a/system/bta/av/bta_av_main.cc
+++ b/system/bta/av/bta_av_main.cc
@@ -31,19 +31,19 @@
 #include "bt_target.h"  // Must be first to define build configuration
 #include "bta/av/bta_av_int.h"
 #include "bta/include/bta_ar_api.h"
+#include "bta/include/bta_av_co.h"
 #include "bta/include/utl.h"
 #include "btif/avrcp/avrcp_service.h"
 #include "btif/include/btif_av.h"
 #include "btif/include/btif_av_co.h"
 #include "btif/include/btif_config.h"
-#include "main/shim/dumpsys.h"
+#include "gd/os/log.h"
 #include "osi/include/allocator.h"
-#include "osi/include/log.h"
 #include "osi/include/osi.h"  // UNUSED_ATTR
 #include "osi/include/properties.h"
 #include "stack/include/acl_api.h"
 #include "stack/include/bt_hdr.h"
-#include "stack/include/btm_api.h"
+#include "stack/include/hci_error_code.h"
 #include "stack/include/sdp_api.h"
 #include "types/hci_role.h"
 #include "types/raw_address.h"
@@ -107,8 +107,9 @@
 
 static void bta_av_sco_chg_cback(tBTA_SYS_CONN_STATUS status, uint8_t id,
                                  uint8_t app_id, const RawAddress& peer_addr);
-static void bta_av_sys_rs_cback(tBTA_SYS_CONN_STATUS status, uint8_t id,
-                                uint8_t app_id, const RawAddress& peer_addr);
+static void bta_av_sys_rs_cback(tBTA_SYS_CONN_STATUS status, tHCI_ROLE new_role,
+                                tHCI_STATUS hci_status,
+                                const RawAddress& peer_addr);
 
 /*****************************************************************************
  * Global data
@@ -566,7 +567,8 @@
           if (btif_av_src_sink_coexist_enabled()) {
             bta_ar_reg_avrc_for_src_sink_coexist(
                 UUID_SERVCLASS_AV_REM_CTRL_TARGET, "AV Remote Control Target",
-                NULL, p_bta_av_cfg->avrc_tg_cat, BTA_ID_AV + local_role,
+                NULL, p_bta_av_cfg->avrc_tg_cat,
+                static_cast<tBTA_SYS_ID>(BTA_ID_AV + local_role),
                 (bta_av_cb.features & BTA_AV_FEAT_BROWSE), profile_version);
           } else {
             bta_ar_reg_avrc(
@@ -924,7 +926,7 @@
  *
  ******************************************************************************/
 static void bta_av_sys_rs_cback(UNUSED_ATTR tBTA_SYS_CONN_STATUS status,
-                                uint8_t id, uint8_t app_id,
+                                tHCI_ROLE new_role, tHCI_STATUS hci_status,
                                 const RawAddress& peer_addr) {
   int i;
   tBTA_AV_SCB* p_scb = NULL;
@@ -933,7 +935,8 @@
 
   APPL_TRACE_DEBUG(
       "%s: peer %s new_role:%d hci_status:0x%x bta_av_cb.rs_idx:%d", __func__,
-      ADDRESS_TO_LOGGABLE_CSTR(peer_addr), id, app_id, bta_av_cb.rs_idx);
+      ADDRESS_TO_LOGGABLE_CSTR(peer_addr), new_role, hci_status,
+      bta_av_cb.rs_idx);
 
   for (i = 0; i < BTA_AV_NUM_STRS; i++) {
     /* loop through all the SCBs to find matching peer addresses and report the
@@ -945,12 +948,12 @@
           (tBTA_AV_ROLE_RES*)osi_malloc(sizeof(tBTA_AV_ROLE_RES));
       APPL_TRACE_DEBUG(
           "%s: peer %s found: new_role:%d, hci_status:0x%x bta_handle:0x%x",
-          __func__, ADDRESS_TO_LOGGABLE_CSTR(peer_addr), id, app_id,
+          __func__, ADDRESS_TO_LOGGABLE_CSTR(peer_addr), new_role, hci_status,
           p_scb->hndl);
       p_buf->hdr.event = BTA_AV_ROLE_CHANGE_EVT;
       p_buf->hdr.layer_specific = p_scb->hndl;
-      p_buf->new_role = id;
-      p_buf->hci_status = app_id;
+      p_buf->new_role = new_role;
+      p_buf->hci_status = hci_status;
       bta_sys_sendmsg(p_buf);
 
       peer_idx = p_scb->hdi + 1; /* Handle index for the peer_addr */
@@ -958,7 +961,7 @@
   }
 
   /* restore role switch policy, if role switch failed */
-  if ((HCI_SUCCESS != app_id) &&
+  if ((HCI_SUCCESS != hci_status) &&
       (BTM_GetRole(peer_addr, &cur_role) == BTM_SUCCESS) &&
       (cur_role == HCI_ROLE_PERIPHERAL)) {
     BTM_unblock_role_switch_for(peer_addr);
@@ -976,14 +979,15 @@
                        __func__, ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()),
                        bta_av_cb.rs_idx, p_scb->hndl, p_scb->q_tag);
 
-      if (HCI_SUCCESS == app_id || HCI_ERR_NO_CONNECTION == app_id) {
+      if (HCI_SUCCESS == hci_status || HCI_ERR_NO_CONNECTION == hci_status) {
         p_scb->q_info.open.switch_res = BTA_AV_RS_OK;
       } else {
         APPL_TRACE_ERROR(
             "%s: peer %s (p_scb peer %s) role switch failed: new_role:%d "
             "hci_status:0x%x",
             __func__, ADDRESS_TO_LOGGABLE_CSTR(peer_addr),
-            ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()), id, app_id);
+            ADDRESS_TO_LOGGABLE_CSTR(p_scb->PeerAddress()), new_role,
+            hci_status);
         p_scb->q_info.open.switch_res = BTA_AV_RS_FAIL;
       }
 
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index 2cb0252..da9af40 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -780,9 +780,25 @@
       }
 
       csis_group->RemoveDevice(device->addr);
+
       if (csis_group->IsEmpty()) {
         RemoveCsisGroup(group_id);
+
+        /* Remove cached candidate devices for group */
+        devices_.erase(
+            std::remove_if(devices_.begin(), devices_.end(),
+                           [group_id](auto& dev) {
+                             if (dev->GetNumberOfCsisInstances() == 0 &&
+                                 dev->GetExpectedGroupIdMember() == group_id &&
+                                 dev->GetPairingSirkReadFlag() == false) {
+                               return true;
+                             }
+
+                             return false;
+                           }),
+            devices_.end());
       }
+
       device->RemoveCsisInstance(group_id);
     }
 
@@ -888,8 +904,6 @@
 
   void OnGattWriteCcc(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
                       void* user_data) {
-    LOG(INFO) << __func__ << " handle=" << loghex(handle);
-
     auto device = FindDeviceByConnId(conn_id);
     if (device == nullptr) {
       LOG(INFO) << __func__ << " unknown conn_id=" << loghex(conn_id);
@@ -901,6 +915,29 @@
       LOG_INFO("Database out of sync for %s",
                ADDRESS_TO_LOGGABLE_CSTR(device->addr));
       ClearDeviceInformationAndStartSearch(device);
+      return;
+    }
+
+    if (status == GATT_SUCCESS) {
+      LOG_INFO("Successfully registered on ccc: 0x%04x, device: %s", handle,
+               ADDRESS_TO_LOGGABLE_CSTR(device->addr));
+      return;
+    }
+
+    LOG_ERROR(
+        "Failed to register for indications: 0x%04x, device: %s, status: "
+        "0x%02x",
+        handle, ADDRESS_TO_LOGGABLE_CSTR(device->addr), status);
+
+    auto val_handle = device->FindValueHandleByCccHandle(handle);
+    if (!val_handle) {
+      LOG_ERROR("Unknown ccc handle: 0x%04x, device: %s", handle,
+                ADDRESS_TO_LOGGABLE_CSTR(device->addr));
+      return;
+    }
+
+    if (val_handle != GAP_INVALID_HANDLE) {
+      BTA_GATTC_DeregisterForNotifications(gatt_if_, device->addr, val_handle);
     }
   }
 
@@ -1264,8 +1301,6 @@
      * truly is member of group.
      */
     device.get()->SetExpectedGroupIdMember(group_id);
-    devices_.push_back(device);
-
     callbacks_->OnSetMemberAvailable(address,
                                      device.get()->GetExpectedGroupIdMember());
   }
@@ -1628,9 +1663,9 @@
                           const gatt::Service* service,
                           const bluetooth::Uuid& context_uuid,
                           bool is_last_instance) {
-    DLOG(INFO) << __func__ << " service handle: " << loghex(service->handle)
-               << " end handle: " << loghex(service->end_handle)
-               << " uuid: " << context_uuid;
+    LOG_DEBUG("service handle: 0x%04x, end handle: 0x%04x, uuid: %s",
+              service->handle, service->end_handle,
+              context_uuid.ToString().c_str());
 
     auto csis_inst = std::make_shared<CsisInstance>(
         (uint16_t)service->handle, (uint16_t)service->end_handle, context_uuid);
@@ -1640,6 +1675,8 @@
     if (group_id != bluetooth::groups::kGroupUnknown)
       csis_inst->SetGroupId(group_id);
 
+    device->SetCsisInstance(csis_inst->svc_data.start_handle, csis_inst);
+
     /* Initially validate and store GATT service discovery data */
     for (const gatt::Characteristic& charac : service->characteristics) {
       if (charac.uuid == kCsisLockUuid) {
@@ -1647,8 +1684,8 @@
         uint16_t ccc_handle =
             FindCccHandle(device->conn_id, charac.value_handle);
         if (ccc_handle == GAP_INVALID_HANDLE) {
-          DLOG(ERROR) << __func__
-                      << ": no HAS Active Preset CCC descriptor found!";
+          LOG_ERROR("no HAS Active Preset CCC descriptor found!");
+          device->RemoveCsisInstance(group_id);
           return false;
         }
         csis_inst->svc_data.lock_handle.val_hdl = charac.value_handle;
@@ -1657,15 +1694,17 @@
         SubscribeForNotifications(device->conn_id, device->addr,
                                   charac.value_handle, ccc_handle);
 
-        DLOG(INFO) << __func__ << " Lock UUID found handle: "
-                   << loghex(csis_inst->svc_data.lock_handle.val_hdl)
-                   << " ccc handle: "
-                   << loghex(csis_inst->svc_data.lock_handle.ccc_hdl);
+        LOG_DEBUG(
+            "Lock UUID found handle: 0x%04x, ccc handle: 0x%04x, device: %s",
+            csis_inst->svc_data.lock_handle.val_hdl,
+            csis_inst->svc_data.lock_handle.ccc_hdl,
+            ADDRESS_TO_LOGGABLE_CSTR(device->addr));
       } else if (charac.uuid == kCsisRankUuid) {
         csis_inst->svc_data.rank_handle = charac.value_handle;
 
-        DLOG(INFO) << __func__ << " Rank UUID found handle: "
-                   << loghex(csis_inst->svc_data.rank_handle);
+        LOG_DEBUG("Rank UUID found handle: 0x%04x, device: %s",
+                  csis_inst->svc_data.rank_handle,
+                  ADDRESS_TO_LOGGABLE_CSTR(device->addr));
       } else if (charac.uuid == kCsisSirkUuid) {
         /* Find the optional CCC descriptor */
         uint16_t ccc_handle =
@@ -1677,10 +1716,11 @@
           SubscribeForNotifications(device->conn_id, device->addr,
                                     charac.value_handle, ccc_handle);
 
-        DLOG(INFO) << __func__ << " SIRK UUID found handle: "
-                   << loghex(csis_inst->svc_data.sirk_handle.val_hdl)
-                   << " ccc handle: "
-                   << loghex(csis_inst->svc_data.sirk_handle.ccc_hdl);
+        LOG_DEBUG(
+            "SIRK UUID found handle: 0x%04x, ccc handle: 0x%04x, device: %s",
+            csis_inst->svc_data.sirk_handle.val_hdl,
+            csis_inst->svc_data.sirk_handle.ccc_hdl,
+            ADDRESS_TO_LOGGABLE_CSTR(device->addr));
       } else if (charac.uuid == kCsisSizeUuid) {
         /* Find the optional CCC descriptor */
         uint16_t ccc_handle =
@@ -1692,10 +1732,11 @@
           SubscribeForNotifications(device->conn_id, device->addr,
                                     charac.value_handle, ccc_handle);
 
-        DLOG(INFO) << __func__ << " Size UUID found handle: "
-                   << loghex(csis_inst->svc_data.size_handle.val_hdl)
-                   << " ccc handle: "
-                   << loghex(csis_inst->svc_data.size_handle.ccc_hdl);
+        LOG_DEBUG(
+            "Size UUID found handle: 0x%04x, ccc handle: 0x%04x, device: %s",
+            csis_inst->svc_data.size_handle.val_hdl,
+            csis_inst->svc_data.size_handle.ccc_hdl,
+            ADDRESS_TO_LOGGABLE_CSTR(device->addr));
       }
     }
 
@@ -1705,9 +1746,9 @@
     if (csis_inst->svc_data.sirk_handle.val_hdl == GAP_INVALID_HANDLE) {
       /* We have some characteristics but all dependencies are not satisfied */
       LOG(ERROR) << __func__ << " Service has a broken structure.";
+      device->RemoveCsisInstance(group_id);
       return false;
     }
-    device->SetCsisInstance(csis_inst->svc_data.start_handle, csis_inst);
 
     bool notify_after_sirk_read = false;
     bool notify_after_lock_read = false;
@@ -1921,6 +1962,12 @@
       return;
     }
 
+    /* verify encryption enabled */
+    if (!BTM_IsEncrypted(device->addr, BT_TRANSPORT_LE)) {
+      LOG_WARN("Device not yet bonded - waiting for encryption");
+      return;
+    }
+
     /* Ignore if our service data is valid (discovery initiated by someone
      * else?) */
     if (!device->is_gatt_service_valid) {
@@ -2103,10 +2150,10 @@
     UINT16_TO_STREAM(value_ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
     BtaGattQueue::WriteDescriptor(
         conn_id, ccc_handle, std::move(value), GATT_WRITE,
-        [](uint16_t conn_id, tGATT_STATUS status, uint16_t value_handle,
-           uint16_t len, const uint8_t* value, void* user_data) {
+        [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
+           const uint8_t* value, void* user_data) {
           if (instance)
-            instance->OnGattWriteCcc(conn_id, status, value_handle, user_data);
+            instance->OnGattWriteCcc(conn_id, status, handle, user_data);
         },
         nullptr);
   }
@@ -2124,23 +2171,40 @@
     }
   }
 
-  void ReadSirkValue(tGATT_STATUS status, const RawAddress& address,
-                     uint8_t sirk_type, Octet16& received_sirk) {
+  void SirkValueReadCompleteDuringPairing(tGATT_STATUS status,
+                                          const RawAddress& address,
+                                          uint8_t sirk_type,
+                                          Octet16& received_sirk) {
+    LOG_INFO("%s, status: 0x%02x", ADDRESS_TO_LOGGABLE_CSTR(address), status);
+
+    auto device = FindDeviceByAddress(address);
+    if (device == nullptr) {
+      LOG_ERROR("Unknown device %s", ADDRESS_TO_LOGGABLE_CSTR(address));
+      BTA_DmSirkConfirmDeviceReply(address, false);
+      return;
+    }
+
+    auto group_id_to_join = device->GetExpectedGroupIdMember();
+    device->SetPairingSirkReadFlag(false);
+
+    /* Verify group still exist, if not it means user forget the group and
+     * paring should be rejected.
+     */
+    auto csis_group = FindCsisGroup(group_id_to_join);
+    if (!csis_group) {
+      LOG_ERROR("Group %d removed during paring a set member",
+                group_id_to_join);
+      RemoveDevice(address);
+      BTA_DmSirkConfirmDeviceReply(address, false);
+      return;
+    }
+
     if (status != GATT_SUCCESS) {
       LOG_INFO("Invalid member, can't read SIRK (status: %02x)", status);
       BTA_DmSirkConfirmDeviceReply(address, false);
       return;
     }
 
-    auto device = FindDeviceByAddress(address);
-    if (device == nullptr) {
-      LOG_ERROR("Invalid SIRK value read for unknown device");
-      BTA_DmSirkConfirmDeviceReply(address, false);
-      return;
-    }
-
-    LOG_DEBUG("%s, status: 0x%02x", ADDRESS_TO_LOGGABLE_CSTR(address), status);
-
     /* Verify if sirk is not all zeros */
     Octet16 zero{};
     if (memcmp(zero.data(), received_sirk.data(), 16) == 0) {
@@ -2153,30 +2217,11 @@
     if (sirk_type == bluetooth::csis::kCsisSirkTypeEncrypted) {
       /* Decrypt encrypted SIRK */
       Octet16 sirk;
-      sdf(device->addr, received_sirk, sirk);
+      sdf(address, received_sirk, sirk);
       received_sirk = sirk;
     }
 
-    /* SIRK is ready. Add device to the group */
-
-    /* Now having SIRK we can decide if the device belongs to some group we
-     * know or this is a new group
-     */
-    auto csis_group = FindCsisGroup(device->GetExpectedGroupIdMember());
-    if (!csis_group) {
-      LOG_ERROR("Expected group with ID: %d, isn't cached",
-                device->GetExpectedGroupIdMember());
-      return;
-    }
-
-    /* Device will be added to group when upper layer decides */
-    RemoveDevice(device->addr);
-
-    if (csis_group->IsSirkBelongsToGroup(received_sirk)) {
-      LOG_INFO("Device %s, verified successfully by SIRK",
-               ADDRESS_TO_LOGGABLE_CSTR(address));
-      BTA_DmSirkConfirmDeviceReply(address, true);
-    } else {
+    if (!csis_group->IsSirkBelongsToGroup(received_sirk)) {
       /*
        * Joining member must join already existing group otherwise it means
        * that its SIRK is different. Device connection was triggered by RSI
@@ -2185,7 +2230,17 @@
       LOG_ERROR("Joining device %s, does not match any existig group",
                 ADDRESS_TO_LOGGABLE_CSTR(address));
       BTA_DmSirkConfirmDeviceReply(address, false);
+      return;
     }
+
+    LOG_INFO("Device %s, verified successfully by SIRK",
+             ADDRESS_TO_LOGGABLE_CSTR(address));
+    BTA_DmSirkConfirmDeviceReply(address, true);
+
+    /* It was temporary device and we can remove it. When upper layer
+     * decides to connect CSIS it will be added then
+     */
+    RemoveDevice(address);
   }
 
   void VerifySetMember(const RawAddress& address) {
@@ -2200,9 +2255,15 @@
       return;
     }
 
-    gatt_cl_read_sirk_req(address,
-                          base::BindOnce(&CsisClientImpl::ReadSirkValue,
-                                         base::Unretained(instance)));
+    if (!gatt_cl_read_sirk_req(
+            address,
+            base::BindOnce(&CsisClientImpl::SirkValueReadCompleteDuringPairing,
+                           weak_factory_.GetWeakPtr()))) {
+      LOG_ERROR("Could not read SIKR of %s", ADDRESS_TO_LOGGABLE_CSTR(address));
+      BTA_DmSirkConfirmDeviceReply(address, false);
+      return;
+    }
+    device->SetPairingSirkReadFlag(true);
   }
 
   uint8_t gatt_if_;
@@ -2211,6 +2272,8 @@
   std::list<std::shared_ptr<CsisGroup>> csis_groups_;
   DeviceGroups* dev_groups_;
   int discovering_group_ = bluetooth::groups::kGroupUnknown;
+
+  base::WeakPtrFactory<CsisClientImpl> weak_factory_{this};
 };
 
 class DeviceGroupsCallbacksImpl : public DeviceGroupsCallbacks {
diff --git a/system/bta/csis/csis_client_test.cc b/system/bta/csis/csis_client_test.cc
index 64fcf5e..047a390 100644
--- a/system/bta/csis/csis_client_test.cc
+++ b/system/bta/csis/csis_client_test.cc
@@ -342,6 +342,9 @@
     SetMockCsisLockCallback(&csis_lock_cb);
     callbacks.reset(new MockCsisCallbacks());
 
+    ON_CALL(btm_interface, BTM_IsEncrypted(_, _))
+        .WillByDefault(DoAll(Return(true)));
+
     ON_CALL(gatt_interface, GetCharacteristic(_, _))
         .WillByDefault(
             Invoke([&](uint16_t conn_id,
@@ -427,11 +430,11 @@
     gatt_callback = nullptr;
   }
 
-  void TestConnect(const RawAddress& address) {
+  void TestConnect(const RawAddress& address, bool encrypted = true) {
     // by default indicate link as encrypted
     ON_CALL(btm_interface, GetSecurityFlagsByTransport(address, NotNull(), _))
         .WillByDefault(
-            DoAll(SetArgPointee<1>(BTM_SEC_FLAG_ENCRYPTED), Return(true)));
+            DoAll(SetArgPointee<1>(BTM_SEC_FLAG_ENCRYPTED), Return(encrypted)));
 
     EXPECT_CALL(gatt_interface,
                 Open(gatt_if, address, BTM_BLE_DIRECT_CONNECTION, _));
@@ -494,8 +497,8 @@
       const RawAddress& address, uint16_t conn_id,
       tGATT_DISCONN_REASON reason = GATT_CONN_TERMINATE_PEER_USER) {
     tBTA_GATTC_CLOSE event_data = {
-        .status = GATT_SUCCESS,
         .conn_id = conn_id,
+        .status = GATT_SUCCESS,
         .client_if = gatt_if,
         .remote_bda = address,
         .reason = reason,
@@ -506,8 +509,8 @@
 
   void GetSearchCompleteEvent(uint16_t conn_id) {
     tBTA_GATTC_SEARCH_CMPL event_data = {
-        .status = GATT_SUCCESS,
         .conn_id = conn_id,
+        .status = GATT_SUCCESS,
     };
 
     gatt_callback(BTA_GATTC_SEARCH_CMPL_EVT, (tBTA_GATTC*)&event_data);
@@ -531,10 +534,46 @@
     TestAppUnregister();
   }
 
+  void TestGattWriteCCC(uint16_t ccc_handle, GattStatus status,
+                        int deregister_times) {
+    SetSampleDatabaseCsis(1, 1);
+    TestAppRegister();
+    TestConnect(test_address);
+    InjectConnectedEvent(test_address, 1);
+
+    auto WriteDescriptorCbGenerator = [](tGATT_STATUS status,
+                                         uint16_t ccc_handle) {
+      return [status, ccc_handle](uint16_t conn_id, uint16_t handle,
+                                  std::vector<uint8_t> value,
+                                  tGATT_WRITE_TYPE write_type,
+                                  GATT_WRITE_OP_CB cb, void* cb_data) -> void {
+        if (cb) {
+          if (ccc_handle) {
+            handle = ccc_handle;
+          }
+          cb(conn_id, status, handle, value.size(), value.data(), cb_data);
+        }
+      };
+    };
+
+    // sirk, size, lock
+    EXPECT_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _))
+        .Times(3)
+        .WillOnce(Invoke(WriteDescriptorCbGenerator(GATT_SUCCESS, 0)))
+        .WillOnce(Invoke(WriteDescriptorCbGenerator(GATT_SUCCESS, 0)))
+        .WillOnce(Invoke(WriteDescriptorCbGenerator(status, ccc_handle)));
+
+    EXPECT_CALL(gatt_interface, DeregisterForNotifications(_, _, _))
+        .Times(deregister_times);
+
+    GetSearchCompleteEvent(1);
+    Mock::VerifyAndClearExpectations(&gatt_interface);
+  }
+
   void GetDisconnectedEvent(const RawAddress& address, uint16_t conn_id) {
     tBTA_GATTC_CLOSE event_data = {
-        .status = GATT_SUCCESS,
         .conn_id = conn_id,
+        .status = GATT_SUCCESS,
         .client_if = gatt_if,
         .remote_bda = address,
         .reason = GATT_CONN_TERMINATE_PEER_USER,
@@ -543,21 +582,6 @@
     gatt_callback(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC*)&event_data);
   }
 
-  void SetEncryptionResult(const RawAddress& address, uint16_t conn_id,
-                           bool success) {
-    ON_CALL(btm_interface, BTM_IsEncrypted(address, _))
-        .WillByDefault(DoAll(Return(success)));
-
-    ON_CALL(btm_interface, SetEncryption(address, _, _, _, _))
-        .WillByDefault(
-            Invoke([&](const RawAddress& bd_addr, tBT_TRANSPORT transport,
-                       tBTM_SEC_CALLBACK* p_callback, void* p_ref_data,
-                       tBTM_BLE_SEC_ACT sec_act) -> tBTM_STATUS {
-              InjectEncryptionEvent(bd_addr, conn_id);
-              return BTM_SUCCESS;
-            }));
-  }
-
   void SetSampleDatabaseCsis(uint16_t conn_id, uint8_t rank,
                              uint8_t sirk_msb = 1) {
     set_sample_database(conn_id, true, false, rank, sirk_msb);
@@ -694,6 +718,24 @@
   TestAppUnregister();
 }
 
+TEST_F(CsisClientTest, test_ccc_reg_fail_handle_not_found) {
+  // service handle range: 0x0001 ~ 0x0030
+  uint16_t not_existed_ccc_handle = 0x0031;
+  TestGattWriteCCC(not_existed_ccc_handle, GATT_INVALID_HANDLE, 0);
+}
+
+TEST_F(CsisClientTest, test_ccc_reg_fail_handle_found) {
+  // kCsisLockUuid ccc handle
+  uint16_t existed_ccc_hande = 0x0028;
+  TestGattWriteCCC(existed_ccc_hande, GATT_INVALID_HANDLE, 1);
+}
+
+TEST_F(CsisClientTest, test_ccc_reg_fail_out_of_sync) {
+  // kCsisLockUuid ccc handle
+  uint16_t ccc_handle = 0x0028;
+  TestGattWriteCCC(ccc_handle, GATT_DATABASE_OUT_OF_SYNC, 0);
+}
+
 class CsisClientCallbackTest : public CsisClientTest {
  protected:
   const RawAddress test_address = GetTestAddress(0);
@@ -759,6 +801,41 @@
   TestAppUnregister();
 }
 
+TEST_F(CsisClientTest, test_search_complete_before_encryption) {
+  SetSampleDatabaseCsis(1, 1);
+  TestAppRegister();
+  TestConnect(test_address, false);
+  EXPECT_CALL(*callbacks,
+              OnConnectionState(test_address, ConnectionState::CONNECTED))
+      .Times(0);
+  EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _, _, _, _)).Times(0);
+
+  ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _))
+      .WillByDefault(DoAll(Return(false)));
+
+  InjectConnectedEvent(test_address, 1);
+  GetSearchCompleteEvent(1);
+  Mock::VerifyAndClearExpectations(callbacks.get());
+
+  /* Incject encryption and expect device connection */
+  EXPECT_CALL(*callbacks,
+              OnConnectionState(test_address, ConnectionState::CONNECTED))
+      .Times(1);
+  EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _, _, _, _)).Times(1);
+
+  ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _))
+      .WillByDefault(DoAll(Return(true)));
+  EXPECT_CALL(gatt_interface, ServiceSearchRequest(_, _)).Times(1);
+
+  InjectEncryptionEvent(test_address, 1);
+  GetSearchCompleteEvent(1);
+
+  Mock::VerifyAndClearExpectations(&gatt_interface);
+  Mock::VerifyAndClearExpectations(callbacks.get());
+
+  TestAppUnregister();
+}
+
 TEST_F(CsisClientTest, test_is_group_empty) {
   std::list<std::shared_ptr<CsisGroup>> csis_groups_;
   auto g_1 = std::make_shared<CsisGroup>(666, bluetooth::Uuid::kEmpty);
diff --git a/system/bta/csis/csis_types.h b/system/bta/csis/csis_types.h
index 3bbb1c4..3597272 100644
--- a/system/bta/csis/csis_types.h
+++ b/system/bta/csis/csis_types.h
@@ -220,6 +220,23 @@
     csis_instances_.clear();
   }
 
+  uint16_t FindValueHandleByCccHandle(uint16_t ccc_handle) {
+    uint16_t val_handle = 0;
+    for (const auto& [_, inst] : csis_instances_) {
+      if (inst->svc_data.sirk_handle.ccc_hdl == ccc_handle) {
+        val_handle = inst->svc_data.sirk_handle.val_hdl;
+      } else if (inst->svc_data.lock_handle.ccc_hdl == ccc_handle) {
+        val_handle = inst->svc_data.lock_handle.val_hdl;
+      } else if (inst->svc_data.size_handle.ccc_hdl == ccc_handle) {
+        val_handle = inst->svc_data.size_handle.val_hdl;
+      }
+      if (val_handle) {
+        break;
+      }
+    }
+    return val_handle;
+  }
+
   std::shared_ptr<CsisInstance> GetCsisInstanceByOwningHandle(uint16_t handle) {
     uint16_t hdl = 0;
     for (const auto& [h, inst] : csis_instances_) {
@@ -272,17 +289,25 @@
   }
 
   void SetExpectedGroupIdMember(int group_id) {
-    LOG_DEBUG("Expected Group ID: %d, for member: %s is set", group_id,
-              ADDRESS_TO_LOGGABLE_CSTR(addr));
-    expected_group_id_member = group_id;
+    LOG_INFO("Expected Group ID: %d, for member: %s is set", group_id,
+             ADDRESS_TO_LOGGABLE_CSTR(addr));
+    expected_group_id_member_ = group_id;
   }
 
-  inline int GetExpectedGroupIdMember() { return expected_group_id_member; }
+  void SetPairingSirkReadFlag(bool flag) {
+    LOG_INFO("Pairing flag for Group ID: %d, member: %s is set to %d",
+             expected_group_id_member_, ADDRESS_TO_LOGGABLE_CSTR(addr), flag);
+    pairing_sirk_read_flag_ = flag;
+  }
+
+  inline int GetExpectedGroupIdMember() { return expected_group_id_member_; }
+  inline bool GetPairingSirkReadFlag() { return pairing_sirk_read_flag_; }
 
  private:
   /* Instances per start handle  */
   std::map<uint16_t, std::shared_ptr<CsisInstance>> csis_instances_;
-  int expected_group_id_member = bluetooth::groups::kGroupUnknown;
+  int expected_group_id_member_ = bluetooth::groups::kGroupUnknown;
+  bool pairing_sirk_read_flag_ = false;
 };
 
 /*
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index 65b399f..ef4a2e8 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -32,6 +32,7 @@
 
 #include <cstdint>
 
+#include "bta/dm/bta_dm_gatt_client.h"
 #include "bta/dm/bta_dm_int.h"
 #include "bta/gatt/bta_gattc_int.h"
 #include "bta/include/bta_dm_ci.h"
@@ -191,8 +192,6 @@
 #endif
 }
 
-TimestampedStringCircularBuffer gatt_history_{50};
-
 namespace {
 
 struct WaitForAllAclConnectionsToDrain {
@@ -227,80 +226,6 @@
       static_cast<WaitForAllAclConnectionsToDrain*>(data));
 }
 
-struct gatt_interface_t {
-  void (*BTA_GATTC_CancelOpen)(tGATT_IF client_if, const RawAddress& remote_bda,
-                               bool is_direct);
-  void (*BTA_GATTC_Refresh)(const RawAddress& remote_bda);
-  void (*BTA_GATTC_GetGattDb)(uint16_t conn_id, uint16_t start_handle,
-                              uint16_t end_handle, btgatt_db_element_t** db,
-                              int* count);
-  void (*BTA_GATTC_AppRegister)(tBTA_GATTC_CBACK* p_client_cb,
-                                BtaAppRegisterCallback cb, bool eatt_support);
-  void (*BTA_GATTC_Close)(uint16_t conn_id);
-  void (*BTA_GATTC_ServiceSearchRequest)(uint16_t conn_id,
-                                         const bluetooth::Uuid* p_srvc_uuid);
-  void (*BTA_GATTC_Open)(tGATT_IF client_if, const RawAddress& remote_bda,
-                         tBTM_BLE_CONN_TYPE connection_type,
-                         bool opportunistic);
-} default_gatt_interface = {
-    .BTA_GATTC_CancelOpen =
-        [](tGATT_IF client_if, const RawAddress& remote_bda, bool is_direct) {
-          gatt_history_.Push(base::StringPrintf(
-              "%-32s bd_addr:%s client_if:%hu is_direct:%c", "GATTC_CancelOpen",
-              ADDRESS_TO_LOGGABLE_CSTR(remote_bda), client_if,
-              (is_direct) ? 'T' : 'F'));
-          BTA_GATTC_CancelOpen(client_if, remote_bda, is_direct);
-        },
-    .BTA_GATTC_Refresh =
-        [](const RawAddress& remote_bda) {
-          gatt_history_.Push(
-              base::StringPrintf("%-32s bd_addr:%s", "GATTC_Refresh",
-                                 ADDRESS_TO_LOGGABLE_CSTR(remote_bda)));
-          BTA_GATTC_Refresh(remote_bda);
-        },
-    .BTA_GATTC_GetGattDb =
-        [](uint16_t conn_id, uint16_t start_handle, uint16_t end_handle,
-           btgatt_db_element_t** db, int* count) {
-          gatt_history_.Push(base::StringPrintf(
-              "%-32s conn_id:%hu start_handle:%hu end:handle:%hu",
-              "GATTC_GetGattDb", conn_id, start_handle, end_handle));
-          BTA_GATTC_GetGattDb(conn_id, start_handle, end_handle, db, count);
-        },
-    .BTA_GATTC_AppRegister =
-        [](tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb,
-           bool eatt_support) {
-          gatt_history_.Push(base::StringPrintf("%-32s eatt_support:%c",
-                                                "GATTC_AppRegister",
-                                                (eatt_support) ? 'T' : 'F'));
-          BTA_GATTC_AppRegister(p_client_cb, cb, eatt_support);
-        },
-    .BTA_GATTC_Close =
-        [](uint16_t conn_id) {
-          gatt_history_.Push(
-              base::StringPrintf("%-32s conn_id:%hu", "GATTC_Close", conn_id));
-          BTA_GATTC_Close(conn_id);
-        },
-    .BTA_GATTC_ServiceSearchRequest =
-        [](uint16_t conn_id, const bluetooth::Uuid* p_srvc_uuid) {
-          gatt_history_.Push(base::StringPrintf(
-              "%-32s conn_id:%hu", "GATTC_ServiceSearchRequest", conn_id));
-          BTA_GATTC_ServiceSearchRequest(conn_id, p_srvc_uuid);
-        },
-    .BTA_GATTC_Open =
-        [](tGATT_IF client_if, const RawAddress& remote_bda,
-           tBTM_BLE_CONN_TYPE connection_type, bool opportunistic) {
-          gatt_history_.Push(base::StringPrintf(
-              "%-32s bd_addr:%s client_if:%hu type:0x%x opportunistic:%c",
-              "GATTC_Open", ADDRESS_TO_LOGGABLE_CSTR(remote_bda), client_if,
-              connection_type, (opportunistic) ? 'T' : 'F'));
-          BTA_GATTC_Open(client_if, remote_bda, connection_type, opportunistic);
-        },
-};
-
-gatt_interface_t* gatt_interface = &default_gatt_interface;
-
-gatt_interface_t& get_gatt_interface() { return *gatt_interface; }
-
 }  // namespace
 
 static void bta_dm_reset_sec_dev_pending(const RawAddress& remote_bd_addr);
@@ -1210,7 +1135,9 @@
   tSDP_DISC_ATTR* p_attr =
       get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
           sdp_rec, ATTR_ID_SUPPORTED_FEATURES);
-  if (p_attr == NULL) {
+  if (p_attr == NULL ||
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != UINT_DESC_TYPE ||
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) < 2) {
     return;
   }
 
@@ -2702,12 +2629,13 @@
   }
 
   LOG_INFO(
-      "Role change callback peer:%s info:0x%x new_role:%s dev count:%d "
+      "Role change callback peer:%s info:%s new_role:%s dev count:%d "
       "hci_status:%s",
-      ADDRESS_TO_LOGGABLE_CSTR(bd_addr), p_dev->Info(), RoleText(new_role).c_str(),
-      bta_dm_cb.device_list.count, hci_error_code_text(hci_status).c_str());
+      ADDRESS_TO_LOGGABLE_CSTR(bd_addr), p_dev->info_text().c_str(),
+      RoleText(new_role).c_str(), bta_dm_cb.device_list.count,
+      hci_error_code_text(hci_status).c_str());
 
-  if (p_dev->Info() & BTA_DM_DI_AV_ACTIVE) {
+  if (p_dev->is_av_active()) {
     bool need_policy_change = false;
 
     /* there's AV activity on this link */
@@ -2753,7 +2681,7 @@
       acl_peer_supports_sniff_subrating(bd_addr)) {
     LOG_DEBUG("Device supports sniff subrating peer:%s",
               ADDRESS_TO_LOGGABLE_CSTR(bd_addr));
-    p_dev->info = BTA_DM_DI_USE_SSR;
+    p_dev->set_both_device_ssr_capable();
   } else {
     LOG_DEBUG("Device does NOT support sniff subrating peer:%s",
               ADDRESS_TO_LOGGABLE_CSTR(bd_addr));
@@ -2799,7 +2727,7 @@
            bt_transport_text(transport).c_str(), acl_handle);
   device->conn_state = BTA_DM_CONNECTED;
   device->pref_role = BTA_ANY_ROLE;
-  device->info = BTA_DM_DI_NONE;
+  device->reset_device_info();
   device->transport = transport;
 
   if (controller_get_interface()->supports_sniff_subrating() &&
@@ -2810,7 +2738,7 @@
     // data is when the BTA_dm_notify_remote_features_complete()
     // callback has completed.  The below assignment is kept for
     // transitional informational purposes only.
-    device->info = BTA_DM_DI_USE_SSR;
+    device->set_both_device_ssr_capable();
   }
 
   if (bta_dm_cb.p_sec_cback) {
@@ -2953,10 +2881,9 @@
     LOG_INFO("av_count:%d", bta_dm_cb.cur_av_count);
     for (i = 0; i < bta_dm_cb.device_list.count; i++) {
       p_dev = &bta_dm_cb.device_list.peer_device[i];
-      APPL_TRACE_WARNING("[%d]: state:%d, info:x%x", i, p_dev->conn_state,
-                         p_dev->Info());
-      if ((p_dev->conn_state == BTA_DM_CONNECTED) &&
-          (p_dev->Info() & BTA_DM_DI_AV_ACTIVE)) {
+      APPL_TRACE_WARNING("[%d]: state:%d, info:%s", i, p_dev->conn_state,
+                         p_dev->info_text().c_str());
+      if ((p_dev->conn_state == BTA_DM_CONNECTED) && p_dev->is_av_active()) {
         /* make central and take away the role switch policy */
         BTM_SwitchRoleToCentral(p_dev->peer_bdaddr);
         /* else either already central or can not switch for some reasons */
@@ -3036,11 +2963,11 @@
 
   if (BTA_ID_AV == id) {
     if (status == BTA_SYS_CONN_BUSY) {
-      if (p_dev) p_dev->info |= BTA_DM_DI_AV_ACTIVE;
+      if (p_dev) p_dev->set_av_active();
       /* AV calls bta_sys_conn_open with the A2DP stream count as app_id */
       if (BTA_ID_AV == id) bta_dm_cb.cur_av_count = bta_dm_get_av_count();
     } else if (status == BTA_SYS_CONN_IDLE) {
-      if (p_dev) p_dev->info &= ~BTA_DM_DI_AV_ACTIVE;
+      if (p_dev) p_dev->reset_av_active();
 
       /* get cur_av_count from connected services */
       if (BTA_ID_AV == id) bta_dm_cb.cur_av_count = bta_dm_get_av_count();
@@ -4332,7 +4259,7 @@
   get_gatt_interface().BTA_GATTC_AppRegister(
       bta_dm_gattc_callback, base::Bind([](uint8_t client_id, uint8_t status) {
         tGATT_STATUS gatt_status = static_cast<tGATT_STATUS>(status);
-        gatt_history_.Push(base::StringPrintf(
+        gatt_history_callback(base::StringPrintf(
             "%-32s client_id:%hu status:%s", "GATTC_RegisteredCallback",
             client_id, gatt_status_text(gatt_status).c_str()));
         if (static_cast<tGATT_STATUS>(status) == GATT_SUCCESS) {
@@ -4486,7 +4413,7 @@
   LOG_DEBUG("BTA_GATTC_OPEN_EVT conn_id = %d client_if=%d status = %d",
             p_data->conn_id, p_data->client_if, p_data->status);
 
-  gatt_history_.Push(base::StringPrintf(
+  gatt_history_callback(base::StringPrintf(
       "%-32s bd_addr:%s conn_id:%hu client_if:%hu event:%s",
       "GATTC_EventCallback", ADDRESS_TO_LOGGABLE_CSTR(p_data->remote_bda),
       p_data->conn_id, p_data->client_if,
@@ -4507,7 +4434,7 @@
            ADDRESS_TO_LOGGABLE_CSTR(close.remote_bda),
            gatt_disconnection_reason_text(close.reason).c_str());
 
-  gatt_history_.Push(base::StringPrintf(
+  gatt_history_callback(base::StringPrintf(
       "%-32s bd_addr:%s client_if:%hu status:%s event:%s",
       "GATTC_EventCallback", ADDRESS_TO_LOGGABLE_CSTR(close.remote_bda),
       close.client_if, gatt_status_text(close.status).c_str(),
@@ -4721,7 +4648,7 @@
                                     p_data->search_cmpl.status);
           break;
       }
-      gatt_history_.Push(base::StringPrintf(
+      gatt_history_callback(base::StringPrintf(
           "%-32s conn_id:%hu status:%s", "GATTC_EventCallback",
           p_data->search_cmpl.conn_id,
           gatt_status_text(p_data->search_cmpl.status).c_str()));
@@ -4766,7 +4693,7 @@
     case BTA_GATTC_SRVC_CHG_EVT:
     case BTA_GATTC_SRVC_DISC_DONE_EVT:
     case BTA_GATTC_SUBRATE_CHG_EVT:
-      gatt_history_.Push(
+      gatt_history_callback(
           base::StringPrintf("%-32s event:%s", "GATTC_EventCallback",
                              gatt_client_event_text(event).c_str()));
       break;
diff --git a/system/bta/dm/bta_dm_gatt_client.cc b/system/bta/dm/bta_dm_gatt_client.cc
new file mode 100644
index 0000000..5d93353
--- /dev/null
+++ b/system/bta/dm/bta_dm_gatt_client.cc
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2023 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 "bta/dm/bta_dm_gatt_client.h"
+
+#include <base/strings/stringprintf.h>
+
+#include <cstdint>
+#include <cstring>
+
+#include "bta/include/bta_gatt_api.h"
+#include "gd/common/strings.h"
+#include "osi/include/log.h"
+#include "stack/btm/btm_int_types.h"
+#include "types/bluetooth/uuid.h"
+#include "types/raw_address.h"
+
+namespace {
+TimestampedStringCircularBuffer gatt_history_{50};
+constexpr char kTimeFormatString[] = "%Y-%m-%d %H:%M:%S";
+
+constexpr unsigned MillisPerSecond = 1000;
+std::string EpochMillisToString(long long time_ms) {
+  time_t time_sec = time_ms / MillisPerSecond;
+  struct tm tm;
+  localtime_r(&time_sec, &tm);
+  std::string s = bluetooth::common::StringFormatTime(kTimeFormatString, tm);
+  return base::StringPrintf(
+      "%s.%03u", s.c_str(),
+      static_cast<unsigned int>(time_ms % MillisPerSecond));
+}
+}  // namespace
+
+gatt_interface_t default_gatt_interface = {
+    .BTA_GATTC_CancelOpen =
+        [](tGATT_IF client_if, const RawAddress& remote_bda, bool is_direct) {
+          gatt_history_.Push(base::StringPrintf(
+              "%-32s bd_addr:%s client_if:%hu is_direct:%c", "GATTC_CancelOpen",
+              ADDRESS_TO_LOGGABLE_CSTR(remote_bda), client_if,
+              (is_direct) ? 'T' : 'F'));
+          BTA_GATTC_CancelOpen(client_if, remote_bda, is_direct);
+        },
+    .BTA_GATTC_Refresh =
+        [](const RawAddress& remote_bda) {
+          gatt_history_.Push(
+              base::StringPrintf("%-32s bd_addr:%s", "GATTC_Refresh",
+                                 ADDRESS_TO_LOGGABLE_CSTR(remote_bda)));
+          BTA_GATTC_Refresh(remote_bda);
+        },
+    .BTA_GATTC_GetGattDb =
+        [](uint16_t conn_id, uint16_t start_handle, uint16_t end_handle,
+           btgatt_db_element_t** db, int* count) {
+          gatt_history_.Push(base::StringPrintf(
+              "%-32s conn_id:%hu start_handle:%hu end:handle:%hu",
+              "GATTC_GetGattDb", conn_id, start_handle, end_handle));
+          BTA_GATTC_GetGattDb(conn_id, start_handle, end_handle, db, count);
+        },
+    .BTA_GATTC_AppRegister =
+        [](tBTA_GATTC_CBACK* p_client_cb, BtaAppRegisterCallback cb,
+           bool eatt_support) {
+          gatt_history_.Push(base::StringPrintf("%-32s eatt_support:%c",
+                                                "GATTC_AppRegister",
+                                                (eatt_support) ? 'T' : 'F'));
+          BTA_GATTC_AppRegister(p_client_cb, cb, eatt_support);
+        },
+    .BTA_GATTC_Close =
+        [](uint16_t conn_id) {
+          gatt_history_.Push(
+              base::StringPrintf("%-32s conn_id:%hu", "GATTC_Close", conn_id));
+          BTA_GATTC_Close(conn_id);
+        },
+    .BTA_GATTC_ServiceSearchRequest =
+        [](uint16_t conn_id, const bluetooth::Uuid* p_srvc_uuid) {
+          gatt_history_.Push(base::StringPrintf(
+              "%-32s conn_id:%hu", "GATTC_ServiceSearchRequest", conn_id));
+          BTA_GATTC_ServiceSearchRequest(conn_id, p_srvc_uuid);
+        },
+    .BTA_GATTC_Open =
+        [](tGATT_IF client_if, const RawAddress& remote_bda,
+           tBTM_BLE_CONN_TYPE connection_type, bool opportunistic) {
+          gatt_history_.Push(base::StringPrintf(
+              "%-32s bd_addr:%s client_if:%hu type:0x%x opportunistic:%c",
+              "GATTC_Open", ADDRESS_TO_LOGGABLE_CSTR(remote_bda), client_if,
+              connection_type, (opportunistic) ? 'T' : 'F'));
+          BTA_GATTC_Open(client_if, remote_bda, connection_type, opportunistic);
+        },
+};
+
+gatt_interface_t* gatt_interface = &default_gatt_interface;
+
+gatt_interface_t& get_gatt_interface() { return *gatt_interface; }
+
+void gatt_history_callback(const std::string entry) {
+  gatt_history_.Push(entry);
+}
+
+#define DUMPSYS_TAG "shim::legacy::bta::dm"
+void DumpsysBtaDmGattClient(int fd) {
+  auto gatt_history = gatt_history_.Pull();
+  LOG_DUMPSYS(fd, " last %zu gatt history entries", gatt_history.size());
+  for (const auto& it : gatt_history) {
+    LOG_DUMPSYS(fd, "   %s %s", EpochMillisToString(it.timestamp).c_str(),
+                it.entry.c_str());
+  }
+}
+#undef DUMPSYS_TAG
+
+void bluetooth::testing::set_gatt_interface(const gatt_interface_t& interface) {
+  *gatt_interface = interface;
+}
diff --git a/system/bta/dm/bta_dm_gatt_client.h b/system/bta/dm/bta_dm_gatt_client.h
new file mode 100644
index 0000000..e49eea6
--- /dev/null
+++ b/system/bta/dm/bta_dm_gatt_client.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2023 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 <cstdint>
+#include <cstring>
+
+#include "bta/include/bta_gatt_api.h"
+#include "include/hardware/bt_common_types.h"
+#include "stack/include/btm_ble_api_types.h"
+#include "types/bluetooth/uuid.h"
+#include "types/raw_address.h"
+
+//
+// Interface as a GATT client for bta clients
+//
+struct gatt_interface_t {
+  void (*BTA_GATTC_CancelOpen)(tGATT_IF client_if, const RawAddress& remote_bda,
+                               bool is_direct);
+  void (*BTA_GATTC_Refresh)(const RawAddress& remote_bda);
+  void (*BTA_GATTC_GetGattDb)(uint16_t conn_id, uint16_t start_handle,
+                              uint16_t end_handle, btgatt_db_element_t** db,
+                              int* count);
+  void (*BTA_GATTC_AppRegister)(tBTA_GATTC_CBACK* p_client_cb,
+                                BtaAppRegisterCallback cb, bool eatt_support);
+  void (*BTA_GATTC_Close)(uint16_t conn_id);
+  void (*BTA_GATTC_ServiceSearchRequest)(uint16_t conn_id,
+                                         const bluetooth::Uuid* p_srvc_uuid);
+  void (*BTA_GATTC_Open)(tGATT_IF client_if, const RawAddress& remote_bda,
+                         tBTM_BLE_CONN_TYPE connection_type,
+                         bool opportunistic);
+};
+
+//
+// Returns the current GATT client interface
+//
+gatt_interface_t& get_gatt_interface();
+
+//
+// Appends a callback entry into GATT client API/callback history
+//
+void gatt_history_callback(const std::string entry);
+
+//
+// Dumps the GATT client API/callback history to dumpsys
+//
+void DumpsysBtaDmGattClient(int fd);
+
+namespace bluetooth {
+namespace testing {
+
+//
+// TESTING: Sets a specialzed GATT client interface implementation for testing
+//
+void set_gatt_interface(const gatt_interface_t& interface);
+
+}  // namespace testing
+}  // namespace bluetooth
diff --git a/system/bta/dm/bta_dm_int.h b/system/bta/dm/bta_dm_int.h
index 1a7710c..e15bfbc 100644
--- a/system/bta/dm/bta_dm_int.h
+++ b/system/bta/dm/bta_dm_int.h
@@ -249,20 +249,37 @@
   bool in_use;
 
  private:
-  friend void bta_dm_acl_up(const RawAddress& bd_addr, tBT_TRANSPORT transport,
-                            uint16_t acl_handle);
-  friend void bta_dm_pm_btm_status(const RawAddress& bd_addr,
-                                   tBTM_PM_STATUS status, uint16_t value,
-                                   tHCI_STATUS hci_status);
-  friend void bta_dm_pm_sniff(struct tBTA_DM_PEER_DEVICE* p_peer_dev,
-                              uint8_t index);
-  friend void bta_dm_rm_cback(tBTA_SYS_CONN_STATUS status, uint8_t id,
-                              uint8_t app_id, const RawAddress& peer_addr);
-  friend void handle_remote_features_complete(const RawAddress& bd_addr);
-  tBTA_DM_DEV_INFO info;
+  // Dynamic pieces of operational device information
+  tBTA_DM_DEV_INFO info{BTA_DM_DI_NONE};
 
  public:
-  tBTA_DM_DEV_INFO Info() const { return info; }
+  std::string info_text() const { return device_info_text(info); }
+
+  void reset_device_info() { info = BTA_DM_DI_NONE; }
+
+  void set_av_active() { info |= BTA_DM_DI_AV_ACTIVE; }
+  void reset_av_active() { info &= ~BTA_DM_DI_AV_ACTIVE; }
+  bool is_av_active() const { return info & BTA_DM_DI_AV_ACTIVE; }
+
+  void set_local_init_sniff() { info |= BTA_DM_DI_INT_SNIFF; }
+  bool is_local_init_sniff() const { return info & BTA_DM_DI_INT_SNIFF; }
+  void set_remote_init_sniff() { info |= BTA_DM_DI_ACP_SNIFF; }
+  bool is_remote_init_sniff() const { return info & BTA_DM_DI_ACP_SNIFF; }
+
+  void set_sniff_command_sent() { info |= BTA_DM_DI_SET_SNIFF; }
+  void reset_sniff_command_sent() { info &= ~BTA_DM_DI_SET_SNIFF; }
+  bool is_sniff_command_sent() const { return info & BTA_DM_DI_SET_SNIFF; }
+
+  // NOTE: Why is this not used as a bitmask
+  void set_both_device_ssr_capable() { info = BTA_DM_DI_USE_SSR; }
+
+  void reset_sniff_flags() {
+    info &= ~(BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF | BTA_DM_DI_SET_SNIFF);
+  }
+
+  void set_ssr_active() { info |= BTA_DM_DI_USE_SSR; }
+  void reset_ssr_active() { info &= ~BTA_DM_DI_USE_SSR; }
+  bool is_ssr_active() const { return info & BTA_DM_DI_USE_SSR; }
 
   tBTA_DM_ENCRYPT_CBACK* p_encrypt_cback;
   tBTM_PM_STATUS prev_low; /* previous low power mode used */
diff --git a/system/bta/dm/bta_dm_main.cc b/system/bta/dm/bta_dm_main.cc
index af60d17..90f7845 100644
--- a/system/bta/dm/bta_dm_main.cc
+++ b/system/bta/dm/bta_dm_main.cc
@@ -25,9 +25,11 @@
 #include <stddef.h>
 
 #include "bt_trace.h"
+#include "bta/dm/bta_dm_gatt_client.h"
 #include "bta/dm/bta_dm_int.h"
 #include "gd/common/circular_buffer.h"
 #include "gd/common/strings.h"
+#include "main/shim/dumpsys.h"
 #include "stack/btm/btm_int_types.h"
 #include "stack/include/bt_hdr.h"
 #include "stack/include/bt_types.h"
@@ -247,11 +249,6 @@
   }
   LOG_DUMPSYS(fd, " current bta_dm_search_state:%s",
               bta_dm_state_text(bta_dm_search_get_state()).c_str());
-  auto gatt_history = gatt_history_.Pull();
-  LOG_DUMPSYS(fd, " last %zu gatt history entries", gatt_history.size());
-  for (const auto& it : gatt_history) {
-    LOG_DUMPSYS(fd, "   %s %s", EpochMillisToString(it.timestamp).c_str(),
-                it.entry.c_str());
-  }
+  DumpsysBtaDmGattClient(fd);
 }
 #undef DUMPSYS_TAG
diff --git a/system/bta/dm/bta_dm_pm.cc b/system/bta/dm/bta_dm_pm.cc
index 7cad6a4..d749f33 100644
--- a/system/bta/dm/bta_dm_pm.cc
+++ b/system/bta/dm/bta_dm_pm.cc
@@ -43,7 +43,7 @@
 #include "stack/include/btu.h"  // do_in_main_thread
 #include "types/raw_address.h"
 
-static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, uint8_t id,
+static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, const tBTA_SYS_ID id,
                             uint8_t app_id, const RawAddress& peer_addr);
 static void bta_dm_pm_set_mode(const RawAddress& peer_addr,
                                tBTA_DM_PM_ACTION pm_mode,
@@ -341,13 +341,13 @@
  * Returns          void
  *
  ******************************************************************************/
-static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, uint8_t id,
+static void bta_dm_pm_cback(tBTA_SYS_CONN_STATUS status, const tBTA_SYS_ID id,
                             uint8_t app_id, const RawAddress& peer_addr) {
   uint8_t i, j;
   tBTA_DM_PEER_DEVICE* p_dev;
   tBTA_DM_PM_REQ pm_req = BTA_DM_PM_NEW_REQ;
 
-  LOG_DEBUG("Power management callback status:%s[%hhu] id:%s[%d], app:%hhu",
+  LOG_DEBUG("Power management callback status:%s[%hhu] id:%s[%hhu], app:%hhu",
             bta_sys_conn_status_text(status).c_str(), status,
             BtaIdSysText(id).c_str(), id, app_id);
 
@@ -366,21 +366,20 @@
     return;
   }
 
-  LOG_DEBUG("Stopped all timers for service to device:%s id:%hhu",
-            ADDRESS_TO_LOGGABLE_CSTR(peer_addr), id);
-  bta_dm_pm_stop_timer_by_srvc_id(peer_addr, id);
+  LOG_DEBUG("Stopped all timers for service to device:%s id:%s[%hhu]",
+            ADDRESS_TO_LOGGABLE_CSTR(peer_addr), BtaIdSysText(id).c_str(), id);
+  bta_dm_pm_stop_timer_by_srvc_id(peer_addr, static_cast<uint8_t>(id));
 
   p_dev = bta_dm_find_peer_device(peer_addr);
   if (p_dev) {
-    LOG_DEBUG("Device info:%s", device_info_text(p_dev->Info()).c_str());
+    LOG_DEBUG("Device info:%s", p_dev->info_text().c_str());
   } else {
     LOG_ERROR("Unable to find peer device...yet soldiering on...");
   }
 
   /* set SSR parameters on SYS CONN OPEN */
   int index = BTA_DM_PM_SSR0;
-  if ((BTA_SYS_CONN_OPEN == status) && p_dev &&
-      (p_dev->Info() & BTA_DM_DI_USE_SSR)) {
+  if ((BTA_SYS_CONN_OPEN == status) && p_dev && (p_dev->is_ssr_active())) {
     index = get_bta_dm_pm_spec()[p_bta_dm_pm_cfg[i].spec_idx].ssr;
   } else if (BTA_ID_AV == id) {
     if (BTA_SYS_CONN_BUSY == status) {
@@ -779,9 +778,9 @@
              ADDRESS_TO_LOGGABLE_CSTR(p_peer_dev->peer_bdaddr));
   }
   tBTM_PM_STATUS mode_status = static_cast<tBTM_PM_STATUS>(mode);
-  LOG_DEBUG("Current power mode:%s[0x%x] peer_info:%s[0x%02x]",
+  LOG_DEBUG("Current power mode:%s[0x%x] peer_info:%s",
             power_mode_status_text(mode_status).c_str(), mode_status,
-            device_info_text(p_peer_dev->Info()).c_str(), p_peer_dev->Info());
+            p_peer_dev->info_text().c_str());
 
   uint8_t* p_rem_feat = BTM_ReadRemoteFeatures(p_peer_dev->peer_bdaddr);
 
@@ -789,13 +788,12 @@
   if (mode != BTM_PM_MD_SNIFF ||
       (controller->supports_sniff_subrating() && p_rem_feat &&
        HCI_SNIFF_SUB_RATE_SUPPORTED(p_rem_feat) &&
-       !(p_peer_dev->Info() & BTA_DM_DI_USE_SSR))) {
+       !(p_peer_dev->is_ssr_active()))) {
     /* Dont initiate Sniff if controller has alreay accepted
      * remote sniff params. This avoid sniff loop issue with
      * some agrresive headsets who use sniff latencies more than
      * DUT supported range of Sniff intervals.*/
-    if ((mode == BTM_PM_MD_SNIFF) &&
-        (p_peer_dev->Info() & BTA_DM_DI_ACP_SNIFF)) {
+    if ((mode == BTM_PM_MD_SNIFF) && (p_peer_dev->is_remote_init_sniff())) {
       LOG_DEBUG("Link already in sniff mode peer:%s",
                 ADDRESS_TO_LOGGABLE_CSTR(p_peer_dev->peer_bdaddr));
       return;
@@ -805,24 +803,22 @@
    * If sniff, but SSR is not used in this link, still issue the command */
   tBTM_PM_PWR_MD sniff_entry = get_sniff_entry(index);
   memcpy(&pwr_md, &sniff_entry, sizeof(tBTM_PM_PWR_MD));
-  if (p_peer_dev->Info() & BTA_DM_DI_INT_SNIFF) {
+  if (p_peer_dev->is_local_init_sniff()) {
     LOG_DEBUG("Trying to force power mode");
     pwr_md.mode |= BTM_PM_MD_FORCE;
   }
   status = BTM_SetPowerMode(bta_dm_cb.pm_id, p_peer_dev->peer_bdaddr, &pwr_md);
   if (status == BTM_CMD_STORED || status == BTM_CMD_STARTED) {
-    p_peer_dev->info &= ~(BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF);
-    p_peer_dev->info |= BTA_DM_DI_SET_SNIFF;
+    p_peer_dev->reset_sniff_flags();
+    p_peer_dev->set_sniff_command_sent();
   } else if (status == BTM_SUCCESS) {
     APPL_TRACE_DEBUG("bta_dm_pm_sniff BTM_SetPowerMode() returns BTM_SUCCESS");
-    p_peer_dev->info &=
-        ~(BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF | BTA_DM_DI_SET_SNIFF);
+    p_peer_dev->reset_sniff_flags();
   } else {
     LOG_ERROR("Unable to set power mode peer:%s status:%s",
               ADDRESS_TO_LOGGABLE_CSTR(p_peer_dev->peer_bdaddr),
               btm_status_text(status).c_str());
-    p_peer_dev->info &=
-        ~(BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF | BTA_DM_DI_SET_SNIFF);
+    p_peer_dev->reset_sniff_flags();
   }
 }
 /*******************************************************************************
@@ -1009,7 +1005,6 @@
     return;
   }
 
-  tBTA_DM_DEV_INFO info = p_dev->Info();
   /* check new mode */
   switch (status) {
     case BTM_PM_STS_ACTIVE:
@@ -1017,8 +1012,7 @@
       we should not try it again*/
       if (hci_status != 0) {
         APPL_TRACE_ERROR("%s hci_status=%d", __func__, hci_status);
-        p_dev->info &=
-            ~(BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF | BTA_DM_DI_SET_SNIFF);
+        p_dev->reset_sniff_flags();
 
         if (p_dev->pm_mode_attempted & (BTA_DM_PM_PARK | BTA_DM_PM_SNIFF)) {
           p_dev->pm_mode_failed |=
@@ -1044,7 +1038,7 @@
       /* save the previous low power mode - for SSR.
        * SSR parameters are sent to controller on "conn open".
        * the numbers stay good until park/hold/detach */
-      if (p_dev->info & BTA_DM_DI_USE_SSR) p_dev->prev_low = status;
+      if (p_dev->is_ssr_active()) p_dev->prev_low = status;
       break;
 
     case BTM_PM_STS_SSR:
@@ -1052,11 +1046,11 @@
         LOG_WARN("Received error when attempting to set sniff subrating mode");
       }
       if (interval) {
-        p_dev->info |= BTA_DM_DI_USE_SSR;
+        p_dev->set_ssr_active();
         LOG_DEBUG("Enabling sniff subrating mode for peer:%s",
                   ADDRESS_TO_LOGGABLE_CSTR(bd_addr));
       } else {
-        p_dev->info &= ~BTA_DM_DI_USE_SSR;
+        p_dev->reset_ssr_active();
         LOG_DEBUG("Disabling sniff subrating mode for peer:%s",
                   ADDRESS_TO_LOGGABLE_CSTR(bd_addr));
       }
@@ -1071,17 +1065,17 @@
          */
         bta_dm_pm_stop_timer(bd_addr);
       } else {
-        p_dev->info &=
-            ~(BTA_DM_DI_SET_SNIFF | BTA_DM_DI_INT_SNIFF | BTA_DM_DI_ACP_SNIFF);
-        if (info & BTA_DM_DI_SET_SNIFF)
-          p_dev->info |= BTA_DM_DI_INT_SNIFF;
+        bool is_sniff_command_sent = p_dev->is_sniff_command_sent();
+        p_dev->reset_sniff_flags();
+        if (is_sniff_command_sent)
+          p_dev->set_local_init_sniff();
         else
-          p_dev->info |= BTA_DM_DI_ACP_SNIFF;
+          p_dev->set_remote_init_sniff();
       }
       break;
 
     case BTM_PM_STS_ERROR:
-      p_dev->info &= ~BTA_DM_DI_SET_SNIFF;
+      p_dev->reset_sniff_command_sent();
       break;
 
     default:
diff --git a/system/bta/gatt/bta_gattc_api.cc b/system/bta/gatt/bta_gattc_api.cc
index 061346b..9356936 100644
--- a/system/bta/gatt/bta_gattc_api.cc
+++ b/system/bta/gatt/bta_gattc_api.cc
@@ -152,12 +152,12 @@
                       .event = BTA_GATTC_API_OPEN_EVT,
                   },
               .remote_bda = remote_bda,
-              .remote_addr_type = addr_type,
               .client_if = client_if,
               .connection_type = connection_type,
               .transport = transport,
               .initiating_phys = initiating_phys,
               .opportunistic = opportunistic,
+              .remote_addr_type = addr_type,
           },
   };
 
diff --git a/system/bta/gatt/bta_gattc_cache.cc b/system/bta/gatt/bta_gattc_cache.cc
index e13d6a8..6d5db19 100644
--- a/system/bta/gatt/bta_gattc_cache.cc
+++ b/system/bta/gatt/bta_gattc_cache.cc
@@ -247,9 +247,8 @@
         BTA_GATTC_DISCOVER_REQ_READ_EXT_PROP_DESC;
 
     if (p_srvc_cb->read_multiple_not_supported || descriptors.size() == 1) {
-      tGATT_READ_PARAM read_param{
-          .by_handle = {.handle = descriptors.front(),
-                        .auth_req = GATT_AUTH_REQ_NONE}};
+      tGATT_READ_PARAM read_param{.by_handle = {.auth_req = GATT_AUTH_REQ_NONE,
+                                                .handle = descriptors.front()}};
       GATTC_Read(conn_id, GATT_READ_BY_HANDLE, &read_param);
       // asynchronous continuation in bta_gattc_op_cmpl_during_discovery
       return;
diff --git a/system/bta/has/has_client.cc b/system/bta/has/has_client.cc
index 1b7cfe7..739b302 100644
--- a/system/bta/has/has_client.cc
+++ b/system/bta/has/has_client.cc
@@ -1920,6 +1920,12 @@
 
     DLOG(INFO) << __func__;
 
+    /* verify link is encrypted */
+    if (!BTM_IsEncrypted(device->addr, BT_TRANSPORT_LE)) {
+      LOG_WARN("Device not yet bonded - waiting for encryption");
+      return;
+    }
+
     /* Ignore if our service data is valid (service discovery initiated by
      * someone else?)
      */
diff --git a/system/bta/has/has_client_test.cc b/system/bta/has/has_client_test.cc
index 0e8a0cf..ae489b2 100644
--- a/system/bta/has/has_client_test.cc
+++ b/system/bta/has/has_client_test.cc
@@ -841,8 +841,8 @@
     if (!allow_fake_conn) ASSERT_NE(connected_devices.count(conn_id), 0u);
 
     tBTA_GATTC_CLOSE event_data = {
-        .status = GATT_SUCCESS,
         .conn_id = conn_id,
+        .status = GATT_SUCCESS,
         .client_if = gatt_if,
         .remote_bda = connected_devices[conn_id],
         .reason = reason,
@@ -854,8 +854,8 @@
 
   void InjectSearchCompleteEvent(uint16_t conn_id) {
     tBTA_GATTC_SEARCH_CMPL event_data = {
-        .status = GATT_SUCCESS,
         .conn_id = conn_id,
+        .status = GATT_SUCCESS,
     };
 
     gatt_callback(BTA_GATTC_SEARCH_CMPL_EVT, (tBTA_GATTC*)&event_data);
@@ -1291,6 +1291,37 @@
   TestConnect(test_address);
 }
 
+TEST_F(HasClientTest, test_service_discovery_complete_before_encryption) {
+  const RawAddress test_address = GetTestAddress(1);
+  SetSampleDatabaseHasPresetsNtf(
+      test_address, bluetooth::has::kFeatureBitHearingAidTypeBinaural);
+
+  EXPECT_CALL(*callbacks,
+              OnConnectionState(ConnectionState::DISCONNECTED, test_address))
+      .Times(0);
+  EXPECT_CALL(*callbacks,
+              OnConnectionState(ConnectionState::CONNECTED, test_address))
+      .Times(0);
+
+  SetEncryptionResult(test_address, false);
+  ON_CALL(btm_interface, SetEncryption(_, _, _, _, _))
+      .WillByDefault(Return(BTM_SUCCESS));
+
+  TestConnect(test_address);
+  auto test_conn_id = GetTestConnId(test_address);
+  InjectSearchCompleteEvent(test_conn_id);
+
+  Mock::VerifyAndClearExpectations(callbacks.get());
+
+  EXPECT_CALL(*callbacks,
+              OnConnectionState(ConnectionState::CONNECTED, test_address))
+      .Times(1);
+
+  SetEncryptionResult(test_address, true);
+  InjectEncryptionEvent(test_address);
+  Mock::VerifyAndClearExpectations(callbacks.get());
+}
+
 TEST_F(HasClientTest, test_reconnect_after_encryption_failed) {
   const RawAddress test_address = GetTestAddress(1);
   SetSampleDatabaseHasNoPresetChange(
diff --git a/system/bta/hf_client/bta_hf_client_sdp.cc b/system/bta/hf_client/bta_hf_client_sdp.cc
index fce2d68..c7fc77b 100644
--- a/system/bta/hf_client/bta_hf_client_sdp.cc
+++ b/system/bta/hf_client/bta_hf_client_sdp.cc
@@ -259,7 +259,9 @@
     /* get features */
     p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
         p_rec, ATTR_ID_SUPPORTED_FEATURES);
-    if (p_attr != NULL) {
+    if (p_attr != NULL &&
+        SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
       /* Found attribute. Get value. */
       /* There might be race condition between SDP and BRSF.  */
       /* Do not update if we already received BRSF.           */
@@ -275,7 +277,9 @@
         /* get network for ability to reject calls */
         p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
             p_rec, ATTR_ID_NETWORK);
-        if (p_attr != NULL) {
+        if (p_attr != NULL &&
+            SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+            SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
           if (p_attr->attr_value.v.u16 == 0x01) {
             client_cb->peer_features |= BTA_HF_CLIENT_PEER_REJECT;
           }
diff --git a/system/bta/hh/bta_hh_le.cc b/system/bta/hh/bta_hh_le.cc
index e5c8030..1047ed0 100644
--- a/system/bta/hh/bta_hh_le.cc
+++ b/system/bta/hh/bta_hh_le.cc
@@ -1177,14 +1177,14 @@
     const tBTA_HH_DATA data = {
         .le_close =
             {
-                .conn_id = gattc_data.conn_id,
-                .reason = gattc_data.reason,
                 .hdr =
                     {
                         .event = BTA_HH_GATT_CLOSE_EVT,
                         .layer_specific =
                             static_cast<uint16_t>(p_cb->hid_handle),
                     },
+                .conn_id = gattc_data.conn_id,
+                .reason = gattc_data.reason,
             },
     };
     bta_hh_sm_execute(p_cb, BTA_HH_GATT_CLOSE_EVT, &data);
@@ -1646,12 +1646,12 @@
   tBTA_HH data = {
       .conn =
           {
-              .handle = p_cb->hid_handle,
               .bda = p_cb->addr,
-              .le_hid = true,
-              .scps_supported = p_cb->scps_supported,
               .status = (le_close->reason != GATT_CONN_OK) ? BTA_HH_ERR
                                                            : p_cb->status,
+              .handle = p_cb->hid_handle,
+              .le_hid = true,
+              .scps_supported = p_cb->scps_supported,
           },
   };
 
@@ -2104,13 +2104,13 @@
   const tBTA_HH_DATA data = {
       .le_close =
           {
-              .conn_id = p_cb->conn_id,
-              .reason = GATT_CONN_OK,
               .hdr =
                   {
                       .event = BTA_HH_GATT_CLOSE_EVT,
                       .layer_specific = static_cast<uint16_t>(p_cb->hid_handle),
                   },
+              .conn_id = p_cb->conn_id,
+              .reason = GATT_CONN_OK,
           },
   };
   bta_hh_sm_execute(p_cb, BTA_HH_GATT_CLOSE_EVT, &data);
diff --git a/system/bta/include/bta_le_audio_api.h b/system/bta/include/bta_le_audio_api.h
index 1d61bc3..ad7e742 100644
--- a/system/bta/include/bta_le_audio_api.h
+++ b/system/bta/include/bta_le_audio_api.h
@@ -42,7 +42,7 @@
       base::Closure initCb, base::Callback<bool()> hal_2_1_verifier,
       const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>&
           offloading_preference);
-  static void Cleanup(base::Callback<void()> cleanupCb);
+  static void Cleanup(void);
   static LeAudioClient* Get(void);
   static void DebugDump(int fd);
 
@@ -90,7 +90,4 @@
   static bool GetAsesForStorage(const RawAddress& addr,
                                 std::vector<uint8_t>& out);
   static bool IsLeAudioClientRunning();
-
-  static void InitializeAudioSetConfigurationProvider(void);
-  static void CleanupAudioSetConfigurationProvider(void);
 };
diff --git a/system/bta/le_audio/OWNERS b/system/bta/le_audio/OWNERS
new file mode 100644
index 0000000..c3c1ccc
--- /dev/null
+++ b/system/bta/le_audio/OWNERS
@@ -0,0 +1 @@
+include platform/packages/modules/Bluetooth:/OWNERS_leaudio
diff --git a/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc b/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc
index 75fbcec..5493b41 100644
--- a/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc
+++ b/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc
@@ -235,7 +235,7 @@
 
     // Init sink Audio HAL mock
     is_sink_audio_hal_acquired = false;
-    sink_audio_hal_stream_cb = {.on_suspend_ = nullptr, .on_resume_ = nullptr};
+    sink_audio_hal_stream_cb = {.on_resume_ = nullptr, .on_suspend_ = nullptr};
 
     ON_CALL(mock_client_interface_, GetSink(_, _, _))
         .WillByDefault(DoAll(SaveArg<0>(&sink_audio_hal_stream_cb),
@@ -246,8 +246,8 @@
 
     // Init source Audio HAL mock
     is_source_audio_hal_acquired = false;
-    source_audio_hal_stream_cb = {.on_suspend_ = nullptr,
-                                  .on_resume_ = nullptr};
+    source_audio_hal_stream_cb = {.on_resume_ = nullptr,
+                                  .on_suspend_ = nullptr};
 
     ON_CALL(mock_client_interface_, GetSource(_, _))
         .WillByDefault(DoAll(SaveArg<0>(&source_audio_hal_stream_cb),
diff --git a/system/bta/le_audio/audio_set_scenarios.json b/system/bta/le_audio/audio_set_scenarios.json
index 751ad54..7ed64b1 100644
--- a/system/bta/le_audio/audio_set_scenarios.json
+++ b/system/bta/le_audio/audio_set_scenarios.json
@@ -8,34 +8,34 @@
         {
             "name": "Conversational",
             "configurations": [
+                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
+                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_1",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_2",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_1",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_2",
-                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
-                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
+                "DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
+                "DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
                 "DualDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanMonoSrc_16_2_1",
                 "DualDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanMonoSrc_16_1_1",
-                "DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
-                "DualDev_OneChanStereoSnk_OneChanMonoSrc_32_2_1",
+                "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
+                "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_1",
                 "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
                 "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_2_1",
                 "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
                 "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_16_1_1",
-                "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
-                "DualDev_OneChanDoubleStereoSnk_OneChanMonoSrc_32_2_1",
+                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
+                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_1",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_2",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_1",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_2",
-                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
-                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
@@ -48,12 +48,12 @@
                 "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_1",
                 "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
                 "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_1",
+                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
+                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
-                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
-                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
                 "DualDev_OneChanMonoSrc_16_2_Balanced_Reliability",
                 "SingleDev_OneChanStereoSrc_16_2_Balanced_Reliability",
                 "SingleDev_OneChanMonoSrc_48_4_Balanced_Reliability",
@@ -153,14 +153,14 @@
         {
             "name": "Game",
             "configurations": [
+                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
-                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_48_2_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_48_1_Low_Latency",
+                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
-                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
@@ -168,12 +168,12 @@
                 "VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_TwoChanStereoSrc_16khz_30octs_R3_L12_1",
                 "VND_SingleDev_TwoChanStereoSnk_48khz_75octs_High_Reliability_1",
                 "VND_SingleDev_TwoChanStereoSnk_48khz_75octs_R5_L12_1",
+                "SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
                 "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
                 "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
-                "SingleDev_OneChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
+                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
-                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
                 "DualDev_OneChanStereoSnk_48_2_Low_Latency",
                 "DualDev_OneChanStereoSnk_48_3_Low_Latency",
                 "DualDev_OneChanStereoSnk_48_1_Low_Latency",
@@ -206,32 +206,32 @@
         {
             "name": "VoiceAssistants",
             "configurations": [
+                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
+                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_1",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_1",
-                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
-                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_48_2_Balanced_Reliability",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_48_1_Balanced_Reliability",
+                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
+                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_1",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_1",
-                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
-                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_1",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_1_1",
+                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
+                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
-                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
-                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
                 "DualDev_OneChanStereoSnk_48_4_OneChanStereoSrc_16_2_Balanced_Reliability",
                 "DualDev_OneChanStereoSnk_48_4_OneChanStereoSrc_24_2_Balanced_Reliability",
                 "DualDev_OneChanStereoSnk_48_4_OneChanStereoSrc_32_2_Balanced_Reliability",
@@ -260,20 +260,20 @@
             "configurations": [
                 "VND_SingleDev_TwoChanStereoSrc_48khz_100octs_Balanced_Reliability_1",
                 "VND_SingleDev_TwoChanStereoSrc_48khz_100octs_R11_L40_1",
+                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
+                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_2_1",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_Low_Latency",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_16_1_1",
-                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_Low_Latency",
-                "DualDev_OneChanStereoSnk_OneChanStereoSrc_32_2_1",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_48_2_Balanced_Reliability",
                 "DualDev_OneChanStereoSnk_OneChanStereoSrc_48_1_Balanced_Reliability",
+                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
+                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_2_1",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_16_1_1",
-                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_Low_Latency",
-                "SingleDev_TwoChanStereoSnk_TwoChanStereoSrc_32_2_1",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_Low_Latency",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_32_2_1",
                 "SingleDev_TwoChanStereoSnk_OneChanMonoSrc_16_2_Low_Latency",
@@ -286,12 +286,12 @@
                 "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_2_1",
                 "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_Low_Latency",
                 "SingleDev_OneChanStereoSnk_OneChanMonoSrc_16_1_1",
+                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
+                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_Low_Latency",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_2_1",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_Low_Latency",
                 "SingleDev_OneChanMonoSnk_OneChanMonoSrc_16_1_1",
-                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_Low_Latency",
-                "SingleDev_OneChanMonoSnk_OneChanMonoSrc_32_2_1",
                 "SingleDev_OneChanMonoSrc_48_2_Balanced_Reliability",
                 "SingleDev_OneChanMonoSrc_48_1_Balanced_Reliability",
                 "SingleDev_OneChanMonoSrc_32_2_Balanced_Reliability",
diff --git a/system/bta/le_audio/broadcaster/broadcaster.cc b/system/bta/le_audio/broadcaster/broadcaster.cc
index 81c0a37..b2cd8d4 100644
--- a/system/bta/le_audio/broadcaster/broadcaster.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster.cc
@@ -16,18 +16,19 @@
  */
 
 #include <base/functional/bind.h>
+#include <lc3.h>
 
 #include <mutex>
 
 #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/codec_interface.h"
 #include "bta/le_audio/content_control_id_keeper.h"
 #include "bta/le_audio/le_audio_types.h"
 #include "bta/le_audio/le_audio_utils.h"
 #include "bta/le_audio/metrics_collector.h"
 #include "device/include/controller.h"
-#include "embdrv/lc3/include/lc3.h"
 #include "gd/common/strings.h"
 #include "internal_include/stack_config.h"
 #include "osi/include/log.h"
@@ -371,6 +372,18 @@
     LeAudioLtvMap public_ltv;
     std::vector<LeAudioLtvMap> subgroup_ltvs;
 
+    if (queued_broadcast_.IsQueuedBroadcast()) {
+      LOG_ERROR("Not processed yet queued broadcast");
+      return;
+    }
+
+    if (!queued_broadcast_.CanCreateBroadcast()) {
+      queued_broadcast_.SetQueuedBroadcast(is_public, broadcast_name,
+                                           broadcast_code, public_metadata,
+                                           subgroup_quality, subgroup_metadata);
+      return;
+    }
+
     if (is_public) {
       // Prepare public broadcast announcement format
       bool is_metadata_valid;
@@ -483,8 +496,8 @@
 
       BroadcastStateMachineConfig msg = {
           .is_public = is_public,
-          .broadcast_name = broadcast_name,
           .broadcast_id = broadcast_id,
+          .broadcast_name = broadcast_name,
           .streaming_phy = GetStreamingPhy(),
           .codec_wrapper = codec_config,
           .qos_config = qos_config,
@@ -501,8 +514,8 @@
           le_audio::broadcaster::getStreamConfigForContext(context_type);
       BroadcastStateMachineConfig msg = {
           .is_public = is_public,
-          .broadcast_name = broadcast_name,
           .broadcast_id = broadcast_id,
+          .broadcast_name = broadcast_name,
           .streaming_phy = GetStreamingPhy(),
           .codec_wrapper = codec_qos_pair.first,
           .qos_config = codec_qos_pair.second,
@@ -729,6 +742,18 @@
     }
   }
 
+  void IsoTrafficEventCb(bool is_active) {
+    if (is_active) {
+      queued_broadcast_.SetIsoTrafficFlag();
+    } else {
+      queued_broadcast_.ResetIsoTrafficFlag();
+
+      if (!queued_broadcast_.IsQueuedBroadcast()) return;
+
+      queued_broadcast_.CreateAudioBroadcast();
+    }
+  }
+
   void Dump(int fd) {
     std::stringstream stream;
 
@@ -949,31 +974,21 @@
 
     void CheckAndReconfigureEncoders() {
       auto const& codec_id = codec_wrapper_.GetLeAudioCodecId();
-      if (codec_id.coding_format != kLeAudioCodingFormatLC3) {
-        LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id.coding_format,
-                  codec_id.vendor_company_id, codec_id.vendor_codec_id);
-        return;
-      }
-
-      if (enc_audio_buffers_.size() != codec_wrapper_.GetNumChannels()) {
-        enc_audio_buffers_.resize(codec_wrapper_.GetNumChannels());
-      }
-
-      const int dt_us = codec_wrapper_.GetDataIntervalUs();
-      const int sr_hz = codec_wrapper_.GetSampleRate();
-      const auto encoder_bytes = lc3_encoder_size(dt_us, sr_hz);
-      const auto channel_bytes = codec_wrapper_.GetMaxSduSizePerChannel();
-
       /* TODO: We should act smart and reuse current configurations */
-      encoders_.clear();
-      encoders_mem_.clear();
-      while (encoders_.size() < codec_wrapper_.GetNumChannels()) {
-        auto& encoder_buf = enc_audio_buffers_.at(encoders_.size());
-        encoder_buf.resize(channel_bytes);
+      sw_enc_.clear();
+      while (sw_enc_.size() != codec_wrapper_.GetNumChannels()) {
+        auto codec = le_audio::CodecInterface::CreateInstance(codec_id);
 
-        encoders_mem_.emplace_back(malloc(encoder_bytes), &std::free);
-        encoders_.emplace_back(
-            lc3_setup_encoder(dt_us, sr_hz, 0, encoders_mem_.back().get()));
+        auto codec_status =
+            codec->InitEncoder(codec_wrapper_.GetLeAudioCodecConfiguration(),
+                               codec_wrapper_.GetLeAudioCodecConfiguration());
+        if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) {
+          LOG_ERROR("Channel %d codec setup failed with err: %d",
+                    (uint32_t)sw_enc_.size(), codec_status);
+          return;
+        }
+
+        sw_enc_.emplace_back(std::move(codec));
       }
     }
 
@@ -985,23 +1000,9 @@
       codec_wrapper_ = config;
     }
 
-    void encodeLc3Channel(lc3_encoder_t encoder,
-                          std::vector<uint8_t>& out_buffer,
-                          const std::vector<uint8_t>& data,
-                          int initial_channel_offset, int pitch_samples,
-                          int num_channels) {
-      auto encoder_status =
-          lc3_encode(encoder, LC3_PCM_FORMAT_S16,
-                     (int16_t*)(data.data() + initial_channel_offset),
-                     pitch_samples, out_buffer.size(), out_buffer.data());
-      if (encoder_status != 0) {
-        LOG_ERROR("Encoding error=%d", encoder_status);
-      }
-    }
-
     static void sendBroadcastData(
         const std::unique_ptr<BroadcastStateMachine>& broadcast,
-        std::vector<std::vector<uint8_t>>& encoded_channels) {
+        std::vector<std::unique_ptr<le_audio::CodecInterface>>& encoders) {
       auto const& config = broadcast->GetBigConfig();
       if (config == std::nullopt) {
         LOG_ERROR(
@@ -1012,15 +1013,16 @@
         return;
       }
 
-      if (config->connection_handles.size() < encoded_channels.size()) {
+      if (config->connection_handles.size() < encoders.size()) {
         LOG_ERROR("Not enough BIS'es to broadcast all channels!");
         return;
       }
 
-      for (uint8_t chan = 0; chan < encoded_channels.size(); ++chan) {
-        IsoManager::GetInstance()->SendIsoData(config->connection_handles[chan],
-                                               encoded_channels[chan].data(),
-                                               encoded_channels[chan].size());
+      for (uint8_t chan = 0; chan < encoders.size(); ++chan) {
+        IsoManager::GetInstance()->SendIsoData(
+            config->connection_handles[chan],
+            (const uint8_t*)encoders[chan]->GetDecodedSamples().data(),
+            encoders[chan]->GetDecodedSamples().size() * 2);
       }
     }
 
@@ -1035,9 +1037,9 @@
 
       /* Prepare encoded data for all channels */
       for (uint8_t chan = 0; chan < num_channels; ++chan) {
-        /* TODO: Use encoder agnostic wrapper */
-        encodeLc3Channel(encoders_[chan], enc_audio_buffers_[chan], data,
-                         chan * bytes_per_sample, num_channels, num_channels);
+        auto initial_channel_offset = chan * bytes_per_sample;
+        sw_enc_[chan]->Encode(data.data() + initial_channel_offset,
+                              num_channels, codec_wrapper_.GetFrameLen());
       }
 
       /* Currently there is no way to broadcast multiple distinct streams.
@@ -1049,7 +1051,7 @@
         if ((broadcast->GetState() ==
              BroadcastStateMachine::State::STREAMING) &&
             !broadcast->IsMuted())
-          sendBroadcastData(broadcast, enc_audio_buffers_);
+          sendBroadcastData(broadcast, sw_enc_);
       }
       LOG_VERBOSE("All data sent.");
     }
@@ -1096,11 +1098,84 @@
 
    private:
     BroadcastCodecWrapper codec_wrapper_;
-    std::vector<lc3_encoder_t> encoders_;
-    std::vector<std::unique_ptr<void, decltype(&std::free)>> encoders_mem_;
-    std::vector<std::vector<uint8_t>> enc_audio_buffers_;
+    std::vector<std::unique_ptr<le_audio::CodecInterface>> sw_enc_;
   } audio_receiver_;
 
+  static class QueuedBroadcast {
+   public:
+    bool IsQueuedBroadcast() {
+      LOG_INFO("");
+
+      return is_queued_;
+    }
+
+    void SetQueuedBroadcast(
+        bool is_public, const std::string& broadcast_name,
+        const std::optional<bluetooth::le_audio::BroadcastCode>& broadcast_code,
+        const std::vector<uint8_t>& public_metadata,
+        const std::vector<uint8_t>& subgroup_quality,
+        const std::vector<std::vector<uint8_t>>& subgroup_metadata) {
+      LOG_INFO();
+
+      is_public_ = is_public;
+      broadcast_name_ = broadcast_name;
+      broadcast_code_ = broadcast_code;
+      public_metadata_ = public_metadata;
+      subgroup_quality_ = subgroup_quality;
+      subgroup_metadata_ = subgroup_metadata;
+
+      is_queued_ = true;
+    }
+
+    void CreateAudioBroadcast() {
+      if (!instance || !CanCreateBroadcast()) return;
+
+      LOG_INFO("Create queued broadcast");
+
+      is_queued_ = false;
+
+      instance->CreateAudioBroadcast(is_public_, broadcast_name_,
+                                     broadcast_code_, public_metadata_,
+                                     subgroup_quality_, subgroup_metadata_);
+    }
+
+    void ClearQueuedBroadcast() {
+      LOG_INFO();
+
+      is_queued_ = false;
+    }
+
+    void SetIsoTrafficFlag() {
+      LOG_INFO();
+
+      is_iso_running_ = true;
+    }
+
+    void ResetIsoTrafficFlag() {
+      LOG_INFO();
+
+      is_iso_running_ = false;
+    }
+
+    bool CanCreateBroadcast() {
+      LOG_INFO("%d", is_iso_running_ == false);
+
+      return is_iso_running_ == false;
+    }
+
+   private:
+    /* Queued broadcast data */
+    bool is_public_;
+    std::string broadcast_name_;
+    std::optional<bluetooth::le_audio::BroadcastCode> broadcast_code_;
+    std::vector<uint8_t> public_metadata_;
+    std::vector<uint8_t> subgroup_quality_;
+    std::vector<std::vector<uint8_t>> subgroup_metadata_;
+
+    bool is_iso_running_;
+    bool is_queued_;
+  } queued_broadcast_;
+
   bluetooth::le_audio::LeAudioBroadcasterCallbacks* callbacks_;
   std::map<uint32_t, std::unique_ptr<BroadcastStateMachine>> broadcasts_;
   std::vector<std::unique_ptr<BroadcastStateMachine>> pending_broadcasts_;
@@ -1119,6 +1194,8 @@
     LeAudioBroadcasterImpl::audio_receiver_;
 LeAudioBroadcasterImpl::BroadcastAdvertisingCallbacks
     LeAudioBroadcasterImpl::state_machine_adv_callbacks_;
+LeAudioBroadcasterImpl::QueuedBroadcast
+    LeAudioBroadcasterImpl::queued_broadcast_;
 } /* namespace */
 
 void LeAudioBroadcaster::Initialize(
@@ -1146,6 +1223,14 @@
   instance = new LeAudioBroadcasterImpl(callbacks);
   /* Register HCI event handlers */
   IsoManager::GetInstance()->RegisterBigCallbacks(instance);
+  /* Register for active traffic */
+  IsoManager::GetInstance()->RegisterOnIsoTrafficActiveCallback(
+      [](bool is_active) {
+        if (!instance) {
+          return;
+        }
+        instance->IsoTrafficEventCb(is_active);
+      });
 }
 
 bool LeAudioBroadcaster::IsLeAudioBroadcasterRunning() { return instance; }
diff --git a/system/bta/le_audio/broadcaster/broadcaster_test.cc b/system/bta/le_audio/broadcaster/broadcaster_test.cc
index 0605831..a2a120e 100644
--- a/system/bta/le_audio/broadcaster/broadcaster_test.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster_test.cc
@@ -118,6 +118,7 @@
 class MockAudioHalClientEndpoint;
 MockAudioHalClientEndpoint* mock_audio_source_;
 bool is_audio_hal_acquired;
+void (*iso_active_callback)(bool);
 
 std::unique_ptr<LeAudioSourceAudioHalClient>
 LeAudioSourceAudioHalClient::AcquireBroadcast() {
@@ -218,6 +219,10 @@
       is_audio_hal_acquired = false;
     });
 
+    EXPECT_CALL(*MockIsoManager::GetInstance(),
+                RegisterOnIsoTrafficActiveCallbacks)
+        .WillOnce(SaveArg<0>(&iso_active_callback));
+
     ASSERT_FALSE(LeAudioBroadcaster::IsLeAudioBroadcasterRunning());
     LeAudioBroadcaster::Initialize(&mock_broadcaster_callbacks_,
                                    base::Bind([]() -> bool { return true; }));
@@ -243,6 +248,9 @@
     LeAudioBroadcaster::Cleanup();
     ASSERT_FALSE(LeAudioBroadcaster::IsLeAudioBroadcasterRunning());
 
+    ContentControlIdKeeper::GetInstance()->Stop();
+
+    iso_active_callback = nullptr;
     iso_manager_->Stop();
 
     controller::SetMockControllerInterface(nullptr);
@@ -251,10 +259,12 @@
   uint32_t InstantiateBroadcast(
       std::vector<uint8_t> metadata = default_metadata,
       BroadcastCode code = default_code,
-      uint8_t num_of_groups = default_num_of_groups) {
+      uint8_t num_of_groups = default_num_of_groups, bool is_queued = false) {
     uint32_t broadcast_id = LeAudioBroadcaster::kInstanceIdUndefined;
-    EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastCreated(_, true))
-        .WillOnce(SaveArg<0>(&broadcast_id));
+    if (!is_queued) {
+      EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastCreated(_, true))
+          .WillOnce(SaveArg<0>(&broadcast_id));
+    }
 
     std::vector<uint8_t> quality_array;
     std::vector<std::vector<uint8_t>> metadata_array;
@@ -706,4 +716,41 @@
   // Note: Num of bises at IsoManager level is verified by state machine tests
 }
 
+TEST_F(BroadcasterTest, QueuedBroadcast) {
+  uint32_t broadcast_id = LeAudioBroadcaster::kInstanceIdUndefined;
+
+  iso_active_callback(true);
+
+  EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastCreated(_, true))
+      .WillOnce(SaveArg<0>(&broadcast_id));
+
+  /* Trigger broadcast create but due to active ISO, queue request */
+  InstantiateBroadcast(default_metadata, default_code, default_num_of_groups,
+                       true);
+
+  /* Notify about ISO being free, check if broadcast would be created */
+  iso_active_callback(false);
+  ASSERT_NE(broadcast_id, LeAudioBroadcaster::kInstanceIdUndefined);
+  ASSERT_EQ(broadcast_id,
+            MockBroadcastStateMachine::GetLastInstance()->GetBroadcastId());
+
+  auto& instance_config = MockBroadcastStateMachine::GetLastInstance()->cfg;
+  ASSERT_EQ(instance_config.broadcast_code, default_code);
+  for (auto& subgroup : instance_config.announcement.subgroup_configs) {
+    ASSERT_EQ(types::LeAudioLtvMap(subgroup.metadata).RawPacket(),
+              default_metadata);
+  }
+}
+
+TEST_F(BroadcasterTest, QueuedBroadcastBusyIso) {
+  iso_active_callback(true);
+
+  EXPECT_CALL(mock_broadcaster_callbacks_, OnBroadcastCreated(_, true))
+      .Times(0);
+
+  /* Trigger broadcast create but due to active ISO, queue request */
+  InstantiateBroadcast(default_metadata, default_code, default_num_of_groups,
+                       true);
+}
+
 }  // namespace le_audio
diff --git a/system/bta/le_audio/broadcaster/broadcaster_types.cc b/system/bta/le_audio/broadcaster/broadcaster_types.cc
index e2a5dc8..1d556ef 100644
--- a/system/bta/le_audio/broadcaster/broadcaster_types.cc
+++ b/system/bta/le_audio/broadcaster/broadcaster_types.cc
@@ -17,12 +17,13 @@
 
 #include "broadcaster_types.h"
 
+#include <lc3.h>
+
 #include <vector>
 
 #include "bt_types.h"
 #include "bta_le_audio_broadcaster_api.h"
 #include "btm_ble_api_types.h"
-#include "embdrv/lc3/include/lc3.h"
 #include "internal_include/stack_config.h"
 #include "osi/include/properties.h"
 
@@ -374,10 +375,8 @@
   };
 
   if (codec_id.coding_format == kLeAudioCodecIdLc3.coding_format) {
-    uint16_t bc =
-        lc3_frame_bytes(source_codec_config.data_interval_us, codec_bitrate);
     codec_spec_ltvs[codec_spec_conf::kLeAudioCodecLC3TypeOctetPerFrame] =
-        UINT16_TO_VEC_UINT8(bc);
+        UINT16_TO_VEC_UINT8(codec_frame_len);
   }
 
   if (source_codec_config.num_channels == 1) {
diff --git a/system/bta/le_audio/broadcaster/state_machine.cc b/system/bta/le_audio/broadcaster/state_machine.cc
index 70ae054..ba35c32 100644
--- a/system/bta/le_audio/broadcaster/state_machine.cc
+++ b/system/bta/le_audio/broadcaster/state_machine.cc
@@ -349,7 +349,7 @@
       adv_params.primary_advertising_phy = PHY_LE_1M;
       adv_params.secondary_advertising_phy = streaming_phy;
       adv_params.scan_request_notification_enable = 0;
-      adv_params.own_address_type = BLE_ADDR_RANDOM;
+      adv_params.own_address_type = kBroadcastAdvertisingType;
 
       periodic_params.max_interval = BroadcastStateMachine::kPaIntervalMax;
       periodic_params.min_interval = BroadcastStateMachine::kPaIntervalMin;
diff --git a/system/bta/le_audio/broadcaster/state_machine.h b/system/bta/le_audio/broadcaster/state_machine.h
index da31d89..b3a40ce 100644
--- a/system/bta/le_audio/broadcaster/state_machine.h
+++ b/system/bta/le_audio/broadcaster/state_machine.h
@@ -116,6 +116,9 @@
   // le_advertising_manager will maintain the reg_id together with client_id
   // and java/jni is using negative number
   static constexpr uint8_t kLeAudioBroadcastRegId = 0x1;
+  // Matching the ADDRESS_TYPE_* enums from Java
+  // ADDRESS_TYPE_RANDOM_NON_RESOLVABLE = 2
+  static constexpr int8_t kBroadcastAdvertisingType = 0x2;
 
   static void Initialize(IBroadcastStateMachineCallbacks*,
                          AdvertisingCallbacks* adv_callbacks);
diff --git a/system/bta/le_audio/broadcaster/state_machine_test.cc b/system/bta/le_audio/broadcaster/state_machine_test.cc
index ed35ef9..485e0f4 100644
--- a/system/bta/le_audio/broadcaster/state_machine_test.cc
+++ b/system/bta/le_audio/broadcaster/state_machine_test.cc
@@ -319,8 +319,8 @@
     auto broadcast_id = broadcast_id_lsb++;
     pending_broadcasts_.push_back(BroadcastStateMachine::CreateInstance({
         .is_public = true,
-        .broadcast_name = test_broadcast_name,
         .broadcast_id = broadcast_id,
+        .broadcast_name = test_broadcast_name,
         // .streaming_phy = ,
         .codec_wrapper = codec_qos_pair.first,
         .qos_config = codec_qos_pair.second,
@@ -1003,7 +1003,8 @@
   ASSERT_EQ(p_data[3], ((kBasicAudioAnnouncementServiceUuid >> 8) & 0x00FF));
 
   // Check advertising parameters
-  ASSERT_EQ(adv_params.own_address_type, BLE_ADDR_RANDOM);
+  ASSERT_EQ(adv_params.own_address_type,
+            BroadcastStateMachine::kBroadcastAdvertisingType);
 }
 
 }  // namespace
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 7283d25..abeb5d0 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -17,6 +17,7 @@
 
 #include <base/functional/bind.h>
 #include <base/strings/string_number_conversions.h>
+#include <lc3.h>
 
 #include <deque>
 #include <mutex>
@@ -34,12 +35,12 @@
 #include "btif_profile_storage.h"
 #include "btm_iso_api.h"
 #include "client_parser.h"
+#include "codec_interface.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"
 #include "gatt/bta_gattc_int.h"
 #include "gd/common/strings.h"
 #include "internal_include/stack_config.h"
@@ -166,23 +167,6 @@
                                             int group_id,
                                             LeAudioHealthBasedAction action);
 
-inline uint8_t bits_to_bytes_per_sample(uint8_t bits_per_sample) {
-  // 24 bit audio stream is sent as unpacked, each sample takes 4 bytes.
-  if (bits_per_sample == 24) return 4;
-
-  return bits_per_sample / 8;
-}
-
-inline lc3_pcm_format bits_to_lc3_bits(uint8_t bits_per_sample) {
-  if (bits_per_sample == 16) return LC3_PCM_FORMAT_S16;
-
-  if (bits_per_sample == 24) return LC3_PCM_FORMAT_S24;
-
-  LOG_ALWAYS_FATAL("Encoder/decoder don't know how to handle %d",
-                   bits_per_sample);
-  return LC3_PCM_FORMAT_S16;
-}
-
 class LeAudioClientImpl;
 LeAudioClientImpl* instance;
 std::mutex instance_mutex;
@@ -239,7 +223,7 @@
         active_group_id_(bluetooth::groups::kGroupUnknown),
         configuration_context_type_(LeAudioContextType::UNINITIALIZED),
         local_metadata_context_types_(
-            {sink : AudioContexts(), source : AudioContexts()}),
+            {.sink = AudioContexts(), .source = AudioContexts()}),
         stream_setup_start_timestamp_(0),
         stream_setup_end_timestamp_(0),
         audio_receiver_state_(AudioState::IDLE),
@@ -248,12 +232,6 @@
         in_voip_call_(false),
         current_source_codec_config({0, 0, 0, 0}),
         current_sink_codec_config({0, 0, 0, 0}),
-        lc3_encoder_left_mem(nullptr),
-        lc3_encoder_right_mem(nullptr),
-        lc3_decoder_left_mem(nullptr),
-        lc3_decoder_right_mem(nullptr),
-        lc3_decoder_left(nullptr),
-        lc3_decoder_right(nullptr),
         le_audio_source_hal_client_(nullptr),
         le_audio_sink_hal_client_(nullptr),
         close_vbc_timeout_(alarm_new("LeAudioCloseVbcTimeout")),
@@ -1877,12 +1855,10 @@
 
     LOG_INFO("%s, status 0x%02x", ADDRESS_TO_LOGGABLE_CSTR(address), status);
 
-    /* Remove device from the background connect (it might be either Allow list
-     * or TA) and it will be added back on disconnection
-     */
-    BTA_GATTC_CancelOpen(gatt_if_, address, false);
-
     if (status != GATT_SUCCESS) {
+      /* Clear current connection request and let it be set again if needed */
+      BTA_GATTC_CancelOpen(gatt_if_, address, false);
+
       /* autoconnect connection failed, that's ok */
       if (status != GATT_ILLEGAL_PARAMETER &&
           (leAudioDevice->GetConnectionState() ==
@@ -1908,6 +1884,8 @@
     if (leAudioDevice->group_id_ != bluetooth::groups::kGroupUnknown) {
       auto group = GetGroupIfEnabled(leAudioDevice->group_id_);
       if (group == nullptr) {
+        BTA_GATTC_CancelOpen(gatt_if_, address, false);
+
         LOG_WARN(
             "LeAudio profile is disabled for group_id: %d. %s is not connected",
             leAudioDevice->group_id_, ADDRESS_TO_LOGGABLE_CSTR(address));
@@ -2090,8 +2068,15 @@
      * announcements)
      */
     auto group = aseGroups_.FindById(group_id);
-    if (group == nullptr || !group->IsAnyDeviceConnected()) {
-      LOG_INFO("Group %d is not streaming", group_id);
+    if (group == nullptr) {
+      LOG_INFO("Group %d is destroyed.", group_id);
+      return;
+    }
+
+    if (!group->IsAnyDeviceConnected()) {
+      LOG_INFO("Group %d is not connected", group_id);
+      /* Make sure all devices are in the default reconnection mode */
+      group->ApplyReconnectionMode(gatt_if_, reconnection_mode_);
       return;
     }
 
@@ -2232,6 +2217,11 @@
       return;
     }
 
+    /* Remove device from the background connect (it might be either Allow list
+     * or TA) and it will be added back depends on needs.
+     */
+    BTA_GATTC_CancelOpen(gatt_if_, address, false);
+
     BtaGattQueue::Clean(leAudioDevice->conn_id_);
     LeAudioDeviceGroup* group = aseGroups_.FindById(leAudioDevice->group_id_);
 
@@ -2308,16 +2298,14 @@
 
     /* In other disconnect resons we act based on the autoconnect_flag_ */
     if (leAudioDevice->autoconnect_flag_) {
-      leAudioDevice->SetConnectionState(
-          DeviceConnectState::CONNECTING_AUTOCONNECT);
-
-      BTA_GATTC_Open(gatt_if_, address, reconnection_mode_, false);
       if (group->IsAnyDeviceConnected()) {
         /* If all set is disconnecting, let's give it some time.
          * If not all get disconnected, and there will be group member
          * connected we want to put disconnected devices to allow list
          */
         scheduleGroupConnectedCheck(leAudioDevice->group_id_);
+      } else {
+        group->ApplyReconnectionMode(gatt_if_, reconnection_mode_);
       }
     }
   }
@@ -2483,6 +2471,11 @@
       return;
     }
 
+    if (!leAudioDevice->encrypted_) {
+      LOG_WARN("Device not yet bonded - waiting for encryption");
+      return;
+    }
+
     const std::list<gatt::Service>* services = BTA_GATTC_GetServices(conn_id);
 
     const gatt::Service* pac_svc = nullptr;
@@ -2494,19 +2487,23 @@
 
     for (const gatt::Service& tmp : *services) {
       if (tmp.uuid == le_audio::uuid::kPublishedAudioCapabilityServiceUuid) {
-        LOG(INFO) << "Found Audio Capability service, handle: "
-                  << loghex(tmp.handle);
+        LOG_INFO("Found Audio Capability service, handle: 0x%04x, device: %s",
+                 tmp.handle, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
         pac_svc = &tmp;
       } else if (tmp.uuid == le_audio::uuid::kAudioStreamControlServiceUuid) {
-        LOG(INFO) << "Found Audio Stream Endpoint service, handle: "
-                  << loghex(tmp.handle);
+        LOG_INFO(
+            "Found Audio Stream Endpoint service, handle: 0x%04x, device: %s",
+            tmp.handle, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
         ase_svc = &tmp;
       } else if (tmp.uuid == bluetooth::csis::kCsisServiceUuid) {
-        LOG(INFO) << "Found CSIS service, handle: " << loghex(tmp.handle)
-                  << " is primary? " << tmp.is_primary;
+        LOG_INFO(
+            "Found CSIS service, handle: 0x%04x, is primary: %d, device: %s",
+            tmp.handle, tmp.is_primary,
+            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
         if (tmp.is_primary) csis_primary_handles.push_back(tmp.handle);
       } else if (tmp.uuid == le_audio::uuid::kCapServiceUuid) {
-        LOG(INFO) << "Found CAP Service, handle: " << loghex(tmp.handle);
+        LOG_INFO("Found CAP service, handle: 0x%04x, device: %s", tmp.handle,
+                 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
 
         /* Try to find context for CSIS instances */
         for (auto& included_srvc : tmp.included_services) {
@@ -2519,8 +2516,10 @@
           }
         }
       } else if (tmp.uuid == le_audio::uuid::kTelephonyMediaAudioServiceUuid) {
-        LOG_INFO(", Found Telephony and Media Audio service, handle: %04x",
-                 tmp.handle);
+        LOG_INFO(
+            "Found Telephony and Media Audio service, handle: 0x%04x, device: "
+            "%s",
+            tmp.handle, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
         tmas_svc = &tmp;
       }
     }
@@ -2552,13 +2551,11 @@
         hdl_pair.ccc_hdl = find_ccc_handle(charac);
 
         if (hdl_pair.ccc_hdl == 0) {
-          disconnectInvalidDevice(leAudioDevice,
-                                  ", snk pac char doesn't have ccc",
-                                  LeAudioHealthDeviceStatType::INVALID_DB);
-          return;
+          LOG_INFO(", Sink PACs ccc not available");
         }
 
-        if (!subscribe_for_notification(conn_id, leAudioDevice->address_,
+        if (hdl_pair.ccc_hdl != 0 &&
+            !subscribe_for_notification(conn_id, leAudioDevice->address_,
                                         hdl_pair)) {
           disconnectInvalidDevice(leAudioDevice,
                                   ", cound not subscribe for snk pac char",
@@ -2573,9 +2570,11 @@
         leAudioDevice->snk_pacs_.push_back(std::make_tuple(
             hdl_pair, std::vector<struct le_audio::types::acs_ac_record>()));
 
-        LOG(INFO) << "Found Sink PAC characteristic, handle: "
-                  << loghex(charac.value_handle)
-                  << ", ccc handle: " << loghex(hdl_pair.ccc_hdl);
+        LOG_INFO(
+            "Found Sink PAC characteristic, handle: 0x%04x, ccc handle: "
+            "0x%04x, addr: %s",
+            charac.value_handle, hdl_pair.ccc_hdl,
+            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
       } else if (charac.uuid ==
                  le_audio::uuid::
                      kSourcePublishedAudioCapabilityCharacteristicUuid) {
@@ -2584,13 +2583,11 @@
         hdl_pair.ccc_hdl = find_ccc_handle(charac);
 
         if (hdl_pair.ccc_hdl == 0) {
-          disconnectInvalidDevice(leAudioDevice,
-                                  ", src pac char doesn't have ccc",
-                                  LeAudioHealthDeviceStatType::INVALID_DB);
-          return;
+          LOG_INFO(", Source PACs ccc not available");
         }
 
-        if (!subscribe_for_notification(conn_id, leAudioDevice->address_,
+        if (hdl_pair.ccc_hdl != 0 &&
+            !subscribe_for_notification(conn_id, leAudioDevice->address_,
                                         hdl_pair)) {
           disconnectInvalidDevice(leAudioDevice,
                                   ", could not subscribe for src pac char",
@@ -2605,9 +2602,11 @@
         leAudioDevice->src_pacs_.push_back(std::make_tuple(
             hdl_pair, std::vector<struct le_audio::types::acs_ac_record>()));
 
-        LOG(INFO) << "Found Source PAC characteristic, handle: "
-                  << loghex(charac.value_handle)
-                  << ", ccc handle: " << loghex(hdl_pair.ccc_hdl);
+        LOG_INFO(
+            "Found Source PAC characteristic, handle: 0x%04x, ccc handle: "
+            "0x%04x, addr: %s",
+            charac.value_handle, hdl_pair.ccc_hdl,
+            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
       } else if (charac.uuid ==
                  le_audio::uuid::kSinkAudioLocationCharacteristicUuid) {
         leAudioDevice->snk_audio_locations_hdls_.val_hdl = charac.value_handle;
@@ -2633,9 +2632,12 @@
             conn_id, leAudioDevice->snk_audio_locations_hdls_.val_hdl,
             OnGattReadRspStatic, NULL);
 
-        LOG(INFO) << "Found Sink audio locations characteristic, handle: "
-                  << loghex(charac.value_handle) << ", ccc handle: "
-                  << loghex(leAudioDevice->snk_audio_locations_hdls_.ccc_hdl);
+        LOG_INFO(
+            "Found Sink audio locations characteristic, handle: 0x%04x, ccc "
+            "handle: 0x%04x, addr: %s",
+            charac.value_handle,
+            leAudioDevice->snk_audio_locations_hdls_.ccc_hdl,
+            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
       } else if (charac.uuid ==
                  le_audio::uuid::kSourceAudioLocationCharacteristicUuid) {
         leAudioDevice->src_audio_locations_hdls_.val_hdl = charac.value_handle;
@@ -2661,9 +2663,12 @@
             conn_id, leAudioDevice->src_audio_locations_hdls_.val_hdl,
             OnGattReadRspStatic, NULL);
 
-        LOG(INFO) << "Found Source audio locations characteristic, handle: "
-                  << loghex(charac.value_handle) << ", ccc handle: "
-                  << loghex(leAudioDevice->src_audio_locations_hdls_.ccc_hdl);
+        LOG_INFO(
+            "Found Source audio locations characteristic, handle: 0x%04x, ccc "
+            "handle: 0x%04x, addr: %s",
+            charac.value_handle,
+            leAudioDevice->src_audio_locations_hdls_.ccc_hdl,
+            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
       } else if (charac.uuid ==
                  le_audio::uuid::kAudioContextAvailabilityCharacteristicUuid) {
         leAudioDevice->audio_avail_hdls_.val_hdl = charac.value_handle;
@@ -2689,16 +2694,18 @@
             conn_id, leAudioDevice->audio_avail_hdls_.val_hdl,
             OnGattReadRspStatic, NULL);
 
-        LOG(INFO) << "Found Audio Availability Context characteristic, handle: "
-                  << loghex(charac.value_handle) << ", ccc handle: "
-                  << loghex(leAudioDevice->audio_avail_hdls_.ccc_hdl);
+        LOG_INFO(
+            "Found Audio Availability Context characteristic, handle: 0x%04x, "
+            "ccc handle: 0x%04x, addr: %s",
+            charac.value_handle, leAudioDevice->audio_avail_hdls_.ccc_hdl,
+            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
       } else if (charac.uuid ==
                  le_audio::uuid::kAudioSupportedContextCharacteristicUuid) {
         leAudioDevice->audio_supp_cont_hdls_.val_hdl = charac.value_handle;
         leAudioDevice->audio_supp_cont_hdls_.ccc_hdl = find_ccc_handle(charac);
 
         if (leAudioDevice->audio_supp_cont_hdls_.ccc_hdl == 0) {
-          LOG_INFO(", audio avails char doesn't have ccc");
+          LOG_INFO(", audio supported char doesn't have ccc");
         }
 
         if (leAudioDevice->audio_supp_cont_hdls_.ccc_hdl != 0 &&
@@ -2716,9 +2723,11 @@
             conn_id, leAudioDevice->audio_supp_cont_hdls_.val_hdl,
             OnGattReadRspStatic, NULL);
 
-        LOG(INFO) << "Found Audio Supported Context characteristic, handle: "
-                  << loghex(charac.value_handle) << ", ccc handle: "
-                  << loghex(leAudioDevice->audio_supp_cont_hdls_.ccc_hdl);
+        LOG_INFO(
+            "Found Audio Supported Context characteristic, handle: 0x%04x, ccc "
+            "handle: 0x%04x, addr: %s",
+            charac.value_handle, leAudioDevice->audio_supp_cont_hdls_.ccc_hdl,
+            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
       }
     }
 
@@ -2752,10 +2761,11 @@
         leAudioDevice->ases_.emplace_back(charac.value_handle, ccc_handle,
                                           direction);
 
-        LOG(INFO) << "Found ASE characteristic, handle: "
-                  << loghex(charac.value_handle)
-                  << ", ccc handle: " << loghex(ccc_handle)
-                  << ", direction: " << direction;
+        LOG_INFO(
+            "Found ASE characteristic, handle: 0x%04x, ccc handle: 0x%04x, "
+            "direction: %d, addr: %s",
+            charac.value_handle, ccc_handle, direction,
+            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
       } else if (charac.uuid ==
                  le_audio::uuid::
                      kAudioStreamEndpointControlPointCharacteristicUuid) {
@@ -2776,9 +2786,11 @@
           return;
         }
 
-        LOG(INFO) << "Found ASE Control Point characteristic, handle: "
-                  << loghex(charac.value_handle) << ", ccc handle: "
-                  << loghex(leAudioDevice->ctp_hdls_.ccc_hdl);
+        LOG_INFO(
+            "Found ASE characteristic, handle: 0x%04x, ccc handle: 0x%04x, "
+            "addr: %s",
+            charac.value_handle, leAudioDevice->ctp_hdls_.ccc_hdl,
+            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
       }
     }
 
@@ -2794,16 +2806,16 @@
                                            OnGattReadRspStatic, NULL);
 
           LOG_INFO(
-              ", Found Telephony and Media Profile characteristic, "
-              "handle: %04x",
-              leAudioDevice->tmap_role_hdl_);
+              "Found Telephony and Media Profile characteristic, handle: "
+              "0x%04x, "
+              "device: %s",
+              leAudioDevice->tmap_role_hdl_,
+              ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
         }
       }
     }
 
     leAudioDevice->known_service_handles_ = true;
-    btif_storage_leaudio_update_handles_bin(leAudioDevice->address_);
-
     leAudioDevice->notify_connected_after_read_ = true;
     if (leAudioHealthStatus_) {
       leAudioHealthStatus_->AddStatisticForDevice(
@@ -2861,8 +2873,8 @@
     }
 
     if (status == GATT_SUCCESS) {
-      LOG(INFO) << __func__
-                << ", successfully registered on ccc: " << loghex(hdl);
+      LOG_INFO("Successfully registered on ccc: 0x%04x, device: %s", hdl,
+               ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
 
       if (leAudioDevice->ctp_hdls_.ccc_hdl == hdl &&
           leAudioDevice->known_service_handles_ &&
@@ -2875,9 +2887,10 @@
       return;
     }
 
-    LOG(ERROR) << __func__
-               << ", Failed to register for indications: " << loghex(hdl)
-               << ", status: " << loghex((int)(status));
+    LOG_ERROR(
+        "Failed to register for indications: 0x%04x, device: %s, status: "
+        "0x%02x",
+        hdl, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), status);
 
     ase_it =
         std::find_if(leAudioDevice->ases_.begin(), leAudioDevice->ases_.end(),
@@ -2886,8 +2899,8 @@
                      });
 
     if (ase_it == leAudioDevice->ases_.end()) {
-      LOG(ERROR) << __func__
-                 << ", unknown ccc handle: " << static_cast<int>(hdl);
+      LOG_ERROR("Unknown ccc handle: 0x%04x, device: %s", hdl,
+                ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
       return;
     }
 
@@ -3009,6 +3022,13 @@
         UpdateLocationsAndContextsAvailability(group);
       }
       AttachToStreamingGroupIfNeeded(leAudioDevice);
+
+      if (reconnection_mode_ == BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS) {
+        /* Add other devices to allow list if there are any not yet connected
+         * from the group
+         */
+        group->AddToAllowListNotConnectedGroupMembers(gatt_if_);
+      }
     }
   }
 
@@ -3056,27 +3076,12 @@
   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;
     uint16_t left_cis_handle = 0;
     uint16_t right_cis_handle = 0;
-    uint16_t number_of_required_samples_per_channel;
 
-    int dt_us = current_source_codec_config.data_interval_us;
-    int af_hz = audio_framework_source_config.sample_rate;
-    number_of_required_samples_per_channel = lc3_frame_samples(dt_us, af_hz);
-
-    lc3_pcm_format bits_per_sample =
-        bits_to_lc3_bits(audio_framework_source_config.bits_per_sample);
-    uint8_t bytes_per_sample =
-        bits_to_bytes_per_sample(audio_framework_source_config.bits_per_sample);
-
-    for (auto [cis_handle, audio_location] : stream_conf->sink_streams) {
-      if (audio_location & le_audio::codec_spec_conf::kLeAudioLocationAnyLeft)
-        left_cis_handle = cis_handle;
-      if (audio_location & le_audio::codec_spec_conf::kLeAudioLocationAnyRight)
-        right_cis_handle = cis_handle;
-    }
-
+    uint16_t number_of_required_samples_per_channel =
+        sw_enc_left->GetNumOfSamplesPerChannel();
+    uint8_t bytes_per_sample = sw_enc_left->GetNumOfBytesPerSample();
     if (data.size() < bytes_per_sample * 2 /* channels */ *
                           number_of_required_samples_per_channel) {
       LOG(ERROR) << __func__ << " Missing samples. Data size: " << +data.size()
@@ -3086,29 +3091,30 @@
       return;
     }
 
-    std::vector<uint8_t> chan_left_enc(byte_count, 0);
-    std::vector<uint8_t> chan_right_enc(byte_count, 0);
+    for (auto [cis_handle, audio_location] :
+         stream_conf->stream_params.sink.stream_locations) {
+      if (audio_location & le_audio::codec_spec_conf::kLeAudioLocationAnyLeft)
+        left_cis_handle = cis_handle;
+      if (audio_location & le_audio::codec_spec_conf::kLeAudioLocationAnyRight)
+        right_cis_handle = cis_handle;
+    }
 
-    bool mono = (left_cis_handle == 0) || (right_cis_handle == 0);
-
-    if (!mono) {
-      lc3_encode(lc3_encoder_left, bits_per_sample, data.data(), 2,
-                 chan_left_enc.size(), chan_left_enc.data());
-      lc3_encode(lc3_encoder_right, bits_per_sample,
-                 data.data() + bytes_per_sample, 2, chan_right_enc.size(),
-                 chan_right_enc.data());
-    } else {
+    uint16_t byte_count =
+        stream_conf->stream_params.sink.octets_per_codec_frame;
+    bool mix_to_mono = (left_cis_handle == 0) || (right_cis_handle == 0);
+    if (mix_to_mono) {
       std::vector<uint8_t> mono = mono_blend(
           data, bytes_per_sample, number_of_required_samples_per_channel);
       if (left_cis_handle) {
-        lc3_encode(lc3_encoder_left, bits_per_sample, mono.data(), 1,
-                   chan_left_enc.size(), chan_left_enc.data());
+        sw_enc_left->Encode(mono.data(), 1, byte_count);
       }
 
       if (right_cis_handle) {
-        lc3_encode(lc3_encoder_right, bits_per_sample, mono.data(), 1,
-                   chan_right_enc.size(), chan_right_enc.data());
+        sw_enc_left->Encode(mono.data(), 1, byte_count);
       }
+    } else {
+      sw_enc_left->Encode(data.data(), 2, byte_count);
+      sw_enc_right->Encode(data.data() + bytes_per_sample, 2, byte_count);
     }
 
     DLOG(INFO) << __func__ << " left_cis_handle: " << +left_cis_handle
@@ -3116,59 +3122,52 @@
     /* Send data to the controller */
     if (left_cis_handle)
       IsoManager::GetInstance()->SendIsoData(
-          left_cis_handle, chan_left_enc.data(), chan_left_enc.size());
+          left_cis_handle,
+          (const uint8_t*)sw_enc_left->GetDecodedSamples().data(),
+          sw_enc_left->GetDecodedSamples().size() * 2);
 
     if (right_cis_handle)
       IsoManager::GetInstance()->SendIsoData(
-          right_cis_handle, chan_right_enc.data(), chan_right_enc.size());
+          right_cis_handle,
+          (const uint8_t*)sw_enc_right->GetDecodedSamples().data(),
+          sw_enc_right->GetDecodedSamples().size() * 2);
   }
 
   void PrepareAndSendToSingleCis(
       const std::vector<uint8_t>& data,
       struct le_audio::stream_configuration* stream_conf) {
-    int num_channels = stream_conf->sink_num_of_channels;
-    uint16_t byte_count = stream_conf->sink_octets_per_codec_frame;
-    auto cis_handle = stream_conf->sink_streams.front().first;
-    uint16_t number_of_required_samples_per_channel;
+    uint16_t num_channels = stream_conf->stream_params.sink.num_of_channels;
+    uint16_t cis_handle =
+        stream_conf->stream_params.sink.stream_locations.front().first;
 
-    int dt_us = current_source_codec_config.data_interval_us;
-    int af_hz = audio_framework_source_config.sample_rate;
-    number_of_required_samples_per_channel = lc3_frame_samples(dt_us, af_hz);
-    lc3_pcm_format bits_per_sample =
-        bits_to_lc3_bits(audio_framework_source_config.bits_per_sample);
-    uint8_t bytes_per_sample =
-        bits_to_bytes_per_sample(audio_framework_source_config.bits_per_sample);
-
-    if ((int)data.size() < (2 /* bytes per sample */ * num_channels *
+    uint16_t number_of_required_samples_per_channel =
+        sw_enc_left->GetNumOfSamplesPerChannel();
+    uint8_t bytes_per_sample = sw_enc_left->GetNumOfBytesPerSample();
+    if ((int)data.size() < (bytes_per_sample * num_channels *
                             number_of_required_samples_per_channel)) {
       LOG(ERROR) << __func__ << "Missing samples";
       return;
     }
-    std::vector<uint8_t> chan_encoded(num_channels * byte_count, 0);
 
-    if (num_channels == 1) {
+    uint16_t byte_count =
+        stream_conf->stream_params.sink.octets_per_codec_frame;
+    bool mix_to_mono = (num_channels == 1);
+    if (mix_to_mono) {
       /* Since we always get two channels from framework, lets make it mono here
        */
       std::vector<uint8_t> mono = mono_blend(
           data, bytes_per_sample, number_of_required_samples_per_channel);
-
-      auto err = lc3_encode(lc3_encoder_left, bits_per_sample, mono.data(), 1,
-                            byte_count, chan_encoded.data());
-
-      if (err < 0) {
-        LOG(ERROR) << " error while encoding, error code: " << +err;
-      }
+      sw_enc_left->Encode(mono.data(), 1, byte_count);
     } else {
-      lc3_encode(lc3_encoder_left, bits_per_sample, (const int16_t*)data.data(),
-                 2, byte_count, chan_encoded.data());
-      lc3_encode(lc3_encoder_right, bits_per_sample,
-                 (const int16_t*)data.data() + 1, 2, byte_count,
-                 chan_encoded.data() + byte_count);
+      sw_enc_left->Encode((const uint8_t*)data.data(), 2, byte_count);
+      // Output to the left channel buffer with `byte_count` offset
+      sw_enc_right->Encode((const uint8_t*)data.data() + 2, 2, byte_count,
+                           &sw_enc_left->GetDecodedSamples(), byte_count);
     }
 
-    /* Send data to the controller */
-    IsoManager::GetInstance()->SendIsoData(cis_handle, chan_encoded.data(),
-                                           chan_encoded.size());
+    IsoManager::GetInstance()->SendIsoData(
+        cis_handle, (const uint8_t*)sw_enc_left->GetDecodedSamples().data(),
+        sw_enc_left->GetDecodedSamples().size() * 2);
   }
 
   const struct le_audio::stream_configuration* GetStreamSinkConfiguration(
@@ -3176,7 +3175,7 @@
     const struct le_audio::stream_configuration* stream_conf =
         &group->stream_conf;
     LOG_INFO("group_id: %d", group->group_id_);
-    if (stream_conf->sink_streams.size() == 0) {
+    if (stream_conf->stream_params.sink.stream_locations.size() == 0) {
       return nullptr;
     }
 
@@ -3196,27 +3195,26 @@
     }
 
     auto stream_conf = group->stream_conf;
-    if ((stream_conf.sink_num_of_devices > 2) ||
-        (stream_conf.sink_num_of_devices == 0) ||
-        stream_conf.sink_streams.empty()) {
+    if ((stream_conf.stream_params.sink.num_of_devices > 2) ||
+        (stream_conf.stream_params.sink.num_of_devices == 0) ||
+        stream_conf.stream_params.sink.stream_locations.empty()) {
       LOG(ERROR) << __func__ << " Stream configufation is not valid.";
       return;
     }
 
-    if (stream_conf.sink_num_of_devices == 2) {
-      PrepareAndSendToTwoCises(data, &stream_conf);
-    } else if (stream_conf.sink_streams.size() == 2) {
-      /* Streaming to one device but 2 CISes */
+    if ((stream_conf.stream_params.sink.num_of_devices == 2) ||
+        (stream_conf.stream_params.sink.stream_locations.size() == 2)) {
+      /* Streaming to two devices or one device with 2 CISes */
       PrepareAndSendToTwoCises(data, &stream_conf);
     } else {
+      /* Streaming to one device and 1 CIS */
       PrepareAndSendToSingleCis(data, &stream_conf);
     }
   }
 
   void CleanCachedMicrophoneData() {
-    cached_channel_data_.clear();
     cached_channel_timestamp_ = 0;
-    cached_channel_is_left_ = false;
+    cached_channel_ = nullptr;
   }
 
   /* Handles audio data packets coming from the controller */
@@ -3234,11 +3232,10 @@
       return;
     }
 
-    auto stream_conf = group->stream_conf;
-
     uint16_t left_cis_handle = 0;
     uint16_t right_cis_handle = 0;
-    for (auto [cis_handle, audio_location] : stream_conf.source_streams) {
+    for (auto [cis_handle, audio_location] :
+         group->stream_conf.stream_params.source.stream_locations) {
       if (audio_location & le_audio::codec_spec_conf::kLeAudioLocationAnyLeft) {
         left_cis_handle = cis_handle;
       }
@@ -3248,97 +3245,43 @@
       }
     }
 
-    bool is_left = true;
+    auto decoder = sw_dec_left.get();
     if (cis_conn_hdl == left_cis_handle) {
-      is_left = true;
+      decoder = sw_dec_left.get();
     } else if (cis_conn_hdl == right_cis_handle) {
-      is_left = false;
+      decoder = sw_dec_right.get();
     } else {
       LOG_ERROR("Received data for unknown handle: %04x", cis_conn_hdl);
       return;
     }
 
-    uint16_t required_for_channel_byte_count =
-        stream_conf.source_octets_per_codec_frame;
-
-    int dt_us = current_sink_codec_config.data_interval_us;
-    int af_hz = audio_framework_sink_config.sample_rate;
-    lc3_pcm_format bits_per_sample =
-        bits_to_lc3_bits(audio_framework_sink_config.bits_per_sample);
-
-    int pcm_size;
-    if (dt_us == 10000) {
-      if (af_hz == 44100)
-        pcm_size = 480;
-      else
-        pcm_size = af_hz / 100;
-    } else if (dt_us == 7500) {
-      if (af_hz == 44100)
-        pcm_size = 360;
-      else
-        pcm_size = (af_hz * 3) / 400;
-    } else {
-      LOG(ERROR) << "BAD dt_us: " << dt_us;
-      return;
-    }
-
-    std::vector<int16_t> pcm_data_decoded(pcm_size, 0);
-
-    int err = 0;
-
-    if (required_for_channel_byte_count != size) {
-      LOG(INFO) << "Insufficient data for decoding and send, required: "
-                << int(required_for_channel_byte_count)
-                << ", received: " << int(size) << ", will do PLC";
-      size = 0;
-      data = nullptr;
-    }
-
-    lc3_decoder_t decoder_to_use =
-        is_left ? lc3_decoder_left : lc3_decoder_right;
-
-    err = lc3_decode(decoder_to_use, data, size, bits_per_sample,
-                     pcm_data_decoded.data(), 1 /* pitch */);
-
-    if (err < 0) {
-      LOG(ERROR) << " bad decoding parameters: " << static_cast<int>(err);
-      return;
-    }
-
-    /* AF == Audio Framework */
-    bool af_is_stereo = (audio_framework_sink_config.num_channels == 2);
-
     if (!left_cis_handle || !right_cis_handle) {
       /* mono or just one device connected */
-      SendAudioDataToAF(false /* bt_got_stereo */, af_is_stereo,
-                        &pcm_data_decoded, nullptr);
+      decoder->Decode(data, size);
+      SendAudioDataToAF(&decoder->GetDecodedSamples());
       return;
     }
     /* both devices are connected */
 
-    if (cached_channel_timestamp_ == 0 && cached_channel_data_.empty()) {
+    if (cached_channel_ == nullptr ||
+        cached_channel_->GetDecodedSamples().empty()) {
       /* First packet received, cache it. We need both channel data to send it
        * to AF. */
-      cached_channel_data_ = pcm_data_decoded;
+      decoder->Decode(data, size);
       cached_channel_timestamp_ = timestamp;
-      cached_channel_is_left_ = is_left;
+      cached_channel_ = decoder;
       return;
     }
 
     /* We received either data for the other audio channel, or another
      * packet for same channel */
-
-    if (cached_channel_is_left_ != is_left) {
+    if (cached_channel_ != decoder) {
       /* It's data for the 2nd channel */
       if (timestamp == cached_channel_timestamp_) {
         /* Ready to mix data and send out to AF */
-        if (is_left) {
-          SendAudioDataToAF(true /* bt_got_stereo */, af_is_stereo,
-                            &cached_channel_data_, &pcm_data_decoded);
-        } else {
-          SendAudioDataToAF(true /* bt_got_stereo */, af_is_stereo,
-                            &pcm_data_decoded, &cached_channel_data_);
-        }
+        decoder->Decode(data, size);
+        SendAudioDataToAF(&sw_dec_left->GetDecodedSamples(),
+                          &sw_dec_right->GetDecodedSamples());
 
         CleanCachedMicrophoneData();
         return;
@@ -3347,18 +3290,11 @@
       /* 2nd Channel is in the future compared to the cached data.
        Send the cached data to AF, and keep the new channel data in cache.
        This should happen only during stream setup */
+      SendAudioDataToAF(&decoder->GetDecodedSamples());
 
-      if (cached_channel_is_left_) {
-        SendAudioDataToAF(false /* bt_got_stereo */, af_is_stereo,
-                          &cached_channel_data_, nullptr);
-      } else {
-        SendAudioDataToAF(false /* bt_got_stereo */, af_is_stereo, nullptr,
-                          &cached_channel_data_);
-      }
-
-      cached_channel_data_ = pcm_data_decoded;
+      decoder->Decode(data, size);
       cached_channel_timestamp_ = timestamp;
-      cached_channel_is_left_ = is_left;
+      cached_channel_ = decoder;
       return;
     }
 
@@ -3366,25 +3302,22 @@
      * data */
 
     /* Send the cached data out */
-    if (cached_channel_is_left_) {
-      SendAudioDataToAF(false /* bt_got_stereo */, af_is_stereo,
-                        &cached_channel_data_, nullptr);
-    } else {
-      SendAudioDataToAF(false /* bt_got_stereo */, af_is_stereo, nullptr,
-                        &cached_channel_data_);
-    }
+    SendAudioDataToAF(&decoder->GetDecodedSamples());
 
     /* Cache the data in case 2nd channel connects */
-    cached_channel_data_ = pcm_data_decoded;
+    decoder->Decode(data, size);
     cached_channel_timestamp_ = timestamp;
-    cached_channel_is_left_ = is_left;
+    cached_channel_ = decoder;
   }
 
-  void SendAudioDataToAF(bool bt_got_stereo, bool af_is_stereo,
-                         std::vector<int16_t>* left,
-                         std::vector<int16_t>* right) {
+  void SendAudioDataToAF(std::vector<int16_t>* left,
+                         std::vector<int16_t>* right = nullptr) {
     uint16_t to_write = 0;
     uint16_t written = 0;
+
+    bool af_is_stereo = (audio_framework_sink_config.num_channels == 2);
+    bool bt_got_stereo = (left != nullptr) & (right != nullptr);
+
     if (!af_is_stereo) {
       if (!bt_got_stereo) {
         std::vector<int16_t>* mono = left ? left : right;
@@ -3459,14 +3392,16 @@
     }
 
     LOG_DEBUG("Sink stream config (#%d):\n",
-              static_cast<int>(stream_conf->sink_streams.size()));
-    for (auto stream : stream_conf->sink_streams) {
+              static_cast<int>(
+                  stream_conf->stream_params.sink.stream_locations.size()));
+    for (auto stream : stream_conf->stream_params.sink.stream_locations) {
       LOG_DEBUG("Cis handle: 0x%02x, allocation 0x%04x\n", stream.first,
                 stream.second);
     }
     LOG_DEBUG("Source stream config (#%d):\n",
-              static_cast<int>(stream_conf->source_streams.size()));
-    for (auto stream : stream_conf->source_streams) {
+              static_cast<int>(
+                  stream_conf->stream_params.source.stream_locations.size()));
+    for (auto stream : stream_conf->stream_params.source.stream_locations) {
       LOG_DEBUG("Cis handle: 0x%02x, allocation 0x%04x\n", stream.first,
                 stream.second);
     }
@@ -3475,38 +3410,43 @@
         group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink);
     if (CodecManager::GetInstance()->GetCodecLocation() ==
         le_audio::types::CodecLocation::HOST) {
-      if (lc3_encoder_left_mem) {
+      if (sw_enc_left || sw_enc_right) {
         LOG(WARNING)
             << " The encoder instance should have been already released.";
-        free(lc3_encoder_left_mem);
-        lc3_encoder_left_mem = nullptr;
-        free(lc3_encoder_right_mem);
-        lc3_encoder_right_mem = nullptr;
       }
-      int dt_us = current_source_codec_config.data_interval_us;
-      int sr_hz = current_source_codec_config.sample_rate;
-      int af_hz = audio_framework_source_config.sample_rate;
-      unsigned enc_size = lc3_encoder_size(dt_us, af_hz);
+      sw_enc_left = le_audio::CodecInterface::CreateInstance(stream_conf->id);
+      auto codec_status = sw_enc_left->InitEncoder(
+          audio_framework_source_config, current_source_codec_config);
+      if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) {
+        LOG_ERROR("Left channel codec setup failed with err: %d", codec_status);
+        return false;
+      }
 
-      lc3_encoder_left_mem = malloc(enc_size);
-      lc3_encoder_right_mem = malloc(enc_size);
-
-      lc3_encoder_left =
-          lc3_setup_encoder(dt_us, sr_hz, af_hz, lc3_encoder_left_mem);
-      lc3_encoder_right =
-          lc3_setup_encoder(dt_us, sr_hz, af_hz, lc3_encoder_right_mem);
+      sw_enc_right = le_audio::CodecInterface::CreateInstance(stream_conf->id);
+      codec_status = sw_enc_right->InitEncoder(audio_framework_source_config,
+                                               current_source_codec_config);
+      if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) {
+        LOG_ERROR("Right channel codec setup failed with err: %d",
+                  codec_status);
+        return false;
+      }
     }
 
     le_audio_source_hal_client_->UpdateRemoteDelay(remote_delay_ms);
     ConfirmLocalAudioSourceStreamingRequest();
 
     if (!LeAudioHalVerifier::SupportsStreamActiveApi()) {
-      /* We update the target audio allocation before streamStarted that the
-       * offloder would know how to configure offloader encoder. We should check
-       * if we need to update the current
-       * allocation here as the target allocation and the current allocation is
-       * different */
-      updateOffloaderIfNeeded(group);
+      /* We update the target audio allocation before streamStarted so that the
+       * CodecManager would know how to configure the encoder. */
+      BidirectionalPair<uint16_t> delays_pair = {
+          .sink = group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink),
+          .source =
+              group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSource)};
+      CodecManager::GetInstance()->UpdateActiveAudioConfig(
+          group->stream_conf.stream_params, delays_pair,
+          std::bind(&LeAudioClientImpl::UpdateAudioConfigToHal,
+                    weak_factory_.GetWeakPtr(), std::placeholders::_1,
+                    std::placeholders::_2));
     }
 
     return true;
@@ -3516,7 +3456,7 @@
       LeAudioDeviceGroup* group) {
     const struct le_audio::stream_configuration* stream_conf =
         &group->stream_conf;
-    if (stream_conf->source_streams.size() == 0) {
+    if (stream_conf->stream_params.source.stream_locations.size() == 0) {
       return nullptr;
     }
     LOG_INFO("configuration: %s", stream_conf->conf->name.c_str());
@@ -3542,56 +3482,53 @@
 
     if (CodecManager::GetInstance()->GetCodecLocation() ==
         le_audio::types::CodecLocation::HOST) {
-      if (lc3_decoder_left_mem) {
+      if (sw_dec_left.get() || sw_dec_right.get()) {
         LOG(WARNING)
             << " The decoder instance should have been already released.";
-        free(lc3_decoder_left_mem);
-        lc3_decoder_left_mem = nullptr;
-        free(lc3_decoder_right_mem);
-        lc3_decoder_right_mem = nullptr;
+      }
+      sw_dec_left = le_audio::CodecInterface::CreateInstance(stream_conf->id);
+      auto codec_status = sw_dec_left->InitDecoder(current_sink_codec_config,
+                                                   audio_framework_sink_config);
+      if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) {
+        LOG_ERROR("Left channel codec setup failed with err: %d", codec_status);
+        return;
       }
 
-      int dt_us = current_sink_codec_config.data_interval_us;
-      int sr_hz = current_sink_codec_config.sample_rate;
-      int af_hz = audio_framework_sink_config.sample_rate;
-      unsigned dec_size = lc3_decoder_size(dt_us, af_hz);
-      lc3_decoder_left_mem = malloc(dec_size);
-      lc3_decoder_right_mem = malloc(dec_size);
-
-      lc3_decoder_left =
-          lc3_setup_decoder(dt_us, sr_hz, af_hz, lc3_decoder_left_mem);
-      lc3_decoder_right =
-          lc3_setup_decoder(dt_us, sr_hz, af_hz, lc3_decoder_right_mem);
+      sw_dec_right = le_audio::CodecInterface::CreateInstance(stream_conf->id);
+      codec_status = sw_dec_right->InitDecoder(current_sink_codec_config,
+                                               audio_framework_sink_config);
+      if (codec_status != le_audio::CodecInterface::Status::STATUS_OK) {
+        LOG_ERROR("Right channel codec setup failed with err: %d",
+                  codec_status);
+        return;
+      }
     }
     le_audio_sink_hal_client_->UpdateRemoteDelay(remote_delay_ms);
     ConfirmLocalAudioSinkStreamingRequest();
 
     if (!LeAudioHalVerifier::SupportsStreamActiveApi()) {
-      /* We update the target audio allocation before streamStarted that the
-       * offloder would know how to configure offloader encoder. We should check
-       * if we need to update the current
-       * allocation here as the target allocation and the current allocation is
-       * different */
-      updateOffloaderIfNeeded(group);
+      /* We update the target audio allocation before streamStarted so that the
+       * CodecManager would know how to configure the encoder. */
+      BidirectionalPair<uint16_t> delays_pair = {
+          .sink = group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink),
+          .source =
+              group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSource)};
+      CodecManager::GetInstance()->UpdateActiveAudioConfig(
+          group->stream_conf.stream_params, delays_pair,
+          std::bind(&LeAudioClientImpl::UpdateAudioConfigToHal,
+                    weak_factory_.GetWeakPtr(), std::placeholders::_1,
+                    std::placeholders::_2));
     }
   }
 
   void SuspendAudio(void) {
     CancelStreamingRequest();
 
-    if (lc3_encoder_left_mem) {
-      free(lc3_encoder_left_mem);
-      lc3_encoder_left_mem = nullptr;
-      free(lc3_encoder_right_mem);
-      lc3_encoder_right_mem = nullptr;
-    }
-
-    if (lc3_decoder_left_mem) {
-      free(lc3_decoder_left_mem);
-      lc3_decoder_left_mem = nullptr;
-      free(lc3_decoder_right_mem);
-      lc3_decoder_right_mem = nullptr;
-    }
+    if (sw_enc_left) sw_enc_left.reset();
+    if (sw_enc_right) sw_enc_right.reset();
+    if (sw_dec_left) sw_dec_left.reset();
+    if (sw_dec_right) sw_dec_right.reset();
+    CleanCachedMicrophoneData();
   }
 
   void StopAudio(void) { SuspendAudio(); }
@@ -3652,9 +3589,9 @@
             bluetooth::common::ToString(configuration_context_type_).c_str(),
             configuration_context_type_);
     dprintf(fd, "  local source metadata context type mask: %s\n",
-            local_metadata_context_types_.sink.to_string().c_str());
-    dprintf(fd, "  local sink metadata context type mask: %s\n",
             local_metadata_context_types_.source.to_string().c_str());
+    dprintf(fd, "  local sink metadata context type mask: %s\n",
+            local_metadata_context_types_.sink.to_string().c_str());
     dprintf(fd, "  TBS state: %s\n", in_call_ ? " In call" : "No calls");
     dprintf(fd, "  Start time: ");
     for (auto t : stream_start_history_queue_) {
@@ -3673,7 +3610,7 @@
     }
   }
 
-  void Cleanup(base::Callback<void()> cleanupCb) {
+  void Cleanup() {
     StopVbcCloseTimeout();
     if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_);
 
@@ -3687,8 +3624,6 @@
     leAudioDevices_.Cleanup(gatt_if_);
     if (gatt_if_) BTA_GATTC_AppDeregister(gatt_if_);
 
-    std::move(cleanupCb).Run();
-
     if (leAudioHealthStatus_) {
       leAudioHealthStatus_->Cleanup();
     }
@@ -4471,7 +4406,7 @@
           "invalid/unknown %s context metadata, using 'UNSPECIFIED' instead",
           (remote_dir == le_audio::types::kLeAudioDirectionSink) ? "sink"
                                                                  : "source");
-      contexts_pair.get_ref(remote_dir) =
+      contexts_pair.get(remote_dir) =
           AudioContexts(LeAudioContextType::UNSPECIFIED);
     }
 
@@ -4499,13 +4434,13 @@
       }
 
       LOG_DEBUG("Checking contexts: %s, against the available contexts: %s",
-                ToString(contexts_pair.get_ref(dir)).c_str(),
+                ToString(contexts_pair.get(dir)).c_str(),
                 ToString(group_available_contexts).c_str());
       auto unavail_contexts =
-          contexts_pair.get_ref(dir) & ~group_available_contexts;
+          contexts_pair.get(dir) & ~group_available_contexts;
       if (unavail_contexts.none()) continue;
 
-      contexts_pair.get_ref(dir) &= group_available_contexts;
+      contexts_pair.get(dir) &= group_available_contexts;
       auto unavail_but_supported =
           (unavail_contexts & group->GetSupportedContexts(dir));
       if (unavail_but_supported.none() &&
@@ -4515,7 +4450,7 @@
         /* All unavailable are also unsupported - replace with UNSPECIFIED if
          * available
          */
-        contexts_pair.get_ref(dir).set(LeAudioContextType::UNSPECIFIED);
+        contexts_pair.get(dir).set(LeAudioContextType::UNSPECIFIED);
       } else {
         LOG_DEBUG("Some contexts are supported but currently unavailable: %s!",
                   ToString(unavail_but_supported).c_str());
@@ -4551,12 +4486,12 @@
                 "Other direction is streaming. Aligning other direction"
                 " metadata to match the current direciton context: %s",
                 ToString(contexts_pair.get(other_dir)).c_str());
-            contexts_pair.get_ref(dir) = contexts_pair.get(other_dir);
+            contexts_pair.get(dir) = contexts_pair.get(other_dir);
           }
         } else {
           LOG_DEBUG("Removing UNSPECIFIED from the remote sink context: %s",
                     ToString(contexts_pair.get(other_dir)).c_str());
-          contexts_pair.get_ref(dir).unset(LeAudioContextType::UNSPECIFIED);
+          contexts_pair.get(dir).unset(LeAudioContextType::UNSPECIFIED);
         }
       }
     }
@@ -4692,7 +4627,7 @@
       LOG_DEBUG(
           "The other direction is not streaming bidirectional, ignore that "
           "context.");
-      remote_metadata.get_ref(remote_other_direction).clear();
+      remote_metadata.get(remote_other_direction).clear();
     }
 
     /* Mixed contexts in the voiceback channel scenarios can confuse the remote
@@ -4706,13 +4641,13 @@
           "context");
       if (!is_streaming_other_direction) {
         // Do not take the obsolete metadata
-        remote_metadata.get_ref(remote_other_direction).clear();
+        remote_metadata.get(remote_other_direction).clear();
       }
-      remote_metadata.get_ref(remote_other_direction)
+      remote_metadata.get(remote_other_direction)
           .unset_all(kLeAudioContextAllBidir);
-      remote_metadata.get_ref(remote_other_direction)
+      remote_metadata.get(remote_other_direction)
           .unset_all(kLeAudioContextAllRemoteSinkOnly);
-      remote_metadata.get_ref(remote_other_direction)
+      remote_metadata.get(remote_other_direction)
           .set_all(remote_metadata.get(remote_direction) &
                    ~kLeAudioContextAllRemoteSinkOnly);
     }
@@ -4737,9 +4672,9 @@
         /* Turn off bidirectional contexts on this direction to avoid mixing
          * with the other direction bidirectional context
          */
-        remote_metadata.get_ref(remote_direction)
+        remote_metadata.get(remote_direction)
             .unset_all(kLeAudioContextAllBidir);
-        remote_metadata.get_ref(remote_direction)
+        remote_metadata.get(remote_direction)
             .set_all(remote_metadata.get(remote_other_direction));
       }
     }
@@ -4839,7 +4774,7 @@
               group, le_audio::types::kLeAudioDirectionSource) &&
           (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
       if (has_audio_source_configured) {
-        LOG_DEBUG(
+        LOG_INFO(
             "Audio source is already available in the current configuration "
             "context in %s. Not switching to %s right now.",
             ToString(configuration_context_type_).c_str(),
@@ -4864,9 +4799,9 @@
       LeAudioDeviceGroup* group, LeAudioContextType new_configuration_context,
       BidirectionalPair<AudioContexts> remote_contexts) {
     if (new_configuration_context != configuration_context_type_) {
-      LOG_DEBUG("Changing configuration context from %s to %s",
-                ToString(configuration_context_type_).c_str(),
-                ToString(new_configuration_context).c_str());
+      LOG_INFO("Checking whether to change configuration context from %s to %s",
+               ToString(configuration_context_type_).c_str(),
+               ToString(new_configuration_context).c_str());
 
       LeAudioLogHistory::Get()->AddLogHistory(
           kLogAfCallBt, active_group_id_, RawAddress::kEmpty,
@@ -4881,7 +4816,7 @@
     }
 
     if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
-      LOG_DEBUG(
+      LOG_INFO(
           "The %s configuration did not change. Updating the metadata to "
           "sink=%s, source=%s",
           ToString(configuration_context_type_).c_str(),
@@ -4919,7 +4854,8 @@
     if (data && !!PTR_TO_INT(data)) {
       leAudioDevice->notify_connected_after_read_ = false;
 
-      /* Update PACs and ASEs when all is read.*/
+      /* Update handles, PACs and ASEs when all is read.*/
+      btif_storage_leaudio_update_handles_bin(leAudioDevice->address_);
       btif_storage_leaudio_update_pacs_bin(leAudioDevice->address_);
       btif_storage_leaudio_update_ase_bin(leAudioDevice->address_);
 
@@ -5131,37 +5067,15 @@
     }
   }
 
-  void updateOffloaderIfNeeded(LeAudioDeviceGroup* group) {
-    if (CodecManager::GetInstance()->GetCodecLocation() !=
-        le_audio::types::CodecLocation::ADSP) {
-      return;
+  void UpdateAudioConfigToHal(const ::le_audio::offload_config& config,
+                              uint8_t remote_direction) {
+    if ((remote_direction & le_audio::types::kLeAudioDirectionSink) &&
+        le_audio_source_hal_client_) {
+      le_audio_source_hal_client_->UpdateAudioConfigToHal(config);
     }
-
-    LOG_INFO("Group %p, group_id %d", group, group->group_id_);
-
-    const auto* stream_conf = &group->stream_conf;
-
-    if (stream_conf->sink_offloader_changed || stream_conf->sink_is_initial) {
-      LOG_INFO("Update sink offloader streams");
-      uint16_t remote_delay_ms =
-          group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink);
-      CodecManager::GetInstance()->UpdateActiveSourceAudioConfig(
-          *stream_conf, remote_delay_ms,
-          std::bind(&LeAudioSourceAudioHalClient::UpdateAudioConfigToHal,
-                    le_audio_source_hal_client_.get(), std::placeholders::_1));
-      group->StreamOffloaderUpdated(le_audio::types::kLeAudioDirectionSink);
-    }
-
-    if (stream_conf->source_offloader_changed ||
-        stream_conf->source_is_initial) {
-      LOG_INFO("Update source offloader streams");
-      uint16_t remote_delay_ms =
-          group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSource);
-      CodecManager::GetInstance()->UpdateActiveSinkAudioConfig(
-          *stream_conf, remote_delay_ms,
-          std::bind(&LeAudioSinkAudioHalClient::UpdateAudioConfigToHal,
-                    le_audio_sink_hal_client_.get(), std::placeholders::_1));
-      group->StreamOffloaderUpdated(le_audio::types::kLeAudioDirectionSource);
+    if ((remote_direction & le_audio::types::kLeAudioDirectionSource) &&
+        le_audio_sink_hal_client_) {
+      le_audio_sink_hal_client_->UpdateAudioConfigToHal(config);
     }
   }
 
@@ -5230,11 +5144,16 @@
         }
 
         if (group) {
-          updateOffloaderIfNeeded(group);
-          if (reconnection_mode_ ==
-              BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS) {
-            group->AddToAllowListNotConnectedGroupMembers(gatt_if_);
-          }
+          BidirectionalPair<uint16_t> delays_pair = {
+              .sink =
+                  group->GetRemoteDelay(le_audio::types::kLeAudioDirectionSink),
+              .source = group->GetRemoteDelay(
+                  le_audio::types::kLeAudioDirectionSource)};
+          CodecManager::GetInstance()->UpdateActiveAudioConfig(
+              group->stream_conf.stream_params, delays_pair,
+              std::bind(&LeAudioClientImpl::UpdateAudioConfigToHal,
+                        weak_factory_.GetWeakPtr(), std::placeholders::_1,
+                        std::placeholders::_2));
         }
 
         if (audio_sender_state_ == AudioState::READY_TO_START)
@@ -5286,6 +5205,12 @@
          */
         FALLTHROUGH;
       case GroupStreamStatus::IDLE: {
+        if (sw_enc_left) sw_enc_left.reset();
+        if (sw_enc_right) sw_enc_right.reset();
+        if (sw_dec_left) sw_dec_left.reset();
+        if (sw_dec_right) sw_dec_right.reset();
+        CleanCachedMicrophoneData();
+
         if (group) {
           UpdateLocationsAndContextsAvailability(group->group_id_);
           if (group->IsPendingConfiguration()) {
@@ -5334,6 +5259,17 @@
     }
   }
 
+  void OnUpdatedCisConfiguration(int group_id, uint8_t direction) {
+    LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
+    if (!group) {
+      LOG_ERROR("Invalid group_id: %d", group_id);
+      return;
+    }
+    CodecManager::GetInstance()->UpdateCisConfiguration(
+        group->cises_, group->stream_conf.stream_params.get(direction),
+        direction);
+  }
+
  private:
   tGATT_IF gatt_if_;
   bluetooth::le_audio::LeAudioClientCallbacks* callbacks_;
@@ -5393,17 +5329,11 @@
       .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us,
   };
 
-  void* lc3_encoder_left_mem;
-  void* lc3_encoder_right_mem;
+  std::unique_ptr<le_audio::CodecInterface> sw_enc_left;
+  std::unique_ptr<le_audio::CodecInterface> sw_enc_right;
 
-  lc3_encoder_t lc3_encoder_left;
-  lc3_encoder_t lc3_encoder_right;
-
-  void* lc3_decoder_left_mem;
-  void* lc3_decoder_right_mem;
-
-  lc3_decoder_t lc3_decoder_left;
-  lc3_decoder_t lc3_decoder_right;
+  std::unique_ptr<le_audio::CodecInterface> sw_dec_left;
+  std::unique_ptr<le_audio::CodecInterface> sw_dec_right;
 
   std::vector<uint8_t> encoded_data;
   std::unique_ptr<LeAudioSourceAudioHalClient> le_audio_source_hal_client_;
@@ -5417,9 +5347,8 @@
   alarm_t* disable_timer_;
   static constexpr uint64_t kDeviceAttachDelayMs = 500;
 
-  std::vector<int16_t> cached_channel_data_;
   uint32_t cached_channel_timestamp_ = 0;
-  uint32_t cached_channel_is_left_;
+  le_audio::CodecInterface* cached_channel_ = nullptr;
 
   base::WeakPtrFactory<LeAudioClientImpl> weak_factory_{this};
 
@@ -5558,6 +5487,10 @@
   void OnStateTransitionTimeout(int group_id) override {
     if (instance) instance->OnLeAudioDeviceSetStateTimeout(group_id);
   }
+
+  void OnUpdatedCisConfiguration(int group_id, uint8_t direction) {
+    if (instance) instance->OnUpdatedCisConfiguration(group_id, direction);
+  }
 };
 
 CallbacksImpl stateMachineCallbacksImpl;
@@ -5751,7 +5684,7 @@
   dprintf(fd, "\n");
 }
 
-void LeAudioClient::Cleanup(base::Callback<void()> cleanupCb) {
+void LeAudioClient::Cleanup(void) {
   std::scoped_lock<std::mutex> lock(instance_mutex);
   if (!instance) {
     LOG(ERROR) << "Not initialized";
@@ -5760,7 +5693,7 @@
 
   LeAudioClientImpl* ptr = instance;
   instance = nullptr;
-  ptr->Cleanup(cleanupCb);
+  ptr->Cleanup();
   delete ptr;
   ptr = nullptr;
 
diff --git a/system/bta/le_audio/client_linux.cc b/system/bta/le_audio/client_linux.cc
index 4cc133f..f905add 100644
--- a/system/bta/le_audio/client_linux.cc
+++ b/system/bta/le_audio/client_linux.cc
@@ -50,7 +50,7 @@
     base::Closure initCb, base::Callback<bool()> hal_2_1_verifier,
     const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>&
         offloading_preference) {}
-void LeAudioClient::Cleanup(base::Callback<void()> cleanupCb) {}
+void LeAudioClient::Cleanup(void) {}
 LeAudioClient* LeAudioClient::Get(void) { return nullptr; }
 void LeAudioClient::DebugDump(int fd) {}
 void LeAudioClient::AddFromStorage(const RawAddress& addr, bool autoconnect,
@@ -79,5 +79,3 @@
   return false;
 }
 bool LeAudioClient::IsLeAudioClientRunning() { return false; }
-void LeAudioClient::InitializeAudioSetConfigurationProvider(void) {}
-void LeAudioClient::CleanupAudioSetConfigurationProvider(void) {}
diff --git a/system/bta/le_audio/client_parser.cc b/system/bta/le_audio/client_parser.cc
index 5bcca9f..9ac8c73 100644
--- a/system/bta/le_audio/client_parser.cc
+++ b/system/bta/le_audio/client_parser.cc
@@ -321,15 +321,14 @@
       confs.begin(), confs.end(),
       confs.size() * kCtpCodecConfMinLen + kAseNumSize + kCtpOpSize,
       [&conf_ents_str](size_t cur_len, auto const& conf) {
-        auto ltv_map = conf.codec_config.GetAsLtvMap();
-        for (const auto& [type, value] : ltv_map.Values()) {
+        for (const auto& [type, value] : conf.codec_config.Values()) {
           conf_ents_str +=
               "\ttype: " + std::to_string(type) +
               "\tlen: " + std::to_string(value.size()) +
               "\tdata: " + base::HexEncode(value.data(), value.size()) + "\n";
         };
 
-        return cur_len + ltv_map.RawPacketSize();
+        return cur_len + conf.codec_config.RawPacketSize();
       });
   value.resize(msg_len);
 
@@ -345,11 +344,10 @@
     UINT16_TO_STREAM(msg, conf.codec_id.vendor_company_id);
     UINT16_TO_STREAM(msg, conf.codec_id.vendor_codec_id);
 
-    auto ltv_map = conf.codec_config.GetAsLtvMap();
-    auto codec_spec_conf_len = ltv_map.RawPacketSize();
+    auto codec_spec_conf_len = conf.codec_config.RawPacketSize();
 
     UINT8_TO_STREAM(msg, codec_spec_conf_len);
-    msg = ltv_map.RawPacket(msg);
+    msg = conf.codec_config.RawPacket(msg);
 
     LOG(INFO) << __func__ << ", Codec configuration"
               << "\n\tAse id: " << loghex(conf.ase_id)
diff --git a/system/bta/le_audio/client_parser.h b/system/bta/le_audio/client_parser.h
index f047138..38a7ec1 100644
--- a/system/bta/le_audio/client_parser.h
+++ b/system/bta/le_audio/client_parser.h
@@ -161,7 +161,7 @@
   uint8_t target_latency;
   uint8_t target_phy;
   types::LeAudioCodecId codec_id;
-  types::LeAudioLc3Config codec_config;
+  types::LeAudioLtvMap codec_config;
 };
 
 constexpr uint16_t kCtpQosConfMinLen = 16;
diff --git a/system/bta/le_audio/client_parser_test.cc b/system/bta/le_audio/client_parser_test.cc
index dbb3f1c..c3f0ebc 100644
--- a/system/bta/le_audio/client_parser_test.cc
+++ b/system/bta/le_audio/client_parser_test.cc
@@ -1112,10 +1112,15 @@
   types::LeAudioCodecId codec_id{.coding_format = 0x06,
                                  .vendor_company_id = 0x0203,
                                  .vendor_codec_id = 0x0405};
-  types::LeAudioLc3Config codec_conf{.sampling_frequency = 0x10,
-                                     .frame_duration = 0x03,
-                                     .audio_channel_allocation = 0x04050607,
-                                     .octets_per_codec_frame = 0x0203};
+  types::LeAudioLtvMap codec_conf =
+      types::LeAudioLtvMap()
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeSamplingFreq, (uint8_t)0x10)
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeFrameDuration,
+               (uint8_t)0x03)
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeAudioChannelAllocation,
+               (uint32_t)0x04050607)
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeOctetPerFrame,
+               (uint16_t)0x0203);
 
   confs.push_back(ctp_codec_conf{
       .ase_id = 0x05,
@@ -1168,10 +1173,15 @@
   types::LeAudioCodecId codec_id{.coding_format = 0x06,
                                  .vendor_company_id = 0x0203,
                                  .vendor_codec_id = 0x0405};
-  types::LeAudioLc3Config codec_conf{.sampling_frequency = 0x10,
-                                     .frame_duration = 0x03,
-                                     .audio_channel_allocation = 0x04050607,
-                                     .octets_per_codec_frame = 0x0203};
+  types::LeAudioLtvMap codec_conf =
+      types::LeAudioLtvMap()
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeSamplingFreq, (uint8_t)0x10)
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeFrameDuration,
+               (uint8_t)0x03)
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeAudioChannelAllocation,
+               (uint32_t)0x04050607)
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeOctetPerFrame,
+               (uint16_t)0x0203);
 
   confs.push_back(ctp_codec_conf{
       .ase_id = 0x05,
@@ -1219,10 +1229,15 @@
   types::LeAudioCodecId codec_id2{.coding_format = 0x16,
                                   .vendor_company_id = 0x1213,
                                   .vendor_codec_id = 0x1415};
-  types::LeAudioLc3Config codec_conf2{.sampling_frequency = 0x11,
-                                      .frame_duration = 0x13,
-                                      .audio_channel_allocation = 0x14151617,
-                                      .octets_per_codec_frame = 0x1213};
+  types::LeAudioLtvMap codec_conf2 =
+      types::LeAudioLtvMap()
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeSamplingFreq, (uint8_t)0x11)
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeFrameDuration,
+               (uint8_t)0x13)
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeAudioChannelAllocation,
+               (uint32_t)0x14151617)
+          .Add(codec_spec_conf::kLeAudioCodecLC3TypeOctetPerFrame,
+               (uint16_t)0x1213);
 
   confs.push_back(ctp_codec_conf{
       .ase_id = 0x15,
diff --git a/system/bta/le_audio/codec_interface.cc b/system/bta/le_audio/codec_interface.cc
new file mode 100644
index 0000000..092f73c
--- /dev/null
+++ b/system/bta/le_audio/codec_interface.cc
@@ -0,0 +1,297 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2023 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 "codec_interface.h"
+
+#include <lc3.h>
+
+#include <memory>
+#include <optional>
+#include <vector>
+
+#include "osi/include/log.h"
+
+namespace le_audio {
+
+struct CodecInterface::Impl {
+  Impl(const types::LeAudioCodecId& codec_id) : codec_id_(codec_id) {}
+  ~Impl() { Cleanup(); }
+
+  bool IsReady() { return pcm_config_.has_value(); };
+
+  CodecInterface::Status InitEncoder(
+      const LeAudioCodecConfiguration& pcm_config,
+      const LeAudioCodecConfiguration& codec_config) {
+    // Output codec configuration
+    bt_codec_config_ = codec_config;
+
+    // TODO: For now only blocks_per_sdu = 1 is supported
+    if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) {
+      if (pcm_config_.has_value()) {
+        Cleanup();
+      }
+      pcm_config_ = pcm_config;
+
+      lc3_.pcm_format_ = (pcm_config_->bits_per_sample == 24)
+                             ? LC3_PCM_FORMAT_S24
+                             : LC3_PCM_FORMAT_S16;
+
+      // Prepare the encoder
+      const auto encoder_size = lc3_encoder_size(
+          bt_codec_config_.data_interval_us, pcm_config_->sample_rate);
+      lc3_.codec_mem_.reset(malloc(encoder_size));
+      lc3_.encoder_ = lc3_setup_encoder(
+          bt_codec_config_.data_interval_us, bt_codec_config_.sample_rate,
+          pcm_config_->sample_rate, lc3_.codec_mem_.get());
+
+      return Status::STATUS_OK;
+    }
+
+    LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format,
+              codec_id_.vendor_company_id, codec_id_.vendor_codec_id);
+    return Status::STATUS_ERR_INVALID_CODEC_ID;
+  }
+
+  CodecInterface::Status InitDecoder(
+      const LeAudioCodecConfiguration& codec_config,
+      const LeAudioCodecConfiguration& pcm_config) {
+    // Input codec configuration
+    bt_codec_config_ = codec_config;
+
+    // TODO: For now only blocks_per_sdu = 1 is supported
+    if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) {
+      if (pcm_config_.has_value()) {
+        Cleanup();
+      }
+      pcm_config_ = pcm_config;
+
+      lc3_.pcm_format_ = (pcm_config_->bits_per_sample == 24)
+                             ? LC3_PCM_FORMAT_S24
+                             : LC3_PCM_FORMAT_S16;
+
+      // Prepare the decoded output buffer
+      output_channel_samples_ = lc3_frame_samples(
+          bt_codec_config_.data_interval_us, pcm_config_->sample_rate);
+      adjustOutputBufferSizeIfNeeded(&output_channel_data_);
+
+      // Prepare the decoder
+      const auto decoder_size = lc3_decoder_size(
+          bt_codec_config_.data_interval_us, pcm_config_->sample_rate);
+      lc3_.codec_mem_.reset(malloc(decoder_size));
+      lc3_.decoder_ = lc3_setup_decoder(
+          bt_codec_config_.data_interval_us, bt_codec_config_.sample_rate,
+          pcm_config_->sample_rate, lc3_.codec_mem_.get());
+
+      return Status::STATUS_OK;
+    }
+
+    LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format,
+              codec_id_.vendor_company_id, codec_id_.vendor_codec_id);
+    return Status::STATUS_ERR_INVALID_CODEC_ID;
+  }
+
+  std::vector<int16_t>& GetDecodedSamples() { return output_channel_data_; }
+  CodecInterface::Status Decode(uint8_t* data, uint16_t size) {
+    if (!IsReady()) {
+      LOG_ERROR("decoder not ready");
+      return Status::STATUS_ERR_CODEC_NOT_READY;
+    }
+
+    // For now only LC3 is supported
+    if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) {
+      adjustOutputBufferSizeIfNeeded(&output_channel_data_);
+      auto err = lc3_decode(lc3_.decoder_, data, size, lc3_.pcm_format_,
+                            output_channel_data_.data(), 1 /* stride */);
+      if (err < 0) {
+        LOG(ERROR) << " bad decoding parameters: " << static_cast<int>(err);
+        return Status::STATUS_ERR_CODING_ERROR;
+      }
+
+      return Status::STATUS_OK;
+    }
+
+    LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format,
+              codec_id_.vendor_company_id, codec_id_.vendor_codec_id);
+    return Status::STATUS_ERR_INVALID_CODEC_ID;
+  }
+
+  CodecInterface::Status Encode(const uint8_t* data, int stride,
+                                uint16_t out_size,
+                                std::vector<int16_t>* out_buffer = nullptr,
+                                uint16_t out_offset = 0) {
+    if (!IsReady()) {
+      LOG_ERROR("decoder not ready");
+      return Status::STATUS_ERR_CODEC_NOT_READY;
+    }
+
+    if (out_size == 0) {
+      LOG_ERROR("out_size cannot be 0");
+      return Status::STATUS_ERR_CODING_ERROR;
+    }
+
+    // For now only LC3 is supported
+    if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) {
+      // Prepare the encoded output buffer
+      if (out_buffer == nullptr) {
+        out_buffer = &output_channel_data_;
+      }
+
+      // We have two bytes per sample in the buffer, while out_size and
+      // out_offset are in bytes
+      size_t channel_samples = (out_offset + out_size) / 2;
+      if (output_channel_samples_ < channel_samples) {
+        output_channel_samples_ = channel_samples;
+      }
+      adjustOutputBufferSizeIfNeeded(out_buffer);
+
+      // Encode
+      auto err =
+          lc3_encode(lc3_.encoder_, lc3_.pcm_format_, data, stride, out_size,
+                     ((uint8_t*)out_buffer->data()) + out_offset);
+      if (err < 0) {
+        LOG(ERROR) << " bad encoding parameters: " << static_cast<int>(err);
+        return Status::STATUS_ERR_CODING_ERROR;
+      }
+
+      return Status::STATUS_OK;
+    }
+
+    LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format,
+              codec_id_.vendor_company_id, codec_id_.vendor_codec_id);
+    return Status::STATUS_ERR_INVALID_CODEC_ID;
+  }
+
+  void Cleanup() {
+    pcm_config_ = std::nullopt;
+    if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) {
+      lc3_.Cleanup();
+    }
+    output_channel_data_.clear();
+    output_channel_samples_ = 0;
+  }
+
+  uint16_t GetNumOfSamplesPerChannel() {
+    if (!IsReady()) {
+      LOG_ERROR("decoder not ready");
+      return 0;
+    }
+
+    if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) {
+      return lc3_frame_samples(bt_codec_config_.data_interval_us,
+                               pcm_config_->sample_rate);
+    }
+
+    LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format,
+              codec_id_.vendor_company_id, codec_id_.vendor_codec_id);
+    return 0;
+  }
+
+  uint8_t GetNumOfBytesPerSample() {
+    if (codec_id_.coding_format == types::kLeAudioCodingFormatLC3) {
+      return lc3_.bits_to_bytes_per_sample(bt_codec_config_.bits_per_sample);
+    }
+
+    LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id_.coding_format,
+              codec_id_.vendor_company_id, codec_id_.vendor_codec_id);
+    return 0;
+  }
+
+ private:
+  inline void adjustOutputBufferSizeIfNeeded(std::vector<int16_t>* out_buffer) {
+    if (out_buffer->size() < output_channel_samples_) {
+      out_buffer->resize(output_channel_samples_);
+    }
+  }
+
+  // BT codec params set when codec is initialized
+  types::LeAudioCodecId codec_id_;
+  LeAudioCodecConfiguration bt_codec_config_;
+  std::optional<LeAudioCodecConfiguration> pcm_config_;
+
+  // Output buffer
+  std::vector<int16_t> output_channel_data_;
+  size_t output_channel_samples_ = 0;
+
+  // LC3
+  struct lc3_t {
+    static inline uint8_t bits_to_bytes_per_sample(uint8_t bits_per_sample) {
+      // 24 bit audio stream is sent as unpacked, each sample takes 4 bytes.
+      if (bits_per_sample == 24) return 4;
+      return bits_per_sample / 8;
+    }
+
+    void Cleanup() {
+      decoder_ = nullptr;
+      encoder_ = nullptr;
+      codec_mem_.reset();
+    }
+
+    lc3_t() : codec_mem_(nullptr, &std::free) {}
+    lc3_pcm_format pcm_format_;
+    union {
+      lc3_decoder_t decoder_;
+      lc3_encoder_t encoder_;
+    };
+    std::unique_ptr<void, decltype(&std::free)> codec_mem_;
+  } lc3_;
+};
+
+CodecInterface::CodecInterface(const types::LeAudioCodecId& codec_id) {
+  if (codec_id.coding_format == types::kLeAudioCodingFormatLC3) {
+    impl = new Impl(codec_id);
+  } else {
+    LOG_ERROR("Invalid codec ID: [%d:%d:%d]", codec_id.coding_format,
+              codec_id.vendor_company_id, codec_id.vendor_codec_id);
+  }
+}
+
+CodecInterface::~CodecInterface() { delete impl; }
+
+bool CodecInterface::IsReady() { return impl->IsReady(); };
+CodecInterface::Status CodecInterface::InitEncoder(
+    const LeAudioCodecConfiguration& pcm_config,
+    const LeAudioCodecConfiguration& codec_config) {
+  return impl->InitEncoder(pcm_config, codec_config);
+}
+CodecInterface::Status CodecInterface::InitDecoder(
+    const LeAudioCodecConfiguration& codec_config,
+    const LeAudioCodecConfiguration& pcm_config) {
+  return impl->InitDecoder(codec_config, pcm_config);
+}
+std::vector<int16_t>& CodecInterface::GetDecodedSamples() {
+  return impl->GetDecodedSamples();
+}
+CodecInterface::Status CodecInterface::Decode(uint8_t* data, uint16_t size) {
+  return impl->Decode(data, size);
+}
+CodecInterface::Status CodecInterface::Encode(const uint8_t* data, int stride,
+                                              uint16_t out_size,
+                                              std::vector<int16_t>* out_buffer,
+                                              uint16_t out_offset) {
+  return impl->Encode(data, stride, out_size, out_buffer, out_offset);
+}
+void CodecInterface::Cleanup() { return impl->Cleanup(); }
+
+uint16_t CodecInterface::GetNumOfSamplesPerChannel() {
+  return impl->GetNumOfSamplesPerChannel();
+};
+uint8_t CodecInterface::GetNumOfBytesPerSample() {
+  return impl->GetNumOfBytesPerSample();
+};
+
+}  // namespace le_audio
diff --git a/system/bta/le_audio/codec_interface.h b/system/bta/le_audio/codec_interface.h
new file mode 100644
index 0000000..cd2cca9
--- /dev/null
+++ b/system/bta/le_audio/codec_interface.h
@@ -0,0 +1,73 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2023 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 <stdint.h>
+
+#include "audio_hal_client/audio_hal_client.h"
+#include "le_audio_types.h"
+
+namespace le_audio {
+
+/* CodecInterface provides a thin abstraction layer above the codec instance. It
+ * manages the output buffers internally and resizes them automatically when
+ * needed.
+ * Multi-channel stream encoding requires multiple CodecInterface instances, but
+ * even then it is still possible to encode the stream data into a single output
+ * buffer. Thanks to the optional parameters to the encode() method, the
+ * internal buffer of the first instance can be used as an output buffer by the
+ * second instance, as long as equal life time of both instances is guaranteed.
+ *
+ */
+class CodecInterface {
+ public:
+  enum class Status {
+    STATUS_ERR_CODEC_NOT_READY = -128,
+    STATUS_ERR_INVALID_CODEC_ID = -127,
+    STATUS_ERR_CODING_ERROR = -1,
+    STATUS_OK = 0,
+  };
+
+  CodecInterface(const types::LeAudioCodecId& codec_id);
+  virtual ~CodecInterface();
+  static std::unique_ptr<CodecInterface> CreateInstance(
+      const types::LeAudioCodecId& codec_id) {
+    return std::make_unique<CodecInterface>(codec_id);
+  }
+  virtual CodecInterface::Status InitEncoder(
+      const LeAudioCodecConfiguration& pcm_config,
+      const LeAudioCodecConfiguration& codec_config);
+  virtual CodecInterface::Status InitDecoder(
+      const LeAudioCodecConfiguration& codec_config,
+      const LeAudioCodecConfiguration& pcm_config);
+  virtual CodecInterface::Status Encode(
+      const uint8_t* data, int stride, uint16_t out_size,
+      std::vector<int16_t>* out_buffer = nullptr, uint16_t out_offset = 0);
+  virtual CodecInterface::Status Decode(uint8_t* data, uint16_t size);
+  virtual void Cleanup();
+  virtual bool IsReady();
+  virtual uint16_t GetNumOfSamplesPerChannel();
+  virtual uint8_t GetNumOfBytesPerSample();
+  virtual std::vector<int16_t>& GetDecodedSamples();
+
+ private:
+  struct Impl;
+  Impl* impl;
+};
+}  // namespace le_audio
diff --git a/system/bta/le_audio/codec_manager.cc b/system/bta/le_audio/codec_manager.cc
index c104a4e..145d136 100644
--- a/system/bta/le_audio/codec_manager.cc
+++ b/system/bta/le_audio/codec_manager.cc
@@ -39,9 +39,24 @@
 using le_audio::set_configurations::AudioSetConfigurations;
 using le_audio::set_configurations::SetConfiguration;
 
+typedef struct offloader_stream_maps {
+  std::vector<le_audio::stream_map_info> streams_map_target;
+  std::vector<le_audio::stream_map_info> streams_map_current;
+  bool has_changed;
+  bool is_initial;
+} offloader_stream_maps_t;
 }  // namespace
 
 namespace le_audio {
+template <>
+offloader_stream_maps_t& types::BidirectionalPair<offloader_stream_maps_t>::get(
+    uint8_t direction) {
+  ASSERT_LOG(direction < types::kLeAudioDirectionBoth,
+             "Unsupported complex direction. Reference to a single complex"
+             " direction value is not supported.");
+  return (direction == types::kLeAudioDirectionSink) ? sink : source;
+}
+
 // The mapping for sampling rate, frame duration, and the QoS config
 static std::unordered_map<
     int, std::unordered_map<int, le_audio::broadcaster::BroadcastQosConfig>>
@@ -69,8 +84,7 @@
 
 struct codec_manager_impl {
  public:
-  codec_manager_impl(
-      const std::vector<btle_audio_codec_config_t>& offloading_preference) {
+  codec_manager_impl() {
     offload_enable_ = osi_property_get_bool(
                           "ro.bluetooth.leaudio_offload.supported", false) &&
                       !osi_property_get_bool(
@@ -95,9 +109,13 @@
                             kIsoDataPathPlatformDefault, {});
     btm_configure_data_path(btm_data_direction::CONTROLLER_TO_HOST,
                             kIsoDataPathPlatformDefault, {});
-    UpdateOffloadCapability(offloading_preference);
     SetCodecLocation(CodecLocation::ADSP);
   }
+  void start(
+      const std::vector<btle_audio_codec_config_t>& offloading_preference) {
+    le_audio::AudioSetConfigurationProvider::Initialize(GetCodecLocation());
+    UpdateOffloadCapability(offloading_preference);
+  }
   ~codec_manager_impl() {
     if (GetCodecLocation() != CodecLocation::HOST) {
       btm_configure_data_path(btm_data_direction::HOST_TO_CONTROLLER,
@@ -105,58 +123,48 @@
       btm_configure_data_path(btm_data_direction::CONTROLLER_TO_HOST,
                               kIsoDataPathHci, {});
     }
+    le_audio::AudioSetConfigurationProvider::Cleanup();
   }
   CodecLocation GetCodecLocation(void) const { return codec_location_; }
 
-  void UpdateActiveSourceAudioConfig(
-      const le_audio::stream_configuration& stream_conf, uint16_t delay_ms,
-      std::function<void(const ::le_audio::offload_config& config)>
+  void UpdateActiveAudioConfig(
+      const types::BidirectionalPair<stream_parameters>& stream_params,
+      types::BidirectionalPair<uint16_t> delays_ms,
+      std::function<void(const offload_config& config, uint8_t direction)>
           update_receiver) {
-    if (stream_conf.sink_streams.empty()) return;
-
-    if (stream_conf.sink_is_initial ||
-        LeAudioHalVerifier::SupportsStreamActiveApi()) {
-      sink_config.stream_map =
-          stream_conf.sink_offloader_streams_target_allocation;
-    } else {
-      sink_config.stream_map =
-          stream_conf.sink_offloader_streams_current_allocation;
+    if (GetCodecLocation() != le_audio::types::CodecLocation::ADSP) {
+      return;
     }
-    // TODO: set the default value 16 for now, would change it if we support
-    // mode bits_per_sample
-    sink_config.bits_per_sample = 16;
-    sink_config.sampling_rate = stream_conf.sink_sample_frequency_hz;
-    sink_config.frame_duration = stream_conf.sink_frame_duration_us;
-    sink_config.octets_per_frame = stream_conf.sink_octets_per_codec_frame;
-    sink_config.blocks_per_sdu = stream_conf.sink_codec_frames_blocks_per_sdu;
-    sink_config.peer_delay_ms = delay_ms;
-    update_receiver(sink_config);
-  }
 
-  void UpdateActiveSinkAudioConfig(
-      const le_audio::stream_configuration& stream_conf, uint16_t delay_ms,
-      std::function<void(const ::le_audio::offload_config& config)>
-          update_receiver) {
-    if (stream_conf.source_streams.empty()) return;
+    for (auto direction : {le_audio::types::kLeAudioDirectionSink,
+                           le_audio::types::kLeAudioDirectionSource}) {
+      auto& stream_map = offloader_stream_maps.get(direction);
+      if (!stream_map.has_changed && !stream_map.is_initial) {
+        continue;
+      }
+      if (stream_params.get(direction).stream_locations.empty()) {
+        continue;
+      }
 
-    if (stream_conf.source_is_initial ||
-        LeAudioHalVerifier::SupportsStreamActiveApi()) {
-      source_config.stream_map =
-          stream_conf.source_offloader_streams_target_allocation;
-    } else {
-      source_config.stream_map =
-          stream_conf.source_offloader_streams_current_allocation;
+      le_audio::offload_config unicast_cfg = {
+          .stream_map = (stream_map.is_initial ||
+                         LeAudioHalVerifier::SupportsStreamActiveApi())
+                            ? stream_map.streams_map_target
+                            : stream_map.streams_map_current,
+          // TODO: set the default value 16 for now, would change it if we
+          // support mode bits_per_sample
+          .bits_per_sample = 16,
+          .sampling_rate = stream_params.get(direction).sample_frequency_hz,
+          .frame_duration = stream_params.get(direction).frame_duration_us,
+          .octets_per_frame =
+              stream_params.get(direction).octets_per_codec_frame,
+          .blocks_per_sdu =
+              stream_params.get(direction).codec_frames_blocks_per_sdu,
+          .peer_delay_ms = delays_ms.get(direction),
+      };
+      update_receiver(unicast_cfg, direction);
+      stream_map.is_initial = false;
     }
-    // TODO: set the default value 16 for now, would change it if we support
-    // mode bits_per_sample
-    source_config.bits_per_sample = 16;
-    source_config.sampling_rate = stream_conf.source_sample_frequency_hz;
-    source_config.frame_duration = stream_conf.source_frame_duration_us;
-    source_config.octets_per_frame = stream_conf.source_octets_per_codec_frame;
-    source_config.blocks_per_sdu =
-        stream_conf.source_codec_frames_blocks_per_sdu;
-    source_config.peer_delay_ms = delay_ms;
-    update_receiver(source_config);
   }
 
   const AudioSetConfigurations* GetOffloadCodecConfig(
@@ -257,6 +265,119 @@
     update_receiver(broadcast_config);
   }
 
+  void ClearCisConfiguration(uint8_t direction) {
+    if (GetCodecLocation() != le_audio::types::CodecLocation::ADSP) {
+      return;
+    }
+
+    auto& stream_map = offloader_stream_maps.get(direction);
+    stream_map.streams_map_target.clear();
+    stream_map.streams_map_current.clear();
+  }
+
+  static uint32_t AdjustAllocationForOffloader(uint32_t allocation) {
+    if ((allocation & codec_spec_conf::kLeAudioLocationAnyLeft) &&
+        (allocation & codec_spec_conf::kLeAudioLocationAnyRight)) {
+      return codec_spec_conf::kLeAudioLocationStereo;
+    }
+    if (allocation & codec_spec_conf::kLeAudioLocationAnyLeft) {
+      return codec_spec_conf::kLeAudioLocationFrontLeft;
+    }
+    if (allocation & codec_spec_conf::kLeAudioLocationAnyRight) {
+      return codec_spec_conf::kLeAudioLocationFrontRight;
+    }
+    return 0;
+  }
+
+  void UpdateCisConfiguration(const std::vector<struct types::cis>& cises,
+                              const stream_parameters& stream_params,
+                              uint8_t direction) {
+    if (GetCodecLocation() != le_audio::types::CodecLocation::ADSP) {
+      return;
+    }
+
+    auto available_allocations =
+        AdjustAllocationForOffloader(stream_params.audio_channel_allocation);
+    if (available_allocations == 0) {
+      LOG_ERROR("There is no CIS connected");
+      return;
+    }
+
+    auto& stream_map = offloader_stream_maps.get(direction);
+    if (stream_map.streams_map_target.empty()) {
+      stream_map.is_initial = true;
+    } else if (stream_map.is_initial ||
+               LeAudioHalVerifier::SupportsStreamActiveApi()) {
+      /* As multiple CISes phone call case, the target_allocation already have
+       * the previous data, but the is_initial flag not be cleared. We need to
+       * clear here to avoid make duplicated target allocation stream map. */
+      stream_map.streams_map_target.clear();
+    }
+
+    stream_map.streams_map_current.clear();
+    stream_map.has_changed = true;
+    bool all_cises_connected =
+        (available_allocations == codec_spec_conf::kLeAudioLocationStereo);
+
+    /* If all the cises are connected as stream started, reset changed_flag that
+     * the bt stack wouldn't send another audio configuration for the connection
+     * status. */
+    if (stream_map.is_initial && all_cises_connected) {
+      stream_map.has_changed = false;
+    }
+
+    const std::string tag = types::BidirectionalPair<std::string>(
+                                {.sink = "Sink", .source = "Source"})
+                                .get(direction);
+
+    constexpr types::BidirectionalPair<types::CisType> cis_types = {
+        .sink = types::CisType::CIS_TYPE_UNIDIRECTIONAL_SINK,
+        .source = types::CisType::CIS_TYPE_UNIDIRECTIONAL_SOURCE};
+    auto cis_type = cis_types.get(direction);
+
+    for (auto const& cis_entry : cises) {
+      if ((cis_entry.type == types::CisType::CIS_TYPE_BIDIRECTIONAL ||
+           cis_entry.type == cis_type) &&
+          cis_entry.conn_handle != 0) {
+        uint32_t target_allocation = 0;
+        uint32_t current_allocation = 0;
+        bool is_active = false;
+        for (const auto& s : stream_params.stream_locations) {
+          if (s.first == cis_entry.conn_handle) {
+            is_active = true;
+            target_allocation = AdjustAllocationForOffloader(s.second);
+            current_allocation = target_allocation;
+            if (!all_cises_connected) {
+              /* Tell offloader to mix on this CIS.*/
+              current_allocation = codec_spec_conf::kLeAudioLocationStereo;
+            }
+            break;
+          }
+        }
+
+        if (target_allocation == 0) {
+          /* Take missing allocation for that one .*/
+          target_allocation =
+              codec_spec_conf::kLeAudioLocationStereo & ~available_allocations;
+        }
+
+        LOG_INFO(
+            "%s: Cis handle 0x%04x, target allocation  0x%08x, current "
+            "allocation 0x%08x, active: %d",
+            tag.c_str(), cis_entry.conn_handle, target_allocation,
+            current_allocation, is_active);
+
+        if (stream_map.is_initial ||
+            LeAudioHalVerifier::SupportsStreamActiveApi()) {
+          stream_map.streams_map_target.emplace_back(stream_map_info(
+              cis_entry.conn_handle, target_allocation, is_active));
+        }
+        stream_map.streams_map_current.emplace_back(stream_map_info(
+            cis_entry.conn_handle, current_allocation, is_active));
+      }
+    }
+  }
+
  private:
   void SetCodecLocation(CodecLocation location) {
     if (offload_enable_ == false) return;
@@ -354,7 +475,8 @@
     std::unordered_set<uint8_t> offload_preference_set;
 
     if (AudioSetConfigurationProvider::Get() == nullptr) {
-      LOG(ERROR) << __func__ << " Audio set configuration provider is not available.";
+      LOG(ERROR) << __func__
+                 << " Audio set configuration provider is not available.";
       return;
     }
 
@@ -394,8 +516,7 @@
 
   CodecLocation codec_location_ = CodecLocation::HOST;
   bool offload_enable_ = false;
-  le_audio::offload_config sink_config;
-  le_audio::offload_config source_config;
+  types::BidirectionalPair<offloader_stream_maps_t> offloader_stream_maps;
   std::vector<le_audio::broadcast_offload_config> supported_broadcast_config;
   std::unordered_map<types::LeAudioContextType, AudioSetConfigurations>
       context_type_offload_config_map_;
@@ -411,8 +532,8 @@
   void Start(
       const std::vector<btle_audio_codec_config_t>& offloading_preference) {
     LOG_ASSERT(!codec_manager_impl_);
-    codec_manager_impl_ =
-        std::make_unique<codec_manager_impl>(offloading_preference);
+    codec_manager_impl_ = std::make_unique<codec_manager_impl>();
+    codec_manager_impl_->start(offloading_preference);
   }
 
   void Stop() {
@@ -445,22 +566,14 @@
   return pimpl_->codec_manager_impl_->GetCodecLocation();
 }
 
-void CodecManager::UpdateActiveSourceAudioConfig(
-    const stream_configuration& stream_conf, uint16_t delay_ms,
-    std::function<void(const ::le_audio::offload_config& config)>
+void CodecManager::UpdateActiveAudioConfig(
+    const types::BidirectionalPair<stream_parameters>& stream_params,
+    types::BidirectionalPair<uint16_t> delays_ms,
+    std::function<void(const offload_config& config, uint8_t direction)>
         update_receiver) {
   if (pimpl_->IsRunning())
-    pimpl_->codec_manager_impl_->UpdateActiveSourceAudioConfig(
-        stream_conf, delay_ms, update_receiver);
-}
-
-void CodecManager::UpdateActiveSinkAudioConfig(
-    const stream_configuration& stream_conf, uint16_t delay_ms,
-    std::function<void(const ::le_audio::offload_config& config)>
-        update_receiver) {
-  if (pimpl_->IsRunning())
-    pimpl_->codec_manager_impl_->UpdateActiveSinkAudioConfig(
-        stream_conf, delay_ms, update_receiver);
+    pimpl_->codec_manager_impl_->UpdateActiveAudioConfig(
+        stream_params, delays_ms, update_receiver);
 }
 
 const AudioSetConfigurations* CodecManager::GetOffloadCodecConfig(
@@ -491,4 +604,19 @@
   }
 }
 
+void CodecManager::UpdateCisConfiguration(
+    const std::vector<struct types::cis>& cises,
+    const stream_parameters& stream_params, uint8_t direction) {
+  if (pimpl_->IsRunning()) {
+    return pimpl_->codec_manager_impl_->UpdateCisConfiguration(
+        cises, stream_params, direction);
+  }
+}
+
+void CodecManager::ClearCisConfiguration(uint8_t direction) {
+  if (pimpl_->IsRunning()) {
+    return pimpl_->codec_manager_impl_->ClearCisConfiguration(direction);
+  }
+}
+
 }  // namespace le_audio
diff --git a/system/bta/le_audio/codec_manager.h b/system/bta/le_audio/codec_manager.h
index 8e2a51b..cc89877 100644
--- a/system/bta/le_audio/codec_manager.h
+++ b/system/bta/le_audio/codec_manager.h
@@ -16,10 +16,22 @@
 
 #pragma once
 
+#include "hardware/bt_le_audio.h"
 #include "le_audio_types.h"
 
 namespace le_audio {
 
+struct stream_map_info {
+  stream_map_info(uint16_t stream_handle, uint32_t audio_channel_allocation,
+                  bool is_stream_active)
+      : stream_handle(stream_handle),
+        audio_channel_allocation(audio_channel_allocation),
+        is_stream_active(is_stream_active) {}
+  uint16_t stream_handle;
+  uint32_t audio_channel_allocation;
+  bool is_stream_active;
+};
+
 struct offload_config {
   std::vector<stream_map_info> stream_map;
   uint8_t bits_per_sample;
@@ -44,7 +56,6 @@
 
 class CodecManager {
  public:
-  CodecManager();
   virtual ~CodecManager() = default;
   static CodecManager* GetInstance(void) {
     static CodecManager* instance = new CodecManager();
@@ -54,13 +65,14 @@
                  offloading_preference);
   void Stop(void);
   virtual types::CodecLocation GetCodecLocation(void) const;
-  virtual void UpdateActiveSourceAudioConfig(
-      const stream_configuration& stream_conf, uint16_t delay_ms,
-      std::function<void(const ::le_audio::offload_config& config)>
-          update_receiver);
-  virtual void UpdateActiveSinkAudioConfig(
-      const stream_configuration& stream_conf, uint16_t delay_ms,
-      std::function<void(const ::le_audio::offload_config& config)>
+  virtual void UpdateCisConfiguration(
+      const std::vector<struct types::cis>& cises,
+      const stream_parameters& stream_params, uint8_t direction);
+  virtual void ClearCisConfiguration(uint8_t direction);
+  virtual void UpdateActiveAudioConfig(
+      const types::BidirectionalPair<stream_parameters>& stream_params,
+      types::BidirectionalPair<uint16_t> delays_ms,
+      std::function<void(const offload_config& config, uint8_t direction)>
           update_receiver);
   virtual const ::le_audio::set_configurations::AudioSetConfigurations*
   GetOffloadCodecConfig(::le_audio::types::LeAudioContextType ctx_type);
@@ -72,6 +84,7 @@
           update_receiver);
 
  private:
+  CodecManager();
   struct impl;
   std::unique_ptr<impl> pimpl_;
 };
diff --git a/system/bta/le_audio/codec_manager_test.cc b/system/bta/le_audio/codec_manager_test.cc
new file mode 100644
index 0000000..a9c2c18
--- /dev/null
+++ b/system/bta/le_audio/codec_manager_test.cc
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2023 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 "codec_manager.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "btm_api_mock.h"
+#include "gd/common/init_flags.h"
+#include "mock_controller.h"
+#include "osi/include/properties.h"
+#include "stack/acl/acl.h"
+
+using ::testing::_;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::Test;
+
+using bluetooth::hci::iso_manager::kIsoDataPathHci;
+using bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault;
+using le_audio::set_configurations::AudioSetConfiguration;
+using le_audio::types::CodecLocation;
+using le_audio::types::kLeAudioDirectionSink;
+using le_audio::types::kLeAudioDirectionSource;
+
+void osi_property_set_bool(const char* key, bool value);
+
+template <typename T>
+T& le_audio::types::BidirectionalPair<T>::get(uint8_t direction) {
+  return (direction == le_audio::types::kLeAudioDirectionSink) ? sink : source;
+}
+
+std::vector<AudioSetConfiguration> offload_capabilities(0);
+
+const char* test_flags[] = {
+    "INIT_default_log_level_str=LOG_VERBOSE",
+};
+
+namespace bluetooth {
+namespace audio {
+namespace le_audio {
+std::vector<AudioSetConfiguration> get_offload_capabilities() {
+  return offload_capabilities;
+}
+}  // namespace le_audio
+}  // namespace audio
+}  // namespace bluetooth
+
+namespace le_audio {
+namespace {
+
+static constexpr char kPropLeAudioOffloadSupported[] =
+    "ro.bluetooth.leaudio_offload.supported";
+static constexpr char kPropLeAudioOffloadDisabled[] =
+    "persist.bluetooth.leaudio_offload.disabled";
+
+class CodecManagerTestBase : public Test {
+ public:
+  virtual void SetUp() override {
+    bluetooth::common::InitFlags::Load(test_flags);
+
+    offload_capabilities.clear();
+
+    ON_CALL(controller_interface, SupportsBleIsochronousBroadcaster)
+        .WillByDefault(Return(true));
+    ON_CALL(controller_interface, SupportsConfigureDataPath)
+        .WillByDefault(Return(true));
+
+    controller::SetMockControllerInterface(&controller_interface);
+    bluetooth::manager::SetMockBtmInterface(&btm_interface);
+
+    codec_manager = CodecManager::GetInstance();
+  }
+
+  virtual void TearDown() override {
+    codec_manager->Stop();
+
+    bluetooth::manager::SetMockBtmInterface(nullptr);
+    controller::SetMockControllerInterface(nullptr);
+  }
+
+  NiceMock<bluetooth::manager::MockBtmInterface> btm_interface;
+  NiceMock<controller::MockControllerInterface> controller_interface;
+  CodecManager* codec_manager;
+};
+
+/*----------------- ADSP codec manager tests ------------------*/
+class CodecManagerTestAdsp : public CodecManagerTestBase {
+ public:
+  virtual void SetUp() override {
+    // Enable the HW offloader
+    osi_property_set_bool(kPropLeAudioOffloadSupported, true);
+    osi_property_set_bool(kPropLeAudioOffloadDisabled, false);
+
+    CodecManagerTestBase::SetUp();
+  }
+};
+
+TEST_F(CodecManagerTestAdsp, test_init) {
+  ASSERT_EQ(codec_manager, CodecManager::GetInstance());
+}
+
+TEST_F(CodecManagerTestAdsp, test_start) {
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER,
+                                kIsoDataPathPlatformDefault, _))
+      .Times(1);
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST,
+                                kIsoDataPathPlatformDefault, _))
+      .Times(1);
+
+  const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>
+      offloading_preference(0);
+  codec_manager->Start(offloading_preference);
+  Mock::VerifyAndClearExpectations(&btm_interface);
+
+  ASSERT_EQ(codec_manager->GetCodecLocation(), CodecLocation::ADSP);
+
+  // Verify data path is reset on Stop()
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER,
+                                kIsoDataPathHci, _))
+      .Times(1);
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST,
+                                kIsoDataPathHci, _))
+      .Times(1);
+}
+
+TEST_F(CodecManagerTestAdsp, testStreamConfigurationAdspDownMix) {
+  const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>
+      offloading_preference(0);
+  codec_manager->Start(offloading_preference);
+
+  // Current CIS configuration for two earbuds
+  std::vector<struct types::cis> cises{
+      {
+          .id = 0x00,
+          .type = types::CisType::CIS_TYPE_BIDIRECTIONAL,
+          .conn_handle = 96,
+      },
+      {
+          .id = 0x01,
+          .type = types::CisType::CIS_TYPE_BIDIRECTIONAL,
+          .conn_handle = 97,
+      },
+  };
+
+  // Stream parameters
+  types::BidirectionalPair<stream_parameters> stream_params{
+      .sink =
+          {
+              .sample_frequency_hz = 16000,
+              .frame_duration_us = 10000,
+              .octets_per_codec_frame = 40,
+              .audio_channel_allocation =
+                  codec_spec_conf::kLeAudioLocationFrontLeft,
+              .codec_frames_blocks_per_sdu = 1,
+              .num_of_channels = 1,
+              .num_of_devices = 1,
+              .stream_locations =
+                  {
+                      std::pair<uint16_t, uint32_t>{
+                          97 /*conn_handle*/,
+                          codec_spec_conf::kLeAudioLocationFrontLeft},
+                  },
+          },
+      .source =
+          {
+              .sample_frequency_hz = 16000,
+              .frame_duration_us = 10000,
+              .octets_per_codec_frame = 40,
+              .audio_channel_allocation =
+                  codec_spec_conf::kLeAudioLocationFrontLeft,
+              .codec_frames_blocks_per_sdu = 1,
+              .num_of_channels = 1,
+              .num_of_devices = 1,
+              {
+                  std::pair<uint16_t, uint32_t>{
+                      97 /*conn_handle*/,
+                      codec_spec_conf::kLeAudioLocationBackLeft},
+              },
+          },
+  };
+
+  codec_manager->UpdateCisConfiguration(cises, stream_params.sink,
+                                        kLeAudioDirectionSink);
+  codec_manager->UpdateCisConfiguration(cises, stream_params.source,
+                                        kLeAudioDirectionSource);
+
+  // Verify the offloader config content
+  types::BidirectionalPair<std::optional<offload_config>> out_offload_configs;
+  codec_manager->UpdateActiveAudioConfig(
+      stream_params, {.sink = 44, .source = 44},
+      [&out_offload_configs](const offload_config& config, uint8_t direction) {
+        out_offload_configs.get(direction) = config;
+      });
+
+  // Expect the same configuration for sink and source
+  ASSERT_TRUE(out_offload_configs.sink.has_value());
+  ASSERT_TRUE(out_offload_configs.source.has_value());
+  for (auto direction : {le_audio::types::kLeAudioDirectionSink,
+                         le_audio::types::kLeAudioDirectionSource}) {
+    uint32_t allocation = 0;
+    auto& config = out_offload_configs.get(direction).value();
+    ASSERT_EQ(2lu, config.stream_map.size());
+    for (const auto& info : config.stream_map) {
+      if (info.stream_handle == 96) {
+        ASSERT_EQ(codec_spec_conf::kLeAudioLocationFrontRight,
+                  info.audio_channel_allocation);
+        // The disconnected should be inactive
+        ASSERT_FALSE(info.is_stream_active);
+
+      } else if (info.stream_handle == 97) {
+        ASSERT_EQ(codec_spec_conf::kLeAudioLocationFrontLeft,
+                  info.audio_channel_allocation);
+        // The connected should be active
+        ASSERT_TRUE(info.is_stream_active);
+
+      } else {
+        ASSERT_EQ(97, info.stream_handle);
+      }
+      allocation |= info.audio_channel_allocation;
+    }
+
+    ASSERT_EQ(16, config.bits_per_sample);
+    ASSERT_EQ(16000u, config.sampling_rate);
+    ASSERT_EQ(10000u, config.frame_duration);
+    ASSERT_EQ(40u, config.octets_per_frame);
+    ASSERT_EQ(1, config.blocks_per_sdu);
+    ASSERT_EQ(44, config.peer_delay_ms);
+    ASSERT_EQ(codec_spec_conf::kLeAudioLocationStereo, allocation);
+  }
+
+  // Clear the CIS configuration map (no active CISes).
+  codec_manager->ClearCisConfiguration(kLeAudioDirectionSink);
+  codec_manager->ClearCisConfiguration(kLeAudioDirectionSource);
+  out_offload_configs.sink = std::nullopt;
+  out_offload_configs.source = std::nullopt;
+  codec_manager->UpdateActiveAudioConfig(
+      stream_params, {.sink = 44, .source = 44},
+      [&out_offload_configs](const offload_config& config, uint8_t direction) {
+        out_offload_configs.get(direction) = config;
+      });
+
+  // Expect sink & source configurations with empty CIS channel allocation map.
+  ASSERT_TRUE(out_offload_configs.sink.has_value());
+  ASSERT_TRUE(out_offload_configs.source.has_value());
+  for (auto direction : {le_audio::types::kLeAudioDirectionSink,
+                         le_audio::types::kLeAudioDirectionSource}) {
+    auto& config = out_offload_configs.get(direction).value();
+    ASSERT_EQ(0lu, config.stream_map.size());
+    ASSERT_EQ(16, config.bits_per_sample);
+    ASSERT_EQ(16000u, config.sampling_rate);
+    ASSERT_EQ(10000u, config.frame_duration);
+    ASSERT_EQ(40u, config.octets_per_frame);
+    ASSERT_EQ(1, config.blocks_per_sdu);
+    ASSERT_EQ(44, config.peer_delay_ms);
+  }
+}
+
+// TODO: Add the unit tests for:
+// GetOffloadCodecConfig
+// GetBroadcastOffloadConfig
+// UpdateBroadcastConnHandle
+
+/*----------------- HOST codec manager tests ------------------*/
+class CodecManagerTestHost : public CodecManagerTestBase {
+ public:
+  virtual void SetUp() override {
+    // Enable the HW offloader
+    osi_property_set_bool(kPropLeAudioOffloadSupported, false);
+    osi_property_set_bool(kPropLeAudioOffloadDisabled, false);
+
+    CodecManagerTestBase::SetUp();
+  }
+};
+
+TEST_F(CodecManagerTestHost, test_init) {
+  ASSERT_EQ(codec_manager, CodecManager::GetInstance());
+}
+
+TEST_F(CodecManagerTestHost, test_start) {
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER,
+                                kIsoDataPathPlatformDefault, _))
+      .Times(0);
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST,
+                                kIsoDataPathPlatformDefault, _))
+      .Times(0);
+
+  const std::vector<bluetooth::le_audio::btle_audio_codec_config_t>
+      offloading_preference(0);
+  codec_manager->Start(offloading_preference);
+  Mock::VerifyAndClearExpectations(&btm_interface);
+
+  ASSERT_EQ(codec_manager->GetCodecLocation(), CodecLocation::HOST);
+
+  // Verify data path is NOT reset on Stop() for the Host encoding session
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::HOST_TO_CONTROLLER,
+                                kIsoDataPathHci, _))
+      .Times(0);
+  EXPECT_CALL(btm_interface,
+              ConfigureDataPath(btm_data_direction::CONTROLLER_TO_HOST,
+                                kIsoDataPathHci, _))
+      .Times(0);
+}
+
+}  // namespace
+}  // namespace le_audio
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index a849489..fbaa0f1 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -158,30 +158,36 @@
 
 void LeAudioDeviceGroup::ClearSinksFromConfiguration(void) {
   LOG_INFO("Group %p, group_id %d", this, group_id_);
-  stream_conf.sink_streams.clear();
-  stream_conf.sink_offloader_streams_target_allocation.clear();
-  stream_conf.sink_offloader_streams_current_allocation.clear();
-  stream_conf.sink_audio_channel_allocation = 0;
-  stream_conf.sink_num_of_channels = 0;
-  stream_conf.sink_num_of_devices = 0;
-  stream_conf.sink_sample_frequency_hz = 0;
-  stream_conf.sink_codec_frames_blocks_per_sdu = 0;
-  stream_conf.sink_octets_per_codec_frame = 0;
-  stream_conf.sink_frame_duration_us = 0;
+  auto direction = types::kLeAudioDirectionSink;
+
+  auto& stream_params = stream_conf.stream_params.get(direction);
+  stream_params.stream_locations.clear();
+  stream_params.audio_channel_allocation = 0;
+  stream_params.num_of_channels = 0;
+  stream_params.num_of_devices = 0;
+  stream_params.sample_frequency_hz = 0;
+  stream_params.codec_frames_blocks_per_sdu = 0;
+  stream_params.octets_per_codec_frame = 0;
+  stream_params.frame_duration_us = 0;
+
+  CodecManager::GetInstance()->ClearCisConfiguration(direction);
 }
 
 void LeAudioDeviceGroup::ClearSourcesFromConfiguration(void) {
   LOG_INFO("Group %p, group_id %d", this, group_id_);
-  stream_conf.source_streams.clear();
-  stream_conf.source_offloader_streams_target_allocation.clear();
-  stream_conf.source_offloader_streams_current_allocation.clear();
-  stream_conf.source_audio_channel_allocation = 0;
-  stream_conf.source_num_of_channels = 0;
-  stream_conf.source_num_of_devices = 0;
-  stream_conf.source_sample_frequency_hz = 0;
-  stream_conf.source_codec_frames_blocks_per_sdu = 0;
-  stream_conf.source_octets_per_codec_frame = 0;
-  stream_conf.source_frame_duration_us = 0;
+  auto direction = types::kLeAudioDirectionSource;
+
+  auto& stream_params = stream_conf.stream_params.get(direction);
+  stream_params.stream_locations.clear();
+  stream_params.audio_channel_allocation = 0;
+  stream_params.num_of_channels = 0;
+  stream_params.num_of_devices = 0;
+  stream_params.sample_frequency_hz = 0;
+  stream_params.codec_frames_blocks_per_sdu = 0;
+  stream_params.octets_per_codec_frame = 0;
+  stream_params.frame_duration_us = 0;
+
+  CodecManager::GetInstance()->ClearCisConfiguration(direction);
 }
 
 void LeAudioDeviceGroup::CigClearCis(void) {
@@ -194,26 +200,32 @@
 void LeAudioDeviceGroup::Cleanup(void) {
   /* Bluetooth is off while streaming - disconnect CISes and remove CIG */
   if (GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
-    if (!stream_conf.sink_streams.empty()) {
-      for (auto [cis_handle, audio_location] : stream_conf.sink_streams) {
+    auto& sink_stream_locations =
+        stream_conf.stream_params.sink.stream_locations;
+    auto& source_stream_locations =
+        stream_conf.stream_params.source.stream_locations;
+
+    if (!sink_stream_locations.empty()) {
+      for (const auto kv_pair : sink_stream_locations) {
+        auto cis_handle = kv_pair.first;
         bluetooth::hci::IsoManager::GetInstance()->DisconnectCis(
             cis_handle, HCI_ERR_PEER_USER);
 
-        if (stream_conf.source_streams.empty()) {
+        /* Check the other direction if disconnecting bidirectional CIS */
+        if (source_stream_locations.empty()) {
           continue;
         }
-        uint16_t cis_hdl = cis_handle;
-        stream_conf.source_streams.erase(
+        source_stream_locations.erase(
             std::remove_if(
-                stream_conf.source_streams.begin(),
-                stream_conf.source_streams.end(),
-                [cis_hdl](auto& pair) { return pair.first == cis_hdl; }),
-            stream_conf.source_streams.end());
+                source_stream_locations.begin(), source_stream_locations.end(),
+                [&cis_handle](auto& pair) { return pair.first == cis_handle; }),
+            source_stream_locations.end());
       }
     }
 
-    if (!stream_conf.source_streams.empty()) {
-      for (auto [cis_handle, audio_location] : stream_conf.source_streams) {
+    /* Take care of the non-bidirectional CISes */
+    if (!source_stream_locations.empty()) {
+      for (auto [cis_handle, _] : source_stream_locations) {
         bluetooth::hci::IsoManager::GetInstance()->DisconnectCis(
             cis_handle, HCI_ERR_PEER_USER);
       }
@@ -908,7 +920,8 @@
 bool LeAudioDeviceGroup::IsGroupStreamReady(void) const {
   auto iter =
       std::find_if(leAudioDevices_.begin(), leAudioDevices_.end(), [](auto& d) {
-        if (d.expired())
+        if (d.expired() || (d.lock().get()->GetConnectionState() !=
+                            DeviceConnectState::CONNECTED))
           return false;
         else
           return !(((d.lock()).get())->HaveAllActiveAsesCisEst());
@@ -1021,39 +1034,39 @@
 
   uint8_t idx = 0;
   while (cis_count_bidir > 0) {
-    struct le_audio::types::cis cis_entry = {
-        .id = idx,
-        .addr = RawAddress::kEmpty,
-        .type = CisType::CIS_TYPE_BIDIRECTIONAL,
-        .conn_handle = 0,
-    };
-    cises_.push_back(cis_entry);
-    cis_count_bidir--;
-    idx++;
+      struct le_audio::types::cis cis_entry = {
+          .id = idx,
+          .type = CisType::CIS_TYPE_BIDIRECTIONAL,
+          .conn_handle = 0,
+          .addr = RawAddress::kEmpty,
+      };
+      cises_.push_back(cis_entry);
+      cis_count_bidir--;
+      idx++;
   }
 
   while (cis_count_unidir_sink > 0) {
-    struct le_audio::types::cis cis_entry = {
-        .id = idx,
-        .addr = RawAddress::kEmpty,
-        .type = CisType::CIS_TYPE_UNIDIRECTIONAL_SINK,
-        .conn_handle = 0,
-    };
-    cises_.push_back(cis_entry);
-    cis_count_unidir_sink--;
-    idx++;
+      struct le_audio::types::cis cis_entry = {
+          .id = idx,
+          .type = CisType::CIS_TYPE_UNIDIRECTIONAL_SINK,
+          .conn_handle = 0,
+          .addr = RawAddress::kEmpty,
+      };
+      cises_.push_back(cis_entry);
+      cis_count_unidir_sink--;
+      idx++;
   }
 
   while (cis_count_unidir_source > 0) {
-    struct le_audio::types::cis cis_entry = {
-        .id = idx,
-        .addr = RawAddress::kEmpty,
-        .type = CisType::CIS_TYPE_UNIDIRECTIONAL_SOURCE,
-        .conn_handle = 0,
-    };
-    cises_.push_back(cis_entry);
-    cis_count_unidir_source--;
-    idx++;
+      struct le_audio::types::cis cis_entry = {
+          .id = idx,
+          .type = CisType::CIS_TYPE_UNIDIRECTIONAL_SOURCE,
+          .conn_handle = 0,
+          .addr = RawAddress::kEmpty,
+      };
+      cises_.push_back(cis_entry);
+      cis_count_unidir_source--;
+      idx++;
   }
 }
 
@@ -1582,6 +1595,8 @@
   for (; needed_ase && ase; needed_ase--) {
     ase->active = true;
     ase->configured_for_context_type = context_type;
+    ase->is_codec_in_controller = ent.is_codec_in_controller;
+    ase->data_path_id = ent.data_path_id;
     active_ases++;
 
     /* In case of late connect, we could be here for STREAMING ase.
@@ -1601,7 +1616,7 @@
       /*Let's choose audio channel allocation if not set */
       ase->codec_config.audio_channel_allocation =
           PickAudioLocation(strategy, audio_locations,
-                            group_audio_locations_memo.get_ref(ent.direction));
+                            group_audio_locations_memo.get(ent.direction));
 
       /* Get default value if no requirement for specific frame blocks per sdu
        */
@@ -1880,25 +1895,20 @@
 }
 
 bool LeAudioDeviceGroup::IsCisPartOfCurrentStream(uint16_t cis_conn_hdl) const {
+  auto& sink_stream_locations = stream_conf.stream_params.sink.stream_locations;
   auto iter = std::find_if(
-      stream_conf.sink_streams.begin(), stream_conf.sink_streams.end(),
+      sink_stream_locations.begin(), sink_stream_locations.end(),
       [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });
 
-  if (iter != stream_conf.sink_streams.end()) return true;
+  if (iter != sink_stream_locations.end()) return true;
 
+  auto& source_stream_locations =
+      stream_conf.stream_params.source.stream_locations;
   iter = std::find_if(
-      stream_conf.source_streams.begin(), stream_conf.source_streams.end(),
+      source_stream_locations.begin(), source_stream_locations.end(),
       [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });
 
-  return (iter != stream_conf.source_streams.end());
-}
-
-void LeAudioDeviceGroup::StreamOffloaderUpdated(uint8_t direction) {
-  if (direction == le_audio::types::kLeAudioDirectionSource) {
-    stream_conf.source_is_initial = false;
-  } else {
-    stream_conf.sink_is_initial = false;
-  }
+  return (iter != source_stream_locations.end());
 }
 
 void LeAudioDeviceGroup::RemoveCisFromStreamIfNeeded(
@@ -1907,185 +1917,67 @@
 
   if (!IsCisPartOfCurrentStream(cis_conn_hdl)) return;
 
-  auto sink_channels = stream_conf.sink_num_of_channels;
-  auto source_channels = stream_conf.source_num_of_channels;
+  /* Cache the old values for comparison */
+  auto old_sink_channels = stream_conf.stream_params.sink.num_of_channels;
+  auto old_source_channels = stream_conf.stream_params.source.num_of_channels;
 
-  if (!stream_conf.sink_streams.empty() ||
-      !stream_conf.source_streams.empty()) {
-    stream_conf.sink_streams.erase(
+  for (auto dir :
+       {types::kLeAudioDirectionSink, types::kLeAudioDirectionSource}) {
+    auto& params = stream_conf.stream_params.get(dir);
+    params.stream_locations.erase(
         std::remove_if(
-            stream_conf.sink_streams.begin(), stream_conf.sink_streams.end(),
-            [leAudioDevice, &cis_conn_hdl, this](auto& pair) {
+            params.stream_locations.begin(), params.stream_locations.end(),
+            [leAudioDevice, &cis_conn_hdl, &params, dir](auto& pair) {
               if (!cis_conn_hdl) {
                 cis_conn_hdl = pair.first;
               }
               auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
-              if (ases_pair.sink && cis_conn_hdl == pair.first) {
-                stream_conf.sink_num_of_devices--;
-                stream_conf.sink_num_of_channels -=
-                    ases_pair.sink->codec_config.channel_count;
-                stream_conf.sink_audio_channel_allocation &= ~pair.second;
+              if (ases_pair.get(dir) && cis_conn_hdl == pair.first) {
+                params.num_of_devices--;
+                params.num_of_channels -=
+                    ases_pair.get(dir)->codec_config.channel_count;
+                params.audio_channel_allocation &= ~pair.second;
               }
-              return (ases_pair.sink && cis_conn_hdl == pair.first);
+              return (ases_pair.get(dir) && cis_conn_hdl == pair.first);
             }),
-        stream_conf.sink_streams.end());
-
-    stream_conf.source_streams.erase(
-        std::remove_if(
-            stream_conf.source_streams.begin(),
-            stream_conf.source_streams.end(),
-            [leAudioDevice, &cis_conn_hdl, this](auto& pair) {
-              if (!cis_conn_hdl) {
-                cis_conn_hdl = pair.first;
-              }
-              auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
-              if (ases_pair.source && cis_conn_hdl == pair.first) {
-                stream_conf.source_num_of_devices--;
-                stream_conf.source_num_of_channels -=
-                    ases_pair.source->codec_config.channel_count;
-                stream_conf.source_audio_channel_allocation &= ~pair.second;
-              }
-              return (ases_pair.source && cis_conn_hdl == pair.first);
-            }),
-        stream_conf.source_streams.end());
-
-    LOG_INFO(
-        " Sink Number Of Devices: %d"
-        ", Sink Number Of Channels: %d"
-        ", Source Number Of Devices: %d"
-        ", Source Number Of Channels: %d",
-        stream_conf.sink_num_of_devices, stream_conf.sink_num_of_channels,
-        stream_conf.source_num_of_devices, stream_conf.source_num_of_channels);
+        params.stream_locations.end());
   }
 
-  if (stream_conf.sink_num_of_channels == 0) {
+  LOG_INFO(
+      " Sink Number Of Devices: %d"
+      ", Sink Number Of Channels: %d"
+      ", Source Number Of Devices: %d"
+      ", Source Number Of Channels: %d",
+      stream_conf.stream_params.sink.num_of_devices,
+      stream_conf.stream_params.sink.num_of_channels,
+      stream_conf.stream_params.source.num_of_devices,
+      stream_conf.stream_params.source.num_of_channels);
+
+  if (stream_conf.stream_params.sink.num_of_channels == 0) {
     ClearSinksFromConfiguration();
   }
 
-  if (stream_conf.source_num_of_channels == 0) {
+  if (stream_conf.stream_params.source.num_of_channels == 0) {
     ClearSourcesFromConfiguration();
   }
 
-  /* Update offloader streams if needed */
-  if (sink_channels > stream_conf.sink_num_of_channels) {
-    CreateStreamVectorForOffloader(le_audio::types::kLeAudioDirectionSink);
+  /* Update CodecManager CIS configuration */
+  if (old_sink_channels > stream_conf.stream_params.sink.num_of_channels) {
+    CodecManager::GetInstance()->UpdateCisConfiguration(
+        cises_,
+        stream_conf.stream_params.get(le_audio::types::kLeAudioDirectionSink),
+        le_audio::types::kLeAudioDirectionSink);
   }
-  if (source_channels > stream_conf.source_num_of_channels) {
-    CreateStreamVectorForOffloader(le_audio::types::kLeAudioDirectionSource);
+  if (old_source_channels > stream_conf.stream_params.source.num_of_channels) {
+    CodecManager::GetInstance()->UpdateCisConfiguration(
+        cises_,
+        stream_conf.stream_params.get(le_audio::types::kLeAudioDirectionSource),
+        le_audio::types::kLeAudioDirectionSource);
   }
 
   CigUnassignCis(leAudioDevice);
 }
 
-void LeAudioDeviceGroup::CreateStreamVectorForOffloader(uint8_t direction) {
-  if (CodecManager::GetInstance()->GetCodecLocation() !=
-      le_audio::types::CodecLocation::ADSP) {
-    return;
-  }
-
-  CisType cis_type;
-  std::vector<std::pair<uint16_t, uint32_t>>* streams;
-  std::vector<stream_map_info>* offloader_streams_target_allocation;
-  std::vector<stream_map_info>* offloader_streams_current_allocation;
-  std::string tag;
-  uint32_t available_allocations = 0;
-  bool* changed_flag;
-  bool* is_initial;
-  if (direction == le_audio::types::kLeAudioDirectionSource) {
-    changed_flag = &stream_conf.source_offloader_changed;
-    is_initial = &stream_conf.source_is_initial;
-    cis_type = CisType::CIS_TYPE_UNIDIRECTIONAL_SOURCE;
-    streams = &stream_conf.source_streams;
-    offloader_streams_target_allocation =
-        &stream_conf.source_offloader_streams_target_allocation;
-    offloader_streams_current_allocation =
-        &stream_conf.source_offloader_streams_current_allocation;
-    tag = "Source";
-    available_allocations = AdjustAllocationForOffloader(
-        stream_conf.source_audio_channel_allocation);
-  } else {
-    changed_flag = &stream_conf.sink_offloader_changed;
-    is_initial = &stream_conf.sink_is_initial;
-    cis_type = CisType::CIS_TYPE_UNIDIRECTIONAL_SINK;
-    streams = &stream_conf.sink_streams;
-    offloader_streams_target_allocation =
-        &stream_conf.sink_offloader_streams_target_allocation;
-    offloader_streams_current_allocation =
-        &stream_conf.sink_offloader_streams_current_allocation;
-    tag = "Sink";
-    available_allocations =
-        AdjustAllocationForOffloader(stream_conf.sink_audio_channel_allocation);
-  }
-
-  if (available_allocations == 0) {
-    LOG_ERROR("There is no CIS connected");
-    return;
-  }
-
-  if (offloader_streams_target_allocation->size() == 0) {
-    *is_initial = true;
-  } else if (*is_initial || LeAudioHalVerifier::SupportsStreamActiveApi()) {
-    // As multiple CISes phone call case, the target_allocation already have the
-    // previous data, but the is_initial flag not be cleared. We need to clear
-    // here to avoid make duplicated target allocation stream map.
-    offloader_streams_target_allocation->clear();
-  }
-
-  offloader_streams_current_allocation->clear();
-  *changed_flag = true;
-  bool not_all_cises_connected = false;
-  if (available_allocations != codec_spec_conf::kLeAudioLocationStereo) {
-    not_all_cises_connected = true;
-  }
-
-  /* If the all cises are connected as stream started, reset changed_flag that
-   * the bt stack wouldn't send another audio configuration for the connection
-   * status */
-  if (*is_initial && !not_all_cises_connected) {
-    *changed_flag = false;
-  }
-  for (auto& cis_entry : cises_) {
-    if ((cis_entry.type == CisType::CIS_TYPE_BIDIRECTIONAL ||
-         cis_entry.type == cis_type) &&
-        cis_entry.conn_handle != 0) {
-      uint32_t target_allocation = 0;
-      uint32_t current_allocation = 0;
-      bool is_active = false;
-      for (const auto& s : *streams) {
-        if (s.first == cis_entry.conn_handle) {
-          is_active = true;
-          target_allocation = AdjustAllocationForOffloader(s.second);
-          current_allocation = target_allocation;
-          if (not_all_cises_connected) {
-            /* Tell offloader to mix on this CIS.*/
-            current_allocation = codec_spec_conf::kLeAudioLocationStereo;
-          }
-          break;
-        }
-      }
-
-      if (target_allocation == 0) {
-        /* Take missing allocation for that one .*/
-        target_allocation =
-            codec_spec_conf::kLeAudioLocationStereo & ~available_allocations;
-      }
-
-      LOG_INFO(
-          "%s: Cis handle 0x%04x, target allocation  0x%08x, current "
-          "allocation 0x%08x, active: %d",
-          tag.c_str(), cis_entry.conn_handle, target_allocation,
-          current_allocation, is_active);
-
-      if (*is_initial || LeAudioHalVerifier::SupportsStreamActiveApi()) {
-        offloader_streams_target_allocation->emplace_back(stream_map_info(
-            cis_entry.conn_handle, target_allocation, is_active));
-      }
-      offloader_streams_current_allocation->emplace_back(stream_map_info(
-          cis_entry.conn_handle, current_allocation, is_active));
-    }
-  }
-}
-
 bool LeAudioDeviceGroup::IsPendingConfiguration(void) const {
   return stream_conf.pending_configuration;
 }
@@ -2176,6 +2068,20 @@
   }
 }
 
+void LeAudioDeviceGroup::ApplyReconnectionMode(
+    int gatt_if, tBTM_BLE_CONN_TYPE reconnection_mode) {
+  for (const auto& device_iter : leAudioDevices_) {
+    BTA_GATTC_CancelOpen(gatt_if, device_iter.lock()->address_, false);
+    BTA_GATTC_Open(gatt_if, device_iter.lock()->address_, reconnection_mode,
+                   false);
+    LOG_INFO("Group %d in state %s. Adding %s to default reconnection mode ",
+             group_id_, bluetooth::common::ToString(GetState()).c_str(),
+             ADDRESS_TO_LOGGABLE_CSTR(device_iter.lock()->address_));
+    device_iter.lock()->SetConnectionState(
+        DeviceConnectState::CONNECTING_AUTOCONNECT);
+  }
+}
+
 bool LeAudioDeviceGroup::IsConfiguredForContext(
     types::LeAudioContextType context_type) const {
   /* Check if all connected group members are configured */
@@ -2361,11 +2267,12 @@
          << "\n"
          << "      num of devices(connected): " << Size() << "("
          << NumOfConnected() << ")\n"
-         << ",     num of sinks(connected): " << stream_conf.sink_num_of_devices
-         << "(" << stream_conf.sink_streams.size() << ")\n"
+         << ",     num of sinks(connected): "
+         << stream_conf.stream_params.sink.num_of_devices << "("
+         << stream_conf.stream_params.sink.stream_locations.size() << ")\n"
          << "      num of sources(connected): "
-         << stream_conf.source_num_of_devices << "("
-         << stream_conf.source_streams.size() << ")\n"
+         << stream_conf.stream_params.source.num_of_devices << "("
+         << stream_conf.stream_params.source.stream_locations.size() << ")\n"
          << "      allocated CISes: " << static_cast<int>(cises_.size());
 
   if (cises_.size() > 0) {
@@ -2709,7 +2616,10 @@
 bool LeAudioDevice::HaveAllActiveAsesCisEst(void) {
   if (ases_.empty()) {
     LOG_WARN("No ases for device %s", ADDRESS_TO_LOGGABLE_CSTR(address_));
-    return false;
+    /* If there is no ASEs at all, it means we are good here - meaning, it is
+     * not waiting for any CIS to be established.
+     */
+    return true;
   }
 
   auto iter = std::find_if(ases_.begin(), ases_.end(), [](const auto& ase) {
diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h
index 4c82563..d978f3a 100644
--- a/system/bta/le_audio/devices.h
+++ b/system/bta/le_audio/devices.h
@@ -186,9 +186,14 @@
       bool reuse_cis_id);
 
   inline types::AudioContexts GetSupportedContexts(
-      int direction = (types::kLeAudioDirectionSink |
-                       types::kLeAudioDirectionSource)) const {
-    return supp_contexts_.get(direction);
+      int direction = types::kLeAudioDirectionBoth) const {
+    ASSERT_LOG(direction <= (types::kLeAudioDirectionBoth),
+               "Invalid direction used.");
+
+    if (direction < types::kLeAudioDirectionBoth)
+      return supp_contexts_.get(direction);
+    else
+      return types::get_bidirectional(supp_contexts_);
   }
   inline void SetSupportedContexts(
       types::BidirectionalPair<types::AudioContexts> contexts) {
@@ -196,9 +201,14 @@
   }
 
   inline types::AudioContexts GetAvailableContexts(
-      int direction = (types::kLeAudioDirectionSink |
-                       types::kLeAudioDirectionSource)) const {
-    return avail_contexts_.get(direction);
+      int direction = types::kLeAudioDirectionBoth) const {
+    ASSERT_LOG(direction <= (types::kLeAudioDirectionBoth),
+               "Invalid direction used.");
+
+    if (direction < types::kLeAudioDirectionBoth)
+      return avail_contexts_.get(direction);
+    else
+      return types::get_bidirectional(avail_contexts_);
   }
   void SetAvailableContexts(
       types::BidirectionalPair<types::AudioContexts> cont_val);
@@ -390,6 +400,7 @@
   void SetPendingConfiguration(void);
   void ClearPendingConfiguration(void);
   void AddToAllowListNotConnectedGroupMembers(int gatt_if);
+  void ApplyReconnectionMode(int gatt_if, tBTM_BLE_CONN_TYPE reconnection_mode);
   void Disable(int gatt_if);
   void Enable(int gatt_if, tBTM_BLE_CONN_TYPE reconnection_mode);
   bool IsEnabled(void) const;
@@ -406,8 +417,6 @@
   bool IsMetadataChanged(
       const types::BidirectionalPair<types::AudioContexts>& context_types,
       const types::BidirectionalPair<std::vector<uint8_t>>& ccid_lists) const;
-  void CreateStreamVectorForOffloader(uint8_t direction);
-  void StreamOffloaderUpdated(uint8_t direction);
   bool IsConfiguredForContext(types::LeAudioContextType context_type) const;
   void RemoveCisFromStreamIfNeeded(LeAudioDevice* leAudioDevice,
                                    uint16_t cis_conn_hdl);
@@ -475,21 +484,25 @@
         group_available_contexts_.source.to_string().c_str());
   }
 
-  inline types::AudioContexts GetAvailableContexts(
-      int direction = (types::kLeAudioDirectionSink |
-                       types::kLeAudioDirectionSource)) const {
-    LOG_DEBUG(
-        " group id: %d, available contexts sink: %s, available contexts "
-        "source: "
-        "%s",
-        group_id_, group_available_contexts_.sink.to_string().c_str(),
-        group_available_contexts_.source.to_string().c_str());
-    return group_available_contexts_.get(direction);
+  types::AudioContexts GetAvailableContexts(
+      int direction = types::kLeAudioDirectionBoth) const {
+    ASSERT_LOG(direction <= (types::kLeAudioDirectionBoth),
+               "Invalid direction used.");
+    if (direction < types::kLeAudioDirectionBoth) {
+      LOG_DEBUG(
+          " group id: %d, available contexts sink: %s, available contexts "
+          "source: "
+          "%s",
+          group_id_, group_available_contexts_.sink.to_string().c_str(),
+          group_available_contexts_.source.to_string().c_str());
+      return group_available_contexts_.get(direction);
+    } else {
+      return types::get_bidirectional(group_available_contexts_);
+    }
   }
 
   types::AudioContexts GetSupportedContexts(
-      int direction = (types::kLeAudioDirectionSink |
-                       types::kLeAudioDirectionSource)) const;
+      int direction = types::kLeAudioDirectionBoth) const;
 
   types::BidirectionalPair<types::AudioContexts> GetLatestAvailableContexts(
       void) const;
diff --git a/system/bta/le_audio/devices_test.cc b/system/bta/le_audio/devices_test.cc
index 64be816..89ff7c6 100644
--- a/system/bta/le_audio/devices_test.cc
+++ b/system/bta/le_audio/devices_test.cc
@@ -431,7 +431,8 @@
     group_ = new LeAudioDeviceGroup(group_id_);
     bluetooth::manager::SetMockBtmInterface(&btm_interface_);
     controller::SetMockControllerInterface(&controller_interface_);
-    ::le_audio::AudioSetConfigurationProvider::Initialize();
+    ::le_audio::AudioSetConfigurationProvider::Initialize(
+        ::le_audio::types::CodecLocation::ADSP);
     MockCsisClient::SetMockInstanceForTesting(&mock_csis_client_module_);
     ON_CALL(mock_csis_client_module_, Get())
         .WillByDefault(Return(&mock_csis_client_module_));
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index 665d2b9..cec3f25 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -37,6 +37,7 @@
 #include "le_audio_health_status.h"
 #include "le_audio_set_configuration_provider.h"
 #include "le_audio_types.h"
+#include "mock_codec_manager.h"
 #include "mock_controller.h"
 #include "mock_csis_client.h"
 #include "mock_device_groups.h"
@@ -433,6 +434,22 @@
             base::Unretained(this->gatt_callback), event_data));
   }
 
+  void InjectEncryptionChangedEvent(const RawAddress& address) {
+    tBTA_GATTC_ENC_CMPL_CB event_data = {
+        .client_if = gatt_if,
+        .remote_bda = address,
+    };
+
+    do_in_main_thread(FROM_HERE,
+                      base::BindOnce(
+                          [](tBTA_GATTC_CBACK* gatt_callback,
+                             tBTA_GATTC_ENC_CMPL_CB event_data) {
+                            gatt_callback(BTA_GATTC_ENC_CMPL_CB_EVT,
+                                          (tBTA_GATTC*)&event_data);
+                          },
+                          base::Unretained(this->gatt_callback), event_data));
+  }
+
   void InjectDisconnectedEvent(
       uint16_t conn_id,
       tGATT_DISCONN_REASON reason = GATT_CONN_TERMINATE_LOCAL_HOST) {
@@ -440,8 +457,8 @@
     ASSERT_NE(peer_devices.count(conn_id), 0u);
 
     tBTA_GATTC_CLOSE event_data = {
-        .status = GATT_SUCCESS,
         .conn_id = conn_id,
+        .status = GATT_SUCCESS,
         .client_if = gatt_if,
         .remote_bda = peer_devices.at(conn_id)->addr,
         .reason = reason,
@@ -460,8 +477,8 @@
   void InjectSearchCompleteEvent(uint16_t conn_id) {
     ASSERT_NE(conn_id, GATT_INVALID_CONN_ID);
     tBTA_GATTC_SEARCH_CMPL event_data = {
-        .status = GATT_SUCCESS,
         .conn_id = conn_id,
+        .status = GATT_SUCCESS,
     };
 
     do_in_main_thread(FROM_HERE,
@@ -797,19 +814,21 @@
 
             /* Copied from state_machine.cc ProcessHciNotifSetupIsoDataPath */
             if (ase.direction == le_audio::types::kLeAudioDirectionSource) {
-              auto iter = std::find_if(stream_conf->source_streams.begin(),
-                                       stream_conf->source_streams.end(),
-                                       [cis_conn_hdl](auto& pair) {
-                                         return cis_conn_hdl == pair.first;
-                                       });
+              auto iter = std::find_if(
+                  stream_conf->stream_params.source.stream_locations.begin(),
+                  stream_conf->stream_params.source.stream_locations.end(),
+                  [cis_conn_hdl](auto& pair) {
+                    return cis_conn_hdl == pair.first;
+                  });
 
-              if (iter == stream_conf->source_streams.end()) {
-                stream_conf->source_streams.emplace_back(
+              if (iter ==
+                  stream_conf->stream_params.source.stream_locations.end()) {
+                stream_conf->stream_params.source.stream_locations.emplace_back(
                     std::make_pair(ase.cis_conn_hdl,
                                    *ase.codec_config.audio_channel_allocation));
 
-                stream_conf->source_num_of_devices++;
-                stream_conf->source_num_of_channels +=
+                stream_conf->stream_params.source.num_of_devices++;
+                stream_conf->stream_params.source.num_of_channels +=
                     ase.codec_config.channel_count;
 
                 LOG_INFO(
@@ -820,23 +839,25 @@
                     ", Source Number Of Channels: %d",
                     +ase.cis_conn_hdl,
                     +(*ase.codec_config.audio_channel_allocation),
-                    +stream_conf->source_num_of_devices,
-                    +stream_conf->source_num_of_channels);
+                    +stream_conf->stream_params.source.num_of_devices,
+                    +stream_conf->stream_params.source.num_of_channels);
               }
             } else {
-              auto iter = std::find_if(stream_conf->sink_streams.begin(),
-                                       stream_conf->sink_streams.end(),
-                                       [cis_conn_hdl](auto& pair) {
-                                         return cis_conn_hdl == pair.first;
-                                       });
+              auto iter = std::find_if(
+                  stream_conf->stream_params.sink.stream_locations.begin(),
+                  stream_conf->stream_params.sink.stream_locations.end(),
+                  [cis_conn_hdl](auto& pair) {
+                    return cis_conn_hdl == pair.first;
+                  });
 
-              if (iter == stream_conf->sink_streams.end()) {
-                stream_conf->sink_streams.emplace_back(
+              if (iter ==
+                  stream_conf->stream_params.sink.stream_locations.end()) {
+                stream_conf->stream_params.sink.stream_locations.emplace_back(
                     std::make_pair(ase.cis_conn_hdl,
                                    *ase.codec_config.audio_channel_allocation));
 
-                stream_conf->sink_num_of_devices++;
-                stream_conf->sink_num_of_channels +=
+                stream_conf->stream_params.sink.num_of_devices++;
+                stream_conf->stream_params.sink.num_of_channels +=
                     ase.codec_config.channel_count;
 
                 LOG_INFO(
@@ -847,8 +868,8 @@
                     ", Sink Number Of Channels: %d",
                     +ase.cis_conn_hdl,
                     +(*ase.codec_config.audio_channel_allocation),
-                    +stream_conf->sink_num_of_devices,
-                    +stream_conf->sink_num_of_channels);
+                    +stream_conf->stream_params.sink.num_of_devices,
+                    +stream_conf->stream_params.sink.num_of_channels);
               }
             }
           }
@@ -918,59 +939,75 @@
               ase.data_path_state =
                   types::AudioStreamDataPathState::DATA_PATH_ESTABLISHED;
               ase.state = types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING;
+              ase.pres_delay_min = 2500;
+              ase.pres_delay_max = 2500;
+              ase.preferred_pres_delay_min = 2500;
+              ase.preferred_pres_delay_max = 2500;
 
               uint16_t cis_conn_hdl = ase.cis_conn_hdl;
 
               /* Copied from state_machine.cc ProcessHciNotifSetupIsoDataPath */
               if (ase.direction == le_audio::types::kLeAudioDirectionSource) {
-                auto iter = std::find_if(stream_conf->source_streams.begin(),
-                                         stream_conf->source_streams.end(),
-                                         [cis_conn_hdl](auto& pair) {
-                                           return cis_conn_hdl == pair.first;
-                                         });
+                auto iter = std::find_if(
+                    stream_conf->stream_params.source.stream_locations.begin(),
+                    stream_conf->stream_params.source.stream_locations.end(),
+                    [cis_conn_hdl](auto& pair) {
+                      return cis_conn_hdl == pair.first;
+                    });
 
-                if (iter == stream_conf->source_streams.end()) {
-                  stream_conf->source_streams.emplace_back(std::make_pair(
-                      ase.cis_conn_hdl,
-                      *ase.codec_config.audio_channel_allocation));
+                if (iter ==
+                    stream_conf->stream_params.source.stream_locations.end()) {
+                  stream_conf->stream_params.source.stream_locations
+                      .emplace_back(std::make_pair(
+                          ase.cis_conn_hdl,
+                          *ase.codec_config.audio_channel_allocation));
 
-                  stream_conf->source_num_of_devices++;
-                  stream_conf->source_num_of_channels +=
+                  stream_conf->stream_params.source.num_of_devices++;
+                  stream_conf->stream_params.source.num_of_channels +=
                       ase.codec_config.channel_count;
-                  stream_conf->source_audio_channel_allocation |=
+                  stream_conf->stream_params.source.audio_channel_allocation |=
                       *ase.codec_config.audio_channel_allocation;
 
-                  if (stream_conf->source_sample_frequency_hz == 0) {
-                    stream_conf->source_sample_frequency_hz =
+                  if (stream_conf->stream_params.source.sample_frequency_hz ==
+                      0) {
+                    stream_conf->stream_params.source.sample_frequency_hz =
                         ase.codec_config.GetSamplingFrequencyHz();
                   } else {
-                    ASSERT_LOG(stream_conf->source_sample_frequency_hz ==
-                                   ase.codec_config.GetSamplingFrequencyHz(),
-                               "sample freq mismatch: %d!=%d",
-                               stream_conf->source_sample_frequency_hz,
-                               ase.codec_config.GetSamplingFrequencyHz());
+                    ASSERT_LOG(
+                        stream_conf->stream_params.source.sample_frequency_hz ==
+                            ase.codec_config.GetSamplingFrequencyHz(),
+                        "sample freq mismatch: %d!=%d",
+                        stream_conf->stream_params.source.sample_frequency_hz,
+                        ase.codec_config.GetSamplingFrequencyHz());
                   }
 
-                  if (stream_conf->source_octets_per_codec_frame == 0) {
-                    stream_conf->source_octets_per_codec_frame =
+                  if (stream_conf->stream_params.source
+                          .octets_per_codec_frame == 0) {
+                    stream_conf->stream_params.source.octets_per_codec_frame =
                         *ase.codec_config.octets_per_codec_frame;
                   } else {
-                    ASSERT_LOG(stream_conf->source_octets_per_codec_frame ==
+                    ASSERT_LOG(stream_conf->stream_params.source
+                                       .octets_per_codec_frame ==
                                    *ase.codec_config.octets_per_codec_frame,
                                "octets per frame mismatch: %d!=%d",
-                               stream_conf->source_octets_per_codec_frame,
+                               stream_conf->stream_params.source
+                                   .octets_per_codec_frame,
                                *ase.codec_config.octets_per_codec_frame);
                   }
 
-                  if (stream_conf->source_codec_frames_blocks_per_sdu == 0) {
-                    stream_conf->source_codec_frames_blocks_per_sdu =
+                  if (stream_conf->stream_params.source
+                          .codec_frames_blocks_per_sdu == 0) {
+                    stream_conf->stream_params.source
+                        .codec_frames_blocks_per_sdu =
                         *ase.codec_config.codec_frames_blocks_per_sdu;
                   } else {
                     ASSERT_LOG(
-                        stream_conf->source_codec_frames_blocks_per_sdu ==
+                        stream_conf->stream_params.source
+                                .codec_frames_blocks_per_sdu ==
                             *ase.codec_config.codec_frames_blocks_per_sdu,
                         "codec_frames_blocks_per_sdu: %d!=%d",
-                        stream_conf->source_codec_frames_blocks_per_sdu,
+                        stream_conf->stream_params.source
+                            .codec_frames_blocks_per_sdu,
                         *ase.codec_config.codec_frames_blocks_per_sdu);
                   }
 
@@ -982,59 +1019,71 @@
                       ", Source Number Of Channels: %d",
                       +ase.cis_conn_hdl,
                       +(*ase.codec_config.audio_channel_allocation),
-                      +stream_conf->source_num_of_devices,
-                      +stream_conf->source_num_of_channels);
+                      +stream_conf->stream_params.source.num_of_devices,
+                      +stream_conf->stream_params.source.num_of_channels);
                 }
               } else {
-                auto iter = std::find_if(stream_conf->sink_streams.begin(),
-                                         stream_conf->sink_streams.end(),
-                                         [cis_conn_hdl](auto& pair) {
-                                           return cis_conn_hdl == pair.first;
-                                         });
+                auto iter = std::find_if(
+                    stream_conf->stream_params.sink.stream_locations.begin(),
+                    stream_conf->stream_params.sink.stream_locations.end(),
+                    [cis_conn_hdl](auto& pair) {
+                      return cis_conn_hdl == pair.first;
+                    });
 
-                if (iter == stream_conf->sink_streams.end()) {
-                  stream_conf->sink_streams.emplace_back(std::make_pair(
-                      ase.cis_conn_hdl,
-                      *ase.codec_config.audio_channel_allocation));
+                if (iter ==
+                    stream_conf->stream_params.sink.stream_locations.end()) {
+                  stream_conf->stream_params.sink.stream_locations.emplace_back(
+                      std::make_pair(
+                          ase.cis_conn_hdl,
+                          *ase.codec_config.audio_channel_allocation));
 
-                  stream_conf->sink_num_of_devices++;
-                  stream_conf->sink_num_of_channels +=
+                  stream_conf->stream_params.sink.num_of_devices++;
+                  stream_conf->stream_params.sink.num_of_channels +=
                       ase.codec_config.channel_count;
 
-                  stream_conf->sink_audio_channel_allocation |=
+                  stream_conf->stream_params.sink.audio_channel_allocation |=
                       *ase.codec_config.audio_channel_allocation;
 
-                  if (stream_conf->sink_sample_frequency_hz == 0) {
-                    stream_conf->sink_sample_frequency_hz =
+                  if (stream_conf->stream_params.sink.sample_frequency_hz ==
+                      0) {
+                    stream_conf->stream_params.sink.sample_frequency_hz =
                         ase.codec_config.GetSamplingFrequencyHz();
                   } else {
-                    ASSERT_LOG(stream_conf->sink_sample_frequency_hz ==
-                                   ase.codec_config.GetSamplingFrequencyHz(),
-                               "sample freq mismatch: %d!=%d",
-                               stream_conf->sink_sample_frequency_hz,
-                               ase.codec_config.GetSamplingFrequencyHz());
+                    ASSERT_LOG(
+                        stream_conf->stream_params.sink.sample_frequency_hz ==
+                            ase.codec_config.GetSamplingFrequencyHz(),
+                        "sample freq mismatch: %d!=%d",
+                        stream_conf->stream_params.sink.sample_frequency_hz,
+                        ase.codec_config.GetSamplingFrequencyHz());
                   }
 
-                  if (stream_conf->sink_octets_per_codec_frame == 0) {
-                    stream_conf->sink_octets_per_codec_frame =
+                  if (stream_conf->stream_params.sink.octets_per_codec_frame ==
+                      0) {
+                    stream_conf->stream_params.sink.octets_per_codec_frame =
                         *ase.codec_config.octets_per_codec_frame;
                   } else {
-                    ASSERT_LOG(stream_conf->sink_octets_per_codec_frame ==
-                                   *ase.codec_config.octets_per_codec_frame,
-                               "octets per frame mismatch: %d!=%d",
-                               stream_conf->sink_octets_per_codec_frame,
-                               *ase.codec_config.octets_per_codec_frame);
+                    ASSERT_LOG(
+                        stream_conf->stream_params.sink
+                                .octets_per_codec_frame ==
+                            *ase.codec_config.octets_per_codec_frame,
+                        "octets per frame mismatch: %d!=%d",
+                        stream_conf->stream_params.sink.octets_per_codec_frame,
+                        *ase.codec_config.octets_per_codec_frame);
                   }
 
-                  if (stream_conf->sink_codec_frames_blocks_per_sdu == 0) {
-                    stream_conf->sink_codec_frames_blocks_per_sdu =
+                  if (stream_conf->stream_params.sink
+                          .codec_frames_blocks_per_sdu == 0) {
+                    stream_conf->stream_params.sink
+                        .codec_frames_blocks_per_sdu =
                         *ase.codec_config.codec_frames_blocks_per_sdu;
                   } else {
                     ASSERT_LOG(
-                        stream_conf->sink_codec_frames_blocks_per_sdu ==
+                        stream_conf->stream_params.sink
+                                .codec_frames_blocks_per_sdu ==
                             *ase.codec_config.codec_frames_blocks_per_sdu,
                         "codec_frames_blocks_per_sdu: %d!=%d",
-                        stream_conf->sink_codec_frames_blocks_per_sdu,
+                        stream_conf->stream_params.sink
+                            .codec_frames_blocks_per_sdu,
                         *ase.codec_config.codec_frames_blocks_per_sdu);
                   }
 
@@ -1046,8 +1095,8 @@
                       ", Sink Number Of Channels: %d",
                       +ase.cis_conn_hdl,
                       +(*ase.codec_config.audio_channel_allocation),
-                      +stream_conf->sink_num_of_devices,
-                      +stream_conf->sink_num_of_channels);
+                      +stream_conf->stream_params.sink.num_of_devices,
+                      +stream_conf->stream_params.sink.num_of_channels);
                 }
               }
             }
@@ -1102,49 +1151,51 @@
                               LeAudioDevice* leAudioDevice) {
           if (!group) return;
           auto* stream_conf = &group->stream_conf;
-          if (!stream_conf->sink_streams.empty() ||
-              !stream_conf->source_streams.empty()) {
-            stream_conf->sink_streams.erase(
-                std::remove_if(stream_conf->sink_streams.begin(),
-                               stream_conf->sink_streams.end(),
-                               [leAudioDevice, &stream_conf](auto& pair) {
-                                 auto ases = leAudioDevice->GetAsesByCisConnHdl(
-                                     pair.first);
-                                 if (ases.sink) {
-                                   stream_conf->sink_num_of_devices--;
-                                   stream_conf->sink_num_of_channels -=
-                                       ases.sink->codec_config.channel_count;
+          if (!stream_conf->stream_params.sink.stream_locations.empty() ||
+              !stream_conf->stream_params.source.stream_locations.empty()) {
+            stream_conf->stream_params.sink.stream_locations.erase(
+                std::remove_if(
+                    stream_conf->stream_params.sink.stream_locations.begin(),
+                    stream_conf->stream_params.sink.stream_locations.end(),
+                    [leAudioDevice, &stream_conf](auto& pair) {
+                      auto ases =
+                          leAudioDevice->GetAsesByCisConnHdl(pair.first);
+                      if (ases.sink) {
+                        stream_conf->stream_params.sink.num_of_devices--;
+                        stream_conf->stream_params.sink.num_of_channels -=
+                            ases.sink->codec_config.channel_count;
 
-                                   LOG_INFO(
-                                       ", Source Number Of Devices: %d"
-                                       ", Source Number Of Channels: %d",
-                                       +stream_conf->source_num_of_devices,
-                                       +stream_conf->source_num_of_channels);
-                                 }
-                                 return ases.sink;
-                               }),
-                stream_conf->sink_streams.end());
+                        LOG_INFO(
+                            ", Source Number Of Devices: %d"
+                            ", Source Number Of Channels: %d",
+                            +stream_conf->stream_params.source.num_of_devices,
+                            +stream_conf->stream_params.source.num_of_channels);
+                      }
+                      return ases.sink;
+                    }),
+                stream_conf->stream_params.sink.stream_locations.end());
 
-            stream_conf->source_streams.erase(
-                std::remove_if(stream_conf->source_streams.begin(),
-                               stream_conf->source_streams.end(),
-                               [leAudioDevice, &stream_conf](auto& pair) {
-                                 auto ases = leAudioDevice->GetAsesByCisConnHdl(
-                                     pair.first);
-                                 if (ases.source) {
-                                   stream_conf->source_num_of_devices--;
-                                   stream_conf->source_num_of_channels -=
-                                       ases.source->codec_config.channel_count;
+            stream_conf->stream_params.source.stream_locations.erase(
+                std::remove_if(
+                    stream_conf->stream_params.source.stream_locations.begin(),
+                    stream_conf->stream_params.source.stream_locations.end(),
+                    [leAudioDevice, &stream_conf](auto& pair) {
+                      auto ases =
+                          leAudioDevice->GetAsesByCisConnHdl(pair.first);
+                      if (ases.source) {
+                        stream_conf->stream_params.source.num_of_devices--;
+                        stream_conf->stream_params.source.num_of_channels -=
+                            ases.source->codec_config.channel_count;
 
-                                   LOG_INFO(
-                                       ", Source Number Of Devices: %d"
-                                       ", Source Number Of Channels: %d",
-                                       +stream_conf->source_num_of_devices,
-                                       +stream_conf->source_num_of_channels);
-                                 }
-                                 return ases.source;
-                               }),
-                stream_conf->source_streams.end());
+                        LOG_INFO(
+                            ", Source Number Of Devices: %d"
+                            ", Source Number Of Channels: %d",
+                            +stream_conf->stream_params.source.num_of_devices,
+                            +stream_conf->stream_params.source.num_of_channels);
+                      }
+                      return ases.source;
+                    }),
+                stream_conf->stream_params.source.stream_locations.end());
           }
 
           group->CigUnassignCis(leAudioDevice);
@@ -1174,12 +1225,13 @@
               }
               /* Invalidate stream configuration if needed */
               auto* stream_conf = &group->stream_conf;
-              if (!stream_conf->sink_streams.empty() ||
-                  !stream_conf->source_streams.empty()) {
-                stream_conf->sink_streams.erase(
+              if (!stream_conf->stream_params.sink.stream_locations.empty() ||
+                  !stream_conf->stream_params.source.stream_locations.empty()) {
+                stream_conf->stream_params.sink.stream_locations.erase(
                     std::remove_if(
-                        stream_conf->sink_streams.begin(),
-                        stream_conf->sink_streams.end(),
+                        stream_conf->stream_params.sink.stream_locations
+                            .begin(),
+                        stream_conf->stream_params.sink.stream_locations.end(),
                         [leAudioDevice, &stream_conf](auto& pair) {
                           auto ases =
                               leAudioDevice->GetAsesByCisConnHdl(pair.first);
@@ -1189,24 +1241,27 @@
                               ", ase pointer: %p",
                               +(int)(pair.first), +ases.sink);
                           if (ases.sink) {
-                            stream_conf->sink_num_of_devices--;
-                            stream_conf->sink_num_of_channels -=
+                            stream_conf->stream_params.sink.num_of_devices--;
+                            stream_conf->stream_params.sink.num_of_channels -=
                                 ases.sink->codec_config.channel_count;
 
                             LOG_INFO(
                                 " Sink Number Of Devices: %d"
                                 ", Sink Number Of Channels: %d",
-                                +stream_conf->sink_num_of_devices,
-                                +stream_conf->sink_num_of_channels);
+                                +stream_conf->stream_params.sink.num_of_devices,
+                                +stream_conf->stream_params.sink
+                                     .num_of_channels);
                           }
                           return ases.sink;
                         }),
-                    stream_conf->sink_streams.end());
+                    stream_conf->stream_params.sink.stream_locations.end());
 
-                stream_conf->source_streams.erase(
+                stream_conf->stream_params.source.stream_locations.erase(
                     std::remove_if(
-                        stream_conf->source_streams.begin(),
-                        stream_conf->source_streams.end(),
+                        stream_conf->stream_params.source.stream_locations
+                            .begin(),
+                        stream_conf->stream_params.source.stream_locations
+                            .end(),
                         [leAudioDevice, &stream_conf](auto& pair) {
                           auto ases =
                               leAudioDevice->GetAsesByCisConnHdl(pair.first);
@@ -1216,19 +1271,21 @@
                               ", ase pointer: %p",
                               +(int)(pair.first), ases.source);
                           if (ases.source) {
-                            stream_conf->source_num_of_devices--;
-                            stream_conf->source_num_of_channels -=
+                            stream_conf->stream_params.source.num_of_devices--;
+                            stream_conf->stream_params.source.num_of_channels -=
                                 ases.source->codec_config.channel_count;
 
                             LOG_INFO(
                                 ", Source Number Of Devices: %d"
                                 ", Source Number Of Channels: %d",
-                                +stream_conf->source_num_of_devices,
-                                +stream_conf->source_num_of_channels);
+                                +stream_conf->stream_params.source
+                                     .num_of_devices,
+                                +stream_conf->stream_params.source
+                                     .num_of_channels);
                           }
                           return ases.source;
                         }),
-                    stream_conf->source_streams.end());
+                    stream_conf->stream_params.source.stream_locations.end());
               }
 
               group->CigUnassignCis(leAudioDevice);
@@ -1240,38 +1297,39 @@
                device != nullptr; device = group->GetNextDevice(device)) {
             /* Invalidate stream configuration if needed */
             auto* stream_conf = &group->stream_conf;
-            if (!stream_conf->sink_streams.empty() ||
-                !stream_conf->source_streams.empty()) {
-              stream_conf->sink_streams.erase(
-                  std::remove_if(stream_conf->sink_streams.begin(),
-                                 stream_conf->sink_streams.end(),
-                                 [device, &stream_conf](auto& pair) {
-                                   auto ases =
-                                       device->GetAsesByCisConnHdl(pair.first);
-
-                                   LOG_INFO(
-                                       ", sink ase to delete. Cis handle: %d"
-                                       ", ase pointer: %p",
-                                       +(int)(pair.first), +ases.sink);
-                                   if (ases.sink) {
-                                     stream_conf->sink_num_of_devices--;
-                                     stream_conf->sink_num_of_channels -=
-                                         ases.sink->codec_config.channel_count;
-
-                                     LOG_INFO(
-                                         " Sink Number Of Devices: %d"
-                                         ", Sink Number Of Channels: %d",
-                                         +stream_conf->sink_num_of_devices,
-                                         +stream_conf->sink_num_of_channels);
-                                   }
-                                   return ases.sink;
-                                 }),
-                  stream_conf->sink_streams.end());
-
-              stream_conf->source_streams.erase(
+            if (!stream_conf->stream_params.sink.stream_locations.empty() ||
+                !stream_conf->stream_params.source.stream_locations.empty()) {
+              stream_conf->stream_params.sink.stream_locations.erase(
                   std::remove_if(
-                      stream_conf->source_streams.begin(),
-                      stream_conf->source_streams.end(),
+                      stream_conf->stream_params.sink.stream_locations.begin(),
+                      stream_conf->stream_params.sink.stream_locations.end(),
+                      [device, &stream_conf](auto& pair) {
+                        auto ases = device->GetAsesByCisConnHdl(pair.first);
+
+                        LOG_INFO(
+                            ", sink ase to delete. Cis handle: %d"
+                            ", ase pointer: %p",
+                            +(int)(pair.first), +ases.sink);
+                        if (ases.sink) {
+                          stream_conf->stream_params.sink.num_of_devices--;
+                          stream_conf->stream_params.sink.num_of_channels -=
+                              ases.sink->codec_config.channel_count;
+
+                          LOG_INFO(
+                              " Sink Number Of Devices: %d"
+                              ", Sink Number Of Channels: %d",
+                              +stream_conf->stream_params.sink.num_of_devices,
+                              +stream_conf->stream_params.sink.num_of_channels);
+                        }
+                        return ases.sink;
+                      }),
+                  stream_conf->stream_params.sink.stream_locations.end());
+
+              stream_conf->stream_params.source.stream_locations.erase(
+                  std::remove_if(
+                      stream_conf->stream_params.source.stream_locations
+                          .begin(),
+                      stream_conf->stream_params.source.stream_locations.end(),
                       [device, &stream_conf](auto& pair) {
                         auto ases = device->GetAsesByCisConnHdl(pair.first);
 
@@ -1280,19 +1338,20 @@
                             ", ase pointer: %p",
                             +(int)(pair.first), +ases.source);
                         if (ases.source) {
-                          stream_conf->source_num_of_devices--;
-                          stream_conf->source_num_of_channels -=
+                          stream_conf->stream_params.source.num_of_devices--;
+                          stream_conf->stream_params.source.num_of_channels -=
                               ases.source->codec_config.channel_count;
 
                           LOG_INFO(
                               ", Source Number Of Devices: %d"
                               ", Source Number Of Channels: %d",
-                              +stream_conf->source_num_of_devices,
-                              +stream_conf->source_num_of_channels);
+                              +stream_conf->stream_params.source.num_of_devices,
+                              +stream_conf->stream_params.source
+                                   .num_of_channels);
                         }
                         return ases.source;
                       }),
-                  stream_conf->source_streams.end());
+                  stream_conf->stream_params.source.stream_locations.end());
             }
 
             group->CigUnassignCis(device);
@@ -1345,18 +1404,35 @@
     ON_CALL(*mock_iso_manager_, RegisterCigCallbacks(_))
         .WillByDefault(SaveArg<0>(&cig_callbacks_));
 
+    // Required since we call OnAudioDataReady()
+    const auto codec_location = ::le_audio::types::CodecLocation::HOST;
+
     SetUpMockAudioHal();
     SetUpMockGroups();
     SetUpMockGatt();
+    SetUpMockCodecManager(codec_location);
 
     available_snk_context_types_ = 0xffff;
     available_src_context_types_ = 0xffff;
     supported_snk_context_types_ = 0xffff;
     supported_src_context_types_ = 0xffff;
-    le_audio::AudioSetConfigurationProvider::Initialize();
+    le_audio::AudioSetConfigurationProvider::Initialize(codec_location);
     ASSERT_FALSE(LeAudioClient::IsLeAudioClientRunning());
   }
 
+  void SetUpMockCodecManager(types::CodecLocation location) {
+    codec_manager_ = le_audio::CodecManager::GetInstance();
+    ASSERT_NE(codec_manager_, nullptr);
+    std::vector<bluetooth::le_audio::btle_audio_codec_config_t>
+        mock_offloading_preference(0);
+    codec_manager_->Start(mock_offloading_preference);
+    mock_codec_manager_ = MockCodecManager::GetInstance();
+    ASSERT_NE((void*)mock_codec_manager_, (void*)codec_manager_);
+    ASSERT_NE(mock_codec_manager_, nullptr);
+    ON_CALL(*mock_codec_manager_, GetCodecLocation())
+        .WillByDefault(Return(location));
+  }
+
   void TearDown() override {
     if (is_audio_unicast_source_acquired) {
       if (unicast_source_hal_cb_ != nullptr) {
@@ -1383,7 +1459,7 @@
 
     if (LeAudioClient::IsLeAudioClientRunning()) {
       EXPECT_CALL(mock_gatt_interface_, AppDeregister(gatt_if)).Times(1);
-      LeAudioClient::Cleanup(base::DoNothing());
+      LeAudioClient::Cleanup();
       ASSERT_FALSE(LeAudioClient::IsLeAudioClientRunning());
     }
 
@@ -1730,6 +1806,7 @@
   void LocalAudioSourceSuspend(void) {
     ASSERT_NE(unicast_source_hal_cb_, nullptr);
     unicast_source_hal_cb_->OnAudioSuspend();
+    SyncOnMainLoop();
   }
 
   void LocalAudioSourceResume(bool expected_confirmation = true) {
@@ -1752,6 +1829,7 @@
   void LocalAudioSinkSuspend(void) {
     ASSERT_NE(unicast_sink_hal_cb_, nullptr);
     unicast_sink_hal_cb_->OnAudioSuspend();
+    SyncOnMainLoop();
   }
 
   void LocalAudioSinkResume(void) {
@@ -1796,6 +1874,7 @@
                             },
                             unicast_sink_hal_cb_));
     }
+    SyncOnMainLoop();
   }
 
   void StopStreaming(int group_id, bool suspend_source = false) {
@@ -1818,6 +1897,7 @@
       ASSERT_NE(unicast_sink_hal_cb_, nullptr);
       unicast_sink_hal_cb_->OnAudioSuspend();
     }
+    SyncOnMainLoop();
   }
 
   void set_sample_database(uint16_t conn_id, RawAddress addr,
@@ -2438,6 +2518,9 @@
   bluetooth::hci::iso_manager::CigCallbacks* cig_callbacks_ = nullptr;
   uint16_t iso_con_counter_ = 1;
 
+  le_audio::CodecManager* codec_manager_;
+  MockCodecManager* mock_codec_manager_;
+
   uint16_t available_snk_context_types_ = 0xffff;
   uint16_t available_src_context_types_ = 0xffff;
   uint16_t supported_snk_context_types_ = 0xffff;
@@ -2900,6 +2983,7 @@
   Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
 
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   auto health_status = LeAudioHealthStatus::Get();
 
@@ -2948,6 +3032,31 @@
   DisconnectLeAudioWithAclClose(test_address0, 1);
 }
 
+TEST_F(UnicastTest, ConnectRemoteServiceDiscoveryCompleteBeforeEncryption) {
+  const RawAddress test_address0 = GetTestAddress(0);
+  uint16_t conn_id = 1;
+  SetSampleDatabaseEarbudsValid(conn_id, test_address0,
+                                codec_spec_conf::kLeAudioLocationStereo,
+                                codec_spec_conf::kLeAudioLocationStereo);
+  EXPECT_CALL(mock_audio_hal_client_callbacks_,
+              OnConnectionState(ConnectionState::CONNECTED, test_address0))
+      .Times(0);
+  ConnectLeAudio(test_address0, false);
+  InjectSearchCompleteEvent(conn_id);
+
+  SyncOnMainLoop();
+  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+
+  EXPECT_CALL(mock_audio_hal_client_callbacks_,
+              OnConnectionState(ConnectionState::CONNECTED, test_address0))
+      .Times(1);
+  ON_CALL(mock_btm_interface_, BTM_IsEncrypted(test_address0, _))
+      .WillByDefault(DoAll(Return(true)));
+  InjectEncryptionChangedEvent(test_address0);
+  SyncOnMainLoop();
+  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+}
+
 /* same as above case except the disconnect is initiated by remote */
 TEST_F(UnicastTest, ConnectRemoteDisconnectOneEarbud) {
   const RawAddress test_address0 = GetTestAddress(0);
@@ -3638,6 +3747,7 @@
   LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */);
   LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {gmcs_ccid},
                                                           .source = {}};
@@ -3964,14 +4074,21 @@
   EXPECT_CALL(mock_audio_hal_client_callbacks_,
               OnConnectionState(ConnectionState::CONNECTED, test_address0))
       .Times(0);
+  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false))
+      .Times(0);
   ConnectLeAudio(test_address0);
 
   Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
 
   EXPECT_CALL(mock_gatt_queue_, Clean(conn_id)).Times(AtLeast(1));
   EXPECT_CALL(mock_gatt_interface_, Close(conn_id)).Times(1);
+
+  /* First time called when RemoveDevice is called and then second time
+   * OnGattDisconnected. We accept this spare call.
+   */
   EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false))
-      .Times(1);
+      .Times(2);
 
   /*
    * StopStream will put calls on main_loop so to keep the correct order
@@ -4087,14 +4204,17 @@
   EXPECT_CALL(mock_audio_hal_client_callbacks_,
               OnConnectionState(ConnectionState::CONNECTED, test_address0))
       .Times(0);
+  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false))
+      .Times(0);
   ConnectLeAudio(test_address0);
 
   Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
+  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
 
   EXPECT_CALL(mock_gatt_queue_, Clean(conn_id)).Times(AtLeast(1));
   EXPECT_CALL(mock_gatt_interface_, Close(conn_id)).Times(1);
   EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false))
-      .Times(0);
+      .Times(1);
 
   LeAudioClient::Get()->Disconnect(test_address0);
   SyncOnMainLoop();
@@ -4137,6 +4257,7 @@
   LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */);
   LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {gmcs_ccid},
                                                           .source = {}};
@@ -4217,6 +4338,7 @@
   LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */);
   LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {gmcs_ccid},
                                                           .source = {}};
@@ -4296,6 +4418,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
 
@@ -4326,6 +4449,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, OnDestroyed()).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(1);
   LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
+  SyncOnMainLoop();
   Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
 }
 
@@ -4361,6 +4485,7 @@
 
   // Audio sessions are started only when device gets active
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   /* Nothing to do - expect no crash */
 }
@@ -4394,6 +4519,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
 
@@ -4424,6 +4550,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, OnDestroyed()).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(1);
   LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
+  SyncOnMainLoop();
   Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
 }
 
@@ -4471,6 +4598,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_ASSISTANT, AUDIO_CONTENT_TYPE_UNKNOWN, group_id);
 
@@ -4501,12 +4629,14 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, OnDestroyed()).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(1);
   LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
+  SyncOnMainLoop();
   Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
 
   /* When session is closed, the hal client mocks are freed - get new ones */
   SetUpMockAudioHal();
   /* Expect the previous release to clear the old audio session metadata */
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
   EXPECT_CALL(mock_state_machine_,
               StartStream(_, types::LeAudioContextType::VOICEASSISTANTS, _, _))
       .Times(0);
@@ -4541,6 +4671,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
 
@@ -4602,6 +4733,8 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
+
   Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
 
   StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
@@ -4639,9 +4772,160 @@
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Stop()).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, OnDestroyed()).Times(1);
   LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
+  SyncOnMainLoop();
+
   Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
 }
 
+TEST_F(UnicastTest, UpdateActiveAudioConfigForLocalSinkSource) {
+  uint8_t group_size = 2;
+  int group_id = 2;
+
+  // Report working CSIS
+  ON_CALL(mock_csis_client_module_, IsCsisClientRunning())
+      .WillByDefault(Return(true));
+
+  // First earbud
+  const RawAddress test_address0 = GetTestAddress(0);
+  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true))
+      .Times(1);
+  ConnectCsisDevice(test_address0, 1 /*conn_id*/,
+                    codec_spec_conf::kLeAudioLocationFrontLeft,
+                    codec_spec_conf::kLeAudioLocationFrontLeft, group_size,
+                    group_id, 1 /* rank*/);
+
+  // Second earbud
+  const RawAddress test_address1 = GetTestAddress(1);
+  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, true))
+      .Times(1);
+  ConnectCsisDevice(test_address1, 2 /*conn_id*/,
+                    codec_spec_conf::kLeAudioLocationFrontRight,
+                    codec_spec_conf::kLeAudioLocationFrontRight, group_size,
+                    group_id, 2 /* rank*/, true /*connect_through_csis*/);
+
+  ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
+      .WillByDefault(Invoke([&](int group_id) { return 2; }));
+
+  // Set group as active
+  EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
+  EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
+  LeAudioClient::Get()->GroupSetActive(group_id);
+  Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
+
+  // Start streaming
+  EXPECT_CALL(*mock_le_audio_sink_hal_client_, UpdateAudioConfigToHal(_))
+      .Times(1);
+  EXPECT_CALL(*mock_le_audio_source_hal_client_, UpdateAudioConfigToHal(_))
+      .Times(1);
+  EXPECT_CALL(*mock_codec_manager_, UpdateActiveAudioConfig(_, _, _))
+      .Times(1)
+      .WillOnce(
+          [](const types::BidirectionalPair<stream_parameters>& stream_params,
+             types::BidirectionalPair<uint16_t> delays_ms,
+             std::function<void(const offload_config& config,
+                                uint8_t direction)>
+                 update_receiver) {
+            le_audio::offload_config unicast_cfg;
+            if (delays_ms.sink != 0) {
+              update_receiver(unicast_cfg,
+                              le_audio::types::kLeAudioDirectionSink);
+            }
+            if (delays_ms.source != 0) {
+              update_receiver(unicast_cfg,
+                              le_audio::types::kLeAudioDirectionSource);
+            }
+          });
+  StartStreaming(AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_CONTENT_TYPE_SPEECH,
+                 group_id);
+
+  SyncOnMainLoop();
+  Mock::VerifyAndClearExpectations(&mock_le_audio_sink_hal_client_);
+  Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
+  Mock::VerifyAndClearExpectations(&mock_codec_manager_);
+
+  // Verify Data transfer on two peer sinks and two sources
+  uint8_t cis_count_out = 2;
+  uint8_t cis_count_in = 2;
+  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920, 40);
+
+  // Suspend
+  LeAudioClient::Get()->GroupSuspend(group_id);
+  SyncOnMainLoop();
+}
+
+TEST_F(UnicastTest, UpdateActiveAudioConfigForLocalSource) {
+  uint8_t group_size = 2;
+  int group_id = 2;
+
+  // Report working CSIS
+  ON_CALL(mock_csis_client_module_, IsCsisClientRunning())
+      .WillByDefault(Return(true));
+
+  // First earbud
+  const RawAddress test_address0 = GetTestAddress(0);
+  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true))
+      .Times(1);
+  ConnectCsisDevice(test_address0, 1 /*conn_id*/,
+                    codec_spec_conf::kLeAudioLocationFrontLeft,
+                    codec_spec_conf::kLeAudioLocationFrontLeft, group_size,
+                    group_id, 1 /* rank*/);
+
+  // Second earbud
+  const RawAddress test_address1 = GetTestAddress(1);
+  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, true))
+      .Times(1);
+  ConnectCsisDevice(test_address1, 2 /*conn_id*/,
+                    codec_spec_conf::kLeAudioLocationFrontRight,
+                    codec_spec_conf::kLeAudioLocationFrontRight, group_size,
+                    group_id, 2 /* rank*/, true /*connect_through_csis*/);
+
+  ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
+      .WillByDefault(Invoke([&](int group_id) { return 2; }));
+
+  // Set group as active
+  EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
+  LeAudioClient::Get()->GroupSetActive(group_id);
+  Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
+
+  // Start streaming
+  EXPECT_CALL(*mock_le_audio_source_hal_client_, UpdateAudioConfigToHal(_))
+      .Times(1);
+  EXPECT_CALL(*mock_le_audio_sink_hal_client_, UpdateAudioConfigToHal(_))
+      .Times(0);
+  EXPECT_CALL(*mock_codec_manager_, UpdateActiveAudioConfig(_, _, _))
+      .Times(1)
+      .WillOnce(
+          [](const types::BidirectionalPair<stream_parameters>& stream_params,
+             types::BidirectionalPair<uint16_t> delays_ms,
+             std::function<void(const offload_config& config,
+                                uint8_t direction)>
+                 update_receiver) {
+            le_audio::offload_config unicast_cfg;
+            if (delays_ms.sink != 0) {
+              update_receiver(unicast_cfg,
+                              le_audio::types::kLeAudioDirectionSink);
+            }
+            if (delays_ms.source != 0) {
+              update_receiver(unicast_cfg,
+                              le_audio::types::kLeAudioDirectionSource);
+            }
+          });
+  StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
+
+  SyncOnMainLoop();
+  Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
+  Mock::VerifyAndClearExpectations(&mock_codec_manager_);
+
+  // Verify Data transfer on two peer sinks and no source
+  uint8_t cis_count_out = 2;
+  uint8_t cis_count_in = 0;
+  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920, 40);
+
+  // Suspend
+  LeAudioClient::Get()->GroupSuspend(group_id);
+  SyncOnMainLoop();
+}
+
 TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchNoReconfigure) {
   uint8_t group_size = 2;
   int group_id = 2;
@@ -4783,6 +5067,7 @@
   LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */);
   LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {gmcs_ccid},
                                                           .source = {}};
@@ -4866,6 +5151,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
       .WillByDefault(Invoke([&](int group_id) { return 2; }));
@@ -4932,6 +5218,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
       .WillByDefault(Invoke([&](int group_id) { return 2; }));
@@ -5035,6 +5322,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
       .WillByDefault(Invoke([&](int group_id) { return 2; }));
@@ -5096,6 +5384,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
       .WillByDefault(Invoke([&](int group_id) { return 2; }));
@@ -5207,6 +5496,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
       .WillByDefault(Invoke([&](int group_id) { return 2; }));
@@ -5292,6 +5582,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
       .WillByDefault(Invoke([&](int group_id) { return 2; }));
@@ -5356,6 +5647,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
       .WillByDefault(Invoke([&](int group_id) { return 2; }));
@@ -5484,6 +5776,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
   auto group = streaming_groups.at(group_id);
@@ -5505,11 +5798,9 @@
     InjectCisDisconnected(group_id, ase.cis_conn_hdl);
   }
 
-  /* It is called twice. Once with BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS,
-   * and then after delay with BTM_BLE_BKG_CONNECT_ALLOW_LIST
-   */
-  EXPECT_CALL(mock_gatt_interface_, Open(_, device->address_, _, false))
-      .Times(2);
+  EXPECT_CALL(mock_gatt_interface_,
+              Open(_, device->address_, BTM_BLE_BKG_CONNECT_ALLOW_LIST, false))
+      .Times(1);
 
   // Record NumOfConnected when groupStateMachine_ gets notified about the
   // disconnection
@@ -5572,6 +5863,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
 
@@ -5637,6 +5929,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
 
@@ -5738,6 +6031,39 @@
   SyncOnMainLoop();
 }
 
+TEST_F(UnicastTest, TwoEarbudsWithSourceSupporting32kHz) {
+  const RawAddress test_address0 = GetTestAddress(0);
+  int group_id = 0;
+  SetSampleDatabaseEarbudsValid(
+      1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
+      codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt,
+      default_channel_cnt, 0x0024,
+      /* source sample freq 32/16khz */ true, /*add_csis*/
+      true,                                   /*add_cas*/
+      true,                                   /*add_pacs*/
+      default_ase_cnt /*add_ascs_cnt*/);
+  EXPECT_CALL(mock_audio_hal_client_callbacks_,
+              OnConnectionState(ConnectionState::CONNECTED, test_address0))
+      .Times(1);
+  ConnectLeAudio(test_address0);
+
+  // LeAudioCodecConfiguration received_af_sink_config;
+  const LeAudioCodecConfiguration expected_af_sink_config = {
+      .num_channels = 2,
+      .sample_rate = bluetooth::audio::le_audio::kSampleRate32000,
+      .bits_per_sample = bluetooth::audio::le_audio::kBitsPerSample16,
+      .data_interval_us = LeAudioCodecConfiguration::kInterval10000Us,
+  };
+
+  // Audio sessions are started only when device gets active
+  EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
+  EXPECT_CALL(*mock_le_audio_sink_hal_client_,
+              Start(expected_af_sink_config, _))
+      .Times(1);
+  LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
+}
+
 TEST_F(UnicastTest, MicrophoneAttachToCurrentMediaScenario) {
   const RawAddress test_address0 = GetTestAddress(0);
   int group_id = bluetooth::groups::kGroupUnknown;
@@ -5762,6 +6088,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   // When the local audio source resumes we have no knowledge of recording
   EXPECT_CALL(mock_state_machine_,
@@ -5833,9 +6160,9 @@
                        client->GroupSetActive(bluetooth::groups::kGroupUnknown);
                      },
                      LeAudioClient::Get()));
-  Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
-
   SyncOnMainLoop();
+
+  Mock::VerifyAndClearExpectations(&mock_le_audio_source_hal_client_);
 }
 
 /* When a certain context is unavailable and not supported we should stream
@@ -5883,6 +6210,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
                  AUDIO_CONTENT_TYPE_UNKNOWN, group_id);
@@ -5956,6 +6284,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   // When the local audio sink resumes expect only LIVE context
   types::BidirectionalPair<types::AudioContexts> contexts = {
@@ -6103,6 +6432,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   // 1) Start with the call first
   // -----------------------------
@@ -6270,6 +6600,8 @@
       .Times(0);
 
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
+
   StartStreaming(AUDIO_USAGE_EMERGENCY, AUDIO_CONTENT_TYPE_UNKNOWN, group_id,
                  AUDIO_SOURCE_INVALID, false, false);
 
@@ -6328,6 +6660,8 @@
       .Times(0);
 
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
+
   StartStreaming(AUDIO_USAGE_EMERGENCY, AUDIO_CONTENT_TYPE_UNKNOWN, group_id,
                  AUDIO_SOURCE_INVALID, false, false);
 
@@ -6382,6 +6716,8 @@
       .Times(1);
 
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
+
   StartStreaming(AUDIO_USAGE_EMERGENCY, AUDIO_CONTENT_TYPE_UNKNOWN, group_id);
 
   SyncOnMainLoop();
@@ -6426,6 +6762,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
                  AUDIO_CONTENT_TYPE_UNKNOWN, group_id);
@@ -6495,6 +6832,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
                  AUDIO_CONTENT_TYPE_UNKNOWN, group_id);
@@ -6617,6 +6955,7 @@
   EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
   EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
   LeAudioClient::Get()->GroupSetActive(group_id);
+  SyncOnMainLoop();
 
   StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
 
@@ -6638,4 +6977,146 @@
   auto group = streaming_groups.at(group_id);
   ASSERT_EQ(0, static_cast<int>(group->cises_.size()));
 }
+
+TEST_F(UnicastTest, AddMemberToAllowListWhenOneDeviceConnected) {
+  uint8_t group_size = 2;
+  int group_id = 2;
+  int conn_id_dev_0 = 1;
+  int conn_id_dev_1 = 2;
+
+  /*Scenario to test
+   * 1. Connect Device A and disconnect
+   * 2. Connect Device B
+   * 3. verify Device B is in the allow list.
+   */
+  // Report working CSIS
+  ON_CALL(mock_csis_client_module_, IsCsisClientRunning())
+      .WillByDefault(Return(true));
+
+  // First earbud
+  const RawAddress test_address0 = GetTestAddress(0);
+  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true))
+      .Times(1);
+
+  ConnectCsisDevice(test_address0, conn_id_dev_0,
+                    codec_spec_conf::kLeAudioLocationFrontLeft,
+                    codec_spec_conf::kLeAudioLocationFrontLeft, group_size,
+                    group_id, 1 /* rank*/);
+
+  SyncOnMainLoop();
+
+  EXPECT_CALL(mock_gatt_interface_,
+              Open(gatt_if, test_address0,
+                   BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS, _))
+      .Times(1);
+
+  InjectDisconnectedEvent(conn_id_dev_0);
+
+  SyncOnMainLoop();
+  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
+
+  // Second earbud
+  const RawAddress test_address1 = GetTestAddress(1);
+  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, true))
+      .Times(1);
+
+  EXPECT_CALL(mock_gatt_interface_,
+              Open(gatt_if, test_address0, BTM_BLE_BKG_CONNECT_ALLOW_LIST, _))
+      .Times(1);
+
+  ConnectCsisDevice(test_address1, conn_id_dev_1,
+                    codec_spec_conf::kLeAudioLocationFrontRight,
+                    codec_spec_conf::kLeAudioLocationFrontRight, group_size,
+                    group_id, 2 /* rank*/, true /*connect_through_csis*/);
+
+  ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
+      .WillByDefault(Invoke([&](int group_id) { return 2; }));
+
+  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
+}
+
+TEST_F(UnicastTest, ResetToDefaultReconnectionMode) {
+  uint8_t group_size = 2;
+  int group_id = 2;
+  int conn_id_dev_0 = 1;
+  int conn_id_dev_1 = 2;
+
+  /*Scenario to test
+   * 1. Connect Device A and disconnect
+   * 2. Connect Device B
+   * 3. verify Device B is in the allow list.
+   * 4. Disconnect B device
+   * 5, Verify A and B device are back in targeted announcement reconnection
+   * mode
+   */
+  // Report working CSIS
+  ON_CALL(mock_csis_client_module_, IsCsisClientRunning())
+      .WillByDefault(Return(true));
+
+  // First earbud
+  const RawAddress test_address0 = GetTestAddress(0);
+  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address0, true))
+      .Times(1);
+
+  ConnectCsisDevice(test_address0, conn_id_dev_0,
+                    codec_spec_conf::kLeAudioLocationFrontLeft,
+                    codec_spec_conf::kLeAudioLocationFrontLeft, group_size,
+                    group_id, 1 /* rank*/);
+
+  SyncOnMainLoop();
+
+  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false))
+      .Times(1);
+  EXPECT_CALL(mock_gatt_interface_,
+              Open(gatt_if, test_address0,
+                   BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS, _))
+      .Times(1);
+
+  InjectDisconnectedEvent(conn_id_dev_0);
+
+  SyncOnMainLoop();
+  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
+
+  // Second earbud
+  const RawAddress test_address1 = GetTestAddress(1);
+  EXPECT_CALL(mock_btif_storage_, AddLeaudioAutoconnect(test_address1, true))
+      .Times(1);
+
+  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false))
+      .Times(1);
+  EXPECT_CALL(mock_gatt_interface_,
+              Open(gatt_if, test_address0, BTM_BLE_BKG_CONNECT_ALLOW_LIST, _))
+      .Times(1);
+
+  ConnectCsisDevice(test_address1, conn_id_dev_1,
+                    codec_spec_conf::kLeAudioLocationFrontRight,
+                    codec_spec_conf::kLeAudioLocationFrontRight, group_size,
+                    group_id, 2 /* rank*/, true /*connect_through_csis*/);
+
+  ON_CALL(mock_csis_client_module_, GetDesiredSize(group_id))
+      .WillByDefault(Invoke([&](int group_id) { return 2; }));
+
+  SyncOnMainLoop();
+  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
+
+  // Disconnect Device B, expect default reconnection mode
+  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address0, false))
+      .Times(1);
+  EXPECT_CALL(mock_gatt_interface_, CancelOpen(gatt_if, test_address1, false))
+      .Times(2);
+  EXPECT_CALL(mock_gatt_interface_,
+              Open(gatt_if, test_address0,
+                   BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS, _))
+      .Times(1);
+  EXPECT_CALL(mock_gatt_interface_,
+              Open(gatt_if, test_address1,
+                   BTM_BLE_BKG_CONNECT_TARGETED_ANNOUNCEMENTS, _))
+      .Times(1);
+
+  InjectDisconnectedEvent(conn_id_dev_1, GATT_CONN_TERMINATE_PEER_USER);
+  SyncOnMainLoop();
+
+  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);
+}
+
 }  // namespace le_audio
diff --git a/system/bta/le_audio/le_audio_set_configuration_provider.cc b/system/bta/le_audio/le_audio_set_configuration_provider.cc
deleted file mode 100644
index 1ffc042..0000000
--- a/system/bta/le_audio/le_audio_set_configuration_provider.cc
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- *  Copyright (c) 2022 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- */
-
-#include "le_audio_set_configuration_provider.h"
-
-#include "bta_le_audio_api.h"
-
-void LeAudioClient::InitializeAudioSetConfigurationProvider(void) {
-  le_audio::AudioSetConfigurationProvider::Initialize();
-}
-
-void LeAudioClient::CleanupAudioSetConfigurationProvider(void) {
-  le_audio::AudioSetConfigurationProvider::Cleanup();
-}
diff --git a/system/bta/le_audio/le_audio_set_configuration_provider.h b/system/bta/le_audio/le_audio_set_configuration_provider.h
index 7310868..8ece4f2 100644
--- a/system/bta/le_audio/le_audio_set_configuration_provider.h
+++ b/system/bta/le_audio/le_audio_set_configuration_provider.h
@@ -27,7 +27,7 @@
   AudioSetConfigurationProvider();
   virtual ~AudioSetConfigurationProvider() = default;
   static AudioSetConfigurationProvider* Get();
-  static void Initialize();
+  static void Initialize(types::CodecLocation location);
   static void DebugDump(int fd);
   static void Cleanup();
   virtual const set_configurations::AudioSetConfigurations* GetConfigurations(
diff --git a/system/bta/le_audio/le_audio_set_configuration_provider_json.cc b/system/bta/le_audio/le_audio_set_configuration_provider_json.cc
index 91a6bd5..bfcfc6b 100644
--- a/system/bta/le_audio/le_audio_set_configuration_provider_json.cc
+++ b/system/bta/le_audio/le_audio_set_configuration_provider_json.cc
@@ -70,12 +70,12 @@
 struct AudioSetConfigurationProviderJson {
   static constexpr auto kDefaultScenario = "Media";
 
-  AudioSetConfigurationProviderJson() {
+  AudioSetConfigurationProviderJson(types::CodecLocation location) {
     dual_swb_bidirection_supported_ = osi_property_get_bool(
         "persist.bluetooth.leaudio_dual_bidirection_swb."
         "supported",
         true);
-    ASSERT_LOG(LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios),
+    ASSERT_LOG(LoadContent(kLeAudioSetConfigs, kLeAudioSetScenarios, location),
                ": Unable to load le audio set configuration files.");
   }
 
@@ -265,18 +265,18 @@
     codec.config = types::LeAudioLc3Config({
         .sampling_frequency = sampling_frequency,
         .frame_duration = frame_duration,
+        .audio_channel_allocation = audio_channel_allocation,
         .octets_per_codec_frame = octets_per_codec_frame,
         .codec_frames_blocks_per_sdu = codec_frames_blocks_per_sdu,
         .channel_count =
             (uint8_t)std::bitset<32>(audio_channel_allocation).count(),
-        .audio_channel_allocation = audio_channel_allocation,
     });
     return codec;
   }
 
   SetConfiguration SetConfigurationFromFlatSubconfig(
       const bluetooth::le_audio::AudioSetSubConfiguration* flat_subconfig,
-      QosConfigSetting qos) {
+      QosConfigSetting qos, types::CodecLocation location) {
     auto strategy_int =
         static_cast<int>(flat_subconfig->configuration_strategy());
 
@@ -290,12 +290,32 @@
             ? static_cast<types::LeAudioConfigurationStrategy>(strategy_int)
             : types::LeAudioConfigurationStrategy::RFU;
 
-    return SetConfiguration(
+    auto config = SetConfiguration(
         flat_subconfig->direction(), flat_subconfig->device_cnt(),
         flat_subconfig->ase_cnt(),
         CodecCapabilitySettingFromFlat(flat_subconfig->codec_id(),
                                        flat_subconfig->codec_configuration()),
         qos, strategy);
+
+    // Note that these parameters are set here since for now, we are using the
+    // common configuration source for all the codec locations.
+    switch (location) {
+      case types::CodecLocation::ADSP:
+        config.is_codec_in_controller = false;
+        config.data_path_id =
+            bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault;
+        break;
+      case types::CodecLocation::HOST:
+        config.is_codec_in_controller = false;
+        config.data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci;
+        break;
+      case types::CodecLocation::CONTROLLER:
+        config.is_codec_in_controller = true;
+        config.data_path_id =
+            bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault;
+        break;
+    }
+    return config;
   }
 
   static uint8_t ValidateTargetLatency(int flat_target_latency) {
@@ -313,7 +333,8 @@
   AudioSetConfiguration AudioSetConfigurationFromFlat(
       const bluetooth::le_audio::AudioSetConfiguration* flat_cfg,
       std::vector<const bluetooth::le_audio::CodecConfiguration*>* codec_cfgs,
-      std::vector<const bluetooth::le_audio::QosConfiguration*>* qos_cfgs) {
+      std::vector<const bluetooth::le_audio::QosConfiguration*>* qos_cfgs,
+      types::CodecLocation location) {
     ASSERT_LOG(flat_cfg != nullptr, "flat_cfg cannot be null");
     std::string codec_config_key = flat_cfg->codec_config_name()->str();
     auto* qos_config_key_array = flat_cfg->qos_config_name();
@@ -395,13 +416,13 @@
       /* Load subconfigurations */
       for (auto subconfig : *codec_cfg->subconfigurations()) {
         if (subconfig->direction() == le_audio::types::kLeAudioDirectionSink) {
-          processSubconfig(*subconfig, qos_sink,
-                           dual_dev_one_chan_stereo_sink_swb,
-                           single_dev_one_chan_stereo_sink_swb, subconfigs);
+          processSubconfig(
+              *subconfig, qos_sink, dual_dev_one_chan_stereo_sink_swb,
+              single_dev_one_chan_stereo_sink_swb, subconfigs, location);
         } else {
-          processSubconfig(*subconfig, qos_source,
-                           dual_dev_one_chan_stereo_source_swb,
-                           single_dev_one_chan_stereo_source_swb, subconfigs);
+          processSubconfig(
+              *subconfig, qos_source, dual_dev_one_chan_stereo_source_swb,
+              single_dev_one_chan_stereo_source_swb, subconfigs, location);
         }
       }
     } else {
@@ -430,9 +451,10 @@
       const bluetooth::le_audio::AudioSetSubConfiguration& subconfig,
       const QosConfigSetting& qos_setting, bool& dual_dev_one_chan_stereo_swb,
       bool& single_dev_one_chan_stereo_swb,
-      std::vector<SetConfiguration>& subconfigs) {
+      std::vector<SetConfiguration>& subconfigs,
+      types::CodecLocation location) {
     subconfigs.push_back(
-        SetConfigurationFromFlatSubconfig(&subconfig, qos_setting));
+        SetConfigurationFromFlatSubconfig(&subconfig, qos_setting, location));
 
     if (subconfigs.back().codec.GetConfigSamplingFrequency() <
         le_audio::LeAudioCodecConfiguration::kSampleRate32000) {
@@ -448,7 +470,8 @@
   }
 
   bool LoadConfigurationsFromFiles(const char* schema_file,
-                                   const char* content_file) {
+                                   const char* content_file,
+                                   types::CodecLocation location) {
     flatbuffers::Parser configurations_parser_;
     std::string configurations_schema_binary_content;
     bool ok = flatbuffers::LoadFile(schema_file, true,
@@ -502,9 +525,9 @@
 
     LOG_DEBUG(": Updating %d config entries.", flat_configs->size());
     for (auto const& flat_cfg : *flat_configs) {
-      configurations_.insert(
-          {flat_cfg->name()->str(),
-           AudioSetConfigurationFromFlat(flat_cfg, &codec_cfgs, &qos_cfgs)});
+      configurations_.insert({flat_cfg->name()->str(),
+                              AudioSetConfigurationFromFlat(
+                                  flat_cfg, &codec_cfgs, &qos_cfgs, location)});
     }
 
     return true;
@@ -580,9 +603,10 @@
       std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
           config_files,
       std::vector<std::pair<const char* /*schema*/, const char* /*content*/>>
-          scenario_files) {
+          scenario_files,
+      types::CodecLocation location) {
     for (auto [schema, content] : config_files) {
-      if (!LoadConfigurationsFromFiles(schema, content)) return false;
+      if (!LoadConfigurationsFromFiles(schema, content, location)) return false;
     }
 
     for (auto [schema, content] : scenario_files) {
@@ -596,10 +620,10 @@
   impl(const AudioSetConfigurationProvider& config_provider)
       : config_provider_(config_provider) {}
 
-  void Initialize() {
+  void Initialize(types::CodecLocation location) {
     ASSERT_LOG(!config_provider_impl_, " Config provider not available.");
     config_provider_impl_ =
-        std::make_unique<AudioSetConfigurationProviderJson>();
+        std::make_unique<AudioSetConfigurationProviderJson>(location);
   }
 
   void Cleanup() {
@@ -652,13 +676,13 @@
 AudioSetConfigurationProvider::AudioSetConfigurationProvider()
     : pimpl_(std::make_unique<AudioSetConfigurationProvider::impl>(*this)) {}
 
-void AudioSetConfigurationProvider::Initialize() {
+void AudioSetConfigurationProvider::Initialize(types::CodecLocation location) {
   std::scoped_lock<std::mutex> lock(instance_mutex);
   if (!config_provider)
     config_provider = std::make_unique<AudioSetConfigurationProvider>();
 
   if (!config_provider->pimpl_->IsRunning())
-    config_provider->pimpl_->Initialize();
+    config_provider->pimpl_->Initialize(location);
 }
 
 void AudioSetConfigurationProvider::DebugDump(int fd) {
diff --git a/system/bta/le_audio/le_audio_types.cc b/system/bta/le_audio/le_audio_types.cc
index 51c2565..f068ab9 100644
--- a/system/bta/le_audio/le_audio_types.cc
+++ b/system/bta/le_audio/le_audio_types.cc
@@ -769,21 +769,6 @@
   return 1;
 }
 
-uint32_t AdjustAllocationForOffloader(uint32_t allocation) {
-  if ((allocation & codec_spec_conf::kLeAudioLocationAnyLeft) &&
-      (allocation & codec_spec_conf::kLeAudioLocationAnyRight)) {
-    return codec_spec_conf::kLeAudioLocationStereo;
-  }
-  if (allocation & codec_spec_conf::kLeAudioLocationAnyLeft) {
-    return codec_spec_conf::kLeAudioLocationFrontLeft;
-  }
-
-  if (allocation & codec_spec_conf::kLeAudioLocationAnyRight) {
-    return codec_spec_conf::kLeAudioLocationFrontRight;
-  }
-  return 0;
-}
-
 namespace types {
 std::ostream& operator<<(std::ostream& os,
                          const AudioStreamDataPathState& state) {
@@ -907,6 +892,23 @@
   return os;
 }
 
+template <typename T>
+const T& BidirectionalPair<T>::get(uint8_t direction) const {
+  ASSERT_LOG(
+      direction < types::kLeAudioDirectionBoth,
+      "Unsupported complex direction. Consider using get_bidirectional<>() "
+      "instead.");
+  return (direction == types::kLeAudioDirectionSink) ? sink : source;
+}
+
+template <typename T>
+T& BidirectionalPair<T>::get(uint8_t direction) {
+  ASSERT_LOG(direction < types::kLeAudioDirectionBoth,
+             "Unsupported complex direction. Reference to a single complex"
+             " direction value is not supported.");
+  return (direction == types::kLeAudioDirectionSink) ? sink : source;
+}
+
 /* Bidirectional getter trait for AudioContexts bidirectional pair */
 template <>
 AudioContexts get_bidirectional(BidirectionalPair<AudioContexts> p) {
@@ -928,7 +930,13 @@
 
 template struct BidirectionalPair<AudioContexts>;
 template struct BidirectionalPair<AudioLocations>;
+template struct BidirectionalPair<CisType>;
+template struct BidirectionalPair<ase*>;
+template struct BidirectionalPair<std::string>;
 template struct BidirectionalPair<std::vector<uint8_t>>;
+template struct BidirectionalPair<stream_configuration>;
+template struct BidirectionalPair<stream_parameters>;
+template struct BidirectionalPair<uint16_t>;
 
 }  // 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 851bd9c..04aa733 100644
--- a/system/bta/le_audio/le_audio_types.h
+++ b/system/bta/le_audio/le_audio_types.h
@@ -31,8 +31,6 @@
 #include <variant>
 #include <vector>
 
-#include "bta_groups.h"
-#include "bta_le_audio_api.h"
 #include "bta_le_audio_uuids.h"
 #include "btm_iso_api_types.h"
 
@@ -41,9 +39,9 @@
 #define UINT8_TO_VEC_UINT8(u8) \
   std::vector<uint8_t> { u8 }
 #define UINT16_TO_VEC_UINT8(u16) \
-  std::vector<uint8_t>((uint8_t*)&u16, (uint8_t*)&u16 + sizeof(u16))
+  std::vector<uint8_t>((uint8_t*)&u16, (uint8_t*)&u16 + sizeof(uint16_t))
 #define UINT32_TO_VEC_UINT8(u32) \
-  std::vector<uint8_t>((uint8_t*)&u32, (uint8_t*)&u32 + sizeof(u32))
+  std::vector<uint8_t>((uint8_t*)&u32, (uint8_t*)&u32 + sizeof(uint32_t))
 
 #define VEC_UINT8_TO_UINT8(vec) vec.data()[0]
 #define VEC_UINT8_TO_UINT16(vec) ((vec.data()[1] << 8) + vec.data()[0])
@@ -312,6 +310,8 @@
 
 constexpr uint8_t kLeAudioDirectionSink = 0x01;
 constexpr uint8_t kLeAudioDirectionSource = 0x02;
+constexpr uint8_t kLeAudioDirectionBoth =
+    kLeAudioDirectionSink | kLeAudioDirectionSource;
 
 /* Audio stream config types */
 constexpr uint8_t kFramingUnframedPduSupported = 0x00;
@@ -477,31 +477,21 @@
   T sink;
   T source;
 
-  T get(uint8_t direction) const {
-    if (direction ==
-        (types::kLeAudioDirectionSink | types::kLeAudioDirectionSource)) {
-      return get_bidirectional(*this);
-    } else if (direction == types::kLeAudioDirectionSink) {
-      return sink;
-    }
-    return source;
-  }
-  T& get_ref(uint8_t direction) {
-    return (direction == types::kLeAudioDirectionSink) ? sink : source;
-  }
+  const T& get(uint8_t direction) const;
+  T& get(uint8_t direction);
 
   BidirectionalPair<T>& operator=(const BidirectionalPair<T>&) = default;
-  bool operator==(const BidirectionalPair<T>& other) const {
-    return (sink == other.sink) && (source == other.source);
-  };
-  bool operator!=(const BidirectionalPair<T>& other) const {
-    return (sink != other.sink) || (source != other.source);
-  };
 };
 
 template <typename T>
 T get_bidirectional(BidirectionalPair<T> p);
 
+template <typename T>
+bool operator==(const types::BidirectionalPair<T>& lhs,
+                const types::BidirectionalPair<T>& rhs) {
+  return (lhs.sink == rhs.sink) && (lhs.source == rhs.source);
+}
+
 /* Configuration strategy */
 enum class LeAudioConfigurationStrategy : uint8_t {
   MONO_ONE_CIS_PER_DEVICE = 0x00, /* Common true wireless speakers */
@@ -554,8 +544,33 @@
       : 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) {
+  LeAudioLtvMap& Add(uint8_t type, std::vector<uint8_t> value) {
     values.insert_or_assign(type, std::move(value));
+    return *this;
+  }
+  LeAudioLtvMap& Add(uint8_t type, uint8_t value) {
+    std::vector<uint8_t> v(sizeof(value));
+    auto ptr = v.data();
+
+    UINT8_TO_STREAM(ptr, value);
+    values.insert_or_assign(type, v);
+    return *this;
+  }
+  LeAudioLtvMap& Add(uint8_t type, uint16_t value) {
+    std::vector<uint8_t> v(sizeof(value));
+    auto ptr = v.data();
+
+    UINT16_TO_STREAM(ptr, value);
+    values.insert_or_assign(type, std::move(v));
+    return *this;
+  }
+  LeAudioLtvMap& Add(uint8_t type, uint32_t value) {
+    std::vector<uint8_t> v(sizeof(value));
+    auto ptr = v.data();
+
+    UINT32_TO_STREAM(ptr, value);
+    values.insert_or_assign(type, std::move(v));
+    return *this;
   }
   void Remove(uint8_t type) { values.erase(type); }
   bool IsEmpty() const { return values.empty(); }
@@ -691,6 +706,8 @@
         data_path_state(AudioStreamDataPathState::IDLE),
         configured_for_context_type(LeAudioContextType::UNINITIALIZED),
         preferred_phy(0),
+        is_codec_in_controller(false),
+        data_path_id(bluetooth::hci::iso_manager::kIsoDataPathDisabled),
         max_sdu_size(0),
         retrans_nb(0),
         max_transport_latency(0),
@@ -718,6 +735,13 @@
   uint8_t framing;
   uint8_t preferred_phy;
 
+  /* Set to true, if the codec is implemented in BT controller, false if it's
+   * implemented in host, or in separate DSP
+   */
+  bool is_codec_in_controller;
+  /* Datapath ID used to configure an ISO channel for these ASEs */
+  uint8_t data_path_id;
+
   /* Qos configuration */
   uint16_t max_sdu_size;
   uint8_t retrans_nb;
@@ -794,6 +818,12 @@
   uint8_t direction;  /* Direction of set */
   uint8_t device_cnt; /* How many devices must be in set */
   uint8_t ase_cnt;    /* How many ASE we need in configuration */
+
+  /* Whether the codec location is transparent to the controller */
+  bool is_codec_in_controller = false;
+  /* Datapath ID used to configure an ISO channel for these ASEs */
+  uint8_t data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci;
+
   CodecCapabilitySetting codec;
   QosConfigSetting qos;
   types::LeAudioConfigurationStrategy strategy;
@@ -834,15 +864,18 @@
     const AudioSetConfiguration* audio_set_configuration);
 }  // namespace set_configurations
 
-struct stream_map_info {
-  stream_map_info(uint16_t stream_handle, uint32_t audio_channel_allocation,
-                  bool is_stream_active)
-      : stream_handle(stream_handle),
-        audio_channel_allocation(audio_channel_allocation),
-        is_stream_active(is_stream_active) {}
-  uint16_t stream_handle;
+struct stream_parameters {
+  /* For now we have always same frequency for all the channels */
+  uint32_t sample_frequency_hz;
+  uint32_t frame_duration_us;
+  uint16_t octets_per_codec_frame;
   uint32_t audio_channel_allocation;
-  bool is_stream_active;
+  uint8_t codec_frames_blocks_per_sdu;
+  /* Number of channels is what we will request from audio framework */
+  uint8_t num_of_channels;
+  int num_of_devices;
+  /* cis_handle, audio location*/
+  std::vector<std::pair<uint16_t, uint32_t>> stream_locations;
 };
 
 struct stream_configuration {
@@ -853,43 +886,9 @@
   /* Pointer to chosen req */
   const le_audio::set_configurations::AudioSetConfiguration* conf;
 
-  /* Sink configuration */
-  /* For now we have always same frequency for all the channels */
-  uint32_t sink_sample_frequency_hz;
-  uint32_t sink_frame_duration_us;
-  uint16_t sink_octets_per_codec_frame;
-  uint32_t sink_audio_channel_allocation;
-  uint8_t sink_codec_frames_blocks_per_sdu;
-  /* Number of channels is what we will request from audio framework */
-  uint8_t sink_num_of_channels;
-  int sink_num_of_devices;
-  /* cis_handle, audio location*/
-  std::vector<std::pair<uint16_t, uint32_t>> sink_streams;
-  /* cis_handle, target allocation, stream active state */
-  std::vector<stream_map_info> sink_offloader_streams_target_allocation;
-  /* cis_handle, current allocation, stream active state */
-  std::vector<stream_map_info> sink_offloader_streams_current_allocation;
-  bool sink_offloader_changed;
-  bool sink_is_initial;
+  /* Sink & Source configuration */
+  types::BidirectionalPair<stream_parameters> stream_params;
 
-  /* Source configuration */
-  /* For now we have always same frequency for all the channels */
-  uint32_t source_sample_frequency_hz;
-  uint32_t source_frame_duration_us;
-  uint16_t source_octets_per_codec_frame;
-  uint32_t source_audio_channel_allocation;
-  uint8_t source_codec_frames_blocks_per_sdu;
-  /* Number of channels is what we will request from audio framework */
-  uint8_t source_num_of_channels;
-  int source_num_of_devices;
-  /* cis_handle, audio location*/
-  std::vector<std::pair<uint16_t, uint32_t>> source_streams;
-  /* cis_handle, target allocation, stream active state */
-  std::vector<stream_map_info> source_offloader_streams_target_allocation;
-  /* cis_handle, current allocation, stream active state */
-  std::vector<stream_map_info> source_offloader_streams_current_allocation;
-  bool source_offloader_changed;
-  bool source_is_initial;
   bool is_active;
 };
 
@@ -898,5 +897,4 @@
 void AppendMetadataLtvEntryForStreamingContext(
     std::vector<uint8_t>& metadata, types::AudioContexts context_type);
 uint8_t GetMaxCodecFramesPerSduFromPac(const types::acs_ac_record* pac_record);
-uint32_t AdjustAllocationForOffloader(uint32_t allocation);
 }  // namespace le_audio
\ No newline at end of file
diff --git a/system/bta/le_audio/le_audio_types_test.cc b/system/bta/le_audio/le_audio_types_test.cc
index 23066d9..44a49a6 100644
--- a/system/bta/le_audio/le_audio_types_test.cc
+++ b/system/bta/le_audio/le_audio_types_test.cc
@@ -88,6 +88,22 @@
   ASSERT_THAT(ltv_map2.RawPacket(), ElementsAreArray(ltv_test_vec2));
 }
 
+TEST(LeAudioLtvMapTest, test_serialization_macros) {
+  uint64_t value = 0x08090A0B0C0D0E0F;
+
+  auto u16vec = UINT16_TO_VEC_UINT8(value);
+  ASSERT_EQ(sizeof(uint16_t), u16vec.size());
+  ASSERT_EQ(0x0F, u16vec[0]);
+  ASSERT_EQ(0x0E, u16vec[1]);
+
+  auto u32vec = UINT32_TO_VEC_UINT8(value);
+  ASSERT_EQ(sizeof(uint32_t), u32vec.size());
+  ASSERT_EQ(0x0F, u32vec[0]);
+  ASSERT_EQ(0x0E, u32vec[1]);
+  ASSERT_EQ(0x0D, u32vec[2]);
+  ASSERT_EQ(0x0C, u32vec[3]);
+}
+
 TEST(LeAudioLtvMapTest, test_serialization_ltv_len_is_zero) {
   // clang-format off
   const std::vector<uint8_t> ltv_test_vec{
diff --git a/system/bta/le_audio/le_audio_utils.cc b/system/bta/le_audio/le_audio_utils.cc
index 50edab6..76698ee 100644
--- a/system/bta/le_audio/le_audio_utils.cc
+++ b/system/bta/le_audio/le_audio_utils.cc
@@ -211,9 +211,9 @@
         "sink device. This may result in voice back channel malfunction.");
   }
 
-  LOG_DEBUG("Allowed contexts from sink metadata: %s (0x%08hx)",
-            bluetooth::common::ToString(all_track_contexts).c_str(),
-            all_track_contexts.value());
+  LOG_INFO("Allowed contexts from sink metadata: %s (0x%08hx)",
+           bluetooth::common::ToString(all_track_contexts).c_str(),
+           all_track_contexts.value());
   return all_track_contexts;
 }
 
diff --git a/system/bta/le_audio/mock_codec_interface.cc b/system/bta/le_audio/mock_codec_interface.cc
new file mode 100644
index 0000000..c16aae7
--- /dev/null
+++ b/system/bta/le_audio/mock_codec_interface.cc
@@ -0,0 +1,67 @@
+/*
+ * 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_codec_interface.h"
+
+namespace le_audio {
+
+struct CodecInterface::Impl : public MockCodecInterface {
+ public:
+  Impl(const types::LeAudioCodecId& codec_id) {
+    output_channel_data_.resize(1);
+  };
+  ~Impl() = default;
+
+  std::vector<int16_t>& GetDecodedSamples() { return output_channel_data_; }
+  std::vector<int16_t> output_channel_data_;
+};
+
+CodecInterface::CodecInterface(const types::LeAudioCodecId& codec_id) {
+  impl = new Impl(codec_id);
+}
+CodecInterface::~CodecInterface() { delete impl; }
+bool CodecInterface::IsReady() { return impl->IsReady(); };
+CodecInterface::Status CodecInterface::InitEncoder(
+    const LeAudioCodecConfiguration& pcm_config,
+    const LeAudioCodecConfiguration& codec_config) {
+  return impl->InitEncoder(pcm_config, codec_config);
+}
+CodecInterface::Status CodecInterface::InitDecoder(
+    const LeAudioCodecConfiguration& codec_config,
+    const LeAudioCodecConfiguration& pcm_config) {
+  return impl->InitDecoder(codec_config, pcm_config);
+}
+std::vector<int16_t>& CodecInterface::GetDecodedSamples() {
+  return impl->GetDecodedSamples();
+}
+CodecInterface::Status CodecInterface::Decode(uint8_t* data, uint16_t size) {
+  return impl->Decode(data, size);
+}
+CodecInterface::Status CodecInterface::Encode(const uint8_t* data, int stride,
+                                              uint16_t out_size,
+                                              std::vector<int16_t>* out_buffer,
+                                              uint16_t out_offset) {
+  return impl->Encode(data, stride, out_size, out_buffer, out_offset);
+}
+void CodecInterface::Cleanup() { return impl->Cleanup(); }
+
+uint16_t CodecInterface::GetNumOfSamplesPerChannel() {
+  return impl->GetNumOfSamplesPerChannel();
+};
+uint8_t CodecInterface::GetNumOfBytesPerSample() {
+  return impl->GetNumOfBytesPerSample();
+};
+}  // namespace le_audio
diff --git a/system/bta/le_audio/mock_codec_interface.h b/system/bta/le_audio/mock_codec_interface.h
new file mode 100644
index 0000000..d2acda6
--- /dev/null
+++ b/system/bta/le_audio/mock_codec_interface.h
@@ -0,0 +1,47 @@
+/*
+ * 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 "codec_interface.h"
+
+class MockCodecInterface {
+ public:
+  MockCodecInterface() = default;
+  MockCodecInterface(const MockCodecInterface&) = delete;
+  MockCodecInterface& operator=(const MockCodecInterface&) = delete;
+
+  virtual ~MockCodecInterface() = default;
+
+  MOCK_METHOD((le_audio::CodecInterface::Status), InitEncoder,
+              (const le_audio::LeAudioCodecConfiguration& pcm_config,
+               const le_audio::LeAudioCodecConfiguration& codec_config));
+  MOCK_METHOD(le_audio::CodecInterface::Status, InitDecoder,
+              (const le_audio::LeAudioCodecConfiguration& codec_config,
+               const le_audio::LeAudioCodecConfiguration& pcm_config));
+  MOCK_METHOD(le_audio::CodecInterface::Status, Encode,
+              (const uint8_t* data, int stride, uint16_t out_size,
+               std::vector<int16_t>* out_buffer, uint16_t out_offset));
+  MOCK_METHOD(le_audio::CodecInterface::Status, Decode,
+              (uint8_t * data, uint16_t size));
+  MOCK_METHOD((void), Cleanup, ());
+  MOCK_METHOD((bool), IsReady, ());
+  MOCK_METHOD((uint16_t), GetNumOfSamplesPerChannel, ());
+  MOCK_METHOD((uint8_t), GetNumOfBytesPerSample, ());
+  MOCK_METHOD((std::vector<int16_t>&), GetDecodedSamples, ());
+};
diff --git a/system/bta/le_audio/mock_codec_manager.cc b/system/bta/le_audio/mock_codec_manager.cc
index 2f8aaad..9785d4d 100644
--- a/system/bta/le_audio/mock_codec_manager.cc
+++ b/system/bta/le_audio/mock_codec_manager.cc
@@ -37,22 +37,15 @@
   return pimpl_->GetCodecLocation();
 }
 
-void CodecManager::UpdateActiveSourceAudioConfig(
-    const stream_configuration& stream_conf, uint16_t delay_ms,
-    std::function<void(const ::le_audio::offload_config& config)>
+void CodecManager::UpdateActiveAudioConfig(
+    const types::BidirectionalPair<stream_parameters>& stream_params,
+    types::BidirectionalPair<uint16_t> delays_ms,
+    std::function<void(const ::le_audio::offload_config& config,
+                       uint8_t direction)>
         update_receiver) {
   if (pimpl_)
-    return pimpl_->UpdateActiveSourceAudioConfig(stream_conf, delay_ms,
-                                                 update_receiver);
-}
-
-void CodecManager::UpdateActiveSinkAudioConfig(
-    const stream_configuration& stream_conf, uint16_t delay_ms,
-    std::function<void(const ::le_audio::offload_config& config)>
-        update_receiver) {
-  if (pimpl_)
-    return pimpl_->UpdateActiveSinkAudioConfig(stream_conf, delay_ms,
-                                               update_receiver);
+    return pimpl_->UpdateActiveAudioConfig(stream_params, delays_ms,
+                                           update_receiver);
 }
 
 const set_configurations::AudioSetConfigurations*
@@ -99,6 +92,17 @@
   mock_codec_manager_pimpl_ = nullptr;
 }
 
+void CodecManager::UpdateCisConfiguration(
+    const std::vector<struct types::cis>& cises,
+    const stream_parameters& stream_params, uint8_t direction) {
+  if (pimpl_)
+    return pimpl_->UpdateCisConfiguration(cises, stream_params, direction);
+}
+
+void CodecManager::ClearCisConfiguration(uint8_t direction) {
+  if (pimpl_) return pimpl_->ClearCisConfiguration(direction);
+}
+
 // CodecManager::~CodecManager() = default;
 
 }  // namespace le_audio
diff --git a/system/bta/le_audio/mock_codec_manager.h b/system/bta/le_audio/mock_codec_manager.h
index d73132d..5035eed 100644
--- a/system/bta/le_audio/mock_codec_manager.h
+++ b/system/bta/le_audio/mock_codec_manager.h
@@ -31,15 +31,12 @@
   virtual ~MockCodecManager() = default;
 
   MOCK_METHOD((le_audio::types::CodecLocation), GetCodecLocation, (), (const));
-  MOCK_METHOD((void), UpdateActiveSourceAudioConfig,
-              (const le_audio::stream_configuration& stream_conf,
-               uint16_t delay,
-               std::function<void(const ::le_audio::offload_config& config)>
-                   update_receiver));
-  MOCK_METHOD((void), UpdateActiveSinkAudioConfig,
-              (const le_audio::stream_configuration& stream_conf,
-               uint16_t delay,
-               std::function<void(const ::le_audio::offload_config& config)>
+  MOCK_METHOD((void), UpdateActiveAudioConfig,
+              (const le_audio::types::BidirectionalPair<
+                   le_audio::stream_parameters>& stream_params,
+               le_audio::types::BidirectionalPair<uint16_t> delays_ms,
+               std::function<void(const ::le_audio::offload_config& config,
+                                  uint8_t direction)>
                    update_receiver));
   MOCK_METHOD((le_audio::set_configurations::AudioSetConfigurations*),
               GetOffloadCodecConfig,
@@ -51,6 +48,12 @@
       (const std::vector<uint16_t>& conn_handle,
        std::function<void(const ::le_audio::broadcast_offload_config& config)>
            update_receiver));
+  MOCK_METHOD((void), UpdateCisConfiguration,
+              (const std::vector<struct le_audio::types::cis>& cises,
+               const le_audio::stream_parameters& stream_params,
+               uint8_t direction),
+              (const));
+  MOCK_METHOD((void), ClearCisConfiguration, (uint8_t direction));
 
   MOCK_METHOD((void), Start, ());
   MOCK_METHOD((void), Stop, ());
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 3d0aaaa..f783824 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -759,7 +759,7 @@
     if (group->IsAnyDeviceConnected()) {
       /*
        * ACL of one of the device has been dropped. If number of CISes has
-       * changed notify upper layer so the offloader can be updated with CIS
+       * changed notify upper layer so the CodecManager can be updated with CIS
        * information.
        */
       if (!group->HaveAllCisesDisconnected()) {
@@ -769,7 +769,7 @@
             (group->GetTargetState() ==
              AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) {
           /* We keep streaming but want others to let know user that it might
-           * be need to update offloader with new CIS configuration
+           * be need to update CodecManager with new CIS configuration
            */
           state_machine_callbacks_->StatusReportCb(
               group->group_id_, GroupStreamStatus::STREAMING);
@@ -966,13 +966,11 @@
           ases_pair.sink->state == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
         ases_pair.sink->state =
             AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
-        ases_pair.sink->active = false;
       }
       if (ases_pair.source && ases_pair.source->state ==
                                   AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
         ases_pair.source->state =
             AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
-        ases_pair.source->active = false;
       }
     }
 
@@ -1159,154 +1157,85 @@
 
   void AddCisToStreamConfiguration(LeAudioDeviceGroup* group,
                                    const struct ase* ase) {
-    uint16_t cis_conn_hdl = ase->cis_conn_hdl;
+    group->stream_conf.id = ase->codec_id;
+
+    auto cis_conn_hdl = ase->cis_conn_hdl;
+    auto& params = group->stream_conf.stream_params.get(ase->direction);
     LOG_INFO("Adding cis handle 0x%04x (%s) to stream list", cis_conn_hdl,
              ase->direction == le_audio::types::kLeAudioDirectionSink
                  ? "sink"
                  : "source");
-    auto* stream_conf = &group->stream_conf;
-    if (ase->direction == le_audio::types::kLeAudioDirectionSink) {
-      auto iter = std::find_if(
-          stream_conf->sink_streams.begin(), stream_conf->sink_streams.end(),
-          [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });
 
-      ASSERT_LOG(iter == stream_conf->sink_streams.end(),
-                 "Stream is already there 0x%04x", cis_conn_hdl);
+    auto iter = std::find_if(
+        params.stream_locations.begin(), params.stream_locations.end(),
+        [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });
+    ASSERT_LOG(iter == params.stream_locations.end(),
+               "Stream is already there 0x%04x", cis_conn_hdl);
 
-      stream_conf->sink_streams.emplace_back(std::make_pair(
-          ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation));
+    params.num_of_devices++;
+    params.num_of_channels += ase->codec_config.channel_count;
 
-      stream_conf->sink_num_of_devices++;
-      stream_conf->sink_num_of_channels += ase->codec_config.channel_count;
-      stream_conf->sink_audio_channel_allocation |=
-          *ase->codec_config.audio_channel_allocation;
+    if (!ase->codec_config.audio_channel_allocation.has_value()) {
+      LOG_WARN("ASE has invalid audio location");
+    }
+    auto ase_audio_channel_allocation =
+        ase->codec_config.audio_channel_allocation.value_or(0);
+    params.audio_channel_allocation |= ase_audio_channel_allocation;
+    params.stream_locations.emplace_back(
+        std::make_pair(ase->cis_conn_hdl, ase_audio_channel_allocation));
 
-      if (stream_conf->sink_sample_frequency_hz == 0) {
-        stream_conf->sink_sample_frequency_hz =
-            ase->codec_config.GetSamplingFrequencyHz();
-      } else {
-        ASSERT_LOG(stream_conf->sink_sample_frequency_hz ==
-                       ase->codec_config.GetSamplingFrequencyHz(),
-                   "sample freq mismatch: %d!=%d",
-                   stream_conf->sink_sample_frequency_hz,
-                   ase->codec_config.GetSamplingFrequencyHz());
-      }
-
-      if (stream_conf->sink_octets_per_codec_frame == 0) {
-        stream_conf->sink_octets_per_codec_frame =
-            *ase->codec_config.octets_per_codec_frame;
-      } else {
-        ASSERT_LOG(stream_conf->sink_octets_per_codec_frame ==
-                       *ase->codec_config.octets_per_codec_frame,
-                   "octets per frame mismatch: %d!=%d",
-                   stream_conf->sink_octets_per_codec_frame,
-                   *ase->codec_config.octets_per_codec_frame);
-      }
-
-      if (stream_conf->sink_codec_frames_blocks_per_sdu == 0) {
-        stream_conf->sink_codec_frames_blocks_per_sdu =
-            *ase->codec_config.codec_frames_blocks_per_sdu;
-      } else {
-        ASSERT_LOG(stream_conf->sink_codec_frames_blocks_per_sdu ==
-                       *ase->codec_config.codec_frames_blocks_per_sdu,
-                   "codec_frames_blocks_per_sdu: %d!=%d",
-                   stream_conf->sink_codec_frames_blocks_per_sdu,
-                   *ase->codec_config.codec_frames_blocks_per_sdu);
-      }
-
-      if (stream_conf->sink_frame_duration_us == 0) {
-        stream_conf->sink_frame_duration_us =
-            ase->codec_config.GetFrameDurationUs();
-      } else {
-        ASSERT_LOG(stream_conf->sink_frame_duration_us ==
-                       ase->codec_config.GetFrameDurationUs(),
-                   "frame_duration_us: %d!=%d",
-                   stream_conf->sink_frame_duration_us,
-                   ase->codec_config.GetFrameDurationUs());
-      }
-
-      LOG_INFO(
-          " Added Sink Stream Configuration. CIS Connection Handle: %d"
-          ", Audio Channel Allocation: %d"
-          ", Sink Number Of Devices: %d"
-          ", Sink Number Of Channels: %d",
-          ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation,
-          stream_conf->sink_num_of_devices, stream_conf->sink_num_of_channels);
-
+    if (params.sample_frequency_hz == 0) {
+      params.sample_frequency_hz = ase->codec_config.GetSamplingFrequencyHz();
     } else {
-      /* Source case */
-      auto iter = std::find_if(
-          stream_conf->source_streams.begin(),
-          stream_conf->source_streams.end(),
-          [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });
-
-      ASSERT_LOG(iter == stream_conf->source_streams.end(),
-                 "Stream is already there 0x%04x", cis_conn_hdl);
-
-      stream_conf->source_streams.emplace_back(std::make_pair(
-          ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation));
-
-      stream_conf->source_num_of_devices++;
-      stream_conf->source_num_of_channels += ase->codec_config.channel_count;
-      stream_conf->source_audio_channel_allocation |=
-          *ase->codec_config.audio_channel_allocation;
-
-      if (stream_conf->source_sample_frequency_hz == 0) {
-        stream_conf->source_sample_frequency_hz =
-            ase->codec_config.GetSamplingFrequencyHz();
-      } else {
-        ASSERT_LOG(stream_conf->source_sample_frequency_hz ==
-                       ase->codec_config.GetSamplingFrequencyHz(),
-                   "sample freq mismatch: %d!=%d",
-                   stream_conf->source_sample_frequency_hz,
-                   ase->codec_config.GetSamplingFrequencyHz());
-      }
-
-      if (stream_conf->source_octets_per_codec_frame == 0) {
-        stream_conf->source_octets_per_codec_frame =
-            *ase->codec_config.octets_per_codec_frame;
-      } else {
-        ASSERT_LOG(stream_conf->source_octets_per_codec_frame ==
-                       *ase->codec_config.octets_per_codec_frame,
-                   "octets per frame mismatch: %d!=%d",
-                   stream_conf->source_octets_per_codec_frame,
-                   *ase->codec_config.octets_per_codec_frame);
-      }
-
-      if (stream_conf->source_codec_frames_blocks_per_sdu == 0) {
-        stream_conf->source_codec_frames_blocks_per_sdu =
-            *ase->codec_config.codec_frames_blocks_per_sdu;
-      } else {
-        ASSERT_LOG(stream_conf->source_codec_frames_blocks_per_sdu ==
-                       *ase->codec_config.codec_frames_blocks_per_sdu,
-                   "codec_frames_blocks_per_sdu: %d!=%d",
-                   stream_conf->source_codec_frames_blocks_per_sdu,
-                   *ase->codec_config.codec_frames_blocks_per_sdu);
-      }
-
-      if (stream_conf->source_frame_duration_us == 0) {
-        stream_conf->source_frame_duration_us =
-            ase->codec_config.GetFrameDurationUs();
-      } else {
-        ASSERT_LOG(stream_conf->source_frame_duration_us ==
-                       ase->codec_config.GetFrameDurationUs(),
-                   "frame_duration_us: %d!=%d",
-                   stream_conf->source_frame_duration_us,
-                   ase->codec_config.GetFrameDurationUs());
-      }
-
-      LOG_INFO(
-          " Added Source Stream Configuration. CIS Connection Handle: %d"
-          ", Audio Channel Allocation: %d"
-          ", Source Number Of Devices: %d"
-          ", Source Number Of Channels: %d",
-          ase->cis_conn_hdl, *ase->codec_config.audio_channel_allocation,
-          stream_conf->source_num_of_devices,
-          stream_conf->source_num_of_channels);
+      ASSERT_LOG(params.sample_frequency_hz ==
+                     ase->codec_config.GetSamplingFrequencyHz(),
+                 "sample freq mismatch: %d!=%d", params.sample_frequency_hz,
+                 ase->codec_config.GetSamplingFrequencyHz());
     }
 
-    /* Update offloader streams */
-    group->CreateStreamVectorForOffloader(ase->direction);
+    if (params.octets_per_codec_frame == 0) {
+      params.octets_per_codec_frame = *ase->codec_config.octets_per_codec_frame;
+    } else {
+      ASSERT_LOG(params.octets_per_codec_frame ==
+                     *ase->codec_config.octets_per_codec_frame,
+                 "octets per frame mismatch: %d!=%d",
+                 params.octets_per_codec_frame,
+                 *ase->codec_config.octets_per_codec_frame);
+    }
+
+    if (params.codec_frames_blocks_per_sdu == 0) {
+      params.codec_frames_blocks_per_sdu =
+          *ase->codec_config.codec_frames_blocks_per_sdu;
+    } else {
+      ASSERT_LOG(params.codec_frames_blocks_per_sdu ==
+                     *ase->codec_config.codec_frames_blocks_per_sdu,
+                 "codec_frames_blocks_per_sdu: %d!=%d",
+                 params.codec_frames_blocks_per_sdu,
+                 *ase->codec_config.codec_frames_blocks_per_sdu);
+    }
+
+    if (params.frame_duration_us == 0) {
+      params.frame_duration_us = ase->codec_config.GetFrameDurationUs();
+    } else {
+      ASSERT_LOG(
+          params.frame_duration_us == ase->codec_config.GetFrameDurationUs(),
+          "frame_duration_us: %d!=%d", params.frame_duration_us,
+          ase->codec_config.GetFrameDurationUs());
+    }
+
+    LOG_INFO(
+        " Added %s Stream Configuration. CIS Connection Handle: %d"
+        ", Audio Channel Allocation: %d"
+        ", Number Of Devices: %d"
+        ", Number Of Channels: %d",
+        (ase->direction == le_audio::types::kLeAudioDirectionSink ? "Sink"
+                                                                  : "Source"),
+        cis_conn_hdl, ase_audio_channel_allocation, params.num_of_devices,
+        params.num_of_channels);
+
+    /* Update CodecManager stream configuration */
+    state_machine_callbacks_->OnUpdatedCisConfiguration(group->group_id_,
+                                                        ase->direction);
   }
 
   bool CigCreate(LeAudioDeviceGroup* group) {
@@ -1528,22 +1457,15 @@
   }
 
   static void PrepareDataPath(int group_id, const struct ase* ase) {
-    /* TODO: Handle HW offloading decode as we handle here, force to use SW
-     * decode for now */
-    auto data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci;
-    if (CodecManager::GetInstance()->GetCodecLocation() !=
-        CodecLocation::HOST) {
-      data_path_id = bluetooth::hci::iso_manager::kIsoDataPathPlatformDefault;
-    }
-    /* TODO: Need to set coding_format when we support the codec location inside
-     * the controller, force to use transparent for now */
     bluetooth::hci::iso_manager::iso_data_path_params param = {
         .data_path_dir =
             ase->direction == le_audio::types::kLeAudioDirectionSink
                 ? bluetooth::hci::iso_manager::kIsoDataPathDirectionIn
                 : bluetooth::hci::iso_manager::kIsoDataPathDirectionOut,
-        .data_path_id = data_path_id,
-        .codec_id_format = bluetooth::hci::kIsoCodingFormatTransparent,
+        .data_path_id = ase->data_path_id,
+        .codec_id_format = ase->is_codec_in_controller
+                               ? ase->codec_id.coding_format
+                               : bluetooth::hci::kIsoCodingFormatTransparent,
         .codec_id_company = ase->codec_id.vendor_company_id,
         .codec_id_vendor = ase->codec_id.vendor_codec_id,
         .controller_delay = 0x00000000,
@@ -1731,7 +1653,8 @@
       conf.target_latency = ase->target_latency;
       conf.target_phy = group->GetTargetPhy(ase->direction);
       conf.codec_id = ase->codec_id;
-      conf.codec_config = ase->codec_config;
+      // FIXME: Use LtvMap in ASE
+      conf.codec_config = ase->codec_config.GetAsLtvMap();
       confs.push_back(conf);
 
       msg_stream << "ASE_ID " << +conf.ase_id << ",";
@@ -2444,15 +2367,24 @@
       auto directional_audio_context =
           context_types.get(ase->direction) &
           leAudioDevice->GetAvailableContexts(ase->direction);
+
+      std::vector<uint8_t> new_metadata;
       if (directional_audio_context.any()) {
-        ase->metadata = leAudioDevice->GetMetadata(
+        new_metadata = leAudioDevice->GetMetadata(
             directional_audio_context, ccid_lists.get(ase->direction));
       } else {
-        ase->metadata = leAudioDevice->GetMetadata(
+        new_metadata = leAudioDevice->GetMetadata(
             AudioContexts(LeAudioContextType::UNSPECIFIED),
             std::vector<uint8_t>());
       }
 
+      /* Do not update if metadata did not changed. */
+      if (ase->metadata == new_metadata) {
+        continue;
+      }
+
+      ase->metadata = new_metadata;
+
       struct le_audio::client_parser::ascs::ctp_update_metadata conf;
 
       conf.ase_id = ase->id;
diff --git a/system/bta/le_audio/state_machine.h b/system/bta/le_audio/state_machine.h
index 596df9d..fa69045 100644
--- a/system/bta/le_audio/state_machine.h
+++ b/system/bta/le_audio/state_machine.h
@@ -37,6 +37,7 @@
     virtual void StatusReportCb(
         int group_id, bluetooth::le_audio::GroupStreamStatus status) = 0;
     virtual void OnStateTransitionTimeout(int group_id) = 0;
+    virtual void OnUpdatedCisConfiguration(int group_id, uint8_t direction) = 0;
   };
 
   virtual ~LeAudioGroupStateMachine() = default;
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index f4f2667..dac886f2 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -165,9 +165,11 @@
               (int group_id, bluetooth::le_audio::GroupStreamStatus status),
               (override));
   MOCK_METHOD((void), OnStateTransitionTimeout, (int group_id), (override));
+  MOCK_METHOD((void), OnUpdatedCisConfiguration,
+              (int group_id, uint8_t direction), (override));
 };
 
-class StateMachineTest : public Test {
+class StateMachineTestBase : public Test {
  protected:
   uint8_t ase_id_last_assigned = types::ase::kAseIdInvalid;
   uint8_t additional_snk_ases = 0;
@@ -181,7 +183,7 @@
   uint8_t overwrite_cis_status_idx_;
   std::vector<uint8_t> cis_status_;
 
-  void SetUp() override {
+  virtual void SetUp() override {
     bluetooth::common::InitFlags::Load(test_flags);
     reset_mock_function_count_map();
     controller::SetMockControllerInterface(&mock_controller_);
@@ -194,7 +196,6 @@
     do_not_send_cis_establish_event_ = false;
     cis_status_.clear();
 
-    ::le_audio::AudioSetConfigurationProvider::Initialize();
     LeAudioGroupStateMachine::Initialize(&mock_callbacks_);
 
     ContentControlIdKeeper::GetInstance()->Start();
@@ -243,7 +244,6 @@
         }));
 
     ConfigureIsoManagerMock();
-    ConfigCodecManagerMock();
   }
 
   void HandleCtpOperation(LeAudioDevice* device, std::vector<uint8_t> value,
@@ -455,7 +455,7 @@
         });
   }
 
-  void ConfigCodecManagerMock() {
+  void ConfigCodecManagerMock(types::CodecLocation location) {
     codec_manager_ = le_audio::CodecManager::GetInstance();
     ASSERT_NE(codec_manager_, nullptr);
     std::vector<bluetooth::le_audio::btle_audio_codec_config_t>
@@ -464,7 +464,7 @@
     mock_codec_manager_ = MockCodecManager::GetInstance();
     ASSERT_NE(mock_codec_manager_, nullptr);
     ON_CALL(*mock_codec_manager_, GetCodecLocation())
-        .WillByDefault(Return(types::CodecLocation::HOST));
+        .WillByDefault(Return(location));
   }
 
   void TearDown() override {
@@ -490,6 +490,7 @@
     addresses_.clear();
     cached_codec_configuration_map_.clear();
     cached_ase_to_cis_id_map_.clear();
+    cached_remote_qos_configuration_for_ase_.clear();
     LeAudioGroupStateMachine::Cleanup();
     ::le_audio::AudioSetConfigurationProvider::Cleanup();
   }
@@ -625,6 +626,8 @@
         UINT16_TO_STREAM(p, conf->max_transport_latency);
         UINT24_TO_STREAM(p, conf->pres_delay);
 
+        cached_remote_qos_configuration_for_ase_[ase] = notif_value;
+
         LeAudioGroupStateMachine::Get()->ProcessGattNotifEvent(
             notif_value.data(), notif_value.size(), ase, device, group);
       } break;
@@ -727,7 +730,8 @@
                                  LeAudioContextType context_type,
                                  uint16_t device_cnt,
                                  types::AudioContexts update_contexts,
-                                 bool insert_default_pac_records = true) {
+                                 bool insert_default_pac_records = true,
+                                 bool second_device_0_ases = false) {
     // Prepare fake connected device group
     DeviceConnectState initial_connect_state =
         DeviceConnectState::CONNECTING_BY_USER;
@@ -762,8 +766,15 @@
     }
 
     while (device_cnt) {
-      auto leAudioDevice = PrepareConnectedDevice(
-          device_cnt--, initial_connect_state, num_ase_snk, num_ase_src);
+      std::shared_ptr<LeAudioDevice> leAudioDevice;
+
+      if (device_cnt == 2 && second_device_0_ases == true) {
+        leAudioDevice =
+            PrepareConnectedDevice(device_cnt--, initial_connect_state, 0, 0);
+      } else {
+        leAudioDevice = PrepareConnectedDevice(
+            device_cnt--, initial_connect_state, num_ase_snk, num_ase_src);
+      }
 
       if (insert_default_pac_records) {
         uint16_t attr_handle = ATTR_HANDLE_PACS_POOL_START;
@@ -847,9 +858,10 @@
   LeAudioDeviceGroup* PrepareSingleTestDeviceGroup(
       int leaudio_group_id, LeAudioContextType context_type,
       uint16_t device_cnt = 1,
-      types::AudioContexts update_contexts = types::AudioContexts()) {
+      types::AudioContexts update_contexts = types::AudioContexts(),
+      bool second_device_0_ases = false) {
     MultipleTestDevicePrepare(leaudio_group_id, context_type, device_cnt,
-                              update_contexts);
+                              update_contexts, true, second_device_0_ases);
     return le_audio_device_groups_.count(leaudio_group_id)
                ? le_audio_device_groups_[leaudio_group_id].get()
                : nullptr;
@@ -1271,6 +1283,8 @@
       cached_codec_configuration_map_;
 
   std::map<RawAddress, std::map<int, int>> cached_ase_to_cis_id_map_;
+  std::map<types::ase*, std::vector<uint8_t>>
+      cached_remote_qos_configuration_for_ase_;
 
   MockLeAudioGroupStateMachineCallbacks mock_callbacks_;
   std::vector<std::shared_ptr<LeAudioDevice>> le_audio_devices_;
@@ -1280,6 +1294,24 @@
   bool group_create_command_disallowed_ = false;
 };
 
+class StateMachineTest : public StateMachineTestBase {
+  void SetUp() override {
+    ConfigCodecManagerMock(types::CodecLocation::HOST);
+    ::le_audio::AudioSetConfigurationProvider::Initialize(
+        ::le_audio::types::CodecLocation::HOST);
+    StateMachineTestBase::SetUp();
+  }
+};
+
+class StateMachineTestAdsp : public StateMachineTestBase {
+  void SetUp() override {
+    ConfigCodecManagerMock(types::CodecLocation::ADSP);
+    ::le_audio::AudioSetConfigurationProvider::Initialize(
+        ::le_audio::types::CodecLocation::ADSP);
+    StateMachineTestBase::SetUp();
+  }
+};
+
 TEST_F(StateMachineTest, testInit) {
   ASSERT_NE(LeAudioGroupStateMachine::Get(), nullptr);
 }
@@ -1743,6 +1775,160 @@
   ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
 }
 
+TEST_F(StateMachineTest, testStreamMultipleMedia_OneMemberHasNoAses) {
+  const auto context_type = kContextTypeMedia;
+  const auto leaudio_group_id = 4;
+  const auto num_devices = 2;
+
+  // Prepare multiple fake connected devices in a group. This time one device
+  // has 0 Ases
+  auto* group =
+      PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices,
+                                   types::AudioContexts(), true);
+  ASSERT_EQ(group->Size(), num_devices);
+
+  PrepareConfigureCodecHandler(group);
+  PrepareConfigureQosHandler(group);
+  PrepareEnableHandler(group);
+  PrepareReceiverStartReady(group);
+
+  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(AtLeast(1));
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0);
+  EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0);
+  EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0);
+
+  InjectInitialIdleNotification(group);
+
+  /* Check there are two devices*/
+  auto* leAudioDevice = group->GetFirstDevice();
+  LeAudioDevice* lastDevice = nullptr;
+  /*
+   * First set member has no ASEs, no operations on control point are expected
+   * 0
+   */
+  EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_,
+                                              leAudioDevice->ctp_hdls_.val_hdl,
+                                              _, GATT_WRITE_NO_RSP, _, _))
+      .Times(0);
+
+  auto expected_devices_written = 0;
+  while (leAudioDevice) {
+    expected_devices_written++;
+    lastDevice = leAudioDevice;
+    leAudioDevice = group->GetNextDevice(leAudioDevice);
+  }
+  ASSERT_EQ(expected_devices_written, num_devices);
+
+  /*
+   * Second device will be configured for Streaming. Expecting 3 operations:
+   * 1. Codec Config
+   * 2. QoS Config
+   * 3. Enable
+   */
+  EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_,
+                                              lastDevice->ctp_hdls_.val_hdl, _,
+                                              GATT_WRITE_NO_RSP, _, _))
+      .Times(3);
+
+  // Validate GroupStreamStatus
+  EXPECT_CALL(
+      mock_callbacks_,
+      StatusReportCb(leaudio_group_id,
+                     bluetooth::le_audio::GroupStreamStatus::STREAMING));
+
+  // Start the configuration and stream Media content
+  ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream(
+      group, context_type,
+      {.sink = types::AudioContexts(context_type),
+       .source = types::AudioContexts(context_type)}));
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+  ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
+}
+
+TEST_F(StateMachineTest,
+       testStreamMultipleMedia_OneMemberHasNoAsesAndNotConnected) {
+  const auto context_type = kContextTypeMedia;
+  const auto leaudio_group_id = 4;
+  const auto num_devices = 2;
+
+  // Prepare multiple fake connected devices in a group. This time one device
+  // has 0 Ases
+  auto* group =
+      PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices,
+                                   types::AudioContexts(), true);
+  ASSERT_EQ(group->Size(), num_devices);
+
+  PrepareConfigureCodecHandler(group);
+  PrepareConfigureQosHandler(group);
+  PrepareEnableHandler(group);
+  PrepareReceiverStartReady(group);
+
+  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(AtLeast(1));
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0);
+  EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0);
+  EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0);
+
+  InjectInitialIdleNotification(group);
+
+  /* Check there are two devices*/
+  auto* leAudioDevice = group->GetFirstDevice();
+  LeAudioDevice* lastDevice = nullptr;
+  /*
+   * First set member has no ASEs, no operations on control point are expected
+   * 0
+   */
+  EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_,
+                                              leAudioDevice->ctp_hdls_.val_hdl,
+                                              _, GATT_WRITE_NO_RSP, _, _))
+      .Times(0);
+
+  /* Device with 0 Ases is disconnected */
+  InjectAclDisconnected(group, leAudioDevice);
+
+  auto expected_devices_written = 0;
+  while (leAudioDevice) {
+    expected_devices_written++;
+    lastDevice = leAudioDevice;
+    leAudioDevice = group->GetNextDevice(leAudioDevice);
+  }
+  ASSERT_EQ(expected_devices_written, num_devices);
+
+  /*
+   * Second device will be configured for Streaming. Expecting 3 operations:
+   * 1. Codec Config
+   * 2. QoS Config
+   * 3. Enable
+   */
+  EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_,
+                                              lastDevice->ctp_hdls_.val_hdl, _,
+                                              GATT_WRITE_NO_RSP, _, _))
+      .Times(3);
+
+  // Validate GroupStreamStatus
+  EXPECT_CALL(
+      mock_callbacks_,
+      StatusReportCb(leaudio_group_id,
+                     bluetooth::le_audio::GroupStreamStatus::STREAMING));
+
+  // Start the configuration and stream Media content
+  ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream(
+      group, context_type,
+      {.sink = types::AudioContexts(context_type),
+       .source = types::AudioContexts(context_type)}));
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+  ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
+}
+
 TEST_F(StateMachineTest, testStreamMultipleConversational) {
   const auto context_type = kContextTypeConversational;
   const auto leaudio_group_id = 4;
@@ -1934,9 +2120,12 @@
   const auto leaudio_group_id = 4;
   const auto num_devices = 2;
 
+  auto supported_contexts =
+      types::AudioContexts(kContextTypeMedia | kContextTypeSoundEffects);
+
   // Prepare multiple fake connected devices in a group
-  auto* group =
-      PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices);
+  auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type,
+                                             num_devices, supported_contexts);
   ASSERT_EQ(group->Size(), num_devices);
 
   PrepareConfigureCodecHandler(group);
@@ -2010,6 +2199,90 @@
   ASSERT_EQ(0, get_func_call_count("alarm_cancel"));
 }
 
+TEST_F(StateMachineTest, testUpdateMetadataMultiple_NoUpdatesOnKeyTouch) {
+  const auto context_type = kContextTypeMedia;
+  const auto leaudio_group_id = 4;
+  const auto num_devices = 2;
+
+  /* Only Media is supported and available, */
+  auto supported_contexts = types::AudioContexts(kContextTypeMedia);
+
+  // Prepare multiple fake connected devices in a group
+  auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type,
+                                             num_devices, supported_contexts);
+  ASSERT_EQ(group->Size(), num_devices);
+
+  PrepareConfigureCodecHandler(group);
+  PrepareConfigureQosHandler(group);
+  PrepareEnableHandler(group);
+
+  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(AtLeast(1));
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2);
+  EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0);
+  EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0);
+  EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0);
+
+  InjectInitialIdleNotification(group);
+
+  auto* leAudioDevice = group->GetFirstDevice();
+  auto expected_devices_written = 0;
+  while (leAudioDevice) {
+    EXPECT_CALL(gatt_queue,
+                WriteCharacteristic(leAudioDevice->conn_id_,
+                                    leAudioDevice->ctp_hdls_.val_hdl, _,
+                                    GATT_WRITE_NO_RSP, _, _))
+        .Times(AtLeast(3));
+    expected_devices_written++;
+    leAudioDevice = group->GetNextDevice(leAudioDevice);
+  }
+  ASSERT_EQ(expected_devices_written, num_devices);
+
+  // Validate GroupStreamStatus
+  EXPECT_CALL(
+      mock_callbacks_,
+      StatusReportCb(leaudio_group_id,
+                     bluetooth::le_audio::GroupStreamStatus::STREAMING));
+
+  // Start the configuration and stream Media content
+  ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream(
+      group, context_type,
+      {.sink = types::AudioContexts(context_type),
+       .source = types::AudioContexts(context_type)}));
+
+  testing::Mock::VerifyAndClearExpectations(&gatt_queue);
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+
+  ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
+  reset_mock_function_count_map();
+
+  // Make sure all devices get the metadata update
+  leAudioDevice = group->GetFirstDevice();
+  expected_devices_written = 0;
+  while (leAudioDevice) {
+    EXPECT_CALL(gatt_queue,
+                WriteCharacteristic(leAudioDevice->conn_id_,
+                                    leAudioDevice->ctp_hdls_.val_hdl, _,
+                                    GATT_WRITE_NO_RSP, _, _))
+        .Times(0);
+    expected_devices_written++;
+    leAudioDevice = group->GetNextDevice(leAudioDevice);
+  }
+  ASSERT_EQ(expected_devices_written, num_devices);
+
+  const auto metadata_context_type =
+      kContextTypeMedia | kContextTypeSoundEffects;
+  ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream(
+      group, context_type,
+      {.sink = metadata_context_type, .source = metadata_context_type}));
+
+  /* This is just update metadata - watchdog is not used */
+  ASSERT_EQ(0, get_func_call_count("alarm_cancel"));
+}
+
 TEST_F(StateMachineTest, testDisableSingle) {
   /* Device is banded headphones with 2x snk + 0x src ase
    * (2xunidirectional CIS)
@@ -3185,9 +3458,7 @@
   /* Can be called for every context when fetching the configuration from the
    * AudioSetConfigurationProvider.
    */
-  EXPECT_CALL(*mock_codec_manager_, GetCodecLocation())
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(types::CodecLocation::HOST));
+  EXPECT_CALL(*mock_codec_manager_, GetCodecLocation()).Times(AtLeast(1));
 
   // Prepare fake connected device group
   auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type);
@@ -3213,7 +3484,8 @@
       {.sink = types::AudioContexts(context_type),
        .source = types::AudioContexts(context_type)}));
 }
-TEST_F(StateMachineTest, testConfigureDataPathForAdsp) {
+
+TEST_F(StateMachineTestAdsp, testConfigureDataPathForAdsp) {
   const auto context_type = kContextTypeRingtone;
   const int leaudio_group_id = 4;
   channel_count_ = kLeAudioCodecLC3ChannelCountSingleChannel |
@@ -3222,9 +3494,7 @@
   /* Can be called for every context when fetching the configuration from the
    * AudioSetConfigurationProvider.
    */
-  EXPECT_CALL(*mock_codec_manager_, GetCodecLocation())
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(types::CodecLocation::ADSP));
+  EXPECT_CALL(*mock_codec_manager_, GetCodecLocation()).Times(AtLeast(1));
 
   // Prepare fake connected device group
   auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type);
@@ -3252,7 +3522,7 @@
        .source = types::AudioContexts(context_type)}));
 }
 
-TEST_F(StateMachineTest, testStreamConfigurationAdspDownMix) {
+TEST_F(StateMachineTestAdsp, testStreamConfigurationAdspDownMix) {
   const auto context_type = kContextTypeConversational;
   const int leaudio_group_id = 4;
   const int num_devices = 2;
@@ -3262,12 +3532,19 @@
       leaudio_group_id, context_type, num_devices,
       types::AudioContexts(kContextTypeConversational));
 
-  /* Can be called for every context when fetching the configuration from the
-   * AudioSetConfigurationProvider.
+  EXPECT_CALL(mock_callbacks_,
+              OnUpdatedCisConfiguration(group->group_id_,
+                                        le_audio::types::kLeAudioDirectionSink))
+      .Times(1);
+  EXPECT_CALL(mock_callbacks_,
+              OnUpdatedCisConfiguration(
+                  group->group_id_, le_audio::types::kLeAudioDirectionSource))
+      .Times(1);
+
+  /* Can be called for every context when fetching the configuration from
+   * the AudioSetConfigurationProvider.
    */
-  EXPECT_CALL(*mock_codec_manager_, GetCodecLocation())
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(types::CodecLocation::ADSP));
+  EXPECT_CALL(*mock_codec_manager_, GetCodecLocation()).Times(AtLeast(1));
 
   PrepareConfigureCodecHandler(group);
   PrepareConfigureQosHandler(group);
@@ -3285,48 +3562,11 @@
       {.sink = types::AudioContexts(context_type),
        .source = types::AudioContexts(context_type)}));
 
-  ASSERT_EQ(
-      static_cast<int>(
-          group->stream_conf.sink_offloader_streams_target_allocation.size()),
-      2);
-  ASSERT_EQ(
-      static_cast<int>(
-          group->stream_conf.source_offloader_streams_target_allocation.size()),
-      2);
-
   // Check if group has transitioned to a proper state
   ASSERT_EQ(group->GetState(),
             types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
 
-  uint32_t allocation = 0;
-  for (const auto& s :
-       group->stream_conf.sink_offloader_streams_target_allocation) {
-    allocation |= s.audio_channel_allocation;
-    ASSERT_FALSE(allocation == 0);
-  }
-  ASSERT_TRUE(allocation == codec_spec_conf::kLeAudioLocationStereo);
-
-  allocation = 0;
-  for (const auto& s :
-       group->stream_conf.source_offloader_streams_target_allocation) {
-    allocation |= s.audio_channel_allocation;
-    ASSERT_FALSE(allocation == 0);
-  }
-  ASSERT_TRUE(allocation == codec_spec_conf::kLeAudioLocationStereo);
-
-  for (const auto& s :
-       group->stream_conf.sink_offloader_streams_target_allocation) {
-    ASSERT_TRUE((s.audio_channel_allocation != 0) &&
-                (s.audio_channel_allocation !=
-                 codec_spec_conf::kLeAudioLocationStereo));
-  }
-
-  for (const auto& s :
-       group->stream_conf.source_offloader_streams_target_allocation) {
-    ASSERT_TRUE((s.audio_channel_allocation != 0) &&
-                (s.audio_channel_allocation !=
-                 codec_spec_conf::kLeAudioLocationStereo));
-  }
+  // Note: The actual channel mixing is verified by the CodecManager unit tests.
 }
 
 static void InjectCisDisconnected(LeAudioDeviceGroup* group,
@@ -3455,6 +3695,120 @@
   ASSERT_NE(ase->retrans_nb, 0);
 }
 
+TEST_F(StateMachineTest,
+       testAttachDeviceToTheStream_autonomusQoSConfiguredState) {
+  const auto context_type = kContextTypeMedia;
+  const auto leaudio_group_id = 6;
+  const auto num_devices = 2;
+
+  ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid);
+
+  // Prepare multiple fake connected devices in a group
+  auto* group =
+      PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices);
+  ASSERT_EQ(group->Size(), num_devices);
+
+  PrepareConfigureCodecHandler(group);
+  PrepareConfigureQosHandler(group);
+  PrepareEnableHandler(group);
+  PrepareDisableHandler(group);
+  PrepareReleaseHandler(group);
+
+  auto* leAudioDevice = group->GetFirstDevice();
+  LeAudioDevice* lastDevice;
+  LeAudioDevice* fistDevice = leAudioDevice;
+
+  auto expected_devices_written = 0;
+  while (leAudioDevice) {
+    /* Three Writes:
+     * 1: Codec Config
+     * 2: Codec QoS
+     * 3: Enabling
+     */
+    lastDevice = leAudioDevice;
+    EXPECT_CALL(gatt_queue,
+                WriteCharacteristic(leAudioDevice->conn_id_,
+                                    leAudioDevice->ctp_hdls_.val_hdl, _,
+                                    GATT_WRITE_NO_RSP, _, _))
+        .Times(AtLeast(3));
+    expected_devices_written++;
+    leAudioDevice = group->GetNextDevice(leAudioDevice);
+  }
+  ASSERT_EQ(expected_devices_written, num_devices);
+
+  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2);
+
+  InjectInitialIdleNotification(group);
+
+  // Start the configuration and stream Media content
+  LeAudioGroupStateMachine::Get()->StartStream(
+      group, context_type,
+      {.sink = types::AudioContexts(context_type),
+       .source = types::AudioContexts(context_type)},
+      {.sink = std::vector<uint8_t>(1, media_ccid),
+       .source = std::vector<uint8_t>(1, media_ccid)});
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+  testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_);
+
+  // Inject CIS and ACL disconnection of first device
+  InjectCisDisconnected(group, lastDevice, HCI_ERR_CONNECTION_TOUT);
+
+  // Check if group keeps streaming
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+
+  // Make sure ASE with disconnected CIS are not left in STREAMING
+  ASSERT_EQ(lastDevice->GetFirstAseWithState(
+                ::le_audio::types::kLeAudioDirectionSink,
+                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING),
+            nullptr);
+  ASSERT_EQ(lastDevice->GetFirstAseWithState(
+                ::le_audio::types::kLeAudioDirectionSource,
+                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING),
+            nullptr);
+
+  EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_,
+                                              lastDevice->ctp_hdls_.val_hdl, _,
+                                              GATT_WRITE_NO_RSP, _, _))
+      .Times(1);
+
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1);
+
+  for (auto& ase : lastDevice->ases_) {
+    if (cached_remote_qos_configuration_for_ase_.count(&ase) > 0) {
+      InjectAseStateNotification(
+          &ase, lastDevice, group, ascs::kAseStateQoSConfigured,
+          &(cached_remote_qos_configuration_for_ase_[&ase]));
+    }
+  }
+
+  // Check if group keeps streaming
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+
+  // Verify that the joining device receives the right CCID list
+  auto lastMeta = lastDevice->GetFirstActiveAse()->metadata;
+  bool parsedOk = false;
+  auto ltv = le_audio::types::LeAudioLtvMap::Parse(lastMeta.data(),
+                                                   lastMeta.size(), parsedOk);
+  ASSERT_TRUE(parsedOk);
+
+  auto ccids = ltv.Find(le_audio::types::kLeAudioMetadataTypeCcidList);
+  ASSERT_TRUE(ccids.has_value());
+  ASSERT_NE(std::find(ccids->begin(), ccids->end(), media_ccid), ccids->end());
+
+  /* Verify that ASE of first device are still good*/
+  auto ase = fistDevice->GetFirstActiveAse();
+  ASSERT_NE(ase->max_transport_latency, 0);
+  ASSERT_NE(ase->retrans_nb, 0);
+}
+
 TEST_F(StateMachineTest, testAttachDeviceToTheStreamDoNotAttach) {
   const auto context_type = kContextTypeMedia;
   const auto leaudio_group_id = 6;
@@ -4946,8 +5300,8 @@
   testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_);
 
   /* Separate CIS  for dual CIS device is treated as sink device */
-  ASSERT_EQ(group->stream_conf.sink_num_of_devices, 2);
-  ASSERT_EQ(group->stream_conf.sink_num_of_channels, 2);
+  ASSERT_EQ(group->stream_conf.stream_params.sink.num_of_devices, 2);
+  ASSERT_EQ(group->stream_conf.stream_params.sink.num_of_channels, 2);
 
   // Inject CIS and ACL disconnection of first device
   InjectAclDisconnected(group, firstDevice);
@@ -4955,8 +5309,8 @@
   InjectCisDisconnected(group, lastDevice, HCI_ERR_CONN_CAUSE_LOCAL_HOST);
   InjectAclDisconnected(group, lastDevice);
 
-  ASSERT_EQ(group->stream_conf.sink_num_of_devices, 0);
-  ASSERT_EQ(group->stream_conf.sink_num_of_channels, 0);
+  ASSERT_EQ(group->stream_conf.stream_params.sink.num_of_devices, 0);
+  ASSERT_EQ(group->stream_conf.stream_params.sink.num_of_channels, 0);
 }
 
 }  // namespace internal
diff --git a/system/bta/pan/bta_pan_act.cc b/system/bta/pan/bta_pan_act.cc
index 4ffce80..d78ac63 100644
--- a/system/bta/pan/bta_pan_act.cc
+++ b/system/bta/pan/bta_pan_act.cc
@@ -336,9 +336,9 @@
   tBTA_PAN bta_pan = {
       .set_role =
           {
-              .role = p_data->api_set_role.role,
               .status =
                   (status == PAN_SUCCESS) ? BTA_PAN_SUCCESS : BTA_PAN_FAIL,
+              .role = p_data->api_set_role.role,
           },
   };
 
diff --git a/system/bta/pan/bta_pan_api.cc b/system/bta/pan/bta_pan_api.cc
index 96ab709..a1c696a 100644
--- a/system/bta/pan/bta_pan_api.cc
+++ b/system/bta/pan/bta_pan_api.cc
@@ -116,9 +116,9 @@
                     {
                         .event = BTA_PAN_API_SET_ROLE_EVT,
                     },
-                .role = role,
                 .user_name = {},
                 .nap_name = {},
+                .role = role,
             },
     };
     if (role & BTA_PAN_ROLE_PANU) {
diff --git a/system/bta/sdp/bta_sdp_act.cc b/system/bta/sdp/bta_sdp_act.cc
index 79b63d5..b898051 100644
--- a/system/bta/sdp/bta_sdp_act.cc
+++ b/system/bta/sdp/bta_sdp_act.cc
@@ -54,15 +54,28 @@
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_MAP_SUPPORTED_FEATURES);
   if (p_attr != NULL) {
-    record->mns.supported_features = p_attr->attr_value.v.u32;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 4) {
+      record->mns.supported_features = p_attr->attr_value.v.u32;
+    } else {
+      LOG_ERROR("ATTR_ID_MAP_SUPPORTED_FEATURES attr type or size wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_MAP_SUPPORTED_FEATURES attr not found!!");
   }
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_SERVICE_NAME);
   if (p_attr != NULL) {
-    record->mns.hdr.service_name_length =
-        SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
-    record->mns.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == TEXT_STR_DESC_TYPE) {
+      record->mns.hdr.service_name_length =
+          SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+      record->mns.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr type not TEXT_STR_DESC_TYPE!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr not found!!");
   }
 
   if (get_legacy_stack_sdp_api()->record.SDP_FindProfileVersionInRec(
@@ -78,7 +91,14 @@
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_GOEP_L2CAP_PSM);
   if (p_attr != NULL) {
-    record->mns.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
+      record->mns.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+    } else {
+      LOG_ERROR("ATTR_ID_GOEP_L2CAP_PSM attr type or len wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_GOEP_L2CAP_PSM attr not found!!");
   }
 }
 
@@ -101,27 +121,54 @@
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_MAS_INSTANCE_ID);
   if (p_attr != NULL) {
-    record->mas.mas_instance_id = p_attr->attr_value.v.u8;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1) {
+      record->mas.mas_instance_id = p_attr->attr_value.v.u8;
+    } else {
+      LOG_ERROR("ATTR_ID_MAS_INSTANCE_ID attr type or len wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_MAS_INSTANCE_ID attr not found!!");
   }
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_SUPPORTED_MSG_TYPE);
   if (p_attr != NULL) {
-    record->mas.supported_message_types = p_attr->attr_value.v.u8;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1) {
+      record->mas.supported_message_types = p_attr->attr_value.v.u8;
+    } else {
+      LOG_ERROR("ATTR_ID_SUPPORTED_MSG_TYPE attr type or len wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_SUPPORTED_MSG_TYPE attr not found!!");
   }
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_MAP_SUPPORTED_FEATURES);
   if (p_attr != NULL) {
-    record->mas.supported_features = p_attr->attr_value.v.u32;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 4) {
+      record->mas.supported_features = p_attr->attr_value.v.u32;
+    } else {
+      LOG_ERROR("ATTR_ID_MAP_SUPPORTED_FEATURES attr type or len wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_MAP_SUPPORTED_FEATURES attr not found!!");
   }
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_SERVICE_NAME);
   if (p_attr != NULL) {
-    record->mas.hdr.service_name_length =
-        SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
-    record->mas.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == TEXT_STR_DESC_TYPE) {
+      record->mas.hdr.service_name_length =
+          SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+      record->mas.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr type wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr not found!!");
   }
 
   if (get_legacy_stack_sdp_api()->record.SDP_FindProfileVersionInRec(
@@ -137,7 +184,14 @@
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_GOEP_L2CAP_PSM);
   if (p_attr != NULL) {
-    record->mas.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
+      record->mas.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+    } else {
+      LOG_ERROR("ATTR_ID_GOEP_L2CAP_PSM attr type or len wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_GOEP_L2CAP_PSM attr not found!!");
   }
 }
 
@@ -159,20 +213,41 @@
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_SUPPORTED_REPOSITORIES);
   if (p_attr != NULL) {
-    record->pse.supported_repositories = p_attr->attr_value.v.u8;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1) {
+      record->pse.supported_repositories = p_attr->attr_value.v.u8;
+    } else {
+      LOG_ERROR("ATTR_ID_SUPPORTED_REPOSITORIES attr type or len wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_SUPPORTED_REPOSITORIES attr not found!!");
   }
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_PBAP_SUPPORTED_FEATURES);
   if (p_attr != NULL) {
-    record->pse.supported_features = p_attr->attr_value.v.u32;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 4) {
+      record->pse.supported_features = p_attr->attr_value.v.u32;
+    } else {
+      LOG_ERROR("ATTR_ID_PBAP_SUPPORTED_FEATURES attr type or len wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_PBAP_SUPPORTED_FEATURES attr not found!!");
   }
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_SERVICE_NAME);
   if (p_attr != NULL) {
-    record->pse.hdr.service_name_length =
-        SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
-    record->pse.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == TEXT_STR_DESC_TYPE) {
+      record->pse.hdr.service_name_length =
+          SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+      // TODO: validate the lifetime of this value
+      record->pse.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr type NOT string!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr not found!!");
   }
 
   if (get_legacy_stack_sdp_api()->record.SDP_FindProfileVersionInRec(
@@ -188,7 +263,14 @@
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_GOEP_L2CAP_PSM);
   if (p_attr != NULL) {
-    record->pse.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
+      record->pse.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+    } else {
+      LOG_ERROR("ATTR_ID_GOEP_L2CAP_PSM attr type or len wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_GOEP_L2CAP_PSM attr not found!!");
   }
 }
 
@@ -209,9 +291,15 @@
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_SERVICE_NAME);
   if (p_attr != NULL) {
-    record->ops.hdr.service_name_length =
-        SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
-    record->ops.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == TEXT_STR_DESC_TYPE) {
+      record->ops.hdr.service_name_length =
+          SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+      record->ops.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr type NOT string!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr not found!!");
   }
 
   if (get_legacy_stack_sdp_api()->record.SDP_FindProfileVersionInRec(
@@ -227,8 +315,16 @@
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_GOEP_L2CAP_PSM);
   if (p_attr != NULL) {
-    record->ops.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
+      record->ops.hdr.l2cap_psm = p_attr->attr_value.v.u16;
+    } else {
+      LOG_ERROR("ATTR_ID_GOEP_L2CAP_PSM attr type or len wrong!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_GOEP_L2CAP_PSM attr not found!!");
   }
+
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_SUPPORTED_FORMATS_LIST);
   if (p_attr != NULL) {
@@ -249,7 +345,7 @@
       for (p_sattr = p_attr->attr_value.v.p_sub_attr; p_sattr != NULL;
            p_sattr = p_sattr->p_next_attr) {
         if ((SDP_DISC_ATTR_TYPE(p_sattr->attr_len_type) == UINT_DESC_TYPE) &&
-            (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) == 1)) {
+            (SDP_DISC_ATTR_LEN(p_sattr->attr_len_type) >= 1)) {
           if (count == sizeof(record->ops.supported_formats_list)) {
             APPL_TRACE_ERROR(
                 "%s() - supported_formats_list - count overflow - "
@@ -297,9 +393,15 @@
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_SERVICE_NAME);
   if (p_attr != NULL) {
-    record->sap.hdr.service_name_length =
-        SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
-    record->sap.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == TEXT_STR_DESC_TYPE) {
+      record->sap.hdr.service_name_length =
+          SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+      record->sap.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr type NOT string!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr not found!!");
   }
 
   if (get_legacy_stack_sdp_api()->record.SDP_FindProfileVersionInRec(
@@ -329,45 +431,81 @@
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_SPECIFICATION_ID);
-  if (p_attr != nullptr)
-    record->dip.spec_id = p_attr->attr_value.v.u16;
-  else
+  if (p_attr != nullptr) {
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
+      record->dip.spec_id = p_attr->attr_value.v.u16;
+    } else {
+      LOG_ERROR("ATTR_ID_SPECIFICATION_ID attr type or len wrong!!");
+    }
+  } else {
     APPL_TRACE_ERROR("%s() ATTR_ID_SPECIFICATION_ID not found", __func__);
+  }
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_VENDOR_ID);
-  if (p_attr != nullptr)
-    record->dip.vendor = p_attr->attr_value.v.u16;
-  else
+  if (p_attr != nullptr) {
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
+      record->dip.vendor = p_attr->attr_value.v.u16;
+    } else {
+      LOG_ERROR("ATTR_ID_VENDOR_ID attr type or len wrong!!");
+    }
+  } else {
     APPL_TRACE_ERROR("%s() ATTR_ID_VENDOR_ID not found", __func__);
+  }
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_VENDOR_ID_SOURCE);
-  if (p_attr != nullptr)
-    record->dip.vendor_id_source = p_attr->attr_value.v.u16;
-  else
+  if (p_attr != nullptr) {
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
+      record->dip.vendor_id_source = p_attr->attr_value.v.u16;
+    } else {
+      LOG_ERROR("ATTR_ID_VENDOR_ID_SOURCE attr type or len wrong!!");
+    }
+  } else {
     APPL_TRACE_ERROR("%s() ATTR_ID_VENDOR_ID_SOURCE not found", __func__);
+  }
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_PRODUCT_ID);
-  if (p_attr != nullptr)
-    record->dip.product = p_attr->attr_value.v.u16;
-  else
+  if (p_attr != nullptr) {
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
+      record->dip.product = p_attr->attr_value.v.u16;
+    } else {
+      LOG_ERROR("ATTR_ID_PRODUCT_ID attr type or len wrong!!");
+    }
+  } else {
     APPL_TRACE_ERROR("%s() ATTR_ID_PRODUCT_ID not found", __func__);
+  }
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_PRODUCT_VERSION);
-  if (p_attr != nullptr)
-    record->dip.version = p_attr->attr_value.v.u16;
-  else
+  if (p_attr != nullptr) {
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
+      record->dip.version = p_attr->attr_value.v.u16;
+    } else {
+      LOG_ERROR("ATTR_ID_PRODUCT_VERSION attr type or len wrong!!");
+    }
+  } else {
     APPL_TRACE_ERROR("%s() ATTR_ID_PRODUCT_VERSION not found", __func__);
+  }
 
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_PRIMARY_RECORD);
-  if (p_attr != nullptr)
-    record->dip.primary_record = !(!p_attr->attr_value.v.u8);
-  else
+  if (p_attr != nullptr) {
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == BOOLEAN_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1) {
+      record->dip.primary_record = !(!p_attr->attr_value.v.u8);
+    } else {
+      LOG_ERROR("ATTR_ID_PRIMARY_RECORD attr type or len wrong!!");
+    }
+  } else {
     APPL_TRACE_ERROR("%s() ATTR_ID_PRIMARY_RECORD not found", __func__);
+  }
 }
 
 static void bta_create_raw_sdp_record(bluetooth_sdp_record* record,
@@ -386,9 +524,15 @@
   p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
       p_rec, ATTR_ID_SERVICE_NAME);
   if (p_attr != NULL) {
-    record->pse.hdr.service_name_length =
-        SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
-    record->pse.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == TEXT_STR_DESC_TYPE) {
+      record->pse.hdr.service_name_length =
+          SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+      record->pse.hdr.service_name = (char*)p_attr->attr_value.v.array;
+    } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr type NOT string!!");
+    }
+  } else {
+      LOG_ERROR("ATTR_ID_SERVICE_NAME attr not found!!");
   }
 
   /* Try to extract an RFCOMM channel */
diff --git a/system/bta/sys/bta_sys.h b/system/bta/sys/bta_sys.h
index efb82bb..4098591 100644
--- a/system/bta/sys/bta_sys.h
+++ b/system/bta/sys/bta_sys.h
@@ -162,11 +162,16 @@
   }
 }
 
-/* conn callback for role / low power manager*/
-typedef void(tBTA_SYS_CONN_CBACK)(tBTA_SYS_CONN_STATUS status, uint8_t id,
-                                  uint8_t app_id, const RawAddress& peer_addr);
-
-/* conn callback for role / low power manager*/
+/* conn callback for power mode manager */
+typedef void(tBTA_SYS_CONN_CBACK)(tBTA_SYS_CONN_STATUS status,
+                                  const tBTA_SYS_ID id, uint8_t app_id,
+                                  const RawAddress& peer_addr);
+/* callback for role switch */
+typedef void(tBTA_SYS_ROLE_SWITCH_CBACK)(tBTA_SYS_CONN_STATUS status,
+                                         tHCI_ROLE new_role,
+                                         tHCI_STATUS hci_status,
+                                         const RawAddress& peer_addr);
+/* callback for sniff subrating updates */
 typedef void(tBTA_SYS_SSR_CFG_CBACK)(uint8_t id, uint8_t app_id,
                                      uint16_t latency, uint16_t tout);
 
@@ -237,7 +242,7 @@
 void bta_sys_chg_ssr_config(uint8_t id, uint8_t app_id, uint16_t max_latency,
                             uint16_t min_tout);
 
-void bta_sys_role_chg_register(tBTA_SYS_CONN_CBACK* p_cback);
+void bta_sys_role_chg_register(tBTA_SYS_ROLE_SWITCH_CBACK* p_cback);
 void bta_sys_notify_role_chg(const RawAddress& peer_addr, tHCI_ROLE new_role,
                              tHCI_STATUS hci_status);
 void bta_sys_collision_register(uint8_t bta_id, tBTA_SYS_CONN_CBACK* p_cback);
diff --git a/system/bta/sys/bta_sys_conn.cc b/system/bta/sys/bta_sys_conn.cc
index 28cab0a..58912ae 100644
--- a/system/bta/sys/bta_sys_conn.cc
+++ b/system/bta/sys/bta_sys_conn.cc
@@ -59,7 +59,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_sys_role_chg_register(tBTA_SYS_CONN_CBACK* p_cback) {
+void bta_sys_role_chg_register(tBTA_SYS_ROLE_SWITCH_CBACK* p_cback) {
   bta_sys_cb.p_role_cb = p_cback;
 }
 /*******************************************************************************
diff --git a/system/bta/sys/bta_sys_int.h b/system/bta/sys/bta_sys_int.h
index 7487f4d..65f0ab9 100644
--- a/system/bta/sys/bta_sys_int.h
+++ b/system/bta/sys/bta_sys_int.h
@@ -55,7 +55,8 @@
       ppm_cb; /* low power management callback registered by DM */
   tBTA_SYS_CONN_CBACK*
       p_sco_cb; /* SCO connection change callback registered by AV */
-  tBTA_SYS_CONN_CBACK* p_role_cb; /* role change callback registered by AV */
+  tBTA_SYS_ROLE_SWITCH_CBACK*
+      p_role_cb;                  /* role change callback registered by AV */
   tBTA_SYS_COLLISION colli_reg;   /* collision handling module */
 #if (BTA_EIR_CANNED_UUID_LIST != TRUE)
   tBTA_SYS_EIR_CBACK* eir_cb; /* add/remove UUID into EIR */
diff --git a/system/bta/sys/bta_sys_main.cc b/system/bta/sys/bta_sys_main.cc
index 0ba735a..bb57e1b 100644
--- a/system/bta/sys/bta_sys_main.cc
+++ b/system/bta/sys/bta_sys_main.cc
@@ -85,20 +85,19 @@
  *
  ******************************************************************************/
 static void bta_sys_event(BT_HDR_RIGID* p_msg) {
-  uint8_t id;
   bool freebuf = true;
 
   APPL_TRACE_EVENT("%s: Event 0x%x", __func__, p_msg->event);
 
   /* get subsystem id from event */
-  id = (uint8_t)(p_msg->event >> 8);
+  uint8_t id = (uint8_t)(p_msg->event >> 8);
 
   /* verify id and call subsystem event handler */
   if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL)) {
     freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg);
   } else {
-    LOG_INFO("Ignoring receipt of unregistered event id:%s",
-             BtaIdSysText(id).c_str());
+    LOG_INFO("Ignoring receipt of unregistered event id:%s[%hhu]",
+             BtaIdSysText(static_cast<tBTA_SYS_ID>(id)).c_str(), id);
   }
 
   if (freebuf) {
diff --git a/system/bta/test/bta_ag_sco_test.cc b/system/bta/test/bta_ag_sco_test.cc
index 2f5a090..136ebba 100644
--- a/system/bta/test/bta_ag_sco_test.cc
+++ b/system/bta/test/bta_ag_sco_test.cc
@@ -56,10 +56,10 @@
   const auto [feature, peer_feature, is_local] = GetParam();
   tBTA_AG_SCB scb{
       .peer_addr = kRawAddress,
-      .inuse_codec = UUID_CODEC_CVSD,
       .features = feature,
       .peer_features = peer_feature,
       .sco_idx = BTM_INVALID_SCO_INDEX,
+      .inuse_codec = UUID_CODEC_CVSD,
   };
 
   this->codec = ESCO_CODEC_UNKNOWN;
@@ -78,10 +78,10 @@
   const auto [feature, peer_feature, is_local] = GetParam();
   tBTA_AG_SCB scb{
       .peer_addr = kRawAddress,
-      .inuse_codec = UUID_CODEC_CVSD,
       .features = feature,
       .peer_features = peer_feature,
       .sco_idx = BTM_INVALID_SCO_INDEX,
+      .inuse_codec = UUID_CODEC_CVSD,
   };
 
   this->codec = ESCO_CODEC_UNKNOWN;
diff --git a/system/bta/test/bta_dip_test.cc b/system/bta/test/bta_dip_test.cc
index a9be7d7..8e92d27 100644
--- a/system/bta/test/bta_dip_test.cc
+++ b/system/bta/test/bta_dip_test.cc
@@ -84,7 +84,7 @@
 
     g_attr_vendor_product_primary_record.p_next_attr = &g_attr_vendor_product_primary_record;
     g_attr_vendor_product_primary_record.attr_id = ATTR_ID_PRIMARY_RECORD;
-    g_attr_vendor_product_primary_record.attr_len_type = (BOOLEAN_DESC_TYPE<<12);
+    g_attr_vendor_product_primary_record.attr_len_type = (BOOLEAN_DESC_TYPE<<12)|1;
     g_attr_vendor_product_primary_record.attr_value.v.u8 = 1;
 
     g_rec.p_first_attr = &g_attr_service_class_id_list;
@@ -126,6 +126,76 @@
   ASSERT_EQ(record.dip.primary_record, true);
 }
 
+// test for b/263958603
+TEST_F(BtaDipTest, test_invalid_type_checks) {
+  bluetooth_sdp_record record{};
+
+  // here we provide the wrong types of records
+  // and verify that the provided values are not accepted
+  g_attr_spec_id.attr_len_type = (BOOLEAN_DESC_TYPE<<12)|1;
+  g_attr_spec_id.attr_value.v.u16 = 0x0103;
+
+  g_attr_vendor_id.attr_len_type = (BOOLEAN_DESC_TYPE<<12)|2;
+  g_attr_vendor_id.attr_value.v.u16 = 0x18d1;
+
+  g_attr_vendor_id_src.attr_len_type = (BOOLEAN_DESC_TYPE<<12)|2;
+  g_attr_vendor_id_src.attr_value.v.u16 = 1;
+
+  g_attr_vendor_product_id.attr_len_type = (BOOLEAN_DESC_TYPE<<12)|2;
+  g_attr_vendor_product_id.attr_value.v.u16 = 0x1234;
+
+  g_attr_vendor_product_version.attr_len_type = (BOOLEAN_DESC_TYPE<<12)|2;
+  g_attr_vendor_product_version.attr_value.v.u16 = 0x0100;
+
+  g_attr_vendor_product_primary_record.attr_len_type = (UINT_DESC_TYPE<<12)|1;
+  g_attr_vendor_product_primary_record.attr_value.v.u8 = 1;
+
+  bluetooth::testing::bta_create_dip_sdp_record(&record, &g_rec);
+
+  ASSERT_EQ(record.dip.spec_id, 0);
+  ASSERT_EQ(record.dip.vendor, 0);
+  ASSERT_EQ(record.dip.vendor_id_source, 0);
+  ASSERT_EQ(record.dip.product, 0);
+  ASSERT_EQ(record.dip.version, 0);
+  ASSERT_EQ(record.dip.primary_record, false);
+}
+
+// test for b/263958603
+TEST_F(BtaDipTest, test_invalid_size_checks) {
+  bluetooth_sdp_record record{};
+
+  // here we provide the wrong sizes of records
+  // and verify that the provided values are not accepted
+  g_attr_spec_id.attr_len_type = (UINT_DESC_TYPE<<12)|1;
+  g_attr_spec_id.attr_value.v.u16 = 0x0103;
+
+  g_attr_vendor_id.attr_len_type = (UINT_DESC_TYPE<<12)|1;
+  g_attr_vendor_id.attr_value.v.u16 = 0x18d1;
+
+  g_attr_vendor_id_src.attr_len_type = (UINT_DESC_TYPE<<12)|1;
+  g_attr_vendor_id_src.attr_value.v.u16 = 1;
+
+  g_attr_vendor_product_id.attr_len_type = (UINT_DESC_TYPE<<12)|1;
+  g_attr_vendor_product_id.attr_value.v.u16 = 0x1234;
+
+  g_attr_vendor_product_version.attr_len_type = (UINT_DESC_TYPE<<12)|1;
+  g_attr_vendor_product_version.attr_value.v.u16 = 0x0100;
+
+  // size greater than 1 is accepted
+  g_attr_vendor_product_primary_record.attr_len_type = (BOOLEAN_DESC_TYPE<<12)|2;
+  g_attr_vendor_product_primary_record.attr_value.v.u8 = 1;
+
+  bluetooth::testing::bta_create_dip_sdp_record(&record, &g_rec);
+
+  ASSERT_EQ(record.dip.spec_id, 0);
+  ASSERT_EQ(record.dip.vendor, 0);
+  ASSERT_EQ(record.dip.vendor_id_source, 0);
+  ASSERT_EQ(record.dip.product, 0);
+  ASSERT_EQ(record.dip.version, 0);
+  ASSERT_EQ(record.dip.primary_record, true);
+}
+
+
 TEST_F(BtaDipTest, test_bta_sdp_search_cback) {
   Uuid* userdata = (Uuid*)malloc(sizeof(Uuid));
 
diff --git a/system/bta/test/bta_dm_test.cc b/system/bta/test/bta_dm_test.cc
index f40bb81..82c06ec 100644
--- a/system/bta/test/bta_dm_test.cc
+++ b/system/bta/test/bta_dm_test.cc
@@ -378,8 +378,8 @@
 
 TEST_F(BtaDmTest, bta_dm_remname_cback__typical) {
   bta_dm_search_cb = {
-      .name_discover_done = false,
       .peer_bdaddr = kRawAddress,
+      .name_discover_done = false,
   };
 
   tBTM_REMOTE_DEV_NAME name = {
@@ -407,8 +407,8 @@
 
 TEST_F(BtaDmTest, bta_dm_remname_cback__wrong_address) {
   bta_dm_search_cb = {
-      .name_discover_done = false,
       .peer_bdaddr = kRawAddress,
+      .name_discover_done = false,
   };
 
   tBTM_REMOTE_DEV_NAME name = {
@@ -436,8 +436,8 @@
 
 TEST_F(BtaDmTest, bta_dm_remname_cback__HCI_ERR_CONNECTION_EXISTS) {
   bta_dm_search_cb = {
-      .name_discover_done = false,
       .peer_bdaddr = kRawAddress,
+      .name_discover_done = false,
   };
 
   tBTM_REMOTE_DEV_NAME name = {
diff --git a/system/bta/test/bta_gatt_test.cc b/system/bta/test/bta_gatt_test.cc
index 5c2c73d..97181b2 100644
--- a/system/bta/test/bta_gatt_test.cc
+++ b/system/bta/test/bta_gatt_test.cc
@@ -121,9 +121,9 @@
               .conn_id = 1,
               .handle = 2,
               .offset = 3,
+              .len = 4,  // length of value below
               .auth_req = GATT_AUTH_REQ_NONE,
               .value = {10, 11, 12, 13},
-              .len = 4,  // length of value above
           },
   };
 
@@ -133,10 +133,10 @@
   tBTA_GATTC_DATA command_queue;
 
   tBTA_GATTC_CLCB client_channel_control_block = {
-      .p_q_cmd = &command_queue,
+      .bta_conn_id = 456,
       .p_rcb = &app_control_block,
       .p_srcb = &service_control_block,
-      .bta_conn_id = 456,
+      .p_q_cmd = &command_queue,
   };
 };
 
diff --git a/system/bta/test/common/mock_controller.cc b/system/bta/test/common/mock_controller.cc
index 7cf2865..1aeb552 100644
--- a/system/bta/test/common/mock_controller.cc
+++ b/system/bta/test/common/mock_controller.cc
@@ -57,6 +57,11 @@
       ->SupportsBleConnectedIsochronousStreamPeripheral();
 }
 
+bool supports_configure_data_path(void) {
+  LOG_ASSERT(controller_interface) << "Mock controller not set!";
+  return controller_interface->SupportsConfigureDataPath();
+}
+
 const controller_t* controller_get_interface() {
   static controller_t* controller_instance = new controller_t();
 
@@ -69,6 +74,8 @@
       &supports_ble_connected_isochronous_stream_central;
   controller_instance->supports_ble_connected_isochronous_stream_peripheral =
       &supports_ble_connected_isochronous_stream_peripheral;
+  controller_instance->supports_configure_data_path =
+      &supports_configure_data_path;
 
   return controller_instance;
 }
diff --git a/system/bta/test/common/mock_controller.h b/system/bta/test/common/mock_controller.h
index 6d27a18..39f4d15 100644
--- a/system/bta/test/common/mock_controller.h
+++ b/system/bta/test/common/mock_controller.h
@@ -30,6 +30,7 @@
   virtual bool SupportsBleConnectedIsochronousStreamPeripheral(void) = 0;
   virtual bool SupportsBleIsochronousBroadcaster(void) = 0;
   virtual bool SupportsBle2mPhy(void) = 0;
+  virtual bool SupportsConfigureDataPath(void) = 0;
 
   virtual ~ControllerInterface() = default;
 };
@@ -44,6 +45,7 @@
               (override));
   MOCK_METHOD((bool), SupportsBleIsochronousBroadcaster, (), (override));
   MOCK_METHOD((bool), SupportsBle2mPhy, (), (override));
+  MOCK_METHOD((bool), SupportsConfigureDataPath, (), (override));
 };
 
 void SetMockControllerInterface(
diff --git a/system/bta/vc/vc.cc b/system/bta/vc/vc.cc
index 93ba834..fc0e629 100644
--- a/system/bta/vc/vc.cc
+++ b/system/bta/vc/vc.cc
@@ -276,6 +276,11 @@
       return;
     }
 
+    if (!device->IsEncryptionEnabled()) {
+      LOG_WARN("Device not yet bonded - waiting for encryption");
+      return;
+    }
+
     bool success = device->UpdateHandles();
     if (!success) {
       LOG(ERROR) << "Incomplete service database";
@@ -615,8 +620,8 @@
       return;
     }
 
-    LOG(INFO) << __func__
-              << "Successfully register for indications: " << loghex(handle);
+    LOG_INFO("Successfully registered on ccc: 0x%04x, device: %s", handle,
+             ADDRESS_TO_LOGGABLE_CSTR(device->address));
 
     verify_device_ready(device, handle);
   }
diff --git a/system/bta/vc/vc_test.cc b/system/bta/vc/vc_test.cc
index 4e3ccbe..eec16ae 100644
--- a/system/bta/vc/vc_test.cc
+++ b/system/bta/vc/vc_test.cc
@@ -432,8 +432,8 @@
 
   void GetDisconnectedEvent(const RawAddress& address, uint16_t conn_id) {
     tBTA_GATTC_CLOSE event_data = {
-        .status = GATT_SUCCESS,
         .conn_id = conn_id,
+        .status = GATT_SUCCESS,
         .client_if = gatt_if,
         .remote_bda = address,
         .reason = GATT_CONN_TERMINATE_PEER_USER,
@@ -444,8 +444,8 @@
 
   void GetSearchCompleteEvent(uint16_t conn_id) {
     tBTA_GATTC_SEARCH_CMPL event_data = {
-        .status = GATT_SUCCESS,
         .conn_id = conn_id,
+        .status = GATT_SUCCESS,
     };
 
     gatt_callback(BTA_GATTC_SEARCH_CMPL_EVT, (tBTA_GATTC*)&event_data);
@@ -747,6 +747,43 @@
   TestAppUnregister();
 }
 
+TEST_F(VolumeControlTest, test_service_discovery_completed_before_encryption) {
+  const RawAddress test_address = GetTestAddress(0);
+  SetSampleDatabaseVCS(1);
+  TestAppRegister();
+  TestConnect(test_address);
+
+  ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _))
+      .WillByDefault(DoAll(Return(false)));
+  ON_CALL(btm_interface, SetEncryption(test_address, _, _, _, _))
+      .WillByDefault(Return(BTM_SUCCESS));
+
+  EXPECT_CALL(*callbacks,
+              OnConnectionState(ConnectionState::CONNECTED, test_address))
+      .Times(0);
+  uint16_t conn_id = 1;
+  GetConnectedEvent(test_address, conn_id);
+  GetSearchCompleteEvent(conn_id);
+  Mock::VerifyAndClearExpectations(&btm_interface);
+  Mock::VerifyAndClearExpectations(callbacks.get());
+
+  EXPECT_CALL(*callbacks,
+              OnConnectionState(ConnectionState::CONNECTED, test_address))
+      .Times(1);
+
+  ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _))
+      .WillByDefault(DoAll(Return(true)));
+  EXPECT_CALL(gatt_interface, ServiceSearchRequest(_, _));
+
+  GetEncryptionCompleteEvt(test_address);
+  GetSearchCompleteEvent(conn_id);
+
+  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&gatt_interface);
+
+  TestAppUnregister();
+}
+
 TEST_F(VolumeControlTest, test_discovery_vcs_found) {
   const RawAddress test_address = GetTestAddress(0);
   SetSampleDatabaseVCS(1);
diff --git a/system/btcore/Android.bp b/system/btcore/Android.bp
index ba65af7..a946aa5 100644
--- a/system/btcore/Android.bp
+++ b/system/btcore/Android.bp
@@ -55,6 +55,7 @@
             cflags: ["-D_GNU_SOURCE"],
         },
     },
+    static_libs: ["libbt_shim_bridge"],
 }
 
 cc_library_static {
@@ -65,6 +66,7 @@
     ],
     defaults: ["libbtcore_defaults"],
     min_sdk_version: "Tiramisu",
+    static_libs: ["libbt_shim_bridge"],
 }
 
 cc_library_headers {
@@ -100,6 +102,7 @@
         "liblog",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbtcore",
         "libchrome",
         "libosi",
@@ -111,4 +114,5 @@
     sanitize: {
         address: true,
     },
+    header_libs: ["libbluetooth_headers"],
 }
diff --git a/system/btcore/fuzzer/Android.bp b/system/btcore/fuzzer/Android.bp
index 2687ff3..33f78e3 100644
--- a/system/btcore/fuzzer/Android.bp
+++ b/system/btcore/fuzzer/Android.bp
@@ -28,6 +28,7 @@
     shared_libs: [
         "android.hardware.bluetooth@1.0",
         "android.hardware.bluetooth@1.1",
+        "android.hardware.bluetooth.audio-V2-ndk",
         "libcrypto",
         "libstatslog_bt",
     ],
diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 6fec1ae..d1b18ef 100644
--- a/system/btif/Android.bp
+++ b/system/btif/Android.bp
@@ -28,7 +28,6 @@
     "packages/modules/Bluetooth/system/stack/include",
     "packages/modules/Bluetooth/system/stack/l2cap",
     "packages/modules/Bluetooth/system/udrv/include",
-    "packages/modules/Bluetooth/system/vnd/include",
     "system/libhwbinder/include",
 ]
 
@@ -130,6 +129,9 @@
     ],
     target: {
         android: {
+            whole_static_libs: [
+                "libPlatformProperties",
+            ],
             srcs: ["src/btif_avrcp_audio_track.cc"],
             shared_libs: ["libaaudio"],
         },
@@ -146,9 +148,12 @@
         "lib-bt-packets-avrcp",
         "lib-bt-packets-base",
         "libaudio-a2dp-hw-utils",
+        "libbluetooth-types",
         "libbt-audio-hal-interface",
+        "libbt-platform-protos-lite",
         "libbt-stack",
         "libbt-stack-core",
+        "libbt_shim_bridge",
         "libbtif-core",
     ],
     shared_libs: [
@@ -173,7 +178,6 @@
         "co/bta_dm_co.cc",
         "co/bta_gatts_co.cc",
         // BTIF implementation
-        "src/btif_activity_attribution.cc",
         "src/btif_ble_scanner.cc",
         "src/btif_bqr.cc",
         "src/btif_config.cc",
@@ -210,9 +214,6 @@
     ],
     target: {
         android: {
-            whole_static_libs: [
-                "libPlatformProperties",
-            ],
             srcs: ["src/btif_avrcp_audio_track.cc"],
             shared_libs: ["libaaudio"],
         },
@@ -228,7 +229,9 @@
         "lib-bt-packets-base",
         "libaudio-a2dp-hw-utils",
         "libbt-audio-hal-interface",
+        "libbt-platform-protos-lite",
         "libbt-stack-core",
+        "libbt_shim_bridge",
         "libstatslog_bt",
     ],
     shared_libs: [
@@ -244,6 +247,7 @@
     ],
     host_supported: true,
     min_sdk_version: "Tiramisu",
+    header_libs: ["libbluetooth_headers"],
 }
 
 // btif unit tests for target
@@ -295,6 +299,7 @@
         "libbt-sbc-encoder",
         "libbt-stack",
         "libbt-stack-core",
+        "libbt_shim_bridge",
         "libbtcore",
         "libbtdevice",
         "libbtif",
@@ -348,6 +353,8 @@
     static_libs: [
         "libbluetooth-types",
         "libbluetooth_gd",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libosi",
     ],
@@ -380,6 +387,8 @@
     ],
     static_libs: [
         "libbluetooth-types",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libosi",
     ],
@@ -418,6 +427,9 @@
         "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
     ],
@@ -461,6 +473,7 @@
     ],
     static_libs: [
         "libbluetooth-types",
+        "libbt_shim_bridge",
         "libc++fs",
         "libchrome",
         "libgmock",
@@ -497,6 +510,7 @@
     ],
     static_libs: [
         "libbluetooth-types",
+        "libbt_shim_bridge",
         "libchrome",
         "libcom.android.sysprop.bluetooth",
         "libosi",
@@ -534,7 +548,6 @@
         "packages/modules/Bluetooth/system/stack/include",
         "packages/modules/Bluetooth/system/stack/l2cap",
         "packages/modules/Bluetooth/system/udrv/include",
-        "packages/modules/Bluetooth/system/vnd/include",
         "system/libfmq/include",
         "system/libhwbinder/include",
     ],
@@ -600,10 +613,13 @@
         "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-audio-hal-interface",
+        "libbt-platform-protos-lite",
         "libbt-sbc-decoder",
         "libbt-sbc-encoder",
         "libbt-stack",
         "libbt-stack-core",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtdevice",
         "libbtif",
         "libbtif-core",
@@ -668,7 +684,6 @@
         "packages/modules/Bluetooth/system/stack/include",
         "packages/modules/Bluetooth/system/stack/l2cap",
         "packages/modules/Bluetooth/system/udrv/include",
-        "packages/modules/Bluetooth/system/vnd/include",
         "system/libfmq/include",
         "system/libhwbinder/include",
     ],
@@ -735,8 +750,11 @@
         "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-audio-hal-interface",
+        "libbt-platform-protos-lite",
         "libbt-sbc-decoder",
         "libbt-sbc-encoder",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtdevice",
         "libbtif",
         "libbtif-core",
diff --git a/system/btif/BUILD.gn b/system/btif/BUILD.gn
index 34af913..862e63a 100644
--- a/system/btif/BUILD.gn
+++ b/system/btif/BUILD.gn
@@ -42,7 +42,6 @@
     "src/btif_a2dp_control.cc",
     "src/btif_a2dp_sink.cc",
     "src/btif_a2dp_source.cc",
-    "src/btif_activity_attribution.cc",
     "src/btif_av.cc",
 
     # TODO(abps) - Move this abstraction elsewhere
diff --git a/system/btif/include/btif_activity_attribution.h b/system/btif/include/btif_activity_attribution.h
deleted file mode 100644
index 44b5855..0000000
--- a/system/btif/include/btif_activity_attribution.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright 2020 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 <hardware/bt_activity_attribution.h>
-
-namespace bluetooth {
-namespace activity_attribution {
-
-ActivityAttributionInterface* get_activity_attribution_instance();
-
-}
-}  // namespace bluetooth
diff --git a/system/btif/include/btif_bqr.h b/system/btif/include/btif_bqr.h
index 43d5943..374a387 100644
--- a/system/btif/include/btif_bqr.h
+++ b/system/btif/include/btif_bqr.h
@@ -108,7 +108,7 @@
 static constexpr uint16_t kLogDumpEventPerFile = 0x00FF;
 // Total length of all parameters of the link Quality related event except
 // Vendor Specific Parameters.
-static constexpr uint8_t kLinkQualityParamTotalLen = 55;
+static constexpr uint8_t kLinkQualityParamTotalLen = 48;
 // 7.8.116 LE Read ISO Link Quality command
 static constexpr uint8_t kISOLinkQualityParamTotalLen = 24;
 // Total length of all parameters of the ROOT_INFLAMMATION event except Vendor
@@ -117,6 +117,9 @@
 // Total length of all parameters of the Log Dump related event except Vendor
 // Specific Parameters.
 static constexpr uint8_t kLogDumpParamTotalLen = 3;
+// Remote address and calibration failure count parameters len
+// Added in BQR V5.0
+static constexpr uint8_t kVersion5_0ParamsTotalLen = 7;
 // Warning criteria of the RSSI value.
 static constexpr int8_t kCriWarnRssi = -80;
 // Warning criteria of the unused AFH channel count.
@@ -166,6 +169,9 @@
 static constexpr uint16_t kBqrIsoVersion = 0x101;
 // The version supports vendor quality and trace log starting v1.02(258)
 static constexpr uint16_t kBqrVndLogVersion = 0x102;
+// The version supports remote address info and calibration failure count
+// start from v1.03(259)
+static constexpr uint16_t kBqrVersion5_0 = 0x103;
 
 // Action definition
 //
diff --git a/system/btif/include/btif_sock_l2cap.h b/system/btif/include/btif_sock_l2cap.h
index 5aadeb8..d43d837 100644
--- a/system/btif/include/btif_sock_l2cap.h
+++ b/system/btif/include/btif_sock_l2cap.h
@@ -18,5 +18,6 @@
                                  int* sock_fd, int flags, int app_uid);
 void btsock_l2cap_signaled(int fd, int flags, uint32_t user_id);
 void on_l2cap_psm_assigned(int id, int psm);
+bt_status_t btsock_l2cap_disconnect(const RawAddress* bd_addr);
 
 #endif
diff --git a/system/btif/include/btif_sock_rfc.h b/system/btif/include/btif_sock_rfc.h
index dc75fe2..ae52e23 100644
--- a/system/btif/include/btif_sock_rfc.h
+++ b/system/btif/include/btif_sock_rfc.h
@@ -43,5 +43,6 @@
                                const bluetooth::Uuid* uuid, int channel,
                                int* sock_fd, int flags, int app_uid);
 void btsock_rfc_signaled(int fd, int flags, uint32_t user_id);
+bt_status_t btsock_rfc_disconnect(const RawAddress* bd_addr);
 
 #endif
diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc
index 6539cf0..176e886 100644
--- a/system/btif/src/bluetooth.cc
+++ b/system/btif/src/bluetooth.cc
@@ -65,7 +65,6 @@
 #include "btif/include/core_callbacks.h"
 #include "btif/include/stack_manager.h"
 #include "btif_a2dp.h"
-#include "btif_activity_attribution.h"
 #include "btif_api.h"
 #include "btif_av.h"
 #include "btif_bqr.h"
@@ -904,10 +903,6 @@
   if (is_profile(profile_id, BT_KEYSTORE_ID))
     return bluetooth::bluetooth_keystore::getBluetoothKeystoreInterface();
 
-  if (is_profile(profile_id, BT_ACTIVITY_ATTRIBUTION_ID)) {
-    return bluetooth::activity_attribution::get_activity_attribution_instance();
-  }
-
 #ifndef TARGET_FLOSS
   if (is_profile(profile_id, BT_PROFILE_LE_AUDIO_ID))
     return btif_le_audio_get_interface();
@@ -999,7 +994,6 @@
 
 static bt_os_callouts_t wakelock_os_callouts_jni = {
     sizeof(wakelock_os_callouts_jni),
-    nullptr /* not used */,
     acquire_wake_lock_cb,
     release_wake_lock_cb,
 };
diff --git a/system/btif/src/btif_activity_attribution.cc b/system/btif/src/btif_activity_attribution.cc
deleted file mode 100644
index 9f161cc..0000000
--- a/system/btif/src/btif_activity_attribution.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2020 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.
- *
- ******************************************************************************/
-
-#define LOG_TAG "bt_btif_activity_attribution"
-
-#include "btif_activity_attribution.h"
-#include "main/shim/activity_attribution.h"
-#include "main/shim/shim.h"
-
-using base::Bind;
-using base::Unretained;
-
-namespace bluetooth {
-namespace activity_attribution {
-
-ActivityAttributionInterface* get_activity_attribution_instance() {
-  return bluetooth::shim::get_activity_attribution_instance();
-}
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/btif/src/btif_bqr.cc b/system/btif/src/btif_bqr.cc
index d9fb6f6..2940496 100644
--- a/system/btif/src/btif_bqr.cc
+++ b/system/btif/src/btif_bqr.cc
@@ -54,7 +54,7 @@
 void BqrVseSubEvt::ParseBqrLinkQualityEvt(uint8_t length,
                                           const uint8_t* p_param_buf) {
   if (length < kLinkQualityParamTotalLen) {
-    LOG(ERROR) << __func__
+    LOG(FATAL) << __func__
                << ": Parameter total length: " << std::to_string(length)
                << " is abnormal. It shall be not shorter than: "
                << std::to_string(kLinkQualityParamTotalLen);
@@ -83,8 +83,27 @@
   STREAM_TO_UINT32(bqr_link_quality_event_.last_flow_on_timestamp, p_param_buf);
   STREAM_TO_UINT32(bqr_link_quality_event_.buffer_overflow_bytes, p_param_buf);
   STREAM_TO_UINT32(bqr_link_quality_event_.buffer_underflow_bytes, p_param_buf);
-  STREAM_TO_BDADDR(bqr_link_quality_event_.bdaddr, p_param_buf);
-  STREAM_TO_UINT8(bqr_link_quality_event_.cal_failed_item_count, p_param_buf);
+
+  if (vendor_cap_supported_version >= kBqrVersion5_0) {
+    if (length < kLinkQualityParamTotalLen + kISOLinkQualityParamTotalLen +
+                     kVersion5_0ParamsTotalLen) {
+      LOG(WARNING) << __func__
+                   << ": Parameter total length: " << std::to_string(length)
+                   << " is abnormal. "
+                   << "vendor_cap_supported_version: "
+                   << vendor_cap_supported_version << " "
+                   << " (>= "
+                   << "kBqrVersion5_0=" << kBqrVersion5_0 << "), "
+                   << "It should not be shorter than: "
+                   << std::to_string(kLinkQualityParamTotalLen +
+                                     kISOLinkQualityParamTotalLen +
+                                     kVersion5_0ParamsTotalLen);
+    } else {
+      STREAM_TO_BDADDR(bqr_link_quality_event_.bdaddr, p_param_buf);
+      STREAM_TO_UINT8(bqr_link_quality_event_.cal_failed_item_count,
+                      p_param_buf);
+    }
+  }
 
   if (vendor_cap_supported_version >= kBqrIsoVersion) {
     if (length < kLinkQualityParamTotalLen + kISOLinkQualityParamTotalLen) {
@@ -180,12 +199,13 @@
      << ", OverFlow: "
      << std::to_string(bqr_link_quality_event_.buffer_overflow_bytes)
      << ", UndFlow: "
-     << std::to_string(bqr_link_quality_event_.buffer_underflow_bytes)
-     << ", RemoteDevAddr: "
-     << bqr_link_quality_event_.bdaddr.ToColonSepHexString()
-     << ", CalFailedItems: "
-     << std::to_string(bqr_link_quality_event_.cal_failed_item_count);
-
+     << std::to_string(bqr_link_quality_event_.buffer_underflow_bytes);
+  if (vendor_cap_supported_version >= kBqrVersion5_0) {
+    ss << ", RemoteDevAddr: "
+       << bqr_link_quality_event_.bdaddr.ToColonSepHexString()
+       << ", CalFailedItems: "
+       << std::to_string(bqr_link_quality_event_.cal_failed_item_count);
+  }
   if (vendor_cap_supported_version >= kBqrIsoVersion) {
     ss << ", TxTotal: "
        << std::to_string(bqr_link_quality_event_.tx_total_packets)
@@ -505,7 +525,7 @@
     case QUALITY_REPORT_ID_LE_AUDIO_CHOPPY:
     case QUALITY_REPORT_ID_CONNECT_FAIL:
       if (length < kLinkQualityParamTotalLen) {
-        LOG(ERROR) << __func__
+        LOG(FATAL) << __func__
                    << ": Parameter total length: " << std::to_string(length)
                    << " is abnormal. It shall be not shorter than: "
                    << std::to_string(kLinkQualityParamTotalLen);
@@ -587,13 +607,20 @@
 
     if (bqrItf != NULL) {
       bd_addr = p_bqr_event->bqr_link_quality_event_.bdaddr;
+      if (bd_addr.IsEmpty()) {
+        tBTM_SEC_DEV_REC* dev = btm_find_dev_by_handle(
+            p_bqr_event->bqr_link_quality_event_.connection_handle);
+        if (dev != NULL) {
+          bd_addr = dev->RemoteAddress();
+        }
+      }
 
       if (!bd_addr.IsEmpty()) {
         bqrItf->bqr_delivery_event(bd_addr, (uint8_t*)p_link_quality_event,
                                    length);
       } else {
         LOG(WARNING) << __func__ << ": failed to deliver BQR, "
-                     << "bdaddr is empty, no address in packet";
+                     << "bdaddr is empty";
       }
     } else {
       LOG(WARNING) << __func__ << ": failed to deliver BQR, bqrItf is NULL";
@@ -752,6 +779,18 @@
     raw_data.insert(raw_data.begin(), bqr_raw_data,
                     bqr_raw_data + bqr_raw_data_len);
 
+    if (vendor_cap_supported_version < kBqrVersion5_0 &&
+        bqr_raw_data_len <
+            kLinkQualityParamTotalLen + kVersion5_0ParamsTotalLen) {
+      std::vector<uint8_t>::iterator it =
+          raw_data.begin() + kLinkQualityParamTotalLen;
+      /**
+       * Insert zeros as remote address and calibration count
+       * for BQR 5.0 incompatible devices
+       */
+      raw_data.insert(it, kVersion5_0ParamsTotalLen, 0);
+    }
+
     uint8_t lmp_ver = 0;
     uint16_t lmp_subver = 0;
     uint16_t manufacturer_id = 0;
diff --git a/system/btif/src/btif_gatt_client.cc b/system/btif/src/btif_gatt_client.cc
index a005469..c10a11a 100644
--- a/system/btif/src/btif_gatt_client.cc
+++ b/system/btif/src/btif_gatt_client.cc
@@ -54,7 +54,6 @@
 #include "types/bluetooth/uuid.h"
 #include "types/bt_transport.h"
 #include "types/raw_address.h"
-#include "vendor_api.h"
 
 using base::Bind;
 using base::Owned;
diff --git a/system/btif/src/btif_le_audio.cc b/system/btif/src/btif_le_audio.cc
index e219398..ed3e35c 100644
--- a/system/btif/src/btif_le_audio.cc
+++ b/system/btif/src/btif_le_audio.cc
@@ -133,7 +133,6 @@
       LOG_INFO("supported codec: %s", codec.ToString().c_str());
     }
 
-    LeAudioClient::InitializeAudioSetConfigurationProvider();
     do_in_main_thread(
         FROM_HERE, Bind(&LeAudioClient::Initialize, this,
                         jni_thread_wrapper(
@@ -160,12 +159,7 @@
 
     initialized = false;
 
-    do_in_main_thread(
-        FROM_HERE,
-        Bind(&LeAudioClient::Cleanup,
-             jni_thread_wrapper(
-                 FROM_HERE,
-                 Bind(&LeAudioClient::CleanupAudioSetConfigurationProvider))));
+    do_in_main_thread(FROM_HERE, Bind(&LeAudioClient::Cleanup));
   }
 
   void RemoveDevice(const RawAddress& address) override {
diff --git a/system/btif/src/btif_rc.cc b/system/btif/src/btif_rc.cc
index e27dfbc..a399a65 100644
--- a/system/btif/src/btif_rc.cc
+++ b/system/btif/src/btif_rc.cc
@@ -5484,8 +5484,8 @@
       clear_cmd_timeout(p_dev, lbl);
     }
     transaction_set->transaction[lbl] = {
-        .label = lbl,
         .in_use = false,
+        .label = lbl,
         .context =
             {
                 .label = MAX_LABEL,
diff --git a/system/btif/src/btif_sock.cc b/system/btif/src/btif_sock.cc
index 794cf05..63dfa49 100644
--- a/system/btif/src/btif_sock.cc
+++ b/system/btif/src/btif_sock.cc
@@ -64,6 +64,7 @@
                                       uint8_t break_signal_seq, bool fc);
 
 static void btsock_signaled(int fd, int type, int flags, uint32_t user_id);
+static bt_status_t btsock_disconnect_all(const RawAddress* bd_addr);
 
 static std::atomic_int thread_handle{-1};
 static thread_t* thread;
@@ -86,10 +87,9 @@
 
 const btsock_interface_t* btif_sock_get_interface(void) {
   static btsock_interface_t interface = {
-      sizeof(interface), btsock_listen,  /* listen */
-      btsock_connect,                    /* connect */
-      btsock_request_max_tx_data_length, /* request_max_tx_data_length */
-      btsock_control_req                 /* send_control_req */
+      sizeof(interface),  btsock_listen,
+      btsock_connect,     btsock_request_max_tx_data_length,
+      btsock_control_req, btsock_disconnect_all,
   };
 
   return &interface;
@@ -401,3 +401,20 @@
       break;
   }
 }
+
+static bt_status_t btsock_disconnect_all(const RawAddress* bd_addr) {
+  CHECK(bd_addr != NULL);
+
+  bt_status_t rfc_status = btsock_rfc_disconnect(bd_addr);
+  bt_status_t l2cap_status = btsock_l2cap_disconnect(bd_addr);
+  /* SCO is disconnected via btif_hf, so is not handled here. */
+
+  LOG_INFO("%s: rfc status: %d, l2cap status: %d", __func__, rfc_status,
+           l2cap_status);
+
+  /* Return error status, if any. */
+  if (rfc_status == BT_STATUS_SUCCESS) {
+    return l2cap_status;
+  }
+  return rfc_status;
+}
diff --git a/system/btif/src/btif_sock_l2cap.cc b/system/btif/src/btif_sock_l2cap.cc
index 0c25593..7ec549d 100644
--- a/system/btif/src/btif_sock_l2cap.cc
+++ b/system/btif/src/btif_sock_l2cap.cc
@@ -952,3 +952,21 @@
       btsock_l2cap_free_l(sock);
   }
 }
+
+bt_status_t btsock_l2cap_disconnect(const RawAddress* bd_addr) {
+  if (!bd_addr) return BT_STATUS_PARM_INVALID;
+  if (!is_inited()) return BT_STATUS_NOT_READY;
+
+  std::unique_lock<std::mutex> lock(state_lock);
+  l2cap_socket* sock = socks;
+
+  while (sock) {
+    l2cap_socket* next = sock->next;
+    if (sock->addr == *bd_addr) {
+      btsock_l2cap_free_l(sock);
+    }
+    sock = next;
+  }
+
+  return BT_STATUS_SUCCESS;
+}
diff --git a/system/btif/src/btif_sock_rfc.cc b/system/btif/src/btif_sock_rfc.cc
index 00643ee..a07bb1c 100644
--- a/system/btif/src/btif_sock_rfc.cc
+++ b/system/btif/src/btif_sock_rfc.cc
@@ -977,3 +977,17 @@
 
   return true;
 }
+
+bt_status_t btsock_rfc_disconnect(const RawAddress* bd_addr) {
+  CHECK(bd_addr != NULL);
+  if (!is_init_done()) return BT_STATUS_NOT_READY;
+
+  std::unique_lock<std::recursive_mutex> lock(slot_lock);
+  for (size_t i = 0; i < ARRAY_SIZE(rfc_slots); ++i) {
+    if (rfc_slots[i].id && rfc_slots[i].addr == *bd_addr) {
+      cleanup_rfc_slot(&rfc_slots[i]);
+    }
+  }
+
+  return BT_STATUS_SUCCESS;
+}
diff --git a/system/btif/test/btif_rc_test.cc b/system/btif/test/btif_rc_test.cc
index 9fc11f5..d986a25 100644
--- a/system/btif/test/btif_rc_test.cc
+++ b/system/btif/test/btif_rc_test.cc
@@ -318,8 +318,8 @@
       g_btrc_browse_connection_state_promise.get_future();
 
   tBTA_AV_RC_BROWSE_OPEN browse_data = {
-      .status = BTA_AV_SUCCESS,
       .rc_handle = 0,
+      .status = BTA_AV_SUCCESS,
   };
 
   btif_rc_cb.rc_multi_cb[0].rc_handle = 0;
diff --git a/system/build/Android.bp b/system/build/Android.bp
index bc5688a..78fa847 100644
--- a/system/build/Android.bp
+++ b/system/build/Android.bp
@@ -32,12 +32,4 @@
         },
     },
     defaults: ["fluoride_types_defaults_fuzzable"],
-    header_libs: ["libbluetooth_headers"],
-    static_libs: [
-        "libbluetooth-types",
-        "libbluetooth_rust_interop",
-        "libbt-platform-protos-lite",
-        "libbt_shim_bridge",
-        "libbt_shim_ffi",
-    ],
 }
diff --git a/system/common/Android.bp b/system/common/Android.bp
index f622c95..61632db 100644
--- a/system/common/Android.bp
+++ b/system/common/Android.bp
@@ -52,6 +52,11 @@
         "com.android.btservices",
     ],
     min_sdk_version: "Tiramisu",
+    header_libs: ["libbluetooth_headers"],
+    static_libs: [
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+    ],
 }
 
 cc_test {
@@ -94,7 +99,10 @@
         "liblog",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
         "libchrome",
         "libevent",
         "libgmock",
@@ -103,6 +111,7 @@
     sanitize: {
         cfi: false,
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -128,6 +137,7 @@
         "libgmock",
         "libosi",
     ],
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_benchmark {
@@ -150,4 +160,5 @@
         "libevent",
         "libosi",
     ],
+    header_libs: ["libbluetooth_headers"],
 }
diff --git a/system/device/Android.bp b/system/device/Android.bp
index 0a116d5..c418b83 100644
--- a/system/device/Android.bp
+++ b/system/device/Android.bp
@@ -32,6 +32,8 @@
         "com.android.btservices",
     ],
     min_sdk_version: "Tiramisu",
+    header_libs: ["libbluetooth_headers"],
+    static_libs: ["libbt_shim_bridge"],
 }
 
 // Bluetooth device unit tests for target
@@ -54,11 +56,14 @@
     static_libs: [
         "libbluetooth-types",
         "libbluetooth_gd",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtcore",
         "libbtdevice",
         "libchrome",
         "libosi",
     ],
+    header_libs: ["libbluetooth_headers"],
 }
 
 // Bluetooth device unit tests for target
@@ -87,9 +92,13 @@
     ],
     static_libs: [
         "libbluetooth-types",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtcore",
         "libbtdevice",
         "libchrome",
         "libgmock",
     ],
+    header_libs: ["libbluetooth_headers"],
 }
diff --git a/system/device/fuzzer/Android.bp b/system/device/fuzzer/Android.bp
index 1f5193d..085b184 100644
--- a/system/device/fuzzer/Android.bp
+++ b/system/device/fuzzer/Android.bp
@@ -36,6 +36,8 @@
     static_libs: [
         "libbluetooth-types",
         "libbluetooth_gd",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtcore",
         "libbtdevice",
         "libchrome",
diff --git a/system/embdrv/g722/Android.bp b/system/embdrv/g722/Android.bp
index 53a7b2b..a893aee 100644
--- a/system/embdrv/g722/Android.bp
+++ b/system/embdrv/g722/Android.bp
@@ -21,7 +21,6 @@
     ],
     host_supported: true,
     apex_available: [
-        "//apex_available:platform",
         "com.android.btservices",
     ],
     min_sdk_version: "Tiramisu",
diff --git a/system/embdrv/lc3/Android.bp b/system/embdrv/lc3/Android.bp
deleted file mode 100644
index 0f6aa61..0000000
--- a/system/embdrv/lc3/Android.bp
+++ /dev/null
@@ -1,84 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "system_bt_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["system_bt_license"],
-}
-
-cc_library_static {
-    name: "liblc3",
-    host_supported: true,
-    apex_available: [
-
-        "com.android.btservices",
-    ],
-    defaults: ["fluoride_defaults"],
-    srcs: [
-        "src/*.c",
-    ],
-    cflags: [
-        "-O3",
-        "-Wmissing-braces",
-        "-Wno-#warnings",
-        "-Wno-implicit-fallthrough",
-        "-Wno-self-assign",
-        "-Wuninitialized",
-        "-ffast-math",
-    ],
-    target: {
-        android: {
-            sanitize: {
-                misc_undefined: [
-                    "bounds",
-                    "signed-integer-overflow",
-                    "unsigned-integer-overflow",
-                ],
-                cfi: true,
-            },
-        },
-    },
-    export_include_dirs: [
-        "include",
-    ],
-    min_sdk_version: "Tiramisu",
-}
-
-cc_fuzz {
-    name: "liblc3_fuzzer",
-
-    srcs: [
-        "fuzzer/liblc3_fuzzer.cpp",
-    ],
-
-    static_libs: [
-        "liblc3",
-    ],
-}
-
-cc_binary {
-    name: "lc3_encoder",
-    host_supported: true,
-    srcs: [
-        "tools/elc3.c",
-        "tools/lc3bin.c",
-        "tools/wave.c",
-    ],
-    static_libs: [
-        "liblc3",
-    ],
-}
-
-cc_binary {
-    name: "lc3_decoder",
-    host_supported: true,
-    srcs: [
-        "tools/dlc3.c",
-        "tools/lc3bin.c",
-        "tools/wave.c",
-    ],
-    static_libs: [
-        "liblc3",
-    ],
-}
diff --git a/system/embdrv/lc3/BUILD.gn b/system/embdrv/lc3/BUILD.gn
deleted file mode 100644
index 73c5c74..0000000
--- a/system/embdrv/lc3/BUILD.gn
+++ /dev/null
@@ -1,46 +0,0 @@
-#
-#  Copyright 2023 Google, Inc.
-#
-#  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.
-#
-
-static_library("liblc3") {
-  cflags = [
-    "-O3",
-    "-ffast-math",
-    "-Wmissing-braces",
-    "-Wno-#warnings",
-    "-Wuninitialized",
-    "-Wno-self-assign",
-    "-Wno-implicit-fallthrough",
-  ]
-
-  sources = [
-    "src/attdet.c",
-    "src/bits.c",
-    "src/bwdet.c",
-    "src/energy.c",
-    "src/lc3.c",
-    "src/ltpf.c",
-    "src/mdct.c",
-    "src/plc.c",
-    "src/sns.c",
-    "src/spec.c",
-    "src/tables.c",
-    "src/tns.c",
-  ]
-
-  include_dirs = [
-    "include",
-  ]
-}
diff --git a/system/embdrv/lc3/README.md b/system/embdrv/lc3/README.md
deleted file mode 100644
index 5538d86..0000000
--- a/system/embdrv/lc3/README.md
+++ /dev/null
@@ -1,102 +0,0 @@
-# Low Complexity Communication Codec (LC3)
-
-The LC3 is an efficient low latency audio codec.
-
-[_Low Complexity Communication Codec_](https://www.bluetooth.org/DocMan/handlers/DownloadDoc.ashx?doc_id=502107&vId=542963)
-
-## Overview
-
-The directory layout is as follows :
-- include:      Library interface
-- src:          Source files
-- tools:        Standalone encoder/decoder tools
-- test:         Python implentation, used as reference for unit testing
-- build:        Building outputs
-- bin:          Compilation output
-
-## How to build
-
-The default toolchain used is GCC. Invoke `make` to build the library.
-
-```sh
-$ make -j
-```
-
-Compiled library `liblc3.a` will be found in `bin` directory.
-
-#### Cross compilation
-
-The cc, as, ld and ar can be selected with respective Makefile variables `CC`,
-`AS`, `LD` and `AR`. The `AS` and `LD` selections are optionnal, and fallback
-to `CC` selection when not defined.
-
-The `LIBC` must be set to `bionic` for android cross-compilation. This switch
-prevent link with `pthread` and `rt` libraries, that is included in the
-bionic libc.
-
-Following example build for android, using NDK toolset.
-
-```sh
-$ make -j CC=path_to_android_ndk_prebuilt/toolchain-prefix-clang LIBC=bionic
-```
-
-Compiled library will be found in `bin` directory.
-
-## Tools
-
-Tools can be all compiled, while involking `make` as follows :
-
-```sh
-$ make tools
-```
-
-The standalone encoder `elc3` take a `wave` file as input and encode it
-according given parameter. The LC3 binary file format used is the non
-standard format described by the reference encoder / decoder tools.
-The standalone decoder `dlc3` do the inverse operation.
-
-Refer to `elc3 -h` or `dlc3 -h` for options.
-
-Note that `elc3` output bitstream to standard output when output file is
-omitted. On the other side `dlc3` read from standard input when input output
-file are omitted.
-In such way you can easly test encoding / decoding loop with :
-
-```sh
-$ ./elc3 <in.wav> -b <bitrate> | ./dlc3 > <out.wav>
-```
-
-Adding Linux `aplay` tools, you will be able to instant hear the result :
-
-```sh
-$ ./elc3 <in.wav> -b <bitrate> | ./dlc3 | aplay
-```
-
-## Test
-
-A python implementation of the encoder is provided in `test` diretory.
-The C implementation is unitary validated against this implementation and
-intermediate values given in Appendix C of the specification.
-
-#### Prerequisite
-
-```sh
-# apt install python3 python3-dev python3-pip
-$ pip3 install scipy numpy
-```
-
-#### Running test suite
-
-```sh
-$ make test
-```
-
-
-## Conformance
-
-The proposed encoder and decoder implementation have been fully tested and
-validated.
-
-For more detail on conformance, refer to [_Bluetooth Conformance
-Documents and scripts_](https://www.bluetooth.com/specifications/specs/low-complexity-communication-codec-1-0/)
-
diff --git a/system/embdrv/lc3/fuzzer/liblc3_fuzzer.cpp b/system/embdrv/lc3/fuzzer/liblc3_fuzzer.cpp
deleted file mode 100644
index 88577bd..0000000
--- a/system/embdrv/lc3/fuzzer/liblc3_fuzzer.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2021 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 <fuzzer/FuzzedDataProvider.h>
-
-#include "include/lc3.h"
-
-void TestEncoder(FuzzedDataProvider& fdp) {
-  enum lc3_pcm_format pcm_format =
-      fdp.PickValueInArray({LC3_PCM_FORMAT_S16, LC3_PCM_FORMAT_S24});
-  int dt_us = fdp.PickValueInArray({10000, 7500});
-  int sr_hz =
-      fdp.PickValueInArray({8000, 16000, 24000, 32000, /*44100,*/ 48000});
-  unsigned enc_size = lc3_encoder_size(dt_us, sr_hz);
-  uint16_t output_byte_count = fdp.ConsumeIntegralInRange(20, 400);
-  uint16_t num_frames = lc3_frame_samples(dt_us, sr_hz);
-  uint8_t bytes_per_frame = (pcm_format == LC3_PCM_FORMAT_S16 ? 2 : 4);
-
-  if (fdp.remaining_bytes() < num_frames * bytes_per_frame) {
-    return;
-  }
-
-  std::vector<uint32_t> input_frames(
-      num_frames / (pcm_format == LC3_PCM_FORMAT_S16 ? 2 : 1));
-  fdp.ConsumeData(input_frames.data(), num_frames * bytes_per_frame);
-
-  void* lc3_encoder_mem = nullptr;
-  lc3_encoder_mem = malloc(enc_size);
-  lc3_encoder_t lc3_encoder =
-      lc3_setup_encoder(dt_us, sr_hz, 0, lc3_encoder_mem);
-
-  std::vector<uint8_t> output(output_byte_count);
-  lc3_encode(lc3_encoder, pcm_format, (const int16_t*)input_frames.data(), 1,
-             output.size(), output.data());
-
-  free(lc3_encoder_mem);
-  lc3_encoder_mem = nullptr;
-  return;
-}
-
-void TestDecoder(FuzzedDataProvider& fdp) {
-  enum lc3_pcm_format pcm_format =
-      fdp.PickValueInArray({LC3_PCM_FORMAT_S16, LC3_PCM_FORMAT_S24});
-  int dt_us = fdp.PickValueInArray({10000, 7500});
-  int sr_hz =
-      fdp.PickValueInArray({8000, 16000, 24000, 32000, /*44100,*/ 48000});
-  unsigned dec_size = lc3_decoder_size(dt_us, sr_hz);
-  uint16_t input_byte_count = fdp.ConsumeIntegralInRange(20, 400);
-  uint16_t num_frames = lc3_frame_samples(dt_us, sr_hz);
-
-  if (fdp.remaining_bytes() < input_byte_count) {
-    return;
-  }
-
-  std::vector<uint8_t> input(input_byte_count);
-  fdp.ConsumeData(input.data(), input.size());
-
-  void* lc3_decoder_mem = nullptr;
-  lc3_decoder_mem = malloc(dec_size);
-  lc3_decoder_t lc3_decoder =
-      lc3_setup_decoder(dt_us, sr_hz, 0, lc3_decoder_mem);
-
-  std::vector<uint32_t> output(num_frames /
-                               (pcm_format == LC3_PCM_FORMAT_S16 ? 2 : 1));
-  lc3_decode(lc3_decoder, input.data(), input.size(), pcm_format,
-             (int16_t*)output.data(), 1);
-
-  /* Empty input performs PLC (packet loss concealment) */
-  lc3_decode(lc3_decoder, nullptr, 0, pcm_format, (int16_t*)output.data(), 1);
-
-  free(lc3_decoder_mem);
-  lc3_decoder_mem = nullptr;
-  return;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  FuzzedDataProvider fdp(data, size);
-  TestEncoder(fdp);
-  TestDecoder(fdp);
-  return 0;
-}
\ No newline at end of file
diff --git a/system/embdrv/lc3/include/lc3.h b/system/embdrv/lc3/include/lc3.h
deleted file mode 100644
index f590639..0000000
--- a/system/embdrv/lc3/include/lc3.h
+++ /dev/null
@@ -1,305 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * Low Complexity Communication Codec (LC3)
- *
- * This implementation conforms to :
- *   Low Complexity Communication Codec (LC3)
- *   Bluetooth Specification v1.0
- *
- *
- * The LC3 is an efficient low latency audio codec.
- *
- * - Unlike most other codecs, the LC3 codec is focused on audio streaming
- *   in constrained (on packet sizes and interval) tranport layer.
- *   In this way, the LC3 does not handle :
- *   VBR (Variable Bitrate), based on input signal complexity
- *   ABR (Adaptative Bitrate). It does not rely on any bit reservoir,
- *       a frame will be strictly encoded in the bytes budget given by
- *       the user (or transport layer).
- *
- *   However, the bitrate (bytes budget for encoding a frame) can be
- *   freely changed at any time. But will not rely on signal complexity,
- *   it can follow a temporary bandwidth increase or reduction.
- *
- * - Unlike classic codecs, the LC3 codecs does not run on fixed amount
- *   of samples as input. It operates only on fixed frame duration, for
- *   any supported samplerates (8 to 48 KHz). Two frames duration are
- *   available 7.5ms and 10ms.
- *
- *
- * --- About 44.1 KHz samplerate ---
- *
- * The Bluetooth specification reference the 44.1 KHz samplerate, although
- * there is no support in the core algorithm of the codec of 44.1 KHz.
- * We can summarize the 44.1 KHz support by "you can put any samplerate
- * around the defined base samplerates". Please mind the following items :
- *
- *   1. The frame size will not be 7.5 ms or 10 ms, but is scaled
- *      by 'supported samplerate' / 'input samplerate'
- *
- *   2. The bandwidth will be hard limited (to 20 KHz) if you select 48 KHz.
- *      The encoded bandwidth will also be affected by the above inverse
- *      factor of 20 KHz.
- *
- * Applied to 44.1 KHz, we get :
- *
- *   1. About  8.16 ms frame duration, instead of 7.5 ms
- *      About 10.88 ms frame duration, instead of  10 ms
- *
- *   2. The bandwidth becomes limited to 18.375 KHz
- *
- *
- * --- How to encode / decode ---
- *
- * An encoder / decoder context needs to be setup. This context keeps states
- * on the current stream to proceed, and samples that overlapped across
- * frames.
- *
- * You have two ways to setup the encoder / decoder :
- *
- * - Using static memory allocation (this module does not rely on
- *   any dynamic memory allocation). The types `lc3_xxcoder_mem_16k_t`,
- *   and `lc3_xxcoder_mem_48k_t` have size of the memory needed for
- *   encoding up to 16 KHz or 48 KHz.
- *
- * - Using dynamic memory allocation. The `lc3_xxcoder_size()` procedure
- *   returns the needed memory size, for a given configuration. The memory
- *   space must be aligned to a pointer size. As an example, you can setup
- *   encoder like this :
- *
- *   | enc = lc3_setup_encoder(frame_us, samplerate,
- *   |      malloc(lc3_encoder_size(frame_us, samplerate)));
- *   | ...
- *   | free(enc);
- *
- *   Note :
- *   - A NULL memory adress as input, will return a NULL encoder context.
- *   - The returned encoder handle is set at the address of the allocated
- *     memory space, you can directly free the handle.
- *
- * Next, call the `lc3_encode()` encoding procedure, for each frames.
- * To handle multichannel streams (Stereo or more), you can proceed with
- * interleaved channels PCM stream like this :
- *
- *   | for(int ich = 0; ich < nch: ich++)
- *   |     lc3_encode(encoder[ich], pcm + ich, nch, ...);
- *
- *   with `nch` as the number of channels in the PCM stream
- *
- * ---
- *
- * Antoine SOULIER, Tempow / Google LLC
- *
- */
-
-#ifndef __LC3_H
-#define __LC3_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <stdint.h>
-#include <stdbool.h>
-
-#include "lc3_private.h"
-
-
-/**
- * Limitations
- * - On the bitrate, in bps, of a stream
- * - On the size of the frames in bytes
- */
-
-#define LC3_MIN_BITRATE    16000
-#define LC3_MAX_BITRATE   320000
-
-#define LC3_MIN_FRAME_BYTES   20
-#define LC3_MAX_FRAME_BYTES  400
-
-
-/**
- * Parameters check
- *   LC3_CHECK_DT_US(us)  True when frame duration in us is suitable
- *   LC3_CHECK_SR_HZ(sr)  True when samplerate in Hz is suitable
- */
-
-#define LC3_CHECK_DT_US(us) \
-    ( ((us) == 7500) || ((us) == 10000) )
-
-#define LC3_CHECK_SR_HZ(sr) \
-    ( ((sr) ==  8000) || ((sr) == 16000) || ((sr) == 24000) || \
-      ((sr) == 32000) || ((sr) == 48000)                       )
-
-
-/**
- * PCM Sample Format
- *   S16  Signed 16 bits, in 16 bits words (int16_t)
- *   S24  Signed 24 bits, using low three bytes of 32 bits words (int32_t).
- *        The high byte sign extends the sample value (bits 31..24 set to b23).
- */
-
-enum lc3_pcm_format {
-    LC3_PCM_FORMAT_S16,
-    LC3_PCM_FORMAT_S24,
-};
-
-
-/**
- * Handle
- */
-
-typedef struct lc3_encoder *lc3_encoder_t;
-typedef struct lc3_decoder *lc3_decoder_t;
-
-
-/**
- * Static memory of encoder context
- *
- * Propose types suitable for static memory allocation, supporting
- * any frame duration, and maximum samplerates 16k and 48k respectively
- * You can customize your type using the `LC3_ENCODER_MEM_T` or
- * `LC3_DECODER_MEM_T` macro.
- */
-
-typedef LC3_ENCODER_MEM_T(10000, 16000) lc3_encoder_mem_16k_t;
-typedef LC3_ENCODER_MEM_T(10000, 48000) lc3_encoder_mem_48k_t;
-
-typedef LC3_DECODER_MEM_T(10000, 16000) lc3_decoder_mem_16k_t;
-typedef LC3_DECODER_MEM_T(10000, 48000) lc3_decoder_mem_48k_t;
-
-
-/**
- * Return the number of PCM samples in a frame
- * dt_us           Frame duration in us, 7500 or 10000
- * sr_hz           Samplerate in Hz, 8000, 16000, 24000, 32000 or 48000
- * return          Number of PCM samples, -1 on bad parameters
- */
-int lc3_frame_samples(int dt_us, int sr_hz);
-
-/**
- * Return the size of frames, from bitrate
- * dt_us           Frame duration in us, 7500 or 10000
- * bitrate         Target bitrate in bit per second
- * return          The floor size in bytes of the frames, -1 on bad parameters
- */
-int lc3_frame_bytes(int dt_us, int bitrate);
-
-/**
- * Resolve the bitrate, from the size of frames
- * dt_us           Frame duration in us, 7500 or 10000
- * nbytes          Size in bytes of the frames
- * return          The according bitrate in bps, -1 on bad parameters
- */
-int lc3_resolve_bitrate(int dt_us, int nbytes);
-
-/**
- * Return algorithmic delay, as a number of samples
- * dt_us           Frame duration in us, 7500 or 10000
- * sr_hz           Samplerate in Hz, 8000, 16000, 24000, 32000 or 48000
- * return          Number of algorithmic delay samples, -1 on bad parameters
- */
-int lc3_delay_samples(int dt_us, int sr_hz);
-
-/**
- * Return size needed for an encoder
- * dt_us           Frame duration in us, 7500 or 10000
- * sr_hz           Samplerate in Hz, 8000, 16000, 24000, 32000 or 48000
- * return          Size of then encoder in bytes, 0 on bad parameters
- *
- * The `sr_hz` parameter is the samplerate of the PCM input stream,
- * and will match `sr_pcm_hz` of `lc3_setup_encoder()`.
- */
-unsigned lc3_encoder_size(int dt_us, int sr_hz);
-
-/**
- * Setup encoder
- * dt_us           Frame duration in us, 7500 or 10000
- * sr_hz           Samplerate in Hz, 8000, 16000, 24000, 32000 or 48000
- * sr_pcm_hz       Input samplerate, downsampling option of input, or 0
- * mem             Encoder memory space, aligned to pointer type
- * return          Encoder as an handle, NULL on bad parameters
- *
- * The `sr_pcm_hz` parameter is a downsampling option of PCM input,
- * the value `0` fallback to the samplerate of the encoded stream `sr_hz`.
- * When used, `sr_pcm_hz` is intended to be higher or equal to the encoder
- * samplerate `sr_hz`. The size of the context needed, given by
- * `lc3_encoder_size()` will be set accordingly to `sr_pcm_hz`.
- */
-lc3_encoder_t lc3_setup_encoder(
-    int dt_us, int sr_hz, int sr_pcm_hz, void *mem);
-
-/**
- * Encode a frame
- * encoder         Handle of the encoder
- * fmt             PCM input format
- * pcm, stride     Input PCM samples, and count between two consecutives
- * nbytes          Target size, in bytes, of the frame (20 to 400)
- * out             Output buffer of `nbytes` size
- * return          0: On success  -1: Wrong parameters
- */
-int lc3_encode(lc3_encoder_t encoder, enum lc3_pcm_format fmt,
-    const void *pcm, int stride, int nbytes, void *out);
-
-/**
- * Return size needed for an decoder
- * dt_us           Frame duration in us, 7500 or 10000
- * sr_hz           Samplerate in Hz, 8000, 16000, 24000, 32000 or 48000
- * return          Size of then decoder in bytes, 0 on bad parameters
- *
- * The `sr_hz` parameter is the samplerate of the PCM output stream,
- * and will match `sr_pcm_hz` of `lc3_setup_decoder()`.
- */
-unsigned lc3_decoder_size(int dt_us, int sr_hz);
-
-/**
- * Setup decoder
- * dt_us           Frame duration in us, 7500 or 10000
- * sr_hz           Samplerate in Hz, 8000, 16000, 24000, 32000 or 48000
- * sr_pcm_hz       Output samplerate, upsampling option of output (or 0)
- * mem             Decoder memory space, aligned to pointer type
- * return          Decoder as an handle, NULL on bad parameters
- *
- * The `sr_pcm_hz` parameter is an upsampling option of PCM output,
- * the value `0` fallback to the samplerate of the decoded stream `sr_hz`.
- * When used, `sr_pcm_hz` is intended to be higher or equal to the decoder
- * samplerate `sr_hz`. The size of the context needed, given by
- * `lc3_decoder_size()` will be set accordingly to `sr_pcm_hz`.
- */
-lc3_decoder_t lc3_setup_decoder(
-    int dt_us, int sr_hz, int sr_pcm_hz, void *mem);
-
-/**
- * Decode a frame
- * decoder         Handle of the decoder
- * in, nbytes      Input bitstream, and size in bytes, NULL performs PLC
- * fmt             PCM output format
- * pcm, stride     Output PCM samples, and count between two consecutives
- * return          0: On success  1: PLC operated  -1: Wrong parameters
- */
-int lc3_decode(lc3_decoder_t decoder, const void *in, int nbytes,
-    enum lc3_pcm_format fmt, void *pcm, int stride);
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LC3_H */
diff --git a/system/embdrv/lc3/include/lc3_private.h b/system/embdrv/lc3/include/lc3_private.h
deleted file mode 100644
index 467d028..0000000
--- a/system/embdrv/lc3/include/lc3_private.h
+++ /dev/null
@@ -1,162 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#ifndef __LC3_PRIVATE_H
-#define __LC3_PRIVATE_H
-
-#include <stdint.h>
-#include <stdbool.h>
-
-
-/**
- * Return number of samples, delayed samples and
- * encoded spectrum coefficients within a frame
- * - For encoding, keep 1.25 ms of temporal winodw
- * - For decoding, keep 18 ms of history, aligned on frames, and a frame
- */
-
-#define __LC3_NS(dt_us, sr_hz) \
-    ( (dt_us * sr_hz) / 1000 / 1000 )
-
-#define __LC3_ND(dt_us, sr_hz) \
-    ( (dt_us) == 7500 ? 23 * __LC3_NS(dt_us, sr_hz) / 30 \
-                      :  5 * __LC3_NS(dt_us, sr_hz) /  8 )
-
-#define __LC3_NT(sr_hz) \
-    ( (5 * sr_hz) / 4000 )
-
-#define __LC3_NH(dt_us, sr_hz) \
-    ( ((3 - ((dt_us) >= 10000)) + 1) * __LC3_NS(dt_us, sr_hz) )
-
-
-/**
- * Frame duration 7.5ms or 10ms
- */
-
-enum lc3_dt {
-    LC3_DT_7M5,
-    LC3_DT_10M,
-
-    LC3_NUM_DT
-};
-
-/**
- * Sampling frequency
- */
-
-enum lc3_srate {
-    LC3_SRATE_8K,
-    LC3_SRATE_16K,
-    LC3_SRATE_24K,
-    LC3_SRATE_32K,
-    LC3_SRATE_48K,
-
-    LC3_NUM_SRATE,
-};
-
-
-/**
- * Encoder state and memory
- */
-
-typedef struct lc3_attdet_analysis {
-    int32_t en1, an1;
-    int p_att;
-} lc3_attdet_analysis_t;
-
-struct lc3_ltpf_hp50_state {
-    int64_t s1, s2;
-};
-
-typedef struct lc3_ltpf_analysis {
-    bool active;
-    int pitch;
-    float nc[2];
-
-    struct lc3_ltpf_hp50_state hp50;
-    int16_t x_12k8[384];
-    int16_t x_6k4[178];
-    int tc;
-} lc3_ltpf_analysis_t;
-
-typedef struct lc3_spec_analysis {
-    float nbits_off;
-    int nbits_spare;
-} lc3_spec_analysis_t;
-
-struct lc3_encoder {
-    enum lc3_dt dt;
-    enum lc3_srate sr, sr_pcm;
-
-    lc3_attdet_analysis_t attdet;
-    lc3_ltpf_analysis_t ltpf;
-    lc3_spec_analysis_t spec;
-
-    int16_t *xt;
-    float *xs, *xd, s[0];
-};
-
-#define LC3_ENCODER_BUFFER_COUNT(dt_us, sr_hz) \
-    ( ( __LC3_NS(dt_us, sr_hz) + __LC3_NT(sr_hz) ) / 2 + \
-        __LC3_NS(dt_us, sr_hz) + __LC3_ND(dt_us, sr_hz) )
-
-#define LC3_ENCODER_MEM_T(dt_us, sr_hz) \
-    struct { \
-        struct lc3_encoder __e; \
-        float __s[LC3_ENCODER_BUFFER_COUNT(dt_us, sr_hz)]; \
-    }
-
-
-/**
- * Decoder state and memory
- */
-
-typedef struct lc3_ltpf_synthesis {
-    bool active;
-    int pitch;
-    float c[2*12], x[12];
-} lc3_ltpf_synthesis_t;
-
-typedef struct lc3_plc_state {
-    uint16_t seed;
-    int count;
-    float alpha;
-} lc3_plc_state_t;
-
-struct lc3_decoder {
-    enum lc3_dt dt;
-    enum lc3_srate sr, sr_pcm;
-
-    lc3_ltpf_synthesis_t ltpf;
-    lc3_plc_state_t plc;
-
-    float *xh, *xs, *xd, *xg, s[0];
-};
-
-#define LC3_DECODER_BUFFER_COUNT(dt_us, sr_hz) \
-    ( __LC3_NH(dt_us, sr_hz) + __LC3_ND(dt_us, sr_hz) + \
-      __LC3_NS(dt_us, sr_hz) )
-
-#define LC3_DECODER_MEM_T(dt_us, sr_hz) \
-    struct { \
-        struct lc3_decoder __d; \
-        float __s[LC3_DECODER_BUFFER_COUNT(dt_us, sr_hz)]; \
-    }
-
-
-#endif /* __LC3_PRIVATE_H */
diff --git a/system/embdrv/lc3/src/attdet.c b/system/embdrv/lc3/src/attdet.c
deleted file mode 100644
index 3d1528d..0000000
--- a/system/embdrv/lc3/src/attdet.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "attdet.h"
-
-
-/**
- * Time domain attack detector
- */
-bool lc3_attdet_run(enum lc3_dt dt, enum lc3_srate sr,
-    int nbytes, struct lc3_attdet_analysis *attdet, const int16_t *x)
-{
-    /* --- Check enabling --- */
-
-    const int nbytes_ranges[LC3_NUM_DT][LC3_NUM_SRATE - LC3_SRATE_32K][2] = {
-            [LC3_DT_7M5] = { { 61,     149 }, {  75,     149 } },
-            [LC3_DT_10M] = { { 81, INT_MAX }, { 100, INT_MAX } },
-    };
-
-    if (sr < LC3_SRATE_32K ||
-            nbytes < nbytes_ranges[dt][sr - LC3_SRATE_32K][0] ||
-            nbytes > nbytes_ranges[dt][sr - LC3_SRATE_32K][1]   )
-        return 0;
-
-    /* --- Filtering & Energy calculation --- */
-
-    int nblk = 4 - (dt == LC3_DT_7M5);
-    int32_t e[4];
-
-    for (int i = 0; i < nblk; i++) {
-        e[i] = 0;
-
-        if (sr == LC3_SRATE_32K) {
-            int16_t xn2 = (x[-4] + x[-3]) >> 1;
-            int16_t xn1 = (x[-2] + x[-1]) >> 1;
-            int16_t xn, xf;
-
-            for (int j = 0; j < 40; j++, x += 2, xn2 = xn1, xn1 = xn) {
-                xn = (x[0] + x[1]) >> 1;
-                xf = (3 * xn - 4 * xn1 + 1 * xn2) >> 3;
-                e[i] += (xf * xf) >> 5;
-            }
-        }
-
-        else {
-            int16_t xn2 = (x[-6] + x[-5] + x[-4]) >> 2;
-            int16_t xn1 = (x[-3] + x[-2] + x[-1]) >> 2;
-            int16_t xn, xf;
-
-            for (int j = 0; j < 40; j++, x += 3, xn2 = xn1, xn1 = xn) {
-                xn = (x[0] + x[1] + x[2]) >> 2;
-                xf = (3 * xn - 4 * xn1 + 1 * xn2) >> 3;
-                e[i] += (xf * xf) >> 5;
-            }
-        }
-    }
-
-    /* --- Attack detection ---
-     * The attack block `p_att` is defined as the normative value + 1,
-     * in such way, it will be initialized to 0 */
-
-    int p_att = 0;
-    int32_t a[4];
-
-    for (int i = 0; i < nblk; i++) {
-        a[i] = LC3_MAX(attdet->an1 >> 2, attdet->en1);
-        attdet->en1 = e[i], attdet->an1 = a[i];
-
-        if ((e[i] >> 3) > a[i] + (a[i] >> 4))
-            p_att = i + 1;
-    }
-
-    int att = attdet->p_att >= 1 + (nblk >> 1) || p_att > 0;
-    attdet->p_att = p_att;
-
-    return att;
-}
diff --git a/system/embdrv/lc3/src/attdet.h b/system/embdrv/lc3/src/attdet.h
deleted file mode 100644
index 14073bd..0000000
--- a/system/embdrv/lc3/src/attdet.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Time domain attack detector
- *
- * Reference : Low Complexity Communication Codec (LC3)
- *             Bluetooth Specification v1.0
- */
-
-#ifndef __LC3_ATTDET_H
-#define __LC3_ATTDET_H
-
-#include "common.h"
-
-
-/**
- * Time domain attack detector
- * dt, sr          Duration and samplerate of the frame
- * nbytes          Size in bytes of the frame
- * attdet          Context of the Attack Detector
- * x               [-6..-1] Previous, [0..ns-1] Current samples
- * return          1: Attack detected  0: Otherwise
- */
-bool lc3_attdet_run(enum lc3_dt dt, enum lc3_srate sr,
-    int nbytes, lc3_attdet_analysis_t *attdet, const int16_t *x);
-
-
-#endif /* __LC3_ATTDET_H */
diff --git a/system/embdrv/lc3/src/bits.c b/system/embdrv/lc3/src/bits.c
deleted file mode 100644
index 09b6da7..0000000
--- a/system/embdrv/lc3/src/bits.c
+++ /dev/null
@@ -1,375 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "bits.h"
-#include "common.h"
-
-
-/* ----------------------------------------------------------------------------
- *  Common
- * -------------------------------------------------------------------------- */
-
-static inline int ac_get(struct lc3_bits_buffer *);
-static inline void accu_load(struct lc3_bits_accu *, struct lc3_bits_buffer *);
-
-/**
- * Arithmetic coder return range bits
- * ac              Arithmetic coder
- * return          1 + log2(ac->range)
- */
-static int ac_get_range_bits(const struct lc3_bits_ac *ac)
-{
-    int nbits = 0;
-
-    for (unsigned r = ac->range; r; r >>= 1, nbits++);
-
-    return nbits;
-}
-
-/**
- * Arithmetic coder return pending bits
- * ac              Arithmetic coder
- * return          Pending bits
- */
-static int ac_get_pending_bits(const struct lc3_bits_ac *ac)
-{
-    return 26 - ac_get_range_bits(ac) +
-        ((ac->cache >= 0) + ac->carry_count) * 8;
-}
-
-/**
- * Return number of bits left in the bitstream
- * bits            Bitstream context
- * return          >= 0: Number of bits left  < 0: Overflow
- */
-static int get_bits_left(const struct lc3_bits *bits)
-{
-    const struct lc3_bits_buffer *buffer = &bits->buffer;
-    const struct lc3_bits_accu *accu = &bits->accu;
-    const struct lc3_bits_ac *ac = &bits->ac;
-
-    uintptr_t end = (uintptr_t)buffer->p_bw +
-        (bits->mode == LC3_BITS_MODE_READ ? LC3_ACCU_BITS/8 : 0);
-
-    uintptr_t start = (uintptr_t)buffer->p_fw -
-        (bits->mode == LC3_BITS_MODE_READ ? LC3_AC_BITS/8 : 0);
-
-    int n = end > start ? (int)(end - start) : -(int)(start - end);
-
-    return 8 * n - (accu->n + accu->nover + ac_get_pending_bits(ac));
-}
-
-/**
- * Setup bitstream writing
- */
-void lc3_setup_bits(struct lc3_bits *bits,
-    enum lc3_bits_mode mode, void *buffer, int len)
-{
-    *bits = (struct lc3_bits){
-        .mode = mode,
-        .accu = {
-            .n = mode == LC3_BITS_MODE_READ ? LC3_ACCU_BITS : 0,
-        },
-        .ac = {
-            .range = 0xffffff,
-            .cache = -1
-        },
-        .buffer = {
-            .start = (uint8_t *)buffer, .end  = (uint8_t *)buffer + len,
-            .p_fw  = (uint8_t *)buffer, .p_bw = (uint8_t *)buffer + len,
-        }
-    };
-
-    if (mode == LC3_BITS_MODE_READ) {
-        struct lc3_bits_ac *ac = &bits->ac;
-        struct lc3_bits_accu *accu = &bits->accu;
-        struct lc3_bits_buffer *buffer = &bits->buffer;
-
-        ac->low  = ac_get(buffer) << 16;
-        ac->low |= ac_get(buffer) <<  8;
-        ac->low |= ac_get(buffer);
-
-        accu_load(accu, buffer);
-    }
-}
-
-/**
- * Return number of bits left in the bitstream
- */
-int lc3_get_bits_left(const struct lc3_bits *bits)
-{
-    return LC3_MAX(get_bits_left(bits), 0);
-}
-
-/**
- * Return number of bits left in the bitstream
- */
-int lc3_check_bits(const struct lc3_bits *bits)
-{
-    const struct lc3_bits_ac *ac = &bits->ac;
-
-    return -(get_bits_left(bits) < 0 || ac->error);
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Writing
- * -------------------------------------------------------------------------- */
-
-/**
- * Flush the bits accumulator
- * accu            Bitstream accumulator
- * buffer          Bitstream buffer
- */
-static inline void accu_flush(
-    struct lc3_bits_accu *accu, struct lc3_bits_buffer *buffer)
-{
-    int nbytes = LC3_MIN(accu->n >> 3,
-        LC3_MAX(buffer->p_bw - buffer->p_fw, 0));
-
-    accu->n -= 8 * nbytes;
-
-    for ( ; nbytes; accu->v >>= 8, nbytes--)
-        *(--buffer->p_bw) = accu->v & 0xff;
-
-    if (accu->n >= 8)
-        accu->n = 0;
-}
-
-/**
- * Arithmetic coder put byte
- * buffer          Bitstream buffer
- * byte            Byte to output
- */
-static inline void ac_put(struct lc3_bits_buffer *buffer, int byte)
-{
-    if (buffer->p_fw < buffer->end)
-        *(buffer->p_fw++) = byte;
-}
-
-/**
- * Arithmetic coder range shift
- * ac              Arithmetic coder
- * buffer          Bitstream buffer
- */
-LC3_HOT static inline void ac_shift(
-    struct lc3_bits_ac *ac, struct lc3_bits_buffer *buffer)
-{
-    if (ac->low < 0xff0000 || ac->carry)
-    {
-        if (ac->cache >= 0)
-            ac_put(buffer, ac->cache + ac->carry);
-
-        for ( ; ac->carry_count > 0; ac->carry_count--)
-            ac_put(buffer, ac->carry ? 0x00 : 0xff);
-
-         ac->cache = ac->low >> 16;
-         ac->carry = 0;
-    }
-    else
-         ac->carry_count++;
-
-    ac->low = (ac->low << 8) & 0xffffff;
-}
-
-/**
- * Arithmetic coder termination
- * ac              Arithmetic coder
- * buffer          Bitstream buffer
- * end_val/nbits   End value and count of bits to terminate (1 to 8)
- */
-static void ac_terminate(struct lc3_bits_ac *ac,
-    struct lc3_bits_buffer *buffer)
-{
-    int nbits = 25 - ac_get_range_bits(ac);
-    unsigned mask = 0xffffff >> nbits;
-    unsigned val  = ac->low + mask;
-    unsigned high = ac->low + ac->range;
-
-    bool over_val  = val  >> 24;
-    bool over_high = high >> 24;
-
-    val  = (val  & 0xffffff) & ~mask;
-    high = (high & 0xffffff);
-
-    if (over_val == over_high) {
-
-        if (val + mask >= high) {
-            nbits++;
-            mask >>= 1;
-            val = ((ac->low + mask) & 0xffffff) & ~mask;
-        }
-
-        ac->carry |= val < ac->low;
-    }
-
-    ac->low = val;
-
-    for (; nbits > 8; nbits -= 8)
-        ac_shift(ac, buffer);
-    ac_shift(ac, buffer);
-
-    int end_val = ac->cache >> (8 - nbits);
-
-    if (ac->carry_count) {
-        ac_put(buffer, ac->cache);
-        for ( ; ac->carry_count > 1; ac->carry_count--)
-            ac_put(buffer, 0xff);
-
-        end_val = nbits < 8 ? 0 : 0xff;
-    }
-
-    if (buffer->p_fw < buffer->end) {
-        *buffer->p_fw &= 0xff >> nbits;
-        *buffer->p_fw |= end_val << (8 - nbits);
-    }
-}
-
-/**
- * Flush and terminate bitstream
- */
-void lc3_flush_bits(struct lc3_bits *bits)
-{
-    struct lc3_bits_ac *ac = &bits->ac;
-    struct lc3_bits_accu *accu = &bits->accu;
-    struct lc3_bits_buffer *buffer = &bits->buffer;
-
-    int nleft = buffer->p_bw - buffer->p_fw;
-    for (int n = 8 * nleft - accu->n; n > 0; n -= 32)
-        lc3_put_bits(bits, 0, LC3_MIN(n, 32));
-
-    accu_flush(accu, buffer);
-
-    ac_terminate(ac, buffer);
-}
-
-/**
- * Write from 1 to 32 bits,
- * exceeding the capacity of the accumulator
- */
-LC3_HOT void lc3_put_bits_generic(struct lc3_bits *bits, unsigned v, int n)
-{
-    struct lc3_bits_accu *accu = &bits->accu;
-
-    /* --- Fulfill accumulator and flush -- */
-
-    int n1 = LC3_MIN(LC3_ACCU_BITS - accu->n, n);
-    if (n1) {
-        accu->v |= v << accu->n;
-        accu->n = LC3_ACCU_BITS;
-    }
-
-    accu_flush(accu, &bits->buffer);
-
-    /* --- Accumulate remaining bits -- */
-
-    accu->v = v >> n1;
-    accu->n = n - n1;
-}
-
-/**
- * Arithmetic coder renormalization
- */
-LC3_HOT void lc3_ac_write_renorm(struct lc3_bits *bits)
-{
-    struct lc3_bits_ac *ac = &bits->ac;
-
-    for ( ; ac->range < 0x10000; ac->range <<= 8)
-        ac_shift(ac, &bits->buffer);
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Reading
- * -------------------------------------------------------------------------- */
-
-/**
- * Arithmetic coder get byte
- * buffer          Bitstream buffer
- * return          Byte read, 0 on overflow
- */
-static inline int ac_get(struct lc3_bits_buffer *buffer)
-{
-    return buffer->p_fw < buffer->end ? *(buffer->p_fw++) : 0;
-}
-
-/**
- * Load the accumulator
- * accu            Bitstream accumulator
- * buffer          Bitstream buffer
- */
-static inline void accu_load(struct lc3_bits_accu *accu,
-    struct lc3_bits_buffer *buffer)
-{
-    int nbytes = LC3_MIN(accu->n >> 3, buffer->p_bw - buffer->start);
-
-    accu->n -= 8 * nbytes;
-
-    for ( ; nbytes; nbytes--) {
-        accu->v >>= 8;
-        accu->v |= *(--buffer->p_bw) << (LC3_ACCU_BITS - 8);
-    }
-
-    if (accu->n >= 8) {
-        accu->nover = LC3_MIN(accu->nover + accu->n, LC3_ACCU_BITS);
-        accu->v >>= accu->n;
-        accu->n = 0;
-    }
-}
-
-/**
- * Read from 1 to 32 bits,
- * exceeding the capacity of the accumulator
- */
-LC3_HOT unsigned lc3_get_bits_generic(struct lc3_bits *bits, int n)
-{
-    struct lc3_bits_accu *accu = &bits->accu;
-    struct lc3_bits_buffer *buffer = &bits->buffer;
-
-    /* --- Fulfill accumulator and read -- */
-
-    accu_load(accu, buffer);
-
-    int n1 = LC3_MIN(LC3_ACCU_BITS - accu->n, n);
-    unsigned v = (accu->v >> accu->n) & ((1u << n1) - 1);
-    accu->n += n1;
-
-    /* --- Second round --- */
-
-    int n2 = n - n1;
-
-    if (n2) {
-        accu_load(accu, buffer);
-
-        v |= ((accu->v >> accu->n) & ((1u << n2) - 1)) << n1;
-        accu->n += n2;
-    }
-
-    return v;
-}
-
-/**
- * Arithmetic coder renormalization
- */
-LC3_HOT void lc3_ac_read_renorm(struct lc3_bits *bits)
-{
-    struct lc3_bits_ac *ac = &bits->ac;
-
-    for ( ; ac->range < 0x10000; ac->range <<= 8)
-        ac->low = ((ac->low << 8) | ac_get(&bits->buffer)) & 0xffffff;
-}
diff --git a/system/embdrv/lc3/src/bits.h b/system/embdrv/lc3/src/bits.h
deleted file mode 100644
index 5dd56cd..0000000
--- a/system/embdrv/lc3/src/bits.h
+++ /dev/null
@@ -1,315 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Bitstream management
- *
- * The bitstream is written by the 2 ends of the buffer :
- *
- * - Arthmetic coder put bits while increasing memory addresses
- *   in the buffer (forward)
- *
- * - Plain bits are puts starting the end of the buffer, with memeory
- *   addresses decreasing (backward)
- *
- *       .---------------------------------------------------.
- *       | > > > > > > > > > > :         : < < < < < < < < < |
- *       '---------------------------------------------------'
- *       |---------------------> - - - - - - - - - - - - - ->|
- *                              |< - - - <-------------------|
- *          Arithmetic coding                  Plain bits
- *          `lc3_put_symbol()`               `lc3_put_bits()`
- *
- * - The forward writing is protected against buffer overflow, it cannot
- *   write after the buffer, but can overwrite plain bits previously
- *   written in the buffer.
- *
- * - The backward writing is protected against overwrite of the arithmetic
- *   coder bitstream. In such way, the backward bitstream is always limited
- *   by the aritmetic coder bitstream, and can be overwritten by him.
- *
- *       .---------------------------------------------------.
- *       | > > > > > > > > > > :         : < < < < < < < < < |
- *       '---------------------------------------------------'
- *       |---------------------> - - - - - - - - - - - - - ->|
- *       |< - - - - - - - - - - -  - - - <-------------------|
- *          Arithmetic coding                  Plain bits
- *          `lc3_get_symbol()`               `lc3_get_bits()`
- *
- * - Reading is limited to read of the complementary end of the buffer.
- *
- * - The procedure `lc3_check_bits()` returns indication that read has been
- *   made crossing the other bit plane.
- *
- *
- * Reference : Low Complexity Communication Codec (LC3)
- *             Bluetooth Specification v1.0
- *
- */
-
-#ifndef __LC3_BITS_H
-#define __LC3_BITS_H
-
-#include "common.h"
-
-
-/**
- * Bitstream mode
- */
-
-enum lc3_bits_mode {
-    LC3_BITS_MODE_READ,
-    LC3_BITS_MODE_WRITE,
-};
-
-/**
- * Arithmetic coder symbol interval
- * The model split the interval in 17 symbols
- */
-
-struct lc3_ac_symbol {
-    uint16_t low   : 16;
-    uint16_t range : 16;
-};
-
-struct lc3_ac_model {
-    struct lc3_ac_symbol s[17];
-};
-
-/**
- * Bitstream context
- */
-
-#define LC3_ACCU_BITS (int)(8 * sizeof(unsigned))
-
-struct lc3_bits_accu {
-    unsigned v;
-    int n, nover;
-};
-
-#define LC3_AC_BITS (int)(24)
-
-struct lc3_bits_ac {
-    unsigned low, range;
-    int cache, carry, carry_count;
-    bool error;
-};
-
-struct lc3_bits_buffer {
-    const uint8_t *start, *end;
-    uint8_t *p_fw, *p_bw;
-};
-
-typedef struct lc3_bits {
-    enum lc3_bits_mode mode;
-    struct lc3_bits_ac ac;
-    struct lc3_bits_accu accu;
-    struct lc3_bits_buffer buffer;
-} lc3_bits_t;
-
-
-/**
- * Setup bitstream reading/writing
- * bits            Bitstream context
- * mode            Either READ or WRITE mode
- * buffer, len     Output buffer and length (in bytes)
- */
-void lc3_setup_bits(lc3_bits_t *bits,
-    enum lc3_bits_mode mode, void *buffer, int len);
-
-/**
- * Return number of bits left in the bitstream
- * bits            Bitstream context
- * return          Number of bits left
- */
-int lc3_get_bits_left(const lc3_bits_t *bits);
-
-/**
- * Check if error occured on bitstream reading/writing
- * bits            Bitstream context
- * return          0: Ok  -1: Bitstream overflow or AC reading error
- */
-int lc3_check_bits(const lc3_bits_t *bits);
-
-/**
- * Put a bit
- * bits            Bitstream context
- * v               Bit value, 0 or 1
- */
-static inline void lc3_put_bit(lc3_bits_t *bits, int v);
-
-/**
- * Put from 1 to 32 bits
- * bits            Bitstream context
- * v, n            Value, in range 0 to 2^n - 1, and bits count (1 to 32)
- */
-static inline void lc3_put_bits(lc3_bits_t *bits, unsigned v, int n);
-
-/**
- * Put arithmetic coder symbol
- * bits            Bitstream context
- * model, s        Model distribution and symbol value
- */
-static inline void lc3_put_symbol(lc3_bits_t *bits,
-    const struct lc3_ac_model *model, unsigned s);
-
-/**
- * Flush and terminate bitstream writing
- * bits            Bitstream context
- */
-void lc3_flush_bits(lc3_bits_t *bits);
-
-/**
- * Get a bit
- * bits            Bitstream context
- */
-static inline int lc3_get_bit(lc3_bits_t *bits);
-
-/**
- * Get from 1 to 32 bits
- * bits            Bitstream context
- * n               Number of bits to read (1 to 32)
- * return          The value read
- */
-static inline unsigned lc3_get_bits(lc3_bits_t *bits,  int n);
-
-/**
- * Get arithmetic coder symbol
- * bits            Bitstream context
- * model           Model distribution
- * return          The value read
- */
-static inline unsigned lc3_get_symbol(lc3_bits_t *bits,
-    const struct lc3_ac_model *model);
-
-
-
-/* ----------------------------------------------------------------------------
- *  Inline implementations
- * -------------------------------------------------------------------------- */
-
-void lc3_put_bits_generic(lc3_bits_t *bits, unsigned v, int n);
-unsigned lc3_get_bits_generic(struct lc3_bits *bits, int n);
-
-void lc3_ac_read_renorm(lc3_bits_t *bits);
-void lc3_ac_write_renorm(lc3_bits_t *bits);
-
-
-/**
- * Put a bit
- */
-LC3_HOT static inline void lc3_put_bit(lc3_bits_t *bits, int v)
-{
-    lc3_put_bits(bits, v, 1);
-}
-
-/**
- * Put from 1 to 32 bits
- */
-LC3_HOT static inline void lc3_put_bits(
-    struct lc3_bits *bits, unsigned v, int n)
-{
-    struct lc3_bits_accu *accu = &bits->accu;
-
-    if (accu->n + n <= LC3_ACCU_BITS) {
-        accu->v |= v << accu->n;
-        accu->n += n;
-    } else {
-        lc3_put_bits_generic(bits, v, n);
-    }
-}
-
-/**
- * Get a bit
- */
-LC3_HOT static inline int lc3_get_bit(lc3_bits_t *bits)
-{
-    return lc3_get_bits(bits, 1);
-}
-
-/**
- * Get from 1 to 32 bits
- */
-LC3_HOT static inline unsigned lc3_get_bits(struct lc3_bits *bits, int n)
-{
-    struct lc3_bits_accu *accu = &bits->accu;
-
-    if (accu->n + n <= LC3_ACCU_BITS) {
-        int v = (accu->v >> accu->n) & ((1u << n) - 1);
-        return (accu->n += n), v;
-    }
-    else {
-        return lc3_get_bits_generic(bits, n);
-    }
-}
-
-/**
- * Put arithmetic coder symbol
- */
-LC3_HOT static inline void lc3_put_symbol(
-    struct lc3_bits *bits, const struct lc3_ac_model *model, unsigned s)
-{
-    const struct lc3_ac_symbol *symbols = model->s;
-    struct lc3_bits_ac *ac = &bits->ac;
-    unsigned range = ac->range >> 10;
-
-    ac->low += range * symbols[s].low;
-    ac->range = range * symbols[s].range;
-
-    ac->carry |= ac->low >> 24;
-    ac->low &= 0xffffff;
-
-    if (ac->range < 0x10000)
-        lc3_ac_write_renorm(bits);
-}
-
-/**
- * Get arithmetic coder symbol
- */
-LC3_HOT static inline unsigned lc3_get_symbol(
-    lc3_bits_t *bits, const struct lc3_ac_model *model)
-{
-    const struct lc3_ac_symbol *symbols = model->s;
-    struct lc3_bits_ac *ac = &bits->ac;
-
-    unsigned range = (ac->range >> 10) & 0xffff;
-
-    ac->error |= (ac->low >= (range << 10));
-    if (ac->error)
-        ac->low = 0;
-
-    int s = 16;
-
-    if (ac->low < range * symbols[s].low) {
-        s >>= 1;
-        s -= ac->low < range * symbols[s].low ? 4 : -4;
-        s -= ac->low < range * symbols[s].low ? 2 : -2;
-        s -= ac->low < range * symbols[s].low ? 1 : -1;
-        s -= ac->low < range * symbols[s].low;
-    }
-
-    ac->low -= range * symbols[s].low;
-    ac->range = range * symbols[s].range;
-
-    if (ac->range < 0x10000)
-        lc3_ac_read_renorm(bits);
-
-    return s;
-}
-
-#endif /* __LC3_BITS_H */
diff --git a/system/embdrv/lc3/src/bwdet.c b/system/embdrv/lc3/src/bwdet.c
deleted file mode 100644
index 8dc0f5c..0000000
--- a/system/embdrv/lc3/src/bwdet.c
+++ /dev/null
@@ -1,129 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "bwdet.h"
-
-
-/**
- * Bandwidth detector
- */
-enum lc3_bandwidth lc3_bwdet_run(
-    enum lc3_dt dt, enum lc3_srate sr, const float *e)
-{
-    /* Bandwidth regions (Table 3.6)  */
-
-    struct region { int is : 8; int ie : 8; };
-
-    static const struct region bws_table[LC3_NUM_DT]
-            [LC3_NUM_BANDWIDTH-1][LC3_NUM_BANDWIDTH-1] = {
-
-        [LC3_DT_7M5] = {
-            { { 51, 63+1 } },
-            { { 45, 55+1 }, { 58, 63+1 } },
-            { { 42, 51+1 }, { 53, 58+1 }, { 60, 63+1 } },
-            { { 40, 48+1 }, { 51, 55+1 }, { 57, 60+1 }, { 61, 63+1 } },
-        },
-
-        [LC3_DT_10M] = {
-            { { 53, 63+1 } },
-            { { 47, 56+1 }, { 59, 63+1 } },
-            { { 44, 52+1 }, { 54, 59+1 }, { 60, 63+1 } },
-            { { 41, 49+1 }, { 51, 55+1 }, { 57, 60+1 }, { 61, 63+1 } },
-        },
-    };
-
-    static const int l_table[LC3_NUM_DT][LC3_NUM_BANDWIDTH-1] = {
-        [LC3_DT_7M5] = { 4, 4, 3, 2 },
-        [LC3_DT_10M] = { 4, 4, 3, 1 },
-    };
-
-    /* --- Stage 1 ---
-     * Determine bw0 candidate */
-
-    enum lc3_bandwidth bw0 = LC3_BANDWIDTH_NB;
-    enum lc3_bandwidth bwn = (enum lc3_bandwidth)sr;
-
-    if (bwn <= bw0)
-        return bwn;
-
-    const struct region *bwr = bws_table[dt][bwn-1];
-
-    for (enum lc3_bandwidth bw = bw0; bw < bwn; bw++) {
-        int i = bwr[bw].is, ie = bwr[bw].ie;
-        int n = ie - i;
-
-        float se = e[i];
-        for (i++; i < ie; i++)
-            se += e[i];
-
-        if (se >= (10 << (bw == LC3_BANDWIDTH_NB)) * n)
-            bw0 = bw + 1;
-    }
-
-    /* --- Stage 2 ---
-     * Detect drop above cut-off frequency.
-     * The Tc condition (13) is precalculated, as
-     * Tc[] = 10 ^ (n / 10) , n = { 15, 23, 20, 20 } */
-
-    int hold = bw0 >= bwn;
-
-    if (!hold) {
-        int i0 = bwr[bw0].is, l = l_table[dt][bw0];
-        float tc = (const float []){
-             31.62277660, 199.52623150, 100, 100 }[bw0];
-
-        for (int i = i0 - l + 1; !hold && i <= i0 + 1; i++) {
-            hold = e[i-l] > tc * e[i];
-        }
-
-    }
-
-    return hold ? bw0 : bwn;
-}
-
-/**
- * Return number of bits coding the bandwidth value
- */
-int lc3_bwdet_get_nbits(enum lc3_srate sr)
-{
-    return (sr > 0) + (sr > 1) + (sr > 3);
-}
-
-/**
- * Put bandwidth indication
- */
-void lc3_bwdet_put_bw(lc3_bits_t *bits,
-    enum lc3_srate sr, enum lc3_bandwidth bw)
-{
-    int nbits_bw = lc3_bwdet_get_nbits(sr);
-    if (nbits_bw > 0)
-        lc3_put_bits(bits, bw, nbits_bw);
-}
-
-/**
- * Get bandwidth indication
- */
-int lc3_bwdet_get_bw(lc3_bits_t *bits,
-    enum lc3_srate sr, enum lc3_bandwidth *bw)
-{
-    enum lc3_bandwidth max_bw = (enum lc3_bandwidth)sr;
-    int nbits_bw = lc3_bwdet_get_nbits(sr);
-
-    *bw = nbits_bw > 0 ? lc3_get_bits(bits, nbits_bw) : LC3_BANDWIDTH_NB;
-    return *bw > max_bw ? (*bw = max_bw), -1 : 0;
-}
diff --git a/system/embdrv/lc3/src/bwdet.h b/system/embdrv/lc3/src/bwdet.h
deleted file mode 100644
index 19039c7..0000000
--- a/system/embdrv/lc3/src/bwdet.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Bandwidth detector
- *
- * Reference : Low Complexity Communication Codec (LC3)
- *             Bluetooth Specification v1.0
- */
-
-#ifndef __LC3_BWDET_H
-#define __LC3_BWDET_H
-
-#include "common.h"
-#include "bits.h"
-
-
-/**
- * Bandwidth detector (cf. 3.3.5)
- * dt, sr          Duration and samplerate of the frame
- * e               Energy estimation per bands
- * return          Return detected bandwitdth
- */
-enum lc3_bandwidth lc3_bwdet_run(
-    enum lc3_dt dt, enum lc3_srate sr, const float *e);
-
-/**
- * Return number of bits coding the bandwidth value
- * sr              Samplerate of the frame
- * return          Number of bits coding the bandwidth value
- */
-int lc3_bwdet_get_nbits(enum lc3_srate sr);
-
-/**
- * Put bandwidth indication
- * bits            Bitstream context
- * sr              Samplerate of the frame
- * bw              Bandwidth detected
- */
-void lc3_bwdet_put_bw(lc3_bits_t *bits,
-    enum lc3_srate sr, enum lc3_bandwidth bw);
-
-/**
- * Get bandwidth indication
- * bits            Bitstream context
- * sr              Samplerate of the frame
- * bw              Return bandwidth indication
- * return          0: Ok  -1: Invalid bandwidth indication
- */
-int lc3_bwdet_get_bw(lc3_bits_t *bits,
-    enum lc3_srate sr, enum lc3_bandwidth *bw);
-
-
-#endif /* __LC3_BWDET_H */
diff --git a/system/embdrv/lc3/src/common.h b/system/embdrv/lc3/src/common.h
deleted file mode 100644
index a9aaa0e..0000000
--- a/system/embdrv/lc3/src/common.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Common constants and types
- */
-
-#ifndef __LC3_COMMON_H
-#define __LC3_COMMON_H
-
-#include <lc3.h>
-#include "fastmath.h"
-
-#include <stdalign.h>
-#include <limits.h>
-#include <string.h>
-
-#ifdef __ARM_ARCH
-#include <arm_acle.h>
-#endif
-
-
-/**
- * Hot Function attribute
- * Selectively disable sanitizer
- */
-
-#ifdef __clang__
-
-#define LC3_HOT \
-    __attribute__((no_sanitize("bounds"))) \
-    __attribute__((no_sanitize("integer")))
-
-#else /* __clang__ */
-
-#define LC3_HOT
-
-#endif /* __clang__ */
-
-
-/**
- * Macros
- * MIN/MAX  Minimum and maximum between 2 values
- * CLIP     Clip a value between low and high limits
- * SATXX    Signed saturation on 'xx' bits
- * ABS      Return absolute value
- */
-
-#define LC3_MIN(a, b)  ( (a) < (b) ?  (a) : (b) )
-#define LC3_MAX(a, b)  ( (a) > (b) ?  (a) : (b) )
-
-#define LC3_CLIP(v, min, max)  LC3_MIN(LC3_MAX(v, min), max)
-#define LC3_SAT16(v)  LC3_CLIP(v, -(1 << 15), (1 << 15) - 1)
-#define LC3_SAT24(v)  LC3_CLIP(v, -(1 << 23), (1 << 23) - 1)
-
-#define LC3_ABS(v)  ( (v) < 0 ? -(v) : (v) )
-
-
-#ifdef __ARM_FEATURE_SAT
-
-#undef  LC3_SAT16
-#define LC3_SAT16(v) __ssat(v, 16)
-
-#undef  LC3_SAT24
-#define LC3_SAT24(v) __ssat(v, 24)
-
-#endif /* __ARM_FEATURE_SAT */
-
-
-/**
- * Convert `dt` in us and `sr` in KHz
- */
-
-#define LC3_DT_US(dt) \
-    ( (3 + (dt)) * 2500 )
-
-#define LC3_SRATE_KHZ(sr) \
-    ( (1 + (sr) + ((sr) == LC3_SRATE_48K)) * 8 )
-
-
-/**
- * Return number of samples, delayed samples and
- * encoded spectrum coefficients within a frame
- * - For encoding, keep 1.25 ms for temporal window
- * - For decoding, keep 18 ms of history, aligned on frames, and a frame
- */
-
-#define LC3_NS(dt, sr) \
-    ( 20 * (3 + (dt)) * (1 + (sr) + ((sr) == LC3_SRATE_48K)) )
-
-#define LC3_ND(dt, sr) \
-    ( (dt) == LC3_DT_7M5 ? 23 * LC3_NS(dt, sr) / 30 \
-                         :  5 * LC3_NS(dt, sr) /  8 )
-
-#define LC3_NE(dt, sr) \
-    ( 20 * (3 + (dt)) * (1 + (sr)) )
-
-#define LC3_MAX_NE \
-    LC3_NE(LC3_DT_10M, LC3_SRATE_48K)
-
-#define LC3_NT(sr_hz) \
-    ( (5 * LC3_SRATE_KHZ(sr)) / 4 )
-
-#define LC3_NH(dt, sr) \
-    ( ((3 - dt) + 1) * LC3_NS(dt, sr) )
-
-
-/**
- * Bandwidth, mapped to Nyquist frequency of samplerates
- */
-
-enum lc3_bandwidth {
-    LC3_BANDWIDTH_NB = LC3_SRATE_8K,
-    LC3_BANDWIDTH_WB = LC3_SRATE_16K,
-    LC3_BANDWIDTH_SSWB = LC3_SRATE_24K,
-    LC3_BANDWIDTH_SWB = LC3_SRATE_32K,
-    LC3_BANDWIDTH_FB = LC3_SRATE_48K,
-
-    LC3_NUM_BANDWIDTH,
-};
-
-
-/**
- * Complex floating point number
- */
-
-struct lc3_complex
-{
-    float re, im;
-};
-
-
-#endif /* __LC3_COMMON_H */
diff --git a/system/embdrv/lc3/src/energy.c b/system/embdrv/lc3/src/energy.c
deleted file mode 100644
index bf86db7..0000000
--- a/system/embdrv/lc3/src/energy.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "energy.h"
-#include "tables.h"
-
-
-/**
- * Energy estimation per band
- */
-bool lc3_energy_compute(
-    enum lc3_dt dt, enum lc3_srate sr, const float *x, float *e)
-{
-    static const int n1_table[LC3_NUM_DT][LC3_NUM_SRATE] = {
-        [LC3_DT_7M5] = { 56, 34, 27, 24, 22 },
-        [LC3_DT_10M] = { 49, 28, 23, 20, 18 },
-    };
-
-    /* First bands are 1 coefficient width */
-
-    int n1 = n1_table[dt][sr];
-    float e_sum[2] = { 0, 0 };
-    int iband;
-
-    for (iband = 0; iband < n1; iband++) {
-        *e = x[iband] * x[iband];
-        e_sum[0] += *(e++);
-    }
-
-    /* Mean the square of coefficients within each band,
-     * note that 7.5ms 8KHz frame has more bands than samples */
-
-    int nb = LC3_MIN(LC3_NUM_BANDS, LC3_NS(dt, sr));
-    int iband_h = nb - 2*(2 - dt);
-    const int *lim = lc3_band_lim[dt][sr];
-
-    for (int i = lim[iband]; iband < nb; iband++) {
-        int ie = lim[iband+1];
-        int n = ie - i;
-
-        float sx2 = x[i] * x[i];
-        for (i++; i < ie; i++)
-            sx2 += x[i] * x[i];
-
-        *e = sx2 / n;
-        e_sum[iband >= iband_h] += *(e++);
-    }
-
-    for (; iband < LC3_NUM_BANDS; iband++)
-        *(e++) = 0;
-
-    /* Return the near nyquist flag */
-
-    return e_sum[1] > 30 * e_sum[0];
-}
diff --git a/system/embdrv/lc3/src/energy.h b/system/embdrv/lc3/src/energy.h
deleted file mode 100644
index 39f0124..0000000
--- a/system/embdrv/lc3/src/energy.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Energy estimation per band
- *
- * Reference : Low Complexity Communication Codec (LC3)
- *             Bluetooth Specification v1.0
- */
-
-#ifndef __LC3_ENERGY_H
-#define __LC3_ENERGY_H
-
-#include "common.h"
-
-
-/**
- * Energy estimation per band
- * dt, sr          Duration and samplerate of the frame
- * x               Input MDCT coefficient
- * e               Energy estimation per bands
- * return          True when high energy detected near Nyquist frequency
- */
-bool lc3_energy_compute(
-    enum lc3_dt dt, enum lc3_srate sr, const float *x, float *e);
-
-
-#endif /* __LC3_ENERGY_H */
diff --git a/system/embdrv/lc3/src/fastmath.h b/system/embdrv/lc3/src/fastmath.h
deleted file mode 100644
index 4210f2e..0000000
--- a/system/embdrv/lc3/src/fastmath.h
+++ /dev/null
@@ -1,158 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Mathematics function approximation
- */
-
-#ifndef __LC3_FASTMATH_H
-#define __LC3_FASTMATH_H
-
-#include <stdint.h>
-#include <math.h>
-
-
-/**
- * Fast 2^n approximation
- * x               Operand, range -8 to 8
- * return          2^x approximation (max relative error ~ 7e-6)
- */
-static inline float fast_exp2f(float x)
-{
-    float y;
-
-    /* --- Polynomial approx in range -0.5 to 0.5 --- */
-
-    static const float c[] = { 1.27191277e-09, 1.47415221e-07,
-        1.35510312e-05, 9.38375815e-04, 4.33216946e-02 };
-
-    y = (    c[0]) * x;
-    y = (y + c[1]) * x;
-    y = (y + c[2]) * x;
-    y = (y + c[3]) * x;
-    y = (y + c[4]) * x;
-    y = (y + 1.f);
-
-    /* --- Raise to the power of 16  --- */
-
-    y = y*y;
-    y = y*y;
-    y = y*y;
-    y = y*y;
-
-    return y;
-}
-
-/**
- * Fast log2(x) approximation
- * x               Operand, greater than 0
- * return          log2(x) approximation (max absolute error ~ 1e-4)
- */
-static inline float fast_log2f(float x)
-{
-    float y;
-    int e;
-
-    /* --- Polynomial approx in range 0.5 to 1 --- */
-
-    static const float c[] = {
-        -1.29479677, 5.11769018, -8.42295281, 8.10557963, -3.50567360 };
-
-    x = frexpf(x, &e);
-
-    y = (    c[0]) * x;
-    y = (y + c[1]) * x;
-    y = (y + c[2]) * x;
-    y = (y + c[3]) * x;
-    y = (y + c[4]);
-
-    /* --- Add log2f(2^e) and return --- */
-
-    return e + y;
-}
-
-/**
- * Fast log10(x) approximation
- * x               Operand, greater than 0
- * return          log10(x) approximation (max absolute error ~ 1e-4)
- */
-static inline float fast_log10f(float x)
-{
-    return log10f(2) * fast_log2f(x);
-}
-
-/**
- * Fast `10 * log10(x)` (or dB) approximation in fixed Q16
- * x               Operand, in range 2^-63 to 2^63 (1e-19 to 1e19)
- * return          10 * log10(x) in fixed Q16 (-190 to 192 dB)
- *
- * - The 0 value is accepted and return the minimum value ~ -191dB
- * - This function assumed that float 32 bits is coded IEEE 754
- */
-static inline int32_t fast_db_q16(float x)
-{
-    /* --- Table in Q15 --- */
-
-    static const uint16_t t[][2] = {
-
-        /* [n][0] = 10 * log10(2) * log2(1 + n/32), with n = [0..15]     */
-        /* [n][1] = [n+1][0] - [n][0] (while defining [16][0])           */
-
-        {     0, 4379 }, {  4379, 4248 }, {  8627, 4125 }, { 12753, 4009 },
-        { 16762, 3899 }, { 20661, 3795 }, { 24456, 3697 }, { 28153, 3603 },
-        { 31755, 3514 }, { 35269, 3429 }, { 38699, 3349 }, { 42047, 3272 },
-        { 45319, 3198 }, { 48517, 3128 }, { 51645, 3061 }, { 54705, 2996 },
-
-        /* [n][0] = 10 * log10(2) * log2(1 + n/32) - 10 * log10(2) / 2,  */
-        /*     with n = [16..31]                                         */
-        /* [n][1] = [n+1][0] - [n][0] (while defining [32][0])           */
-
-        {  8381, 2934 }, { 11315, 2875 }, { 14190, 2818 }, { 17008, 2763 },
-        { 19772, 2711 }, { 22482, 2660 }, { 25142, 2611 }, { 27754, 2564 },
-        { 30318, 2519 }, { 32837, 2475 }, { 35312, 2433 }, { 37744, 2392 },
-        { 40136, 2352 }, { 42489, 2314 }, { 44803, 2277 }, { 47080, 2241 },
-
-    };
-
-    /* --- Approximation ---
-     *
-     *   10 * log10(x^2) = 10 * log10(2) * log2(x^2)
-     *
-     *   And log2(x^2) = 2 * log2( (1 + m) * 2^e )
-     *                 = 2 * (e + log2(1 + m)) , with m in range [0..1]
-     *
-     * Split the float values in :
-     *   e2  Double value of the exponent (2 * e + k)
-     *   hi  High 5 bits of mantissa, for precalculated result `t[hi][0]`
-     *   lo  Low 16 bits of mantissa, for linear interpolation `t[hi][1]`
-     *
-     * Two cases, from the range of the mantissa :
-     *   0 to 0.5   `k = 0`, use 1st part of the table
-     *   0.5 to 1   `k = 1`, use 2nd part of the table  */
-
-    union { float f; uint32_t u; } x2 = { .f = x*x };
-
-    int e2 = (int)(x2.u >> 22) - 2*127;
-    int hi = (x2.u >> 18) & 0x1f;
-    int lo = (x2.u >>  2) & 0xffff;
-
-    return e2 * 49321 + t[hi][0] + ((t[hi][1] * lo) >> 16);
-}
-
-
-#endif /* __LC3_FASTMATH_H */
diff --git a/system/embdrv/lc3/src/lc3.c b/system/embdrv/lc3/src/lc3.c
deleted file mode 100644
index 219e381..0000000
--- a/system/embdrv/lc3/src/lc3.c
+++ /dev/null
@@ -1,603 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <lc3.h>
-
-#include "common.h"
-#include "bits.h"
-
-#include "attdet.h"
-#include "bwdet.h"
-#include "ltpf.h"
-#include "mdct.h"
-#include "energy.h"
-#include "sns.h"
-#include "tns.h"
-#include "spec.h"
-#include "plc.h"
-
-
-/**
- * Frame side data
- */
-
-struct side_data {
-    enum lc3_bandwidth bw;
-    bool pitch_present;
-    lc3_ltpf_data_t ltpf;
-    lc3_sns_data_t sns;
-    lc3_tns_data_t tns;
-    lc3_spec_side_t spec;
-};
-
-
-/* ----------------------------------------------------------------------------
- *  General
- * -------------------------------------------------------------------------- */
-
-/**
- * Resolve frame duration in us
- * us              Frame duration in us
- * return          Frame duration identifier, or LC3_NUM_DT
- */
-static enum lc3_dt resolve_dt(int us)
-{
-    return us ==  7500 ? LC3_DT_7M5 :
-           us == 10000 ? LC3_DT_10M : LC3_NUM_DT;
-}
-
-/**
- * Resolve samplerate in Hz
- * hz              Samplerate in Hz
- * return          Sample rate identifier, or LC3_NUM_SRATE
- */
-static enum lc3_srate resolve_sr(int hz)
-{
-    return hz ==  8000 ? LC3_SRATE_8K  : hz == 16000 ? LC3_SRATE_16K :
-           hz == 24000 ? LC3_SRATE_24K : hz == 32000 ? LC3_SRATE_32K :
-           hz == 48000 ? LC3_SRATE_48K : LC3_NUM_SRATE;
-}
-
-/**
- * Return the number of PCM samples in a frame
- */
-int lc3_frame_samples(int dt_us, int sr_hz)
-{
-    enum lc3_dt dt = resolve_dt(dt_us);
-    enum lc3_srate sr = resolve_sr(sr_hz);
-
-    if (dt >= LC3_NUM_DT || sr >= LC3_NUM_SRATE)
-        return -1;
-
-    return LC3_NS(dt, sr);
-}
-
-/**
- * Return the size of frames, from bitrate
- */
-int lc3_frame_bytes(int dt_us, int bitrate)
-{
-    if (resolve_dt(dt_us) >= LC3_NUM_DT)
-        return -1;
-
-    if (bitrate < LC3_MIN_BITRATE)
-        return LC3_MIN_FRAME_BYTES;
-
-    if (bitrate > LC3_MAX_BITRATE)
-        return LC3_MAX_FRAME_BYTES;
-
-    int nbytes = ((unsigned)bitrate * dt_us) / (1000*1000*8);
-
-    return LC3_CLIP(nbytes, LC3_MIN_FRAME_BYTES, LC3_MAX_FRAME_BYTES);
-}
-
-/**
- * Resolve the bitrate, from the size of frames
- */
-int lc3_resolve_bitrate(int dt_us, int nbytes)
-{
-    if (resolve_dt(dt_us) >= LC3_NUM_DT)
-        return -1;
-
-    if (nbytes < LC3_MIN_FRAME_BYTES)
-        return LC3_MIN_BITRATE;
-
-    if (nbytes > LC3_MAX_FRAME_BYTES)
-        return LC3_MAX_BITRATE;
-
-    int bitrate = ((unsigned)nbytes * (1000*1000*8) + dt_us/2) / dt_us;
-
-    return LC3_CLIP(bitrate, LC3_MIN_BITRATE, LC3_MAX_BITRATE);
-}
-
-/**
- * Return algorithmic delay, as a number of samples
- */
-int lc3_delay_samples(int dt_us, int sr_hz)
-{
-    enum lc3_dt dt = resolve_dt(dt_us);
-    enum lc3_srate sr = resolve_sr(sr_hz);
-
-    if (dt >= LC3_NUM_DT || sr >= LC3_NUM_SRATE)
-        return -1;
-
-    return (dt == LC3_DT_7M5 ? 8 : 5) * (LC3_SRATE_KHZ(sr) / 2);
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Encoder
- * -------------------------------------------------------------------------- */
-
-/**
- * Input PCM Samples from signed 16 bits
- * encoder         Encoder state
- * pcm, stride     Input PCM samples, and count between two consecutives
- */
-static void load_s16(
-    struct lc3_encoder *encoder, const void *_pcm, int stride)
-{
-    const int16_t *pcm = _pcm;
-
-    enum lc3_dt dt = encoder->dt;
-    enum lc3_srate sr = encoder->sr_pcm;
-
-    int16_t *xt = encoder->xt;
-    float *xs = encoder->xs;
-    int ns = LC3_NS(dt, sr);
-
-    for (int i = 0; i < ns; i++) {
-        int16_t in = pcm[i*stride];
-        xt[i] = in, xs[i] = in;
-    }
-}
-
-/**
- * Input PCM Samples from signed 24 bits
- * encoder         Encoder state
- * pcm, stride     Input PCM samples, and count between two consecutives
- */
-static void load_s24(
-    struct lc3_encoder *encoder, const void *_pcm, int stride)
-{
-    const int32_t *pcm = _pcm;
-
-    enum lc3_dt dt = encoder->dt;
-    enum lc3_srate sr = encoder->sr_pcm;
-
-    int16_t *xt = encoder->xt;
-    float *xs = encoder->xs;
-    int ns = LC3_NS(dt, sr);
-
-    for (int i = 0; i < ns; i++) {
-        int32_t in = pcm[i*stride];
-
-        xt[i] = in >> 8;
-        xs[i] = ldexpf(in, -8);
-    }
-}
-
-/**
- * Frame Analysis
- * encoder         Encoder state
- * nbytes          Size in bytes of the frame
- * side, xq        Return frame data
- */
-static void analyze(struct lc3_encoder *encoder,
-    int nbytes, struct side_data *side, uint16_t *xq)
-{
-    enum lc3_dt dt = encoder->dt;
-    enum lc3_srate sr = encoder->sr;
-    enum lc3_srate sr_pcm = encoder->sr_pcm;
-    int ns = LC3_NS(dt, sr_pcm);
-    int nt = LC3_NT(sr_pcm);
-
-    int16_t *xt = encoder->xt;
-    float *xs = encoder->xs;
-    float *xd = encoder->xd;
-    float *xf = xs;
-
-    /* --- Temporal --- */
-
-    bool att = lc3_attdet_run(dt, sr_pcm, nbytes, &encoder->attdet, xt);
-
-    side->pitch_present =
-        lc3_ltpf_analyse(dt, sr_pcm, &encoder->ltpf, xt, &side->ltpf);
-
-    memmove(xt - nt, xt + (ns-nt), nt * sizeof(*xt));
-
-    /* --- Spectral --- */
-
-    float e[LC3_NUM_BANDS];
-
-    lc3_mdct_forward(dt, sr_pcm, sr, xs, xd, xf);
-
-    bool nn_flag = lc3_energy_compute(dt, sr, xf, e);
-    if (nn_flag)
-        lc3_ltpf_disable(&side->ltpf);
-
-    side->bw = lc3_bwdet_run(dt, sr, e);
-
-    lc3_sns_analyze(dt, sr, e, att, &side->sns, xf, xf);
-
-    lc3_tns_analyze(dt, side->bw, nn_flag, nbytes, &side->tns, xf);
-
-    lc3_spec_analyze(dt, sr,
-        nbytes, side->pitch_present, &side->tns,
-        &encoder->spec, xf, xq, &side->spec);
-}
-
-/**
- * Encode bitstream
- * encoder         Encoder state
- * side, xq        The frame data
- * nbytes          Target size of the frame (20 to 400)
- * buffer          Output bitstream buffer of `nbytes` size
- */
-static void encode(struct lc3_encoder *encoder,
-    const struct side_data *side, uint16_t *xq, int nbytes, void *buffer)
-{
-    enum lc3_dt dt = encoder->dt;
-    enum lc3_srate sr = encoder->sr;
-    enum lc3_bandwidth bw = side->bw;
-    float *xf = encoder->xs;
-
-    lc3_bits_t bits;
-
-    lc3_setup_bits(&bits, LC3_BITS_MODE_WRITE, buffer, nbytes);
-
-    lc3_bwdet_put_bw(&bits, sr, bw);
-
-    lc3_spec_put_side(&bits, dt, sr, &side->spec);
-
-    lc3_tns_put_data(&bits, &side->tns);
-
-    lc3_put_bit(&bits, side->pitch_present);
-
-    lc3_sns_put_data(&bits, &side->sns);
-
-    if (side->pitch_present)
-        lc3_ltpf_put_data(&bits, &side->ltpf);
-
-    lc3_spec_encode(&bits,
-        dt, sr, bw, nbytes, xq, &side->spec, xf);
-
-    lc3_flush_bits(&bits);
-}
-
-/**
- * Return size needed for an encoder
- */
-unsigned lc3_encoder_size(int dt_us, int sr_hz)
-{
-    if (resolve_dt(dt_us) >= LC3_NUM_DT ||
-        resolve_sr(sr_hz) >= LC3_NUM_SRATE)
-        return 0;
-
-    return sizeof(struct lc3_encoder) +
-        LC3_ENCODER_BUFFER_COUNT(dt_us, sr_hz) * sizeof(float);
-}
-
-/**
- * Setup encoder
- */
-struct lc3_encoder *lc3_setup_encoder(
-    int dt_us, int sr_hz, int sr_pcm_hz, void *mem)
-{
-    if (sr_pcm_hz <= 0)
-        sr_pcm_hz = sr_hz;
-
-    enum lc3_dt dt = resolve_dt(dt_us);
-    enum lc3_srate sr = resolve_sr(sr_hz);
-    enum lc3_srate sr_pcm = resolve_sr(sr_pcm_hz);
-
-    if (dt >= LC3_NUM_DT || sr_pcm >= LC3_NUM_SRATE || sr > sr_pcm || !mem)
-        return NULL;
-
-    struct lc3_encoder *encoder = mem;
-    int ns = LC3_NS(dt, sr_pcm);
-    int nt = LC3_NT(sr_pcm);
-
-    *encoder = (struct lc3_encoder){
-        .dt = dt, .sr = sr,
-        .sr_pcm = sr_pcm,
-
-        .xt = (int16_t *)encoder->s + nt,
-        .xs = encoder->s + (nt+ns)/2,
-        .xd = encoder->s + (nt+ns)/2 + ns,
-    };
-
-    memset(encoder->s, 0,
-        LC3_ENCODER_BUFFER_COUNT(dt_us, sr_pcm_hz) * sizeof(float));
-
-    return encoder;
-}
-
-/**
- * Encode a frame
- */
-int lc3_encode(struct lc3_encoder *encoder, enum lc3_pcm_format fmt,
-    const void *pcm, int stride, int nbytes, void *out)
-{
-    static void (* const load[])(struct lc3_encoder *, const void *, int) = {
-        [LC3_PCM_FORMAT_S16] = load_s16,
-        [LC3_PCM_FORMAT_S24] = load_s24,
-    };
-
-    /* --- Check parameters --- */
-
-    if (!encoder || nbytes < LC3_MIN_FRAME_BYTES
-                 || nbytes > LC3_MAX_FRAME_BYTES)
-        return -1;
-
-    /* --- Processing --- */
-
-    struct side_data side;
-    uint16_t xq[LC3_NE(encoder->dt, encoder->sr)];
-
-    load[fmt](encoder, pcm, stride);
-
-    analyze(encoder, nbytes, &side, xq);
-
-    encode(encoder, &side, xq, nbytes, out);
-
-    return 0;
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Decoder
- * -------------------------------------------------------------------------- */
-
-/**
- * Output PCM Samples to signed 16 bits
- * decoder         Decoder state
- * pcm, stride     Output PCM samples, and count between two consecutives
- */
-static void store_s16(
-    struct lc3_decoder *decoder, void *_pcm, int stride)
-{
-    int16_t *pcm = _pcm;
-
-    enum lc3_dt dt = decoder->dt;
-    enum lc3_srate sr = decoder->sr_pcm;
-
-    float *xs = decoder->xs;
-    int ns = LC3_NS(dt, sr);
-
-    for ( ; ns > 0; ns--, xs++, pcm += stride) {
-        int32_t s = *xs >= 0 ? (int)(*xs + 0.5f) : (int)(*xs - 0.5f);
-        *pcm = LC3_SAT16(s);
-    }
-}
-
-/**
- * Output PCM Samples to signed 24 bits
- * decoder         Decoder state
- * pcm, stride     Output PCM samples, and count between two consecutives
- */
-static void store_s24(
-    struct lc3_decoder *decoder, void *_pcm, int stride)
-{
-    int32_t *pcm = _pcm;
-
-    enum lc3_dt dt = decoder->dt;
-    enum lc3_srate sr = decoder->sr_pcm;
-
-    float *xs = decoder->xs;
-    int ns = LC3_NS(dt, sr);
-
-    for ( ; ns > 0; ns--, xs++, pcm += stride) {
-        int32_t s = *xs >= 0 ? (int32_t)(ldexpf(*xs, 8) + 0.5f)
-                             : (int32_t)(ldexpf(*xs, 8) - 0.5f);
-        *pcm = LC3_SAT24(s);
-    }
-}
-
-/**
- * Decode bitstream
- * decoder         Decoder state
- * data, nbytes    Input bitstream buffer
- * side            Return the side data
- * return          0: Ok  < 0: Bitsream error detected
- */
-static int decode(struct lc3_decoder *decoder,
-    const void *data, int nbytes, struct side_data *side)
-{
-    enum lc3_dt dt = decoder->dt;
-    enum lc3_srate sr = decoder->sr;
-
-    float *xf = decoder->xs;
-    int ns = LC3_NS(dt, sr);
-    int ne = LC3_NE(dt, sr);
-
-    lc3_bits_t bits;
-    int ret = 0;
-
-    lc3_setup_bits(&bits, LC3_BITS_MODE_READ, (void *)data, nbytes);
-
-    if ((ret = lc3_bwdet_get_bw(&bits, sr, &side->bw)) < 0)
-        return ret;
-
-    if ((ret = lc3_spec_get_side(&bits, dt, sr, &side->spec)) < 0)
-        return ret;
-
-    lc3_tns_get_data(&bits, dt, side->bw, nbytes, &side->tns);
-
-    side->pitch_present = lc3_get_bit(&bits);
-
-    if ((ret = lc3_sns_get_data(&bits, &side->sns)) < 0)
-        return ret;
-
-    if (side->pitch_present)
-        lc3_ltpf_get_data(&bits, &side->ltpf);
-
-    if ((ret = lc3_spec_decode(&bits, dt, sr,
-                    side->bw, nbytes, &side->spec, xf)) < 0)
-        return ret;
-
-    memset(xf + ne, 0, (ns - ne) * sizeof(float));
-
-    return lc3_check_bits(&bits);
-}
-
-/**
- * Frame synthesis
- * decoder         Decoder state
- * side            Frame data, NULL performs PLC
- * nbytes          Size in bytes of the frame
- */
-static void synthesize(struct lc3_decoder *decoder,
-    const struct side_data *side, int nbytes)
-{
-    enum lc3_dt dt = decoder->dt;
-    enum lc3_srate sr = decoder->sr;
-    enum lc3_srate sr_pcm = decoder->sr_pcm;
-
-    float *xf = decoder->xs;
-    int ns = LC3_NS(dt, sr_pcm);
-    int ne = LC3_NE(dt, sr);
-
-    float *xg = decoder->xg;
-    float *xd = decoder->xd;
-    float *xs = xf;
-
-    if (side) {
-        enum lc3_bandwidth bw = side->bw;
-
-        lc3_plc_suspend(&decoder->plc);
-
-        lc3_tns_synthesize(dt, bw, &side->tns, xf);
-
-        lc3_sns_synthesize(dt, sr, &side->sns, xf, xg);
-
-        lc3_mdct_inverse(dt, sr_pcm, sr, xg, xd, xs);
-
-    } else {
-        lc3_plc_synthesize(dt, sr, &decoder->plc, xg, xf);
-
-        memset(xf + ne, 0, (ns - ne) * sizeof(float));
-
-        lc3_mdct_inverse(dt, sr_pcm, sr, xf, xd, xs);
-    }
-
-    lc3_ltpf_synthesize(dt, sr_pcm, nbytes, &decoder->ltpf,
-        side && side->pitch_present ? &side->ltpf : NULL, decoder->xh, xs);
-}
-
-/**
- * Update decoder state on decoding completion
- * decoder         Decoder state
- */
-static void complete(struct lc3_decoder *decoder)
-{
-    enum lc3_dt dt = decoder->dt;
-    enum lc3_srate sr_pcm = decoder->sr_pcm;
-    int nh = LC3_NH(dt, sr_pcm);
-    int ns = LC3_NS(dt, sr_pcm);
-
-    decoder->xs = decoder->xs - decoder->xh < nh - ns ?
-        decoder->xs + ns : decoder->xh;
-}
-
-/**
- * Return size needed for a decoder
- */
-unsigned lc3_decoder_size(int dt_us, int sr_hz)
-{
-    if (resolve_dt(dt_us) >= LC3_NUM_DT ||
-        resolve_sr(sr_hz) >= LC3_NUM_SRATE)
-        return 0;
-
-    return sizeof(struct lc3_decoder) +
-        LC3_DECODER_BUFFER_COUNT(dt_us, sr_hz) * sizeof(float);
-}
-
-/**
- * Setup decoder
- */
-struct lc3_decoder *lc3_setup_decoder(
-    int dt_us, int sr_hz, int sr_pcm_hz, void *mem)
-{
-    if (sr_pcm_hz <= 0)
-        sr_pcm_hz = sr_hz;
-
-    enum lc3_dt dt = resolve_dt(dt_us);
-    enum lc3_srate sr = resolve_sr(sr_hz);
-    enum lc3_srate sr_pcm = resolve_sr(sr_pcm_hz);
-
-    if (dt >= LC3_NUM_DT || sr_pcm >= LC3_NUM_SRATE || sr > sr_pcm || !mem)
-        return NULL;
-
-    struct lc3_decoder *decoder = mem;
-    int nh = LC3_NH(dt, sr_pcm);
-    int ns = LC3_NS(dt, sr_pcm);
-    int nd = LC3_ND(dt, sr_pcm);
-
-    *decoder = (struct lc3_decoder){
-        .dt = dt, .sr = sr,
-        .sr_pcm = sr_pcm,
-
-        .xh = decoder->s,
-        .xs = decoder->s + nh-ns,
-        .xd = decoder->s + nh,
-        .xg = decoder->s + nh+nd,
-    };
-
-    lc3_plc_reset(&decoder->plc);
-
-    memset(decoder->s, 0,
-        LC3_DECODER_BUFFER_COUNT(dt_us, sr_pcm_hz) * sizeof(float));
-
-    return decoder;
-}
-
-/**
- * Decode a frame
- */
-int lc3_decode(struct lc3_decoder *decoder, const void *in, int nbytes,
-    enum lc3_pcm_format fmt, void *pcm, int stride)
-{
-    static void (* const store[])(struct lc3_decoder *, void *, int) = {
-        [LC3_PCM_FORMAT_S16] = store_s16,
-        [LC3_PCM_FORMAT_S24] = store_s24,
-    };
-
-    /* --- Check parameters --- */
-
-    if (!decoder)
-        return -1;
-
-    if (in && (nbytes < LC3_MIN_FRAME_BYTES ||
-               nbytes > LC3_MAX_FRAME_BYTES   ))
-        return -1;
-
-    /* --- Processing --- */
-
-    struct side_data side;
-
-    int ret = !in || (decode(decoder, in, nbytes, &side) < 0);
-
-    synthesize(decoder, ret ? NULL : &side, nbytes);
-
-    store[fmt](decoder, pcm, stride);
-
-    complete(decoder);
-
-    return ret;
-}
diff --git a/system/embdrv/lc3/src/ltpf.c b/system/embdrv/lc3/src/ltpf.c
deleted file mode 100644
index 3d13c20..0000000
--- a/system/embdrv/lc3/src/ltpf.c
+++ /dev/null
@@ -1,893 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "ltpf.h"
-#include "tables.h"
-
-#include "ltpf_neon.h"
-#include "ltpf_arm.h"
-
-
-/* ----------------------------------------------------------------------------
- *  Resampling
- * -------------------------------------------------------------------------- */
-
-/**
- * Resampling coefficients
- * The coefficients, in fixed Q15, are reordered by phase for each source
- * samplerate (coefficient matrix transposed)
- */
-
-#ifndef resample_8k_12k8
-static const int16_t h_8k_12k8_q15[8*10] = {
-      214,   417, -1052, -4529, 26233, -4529, -1052,   417,   214,     0,
-      180,     0, -1522, -2427, 24506, -5289,     0,   763,   156,   -28,
-       92,  -323, -1361,     0, 19741, -3885,  1317,   861,     0,   -61,
-        0,  -457,  -752,  1873, 13068,     0,  2389,   598,  -213,   -79,
-      -61,  -398,     0,  2686,  5997,  5997,  2686,     0,  -398,   -61,
-      -79,  -213,   598,  2389,     0, 13068,  1873,  -752,  -457,     0,
-      -61,     0,   861,  1317, -3885, 19741,     0, -1361,  -323,    92,
-      -28,   156,   763,     0, -5289, 24506, -2427, -1522,     0,   180,
-};
-#endif /* resample_8k_12k8 */
-
-#ifndef resample_16k_12k8
-static const int16_t h_16k_12k8_q15[4*20] = {
-      -61,   214,  -398,   417,     0, -1052,  2686, -4529,  5997, 26233,
-     5997, -4529,  2686, -1052,     0,   417,  -398,   214,   -61,     0,
-
-      -79,   180,  -213,     0,   598, -1522,  2389, -2427,     0, 24506,
-    13068, -5289,  1873,     0,  -752,   763,  -457,   156,     0,   -28,
-
-      -61,    92,     0,  -323,   861, -1361,  1317,     0, -3885, 19741,
-    19741, -3885,     0,  1317, -1361,   861,  -323,     0,    92,   -61,
-
-      -28,     0,   156,  -457,   763,  -752,     0,  1873, -5289, 13068,
-    24506,     0, -2427,  2389, -1522,   598,     0,  -213,   180,   -79,
-};
-#endif /* resample_16k_12k8 */
-
-#ifndef resample_32k_12k8
-static const int16_t h_32k_12k8_q15[2*40] = {
-      -30,   -31,    46,   107,     0,  -199,  -162,   209,   430,     0,
-     -681,  -526,   658,  1343,     0, -2264, -1943,  2999,  9871, 13116,
-     9871,  2999, -1943, -2264,     0,  1343,   658,  -526,  -681,     0,
-      430,   209,  -162,  -199,     0,   107,    46,   -31,   -30,     0,
-
-      -14,   -39,     0,    90,    78,  -106,  -229,     0,   382,   299,
-     -376,  -761,     0,  1194,   937, -1214, -2644,     0,  6534, 12253,
-    12253,  6534,     0, -2644, -1214,   937,  1194,     0,  -761,  -376,
-      299,   382,     0,  -229,  -106,    78,    90,     0,   -39,   -14,
-};
-#endif /* resample_32k_12k8 */
-
-#ifndef resample_24k_12k8
-static const int16_t h_24k_12k8_q15[8*30] = {
-      -50,    19,   143,   -93,  -290,   278,   485,  -658,  -701,  1396,
-      901, -3019, -1042, 10276, 17488, 10276, -1042, -3019,   901,  1396,
-     -701,  -658,   485,   278,  -290,   -93,   143,    19,   -50,     0,
-
-      -46,     0,   141,   -45,  -305,   185,   543,  -501,  -854,  1153,
-     1249, -2619, -1908,  8712, 17358, 11772,     0, -3319,   480,  1593,
-     -504,  -796,   399,   367,  -261,  -142,   138,    40,   -52,    -5,
-
-      -41,   -17,   133,     0,  -304,    91,   574,  -334,  -959,   878,
-     1516, -2143, -2590,  7118, 16971, 13161,  1202, -3495,     0,  1731,
-     -267,  -908,   287,   445,  -215,  -188,   125,    62,   -52,   -12,
-
-      -34,   -30,   120,    41,  -291,     0,   577,  -164, -1015,   585,
-     1697, -1618, -3084,  5534, 16337, 14406,  2544, -3526,  -523,  1800,
-        0,  -985,   152,   509,  -156,  -230,   104,    83,   -48,   -19,
-
-      -26,   -41,   103,    76,  -265,   -83,   554,     0, -1023,   288,
-     1791, -1070, -3393,  3998, 15474, 15474,  3998, -3393, -1070,  1791,
-      288, -1023,     0,   554,   -83,  -265,    76,   103,   -41,   -26,
-
-      -19,   -48,    83,   104,  -230,  -156,   509,   152,  -985,     0,
-     1800,  -523, -3526,  2544, 14406, 16337,  5534, -3084, -1618,  1697,
-      585, -1015,  -164,   577,     0,  -291,    41,   120,   -30,   -34,
-
-      -12,   -52,    62,   125,  -188,  -215,   445,   287,  -908,  -267,
-     1731,     0, -3495,  1202, 13161, 16971,  7118, -2590, -2143,  1516,
-      878,  -959,  -334,   574,    91,  -304,     0,   133,   -17,   -41,
-
-       -5,   -52,    40,   138,  -142,  -261,   367,   399,  -796,  -504,
-     1593,   480, -3319,     0, 11772, 17358,  8712, -1908, -2619,  1249,
-     1153,  -854,  -501,   543,   185,  -305,   -45,   141,     0,   -46,
-};
-#endif /* resample_24k_12k8 */
-
-#ifndef resample_48k_12k8
-static const int16_t h_48k_12k8_q15[4*60] = {
-      -13,   -25,   -20,    10,    51,    71,    38,   -47,  -133,  -145,
-      -42,   139,   277,   242,     0,  -329,  -511,  -351,   144,   698,
-      895,   450,  -535, -1510, -1697,  -521,  1999,  5138,  7737,  8744,
-     7737,  5138,  1999,  -521, -1697, -1510,  -535,   450,   895,   698,
-      144,  -351,  -511,  -329,     0,   242,   277,   139,   -42,  -145,
-     -133,   -47,    38,    71,    51,    10,   -20,   -25,   -13,     0,
-
-       -9,   -23,   -24,     0,    41,    71,    52,   -23,  -115,  -152,
-      -78,    92,   254,   272,    76,  -251,  -493,  -427,     0,   576,
-      900,   624,  -262, -1309, -1763,  -954,  1272,  4356,  7203,  8679,
-     8169,  5886,  2767,     0, -1542, -1660,  -809,   240,   848,   796,
-      292,  -252,  -507,  -398,   -82,   199,   288,   183,     0,  -130,
-     -145,   -71,    20,    69,    60,    20,   -15,   -26,   -17,    -3,
-
-       -6,   -20,   -26,    -8,    31,    67,    62,     0,   -94,  -152,
-     -108,    45,   223,   287,   143,  -167,  -454,  -480,  -134,   439,
-      866,   758,     0, -1071, -1748, -1295,   601,  3559,  6580,  8485,
-     8485,  6580,  3559,   601, -1295, -1748, -1071,     0,   758,   866,
-      439,  -134,  -480,  -454,  -167,   143,   287,   223,    45,  -108,
-     -152,   -94,     0,    62,    67,    31,    -8,   -26,   -20,    -6,
-
-       -3,   -17,   -26,   -15,    20,    60,    69,    20,   -71,  -145,
-     -130,     0,   183,   288,   199,   -82,  -398,  -507,  -252,   292,
-      796,   848,   240,  -809, -1660, -1542,     0,  2767,  5886,  8169,
-     8679,  7203,  4356,  1272,  -954, -1763, -1309,  -262,   624,   900,
-      576,     0,  -427,  -493,  -251,    76,   272,   254,    92,   -78,
-     -152,  -115,   -23,    52,    71,    41,     0,   -24,   -23,    -9,
-};
-#endif /* resample_48k_12k8 */
-
-
-/**
- * High-pass 50Hz filtering, at 12.8 KHz samplerate
- * hp50            Biquad filter state
- * xn              Input sample, in fixed Q30
- * return          Filtered sample, in fixed Q30
- */
-LC3_HOT static inline int32_t filter_hp50(
-    struct lc3_ltpf_hp50_state *hp50, int32_t xn)
-{
-    int32_t yn;
-
-    const int32_t a1 = -2110217691, a2 = 1037111617;
-    const int32_t b1 = -2110535566, b2 = 1055267782;
-
-    yn       = (hp50->s1 + (int64_t)xn * b2) >> 30;
-    hp50->s1 = (hp50->s2 + (int64_t)xn * b1 - (int64_t)yn * a1);
-    hp50->s2 = (           (int64_t)xn * b2 - (int64_t)yn * a2);
-
-    return yn;
-}
-
-/**
- * Resample from 8 / 16 / 32 KHz to 12.8 KHz Template
- * p               Resampling factor with compared to 192 KHz (8, 4 or 2)
- * h               Arrange by phase coefficients table
- * hp50            High-Pass biquad filter state
- * x               [-d..-1] Previous, [0..ns-1] Current samples, Q15
- * y, n            [0..n-1] Output `n` processed samples, Q14
- *
- * The `x` vector is aligned on 32 bits
- * The number of previous samples `d` accessed on `x` is :
- *   d: { 10, 20, 40 } - 1 for resampling factors 8, 4 and 2.
- */
-#if !defined(resample_8k_12k8) || !defined(resample_16k_12k8) \
-    || !defined(resample_32k_12k8)
-LC3_HOT static inline void resample_x64k_12k8(const int p, const int16_t *h,
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    const int w = 2*(40 / p);
-
-    x -= w - 1;
-
-    for (int i = 0; i < 5*n; i += 5) {
-        const int16_t *hn = h + (i % p) * w;
-        const int16_t *xn = x + (i / p);
-        int32_t un = 0;
-
-        for (int k = 0; k < w; k += 10) {
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-        }
-
-        int32_t yn = filter_hp50(hp50, un);
-        *(y++) = (yn + (1 << 15)) >> 16;
-    }
-}
-#endif
-
-/**
- * Resample from 24 / 48 KHz to 12.8 KHz Template
- * p               Resampling factor with compared to 192 KHz (8 or 4)
- * h               Arrange by phase coefficients table
- * hp50            High-Pass biquad filter state
- * x               [-d..-1] Previous, [0..ns-1] Current samples, Q15
- * y, n            [0..n-1] Output `n` processed samples, Q14
- *
- * The `x` vector is aligned on 32 bits
- * The number of previous samples `d` accessed on `x` is :
- *   d: { 30, 60 } - 1 for resampling factors 8 and 4.
- */
-#if !defined(resample_24k_12k8) || !defined(resample_48k_12k8)
-LC3_HOT static inline void resample_x192k_12k8(const int p, const int16_t *h,
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    const int w = 2*(120 / p);
-
-    x -= w - 1;
-
-    for (int i = 0; i < 15*n; i += 15) {
-        const int16_t *hn = h + (i % p) * w;
-        const int16_t *xn = x + (i / p);
-        int32_t un = 0;
-
-        for (int k = 0; k < w; k += 15) {
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-            un += *(xn++) * *(hn++);
-        }
-
-        int32_t yn = filter_hp50(hp50, un);
-        *(y++) = (yn + (1 << 15)) >> 16;
-    }
-}
-#endif
-
-/**
- * Resample from 8 Khz to 12.8 KHz
- * hp50            High-Pass biquad filter state
- * x               [-10..-1] Previous, [0..ns-1] Current samples, Q15
- * y, n            [0..n-1] Output `n` processed samples, Q14
- *
- * The `x` vector is aligned on 32 bits
- */
-#ifndef resample_8k_12k8
-LC3_HOT static void resample_8k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    resample_x64k_12k8(8, h_8k_12k8_q15, hp50, x, y, n);
-}
-#endif /* resample_8k_12k8 */
-
-/**
- * Resample from 16 Khz to 12.8 KHz
- * hp50            High-Pass biquad filter state
- * x               [-20..-1] Previous, [0..ns-1] Current samples, in fixed Q15
- * y, n            [0..n-1] Output `n` processed samples, in fixed Q14
- *
- * The `x` vector is aligned on 32 bits
- */
-#ifndef resample_16k_12k8
-LC3_HOT static void resample_16k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    resample_x64k_12k8(4, h_16k_12k8_q15, hp50, x, y, n);
-}
-#endif /* resample_16k_12k8 */
-
-/**
- * Resample from 32 Khz to 12.8 KHz
- * hp50            High-Pass biquad filter state
- * x               [-30..-1] Previous, [0..ns-1] Current samples, in fixed Q15
- * y, n            [0..n-1] Output `n` processed samples, in fixed Q14
- *
- * The `x` vector is aligned on 32 bits
- */
-#ifndef resample_32k_12k8
-LC3_HOT static void resample_32k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    resample_x64k_12k8(2, h_32k_12k8_q15, hp50, x, y, n);
-}
-#endif /* resample_32k_12k8 */
-
-/**
- * Resample from 24 Khz to 12.8 KHz
- * hp50            High-Pass biquad filter state
- * x               [-30..-1] Previous, [0..ns-1] Current samples, in fixed Q15
- * y, n            [0..n-1] Output `n` processed samples, in fixed Q14
- *
- * The `x` vector is aligned on 32 bits
- */
-#ifndef resample_24k_12k8
-LC3_HOT static void resample_24k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    resample_x192k_12k8(8, h_24k_12k8_q15, hp50, x, y, n);
-}
-#endif /* resample_24k_12k8 */
-
-/**
- * Resample from 48 Khz to 12.8 KHz
- * hp50            High-Pass biquad filter state
- * x               [-60..-1] Previous, [0..ns-1] Current samples, in fixed Q15
- * y, n            [0..n-1] Output `n` processed samples, in fixed Q14
- *
-* The `x` vector is aligned on 32 bits
-*/
-#ifndef resample_48k_12k8
-LC3_HOT static void resample_48k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    resample_x192k_12k8(4, h_48k_12k8_q15, hp50, x, y, n);
-}
-#endif /* resample_48k_12k8 */
-
-/**
-* Resample to 6.4 KHz
-* x               [-3..-1] Previous, [0..n-1] Current samples
-* y, n            [0..n-1] Output `n` processed samples
-*
-* The `x` vector is aligned on 32 bits
- */
-#ifndef resample_6k4
-LC3_HOT static void resample_6k4(const int16_t *x, int16_t *y, int n)
-{
-    static const int16_t h[] = { 18477, 15424, 8105 };
-    const int16_t *ye = y + n;
-
-    for (x--; y < ye; x += 2)
-        *(y++) = (x[0] * h[0] + (x[-1] + x[1]) * h[1]
-                              + (x[-2] + x[2]) * h[2]) >> 16;
-}
-#endif /* resample_6k4 */
-
-/**
- * LTPF Resample to 12.8 KHz implementations for each samplerates
- */
-
-static void (* const resample_12k8[])
-    (struct lc3_ltpf_hp50_state *, const int16_t *, int16_t *, int ) =
-{
-    [LC3_SRATE_8K ] = resample_8k_12k8,
-    [LC3_SRATE_16K] = resample_16k_12k8,
-    [LC3_SRATE_24K] = resample_24k_12k8,
-    [LC3_SRATE_32K] = resample_32k_12k8,
-    [LC3_SRATE_48K] = resample_48k_12k8,
-};
-
-
-/* ----------------------------------------------------------------------------
- *  Analysis
- * -------------------------------------------------------------------------- */
-
-/**
- * Return dot product of 2 vectors
- * a, b, n         The 2 vectors of size `n` (> 0 and <= 128)
- * return          sum( a[i] * b[i] ), i = [0..n-1]
- *
- * The size `n` of vectors must be multiple of 16, and less or equal to 128
-*/
-#ifndef dot
-LC3_HOT static inline float dot(const int16_t *a, const int16_t *b, int n)
-{
-    int64_t v = 0;
-
-    for (int i = 0; i < (n >> 4); i++)
-        for (int j = 0; j < 16; j++)
-            v += *(a++) * *(b++);
-
-    int32_t v32 = (v + (1 << 5)) >> 6;
-    return (float)v32;
-}
-#endif /* dot */
-
-/**
- * Return vector of correlations
- * a, b, n         The 2 vector of size `n` (> 0 and <= 128)
- * y, nc           Output the correlation vector of size `nc`
- *
- * The first vector `a` is aligned of 32 bits
- * The size `n` of vectors is multiple of 16, and less or equal to 128
- */
-#ifndef correlate
-LC3_HOT static void correlate(
-    const int16_t *a, const int16_t *b, int n, float *y, int nc)
-{
-    for (const float *ye = y + nc; y < ye; )
-        *(y++) = dot(a, b--, n);
-}
-#endif /* correlate */
-
-/**
- * Search the maximum value and returns its argument
- * x, n            The input vector of size `n`
- * x_max           Return the maximum value
- * return          Return the argument of the maximum
- */
-LC3_HOT static int argmax(const float *x, int n, float *x_max)
-{
-    int arg = 0;
-
-    *x_max = x[arg = 0];
-    for (int i = 1; i < n; i++)
-        if (*x_max < x[i])
-            *x_max = x[arg = i];
-
-    return arg;
-}
-
-/**
- * Search the maximum weithed value and returns its argument
- * x, n            The input vector of size `n`
- * w_incr          Increment of the weight
- * x_max, xw_max   Return the maximum not weighted value
- * return          Return the argument of the weigthed maximum
- */
-LC3_HOT static int argmax_weighted(
-    const float *x, int n, float w_incr, float *x_max)
-{
-    int arg;
-
-    float xw_max = (*x_max = x[arg = 0]);
-    float w = 1 + w_incr;
-
-    for (int i = 1; i < n; i++, w += w_incr)
-        if (xw_max < x[i] * w)
-            xw_max = (*x_max = x[arg = i]) * w;
-
-    return arg;
-}
-
-/**
- * Interpolate from pitch detected value (3.3.9.8)
- * x, n            [-2..-1] Previous, [0..n] Current input
- * d               The phase of interpolation (0 to 3)
- * return          The interpolated vector
- *
- * The size `n` of vectors must be multiple of 4
- */
-LC3_HOT static void interpolate(const int16_t *x, int n, int d, int16_t *y)
-{
-    static const int16_t h4_q15[][4] = {
-        { 6877, 19121,  6877,     0 }, { 3506, 18025, 11000,   220 },
-        { 1300, 15048, 15048,  1300 }, {  220, 11000, 18025,  3506 } };
-
-    const int16_t *h = h4_q15[d];
-    int16_t x3 = x[-2], x2 = x[-1], x1, x0;
-
-    x1 = (*x++);
-    for (const int16_t *ye = y + n; y < ye; ) {
-        int32_t yn;
-
-        yn = (x0 = *(x++)) * h[0] + x1 * h[1] + x2 * h[2] + x3 * h[3];
-        *(y++) = yn >> 15;
-
-        yn = (x3 = *(x++)) * h[0] + x0 * h[1] + x1 * h[2] + x2 * h[3];
-        *(y++) = yn >> 15;
-
-        yn = (x2 = *(x++)) * h[0] + x3 * h[1] + x0 * h[2] + x1 * h[3];
-        *(y++) = yn >> 15;
-
-        yn = (x1 = *(x++)) * h[0] + x2 * h[1] + x3 * h[2] + x0 * h[3];
-        *(y++) = yn >> 15;
-    }
-}
-
-/**
- * Interpolate autocorrelation (3.3.9.7)
- * x               [-4..-1] Previous, [0..4] Current input
- * d               The phase of interpolation (-3 to 3)
- * return          The interpolated value
- */
-LC3_HOT static float interpolate_corr(const float *x, int d)
-{
-    static const float h4[][8] = {
-        {  1.53572770e-02, -4.72963246e-02,  8.35788573e-02,  8.98638285e-01,
-           8.35788573e-02, -4.72963246e-02,  1.53572770e-02,                 },
-        {  2.74547165e-03,  4.59833449e-03, -7.54404636e-02,  8.17488686e-01,
-           3.30182571e-01, -1.05835916e-01,  2.86823405e-02, -2.87456116e-03 },
-        { -3.00125103e-03,  2.95038503e-02, -1.30305021e-01,  6.03297008e-01,
-           6.03297008e-01, -1.30305021e-01,  2.95038503e-02, -3.00125103e-03 },
-        { -2.87456116e-03,  2.86823405e-02, -1.05835916e-01,  3.30182571e-01,
-           8.17488686e-01, -7.54404636e-02,  4.59833449e-03,  2.74547165e-03 },
-    };
-
-    const float *h = h4[(4+d) % 4];
-
-    float y = d < 0 ? x[-4] * *(h++) :
-              d > 0 ? x[ 4] * *(h+7) : 0;
-
-    y += x[-3] * h[0] + x[-2] * h[1] + x[-1] * h[2] + x[0] * h[3] +
-         x[ 1] * h[4] + x[ 2] * h[5] + x[ 3] * h[6];
-
-    return y;
-}
-
-/**
- * Pitch detection algorithm (3.3.9.5-6)
- * ltpf            Context of analysis
- * x, n            [-114..-17] Previous, [0..n-1] Current 6.4KHz samples
- * tc              Return the pitch-lag estimation
- * return          True when pitch present
- *
- * The `x` vector is aligned on 32 bits
- */
-static bool detect_pitch(
-    struct lc3_ltpf_analysis *ltpf, const int16_t *x, int n, int *tc)
-{
-    float rm1, rm2;
-    float r[98];
-
-    const int r0 = 17, nr = 98;
-    int k0 = LC3_MAX(   0, ltpf->tc-4);
-    int nk = LC3_MIN(nr-1, ltpf->tc+4) - k0 + 1;
-
-    correlate(x, x - r0, n, r, nr);
-
-    int t1 = argmax_weighted(r, nr, -.5f/(nr-1), &rm1);
-    int t2 = k0 + argmax(r + k0, nk, &rm2);
-
-    const int16_t *x1 = x - (r0 + t1);
-    const int16_t *x2 = x - (r0 + t2);
-
-    float nc1 = rm1 <= 0 ? 0 :
-        rm1 / sqrtf(dot(x, x, n) * dot(x1, x1, n));
-
-    float nc2 = rm2 <= 0 ? 0 :
-        rm2 / sqrtf(dot(x, x, n) * dot(x2, x2, n));
-
-    int t1sel = nc2 <= 0.85f * nc1;
-    ltpf->tc = (t1sel ? t1 : t2);
-
-    *tc = r0 + ltpf->tc;
-    return (t1sel ? nc1 : nc2) > 0.6f;
-}
-
-/**
- * Pitch-lag parameter (3.3.9.7)
- * x, n            [-232..-28] Previous, [0..n-1] Current 12.8KHz samples, Q14
- * tc              Pitch-lag estimation
- * pitch           The pitch value, in fixed .4
- * return          The bitstream pitch index value
- *
- * The `x` vector is aligned on 32 bits
- */
-static int refine_pitch(const int16_t *x, int n, int tc, int *pitch)
-{
-    float r[17], rm;
-    int e, f;
-
-    int r0 = LC3_MAX( 32, 2*tc - 4);
-    int nr = LC3_MIN(228, 2*tc + 4) - r0 + 1;
-
-    correlate(x, x - (r0 - 4), n, r, nr + 8);
-
-    e = r0 + argmax(r + 4, nr, &rm);
-    const float *re = r + (e - (r0 - 4));
-
-    float dm = interpolate_corr(re, f = 0);
-    for (int i = 1; i <= 3; i++) {
-        float d;
-
-        if (e >= 127 && ((i & 1) | (e >= 157)))
-            continue;
-
-        if ((d = interpolate_corr(re, i)) > dm)
-            dm = d, f = i;
-
-        if (e > 32 && (d = interpolate_corr(re, -i)) > dm)
-            dm = d, f = -i;
-    }
-
-    e -=   (f < 0);
-    f += 4*(f < 0);
-
-    *pitch = 4*e + f;
-    return e < 127 ? 4*e +  f       - 128 :
-           e < 157 ? 2*e + (f >> 1) + 126 : e + 283;
-}
-
-/**
- * LTPF Analysis
- */
-bool lc3_ltpf_analyse(
-    enum lc3_dt dt, enum lc3_srate sr, struct lc3_ltpf_analysis *ltpf,
-    const int16_t *x, struct lc3_ltpf_data *data)
-{
-    /* --- Resampling to 12.8 KHz --- */
-
-    int z_12k8 = sizeof(ltpf->x_12k8) / sizeof(*ltpf->x_12k8);
-    int n_12k8 = dt == LC3_DT_7M5 ? 96 : 128;
-
-    memmove(ltpf->x_12k8, ltpf->x_12k8 + n_12k8,
-        (z_12k8 - n_12k8) * sizeof(*ltpf->x_12k8));
-
-    int16_t *x_12k8 = ltpf->x_12k8 + (z_12k8 - n_12k8);
-
-    resample_12k8[sr](&ltpf->hp50, x, x_12k8, n_12k8);
-
-    x_12k8 -= (dt == LC3_DT_7M5 ? 44 :  24);
-
-    /* --- Resampling to 6.4 KHz --- */
-
-    int z_6k4 = sizeof(ltpf->x_6k4) / sizeof(*ltpf->x_6k4);
-    int n_6k4 = n_12k8 >> 1;
-
-    memmove(ltpf->x_6k4, ltpf->x_6k4 + n_6k4,
-        (z_6k4 - n_6k4) * sizeof(*ltpf->x_6k4));
-
-    int16_t *x_6k4 = ltpf->x_6k4 + (z_6k4 - n_6k4);
-
-    resample_6k4(x_12k8, x_6k4, n_6k4);
-
-    /* --- Pitch detection --- */
-
-    int tc, pitch = 0;
-    float nc = 0;
-
-    bool pitch_present = detect_pitch(ltpf, x_6k4, n_6k4, &tc);
-
-    if (pitch_present) {
-        int16_t u[n_12k8], v[n_12k8];
-
-        data->pitch_index = refine_pitch(x_12k8, n_12k8, tc, &pitch);
-
-        interpolate(x_12k8, n_12k8, 0, u);
-        interpolate(x_12k8 - (pitch >> 2), n_12k8, pitch & 3, v);
-
-        nc = dot(u, v, n_12k8) / sqrtf(dot(u, u, n_12k8) * dot(v, v, n_12k8));
-    }
-
-    /* --- Activation --- */
-
-     if (ltpf->active) {
-        int pitch_diff =
-            LC3_MAX(pitch, ltpf->pitch) - LC3_MIN(pitch, ltpf->pitch);
-        float nc_diff = nc - ltpf->nc[0];
-
-        data->active = pitch_present &&
-            ((nc > 0.9f) || (nc > 0.84f && pitch_diff < 8 && nc_diff > -0.1f));
-
-     } else {
-         data->active = pitch_present &&
-            ( (dt == LC3_DT_10M || ltpf->nc[1] > 0.94f) &&
-              (ltpf->nc[0] > 0.94f && nc > 0.94f) );
-     }
-
-     ltpf->active = data->active;
-     ltpf->pitch = pitch;
-     ltpf->nc[1] = ltpf->nc[0];
-     ltpf->nc[0] = nc;
-
-     return pitch_present;
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Synthesis
- * -------------------------------------------------------------------------- */
-
-/**
- * Synthesis filter template
- * xh, nh          History ring buffer of filtered samples
- * lag             Lag parameter in the ring buffer
- * x0              w-1 previous input samples
- * x, n            Current samples as input, filtered as output
- * c, w            Coefficients `den` then `num`, and width of filter
- * fade            Fading mode of filter  -1: Out  1: In  0: None
- */
-LC3_HOT static inline void synthesize_template(
-    const float *xh, int nh, int lag,
-    const float *x0, float *x, int n,
-    const float *c, const int w, int fade)
-{
-    float g = (float)(fade <= 0);
-    float g_incr = (float)((fade > 0) - (fade < 0)) / n;
-    float u[w];
-
-    /* --- Load previous samples --- */
-
-    lag += (w >> 1);
-
-    const float *y = x - xh < lag ? x + (nh - lag) : x - lag;
-    const float *y_end = xh + nh - 1;
-
-    for (int j = 0; j < w-1; j++) {
-
-        u[j] = 0;
-
-        float yi = *y, xi = *(x0++);
-        y = y < y_end ? y + 1 : xh;
-
-        for (int k = 0; k <= j; k++)
-            u[j-k] -= yi * c[k];
-
-        for (int k = 0; k <= j; k++)
-            u[j-k] += xi * c[w+k];
-    }
-
-    u[w-1] = 0;
-
-    /* --- Process by filter length --- */
-
-    for (int i = 0; i < n; i += w)
-        for (int j = 0; j < w; j++, g += g_incr) {
-
-            float yi = *y, xi = *x;
-            y = y < y_end ? y + 1 : xh;
-
-            for (int k = 0; k < w; k++)
-                u[(j+(w-1)-k)%w] -= yi * c[k];
-
-            for (int k = 0; k < w; k++)
-                u[(j+(w-1)-k)%w] += xi * c[w+k];
-
-            *(x++) = xi - g * u[j];
-            u[j] = 0;
-        }
-}
-
-/**
- * Synthesis filter for each samplerates (width of filter)
- */
-
-LC3_HOT static void synthesize_4(const float *xh, int nh, int lag,
-    const float *x0, float *x, int n, const float *c, int fade)
-{
-    synthesize_template(xh, nh, lag, x0, x, n, c, 4, fade);
-}
-
-LC3_HOT static void synthesize_6(const float *xh, int nh, int lag,
-    const float *x0, float *x, int n, const float *c, int fade)
-{
-    synthesize_template(xh, nh, lag, x0, x, n, c, 6, fade);
-}
-
-LC3_HOT static void synthesize_8(const float *xh, int nh, int lag,
-    const float *x0, float *x, int n, const float *c, int fade)
-{
-    synthesize_template(xh, nh, lag, x0, x, n, c, 8, fade);
-}
-
-LC3_HOT static void synthesize_12(const float *xh, int nh, int lag,
-    const float *x0, float *x, int n, const float *c, int fade)
-{
-    synthesize_template(xh, nh, lag, x0, x, n, c, 12, fade);
-}
-
-static void (* const synthesize[])(const float *, int, int,
-    const float *, float *, int, const float *, int) =
-{
-    [LC3_SRATE_8K ] = synthesize_4,
-    [LC3_SRATE_16K] = synthesize_4,
-    [LC3_SRATE_24K] = synthesize_6,
-    [LC3_SRATE_32K] = synthesize_8,
-    [LC3_SRATE_48K] = synthesize_12,
-};
-
-
-/**
- * LTPF Synthesis
- */
-void lc3_ltpf_synthesize(enum lc3_dt dt, enum lc3_srate sr, int nbytes,
-    lc3_ltpf_synthesis_t *ltpf, const lc3_ltpf_data_t *data,
-    const float *xh, float *x)
-{
-    int nh = LC3_NH(dt, sr);
-    int dt_us = LC3_DT_US(dt);
-
-    /* --- Filter parameters --- */
-
-    int p_idx = data ? data->pitch_index : 0;
-    int pitch =
-        p_idx >= 440 ? (((p_idx     ) - 283) << 2)  :
-        p_idx >= 380 ? (((p_idx >> 1) -  63) << 2) + (((p_idx & 1)) << 1) :
-                       (((p_idx >> 2) +  32) << 2) + (((p_idx & 3)) << 0)  ;
-
-    pitch = (pitch * LC3_SRATE_KHZ(sr) * 10 + 64) / 128;
-
-    int nbits = (nbytes*8 * 10000 + (dt_us/2)) / dt_us;
-    int g_idx = LC3_MAX(nbits / 80, 3 + (int)sr) - (3 + sr);
-    bool active = data && data->active && g_idx < 4;
-
-    int w = LC3_MAX(4, LC3_SRATE_KHZ(sr) / 4);
-    float c[2*w];
-
-    for (int i = 0; i < w; i++) {
-        float g = active ? 0.4f - 0.05f * g_idx : 0;
-        c[  i] = g * lc3_ltpf_cden[sr][pitch & 3][(w-1)-i];
-        c[w+i] = 0.85f * g * lc3_ltpf_cnum[sr][LC3_MIN(g_idx, 3)][(w-1)-i];
-    }
-
-    /* --- Transition handling --- */
-
-    int ns = LC3_NS(dt, sr);
-    int nt = ns / (3 + dt);
-    float x0[w];
-
-    if (active)
-        memcpy(x0, x + nt-(w-1), (w-1) * sizeof(float));
-
-    if (!ltpf->active && active)
-        synthesize[sr](xh, nh, pitch/4, ltpf->x, x, nt, c, 1);
-    else if (ltpf->active && !active)
-        synthesize[sr](xh, nh, ltpf->pitch/4, ltpf->x, x, nt, ltpf->c, -1);
-    else if (ltpf->active && active && ltpf->pitch == pitch)
-        synthesize[sr](xh, nh, pitch/4, ltpf->x, x, nt, c, 0);
-    else if (ltpf->active && active) {
-        synthesize[sr](xh, nh, ltpf->pitch/4, ltpf->x, x, nt, ltpf->c, -1);
-        synthesize[sr](xh, nh, pitch/4,
-            (x <= xh ? x + nh : x) - (w-1), x, nt, c, 1);
-    }
-
-    /* --- Remainder --- */
-
-    memcpy(ltpf->x, x + ns - (w-1), (w-1) * sizeof(float));
-
-    if (active)
-        synthesize[sr](xh, nh, pitch/4, x0, x + nt, ns-nt, c, 0);
-
-    /* --- Update state --- */
-
-    ltpf->active = active;
-    ltpf->pitch = pitch;
-    memcpy(ltpf->c, c, 2*w * sizeof(*ltpf->c));
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Bitstream data
- * -------------------------------------------------------------------------- */
-
-/**
- * LTPF disable
- */
-void lc3_ltpf_disable(struct lc3_ltpf_data *data)
-{
-    data->active = false;
-}
-
-/**
- * Return number of bits coding the bitstream data
- */
-int lc3_ltpf_get_nbits(bool pitch)
-{
-    return 1 + 10 * pitch;
-}
-
-/**
- * Put bitstream data
- */
-void lc3_ltpf_put_data(lc3_bits_t *bits,
-    const struct lc3_ltpf_data *data)
-{
-    lc3_put_bit(bits, data->active);
-    lc3_put_bits(bits, data->pitch_index, 9);
-}
-
-/**
- * Get bitstream data
- */
-void lc3_ltpf_get_data(lc3_bits_t *bits, struct lc3_ltpf_data *data)
-{
-    data->active = lc3_get_bit(bits);
-    data->pitch_index = lc3_get_bits(bits, 9);
-}
diff --git a/system/embdrv/lc3/src/ltpf.h b/system/embdrv/lc3/src/ltpf.h
deleted file mode 100644
index 0d5bb3c..0000000
--- a/system/embdrv/lc3/src/ltpf.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Long Term Postfilter
- *
- * Reference : Low Complexity Communication Codec (LC3)
- *             Bluetooth Specification v1.0
- */
-
-#ifndef __LC3_LTPF_H
-#define __LC3_LTPF_H
-
-#include "common.h"
-#include "bits.h"
-
-
-/**
- * LTPF data
- */
-
-typedef struct lc3_ltpf_data {
-    bool active;
-    int pitch_index;
-} lc3_ltpf_data_t;
-
-
-/* ----------------------------------------------------------------------------
- *  Encoding
- * -------------------------------------------------------------------------- */
-
-/**
- * LTPF analysis
- * dt, sr          Duration and samplerate of the frame
- * ltpf            Context of analysis
- * allowed         True when activation of LTPF is allowed
- * x               [-d..-1] Previous, [0..ns-1] Current samples
- * data            Return bitstream data
- * return          True when pitch present, False otherwise
- *
- * The `x` vector is aligned on 32 bits
- * The number of previous samples `d` accessed on `x` is :
- *   d: { 10, 20, 30, 40, 60 } - 1 for samplerates from 8KHz to 48KHz
- */
-bool lc3_ltpf_analyse(enum lc3_dt dt, enum lc3_srate sr,
-    lc3_ltpf_analysis_t *ltpf, const int16_t *x, lc3_ltpf_data_t *data);
-
-/**
- * LTPF disable
- * data            LTPF data, disabled activation on return
- */
-void lc3_ltpf_disable(lc3_ltpf_data_t *data);
-
-/**
- * Return number of bits coding the bitstream data
- * pitch           True when pitch present, False otherwise
- * return          Bit consumption, including the pitch present flag
- */
-int lc3_ltpf_get_nbits(bool pitch);
-
-/**
- * Put bitstream data
- * bits            Bitstream context
- * data            LTPF data
- */
-void lc3_ltpf_put_data(lc3_bits_t *bits, const lc3_ltpf_data_t *data);
-
-
-/* ----------------------------------------------------------------------------
- *  Decoding
- * -------------------------------------------------------------------------- */
-/**
- * Get bitstream data
- * bits            Bitstream context
- * data            Return bitstream data
- */
-void lc3_ltpf_get_data(lc3_bits_t *bits, lc3_ltpf_data_t *data);
-
-/**
- * LTPF synthesis
- * dt, sr          Duration and samplerate of the frame
- * nbytes          Size in bytes of the frame
- * ltpf            Context of synthesis
- * data            Bitstream data, NULL when pitch not present
- * xr              Base address of ring buffer of decoded samples
- * x               Samples to proceed in the ring buffer, filtered as output
- *
- * The size of the ring buffer is `nh + ns`.
- * The filtering needs an history of at least 18 ms.
- */
-void lc3_ltpf_synthesize(enum lc3_dt dt, enum lc3_srate sr, int nbytes,
-    lc3_ltpf_synthesis_t *ltpf, const lc3_ltpf_data_t *data,
-    const float *xr, float *x);
-
-
-#endif /* __LC3_LTPF_H */
diff --git a/system/embdrv/lc3/src/ltpf_arm.h b/system/embdrv/lc3/src/ltpf_arm.h
deleted file mode 100644
index a812b3c..0000000
--- a/system/embdrv/lc3/src/ltpf_arm.h
+++ /dev/null
@@ -1,476 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#if __ARM_FEATURE_SIMD32
-
-#ifndef TEST_ARM
-
-#include <arm_acle.h>
-
-static inline int16x2_t __pkhbt(int16x2_t a, int16x2_t b)
-{
-    int16x2_t r;
-    __asm("pkhbt %0, %1, %2" : "=r" (r) : "r" (a), "r" (b));
-    return r;
-}
-
-#endif /* TEST_ARM */
-
-
-/**
- * Import
- */
-
-static inline int32_t filter_hp50(struct lc3_ltpf_hp50_state *, int32_t);
-static inline float dot(const int16_t *, const int16_t *, int);
-
-
-/**
- * Resample from 8 / 16 / 32 KHz to 12.8 KHz Template
- */
-#if !defined(resample_8k_12k8) || !defined(resample_16k_12k8) \
-    || !defined(resample_32k_12k8)
-static inline void arm_resample_x64k_12k8(const int p, const int16x2_t *h,
-    struct lc3_ltpf_hp50_state *hp50, const int16x2_t *x, int16_t *y, int n)
-{
-    const int w = 40 / p;
-
-    x -= w;
-
-    for (int i = 0; i < 5*n; i += 5) {
-        const int16x2_t *hn = h + (i % (2*p)) * (48 / p);
-        const int16x2_t *xn = x + (i / (2*p));
-
-        int32_t un = __smlad(*(xn++), *(hn++), 0);
-
-        for (int k = 0; k < w; k += 5) {
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-        }
-
-        int32_t yn = filter_hp50(hp50, un);
-        *(y++) = (yn + (1 << 15)) >> 16;
-    }
-}
-#endif
-
-/**
- * Resample from 24 / 48 KHz to 12.8 KHz Template
- */
-#if !defined(resample_24k_12k8) || !defined(resample_48k_12k8)
-static inline void arm_resample_x192k_12k8(const int p, const int16x2_t *h,
-    struct lc3_ltpf_hp50_state *hp50, const int16x2_t *x, int16_t *y, int n)
-{
-    const int w = 120 / p;
-
-    x -= w;
-
-    for (int i = 0; i < 15*n; i += 15) {
-        const int16x2_t *hn = h + (i % (2*p)) * (128 / p);
-        const int16x2_t *xn = x + (i / (2*p));
-
-        int32_t un = __smlad(*(xn++), *(hn++), 0);
-
-        for (int k = 0; k < w; k += 15) {
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-            un = __smlad(*(xn++), *(hn++), un);
-        }
-
-        int32_t yn = filter_hp50(hp50, un);
-        *(y++) = (yn + (1 << 15)) >> 16;
-    }
-}
-#endif
-
-/**
- * Resample from 8 Khz to 12.8 KHz
- */
-#ifndef resample_8k_12k8
-#define resample_8k_12k8 arm_resample_8k_12k8
-static void arm_resample_8k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    static const int16_t alignas(int32_t) h[2*8*12] = {
-        0, 214,  417, -1052, -4529, 26233, -4529, -1052,   417,  214,   0, 0,
-        0, 180,    0, -1522, -2427, 24506, -5289,     0,   763,  156, -28, 0,
-        0,  92, -323, -1361,     0, 19741, -3885,  1317,   861,    0, -61, 0,
-        0,   0, -457,  -752,  1873, 13068,     0,  2389,   598, -213, -79, 0,
-        0, -61, -398,     0,  2686,  5997,  5997,  2686,     0, -398, -61, 0,
-        0, -79, -213,   598,  2389,     0, 13068,  1873,  -752, -457,   0, 0,
-        0, -61,    0,   861,  1317, -3885, 19741,     0, -1361, -323,  92, 0,
-        0, -28,  156,   763,     0, -5289, 24506, -2427, -1522,    0, 180, 0,
-        0, 0, 214,  417, -1052, -4529, 26233, -4529, -1052,   417,  214,   0,
-        0, 0, 180,    0, -1522, -2427, 24506, -5289,     0,   763,  156, -28,
-        0, 0,  92, -323, -1361,     0, 19741, -3885,  1317,   861,    0, -61,
-        0, 0,   0, -457,  -752,  1873, 13068,     0,  2389,   598, -213, -79,
-        0, 0, -61, -398,     0,  2686,  5997,  5997,  2686,     0, -398, -61,
-        0, 0, -79, -213,   598,  2389,     0, 13068,  1873,  -752, -457,   0,
-        0, 0, -61,    0,   861,  1317, -3885, 19741,     0, -1361, -323,  92,
-        0, 0, -28,  156,   763,     0, -5289, 24506, -2427, -1522,    0, 180,
-    };
-
-    arm_resample_x64k_12k8(
-        8, (const int16x2_t *)h, hp50, (int16x2_t *)x, y, n);
-}
-#endif /* resample_8k_12k8 */
-
-/**
- * Resample from 16 Khz to 12.8 KHz
- */
-#ifndef resample_16k_12k8
-#define resample_16k_12k8 arm_resample_16k_12k8
-static void arm_resample_16k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    static const int16_t alignas(int32_t) h[2*4*24] = {
-
-            0,   -61,   214,  -398,   417,     0, -1052,  2686,
-        -4529,  5997, 26233,  5997, -4529,  2686, -1052,     0,
-          417,  -398,   214,   -61,     0,     0,     0,     0,
-
-
-            0,   -79,   180,  -213,     0,   598, -1522,  2389,
-        -2427,     0, 24506, 13068, -5289,  1873,     0,  -752,
-          763,  -457,   156,     0,   -28,     0,     0,     0,
-
-
-            0,   -61,    92,     0,  -323,   861, -1361,  1317,
-            0, -3885, 19741, 19741, -3885,     0,  1317, -1361,
-          861,  -323,     0,    92,   -61,     0,     0,     0,
-
-            0,   -28,     0,   156,  -457,   763,  -752,     0,
-         1873, -5289, 13068, 24506,     0, -2427,  2389, -1522,
-          598,     0,  -213,   180,   -79,     0,     0,     0,
-
-
-            0,     0,   -61,   214,  -398,   417,     0, -1052,
-         2686, -4529,  5997, 26233,  5997, -4529,  2686, -1052,
-            0,   417,  -398,   214,   -61,     0,     0,     0,
-
-
-            0,     0,   -79,   180,  -213,     0,   598, -1522,
-         2389, -2427,     0, 24506, 13068, -5289,  1873,     0,
-         -752,   763,  -457,   156,     0,   -28,     0,     0,
-
-
-            0,     0,   -61,    92,     0,  -323,   861, -1361,
-         1317,     0, -3885, 19741, 19741, -3885,     0,  1317,
-        -1361,   861,  -323,     0,    92,   -61,     0,     0,
-
-            0,     0,   -28,     0,   156,  -457,   763,  -752,
-            0,  1873, -5289, 13068, 24506,     0, -2427,  2389,
-        -1522,   598,     0,  -213,   180,   -79,     0,     0,
-    };
-
-    arm_resample_x64k_12k8(
-        4, (const int16x2_t *)h, hp50, (int16x2_t *)x, y, n);
-}
-#endif /* resample_16k_12k8 */
-
-/**
- * Resample from 32 Khz to 12.8 KHz
- */
-#ifndef resample_32k_12k8
-#define resample_32k_12k8 arm_resample_32k_12k8
-static void arm_resample_32k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    static const int16_t alignas(int32_t) h[2*2*48] = {
-
-            0,   -30,   -31,    46,   107,     0,  -199,  -162,
-          209,   430,     0,  -681,  -526,   658,  1343,     0,
-        -2264, -1943,  2999,  9871, 13116,  9871,  2999, -1943,
-        -2264,     0,  1343,   658,  -526,  -681,     0,   430,
-          209,  -162,  -199,     0,   107,    46,   -31,   -30,
-            0,     0,     0,     0,     0,     0,     0,     0,
-
-            0,   -14,   -39,     0,    90,    78,  -106,  -229,
-            0,   382,   299,  -376,  -761,     0,  1194,   937,
-        -1214, -2644,     0,  6534, 12253, 12253,  6534,     0,
-        -2644, -1214,   937,  1194,     0,  -761,  -376,   299,
-          382,     0,  -229,  -106,    78,    90,     0,   -39,
-          -14,     0,     0,     0,     0,     0,     0,     0,
-
-            0,     0,   -30,   -31,    46,   107,     0,  -199,
-         -162,   209,   430,     0,  -681,  -526,   658,  1343,
-            0, -2264, -1943,  2999,  9871, 13116,  9871,  2999,
-        -1943, -2264,     0,  1343,   658,  -526,  -681,     0,
-          430,   209,  -162,  -199,     0,   107,    46,   -31,
-          -30,     0,     0,     0,     0,     0,     0,     0,
-
-            0,     0,   -14,   -39,     0,    90,    78,  -106,
-         -229,     0,   382,   299,  -376,  -761,     0,  1194,
-          937, -1214, -2644,     0,  6534, 12253, 12253,  6534,
-            0, -2644, -1214,   937,  1194,     0,  -761,  -376,
-          299,   382,     0,  -229,  -106,    78,    90,     0,
-          -39,   -14,     0,     0,     0,     0,     0,     0,
-    };
-
-    arm_resample_x64k_12k8(
-        2, (const int16x2_t *)h, hp50, (int16x2_t *)x, y, n);
-}
-#endif /* resample_32k_12k8 */
-
-/**
- * Resample from 24 Khz to 12.8 KHz
- */
-#ifndef resample_24k_12k8
-#define resample_24k_12k8 arm_resample_24k_12k8
-static void arm_resample_24k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    static const int16_t alignas(int32_t) h[2*8*32] = {
-
-            0,   -50,    19,   143,   -93,  -290,   278,   485,
-         -658,  -701,  1396,   901, -3019, -1042, 10276, 17488,
-        10276, -1042, -3019,   901,  1396,  -701,  -658,   485,
-          278,  -290,   -93,   143,    19,   -50,     0,     0,
-
-            0,   -46,     0,   141,   -45,  -305,   185,   543,
-         -501,  -854,  1153,  1249, -2619, -1908,  8712, 17358,
-        11772,     0, -3319,   480,  1593,  -504,  -796,   399,
-          367,  -261,  -142,   138,    40,   -52,    -5,     0,
-
-            0,   -41,   -17,   133,     0,  -304,    91,   574,
-         -334,  -959,   878,  1516, -2143, -2590,  7118, 16971,
-        13161,  1202, -3495,     0,  1731,  -267,  -908,   287,
-          445,  -215,  -188,   125,    62,   -52,   -12,     0,
-
-            0,   -34,   -30,   120,    41,  -291,     0,   577,
-         -164, -1015,   585,  1697, -1618, -3084,  5534, 16337,
-        14406,  2544, -3526,  -523,  1800,     0,  -985,   152,
-          509,  -156,  -230,   104,    83,   -48,   -19,     0,
-
-            0,   -26,   -41,   103,    76,  -265,   -83,   554,
-            0, -1023,   288,  1791, -1070, -3393,  3998, 15474,
-        15474,  3998, -3393, -1070,  1791,   288, -1023,     0,
-          554,   -83,  -265,    76,   103,   -41,   -26,     0,
-
-            0,   -19,   -48,    83,   104,  -230,  -156,   509,
-          152,  -985,     0,  1800,  -523, -3526,  2544, 14406,
-        16337,  5534, -3084, -1618,  1697,   585, -1015,  -164,
-          577,     0,  -291,    41,   120,   -30,   -34,     0,
-
-            0,   -12,   -52,    62,   125,  -188,  -215,   445,
-          287,  -908,  -267,  1731,     0, -3495,  1202, 13161,
-        16971,  7118, -2590, -2143,  1516,   878,  -959,  -334,
-          574,    91,  -304,     0,   133,   -17,   -41,     0,
-
-            0,    -5,   -52,    40,   138,  -142,  -261,   367,
-          399,  -796,  -504,  1593,   480, -3319,     0, 11772,
-        17358,  8712, -1908, -2619,  1249,  1153,  -854,  -501,
-          543,   185,  -305,   -45,   141,     0,   -46,     0,
-
-            0,     0,   -50,    19,   143,   -93,  -290,   278,
-          485,  -658,  -701,  1396,   901, -3019, -1042, 10276,
-        17488, 10276, -1042, -3019,   901,  1396,  -701,  -658,
-          485,   278,  -290,   -93,   143,    19,   -50,     0,
-
-            0,     0,   -46,     0,   141,   -45,  -305,   185,
-          543,  -501,  -854,  1153,  1249, -2619, -1908,  8712,
-        17358, 11772,     0, -3319,   480,  1593,  -504,  -796,
-          399,   367,  -261,  -142,   138,    40,   -52,    -5,
-
-            0,     0,   -41,   -17,   133,     0,  -304,    91,
-          574,  -334,  -959,   878,  1516, -2143, -2590,  7118,
-        16971, 13161,  1202, -3495,     0,  1731,  -267,  -908,
-          287,   445,  -215,  -188,   125,    62,   -52,   -12,
-
-            0,     0,   -34,   -30,   120,    41,  -291,     0,
-          577,  -164, -1015,   585,  1697, -1618, -3084,  5534,
-        16337, 14406,  2544, -3526,  -523,  1800,     0,  -985,
-          152,   509,  -156,  -230,   104,    83,   -48,   -19,
-
-            0,     0,   -26,   -41,   103,    76,  -265,   -83,
-          554,     0, -1023,   288,  1791, -1070, -3393,  3998,
-        15474, 15474,  3998, -3393, -1070,  1791,   288, -1023,
-            0,   554,   -83,  -265,    76,   103,   -41,   -26,
-
-            0,     0,   -19,   -48,    83,   104,  -230,  -156,
-          509,   152,  -985,     0,  1800,  -523, -3526,  2544,
-        14406, 16337,  5534, -3084, -1618,  1697,   585, -1015,
-         -164,   577,     0,  -291,    41,   120,   -30,   -34,
-
-            0,     0,   -12,   -52,    62,   125,  -188,  -215,
-          445,   287,  -908,  -267,  1731,     0, -3495,  1202,
-        13161, 16971,  7118, -2590, -2143,  1516,   878,  -959,
-         -334,   574,    91,  -304,     0,   133,   -17,   -41,
-
-            0,     0,    -5,   -52,    40,   138,  -142,  -261,
-          367,   399,  -796,  -504,  1593,   480, -3319,     0,
-        11772, 17358,  8712, -1908, -2619,  1249,  1153,  -854,
-         -501,   543,   185,  -305,   -45,   141,     0,   -46,
-    };
-
-    arm_resample_x192k_12k8(
-        8, (const int16x2_t *)h, hp50, (int16x2_t *)x, y, n);
-}
-#endif /* resample_24k_12k8 */
-
-/**
- * Resample from 48 Khz to 12.8 KHz
- */
-#ifndef resample_48k_12k8
-#define resample_48k_12k8 arm_resample_48k_12k8
-static void arm_resample_48k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    static const int16_t alignas(int32_t) h[2*4*64] = {
-
-            0,   -13,   -25,   -20,    10,    51,    71,    38,
-          -47,  -133,  -145,   -42,   139,   277,   242,     0,
-         -329,  -511,  -351,   144,   698,   895,   450,  -535,
-        -1510, -1697,  -521,  1999,  5138,  7737,  8744,  7737,
-         5138,  1999,  -521, -1697, -1510,  -535,   450,   895,
-          698,   144,  -351,  -511,  -329,     0,   242,   277,
-          139,   -42,  -145,  -133,   -47,    38,    71,    51,
-           10,   -20,   -25,   -13,     0,     0,     0,     0,
-
-            0,    -9,   -23,   -24,     0,    41,    71,    52,
-          -23,  -115,  -152,   -78,    92,   254,   272,    76,
-         -251,  -493,  -427,     0,   576,   900,   624,  -262,
-        -1309, -1763,  -954,  1272,  4356,  7203,  8679,  8169,
-         5886,  2767,     0, -1542, -1660,  -809,   240,   848,
-          796,   292,  -252,  -507,  -398,   -82,   199,   288,
-          183,     0,  -130,  -145,   -71,    20,    69,    60,
-           20,   -15,   -26,   -17,    -3,     0,     0,     0,
-
-            0,    -6,   -20,   -26,    -8,    31,    67,    62,
-            0,   -94,  -152,  -108,    45,   223,   287,   143,
-         -167,  -454,  -480,  -134,   439,   866,   758,     0,
-        -1071, -1748, -1295,   601,  3559,  6580,  8485,  8485,
-         6580,  3559,   601, -1295, -1748, -1071,     0,   758,
-          866,   439,  -134,  -480,  -454,  -167,   143,   287,
-          223,    45,  -108,  -152,   -94,     0,    62,    67,
-           31,    -8,   -26,   -20,    -6,     0,     0,     0,
-
-            0,    -3,   -17,   -26,   -15,    20,    60,    69,
-           20,   -71,  -145,  -130,     0,   183,   288,   199,
-          -82,  -398,  -507,  -252,   292,   796,   848,   240,
-         -809, -1660, -1542,     0,  2767,  5886,  8169,  8679,
-         7203,  4356,  1272,  -954, -1763, -1309,  -262,   624,
-          900,   576,     0,  -427,  -493,  -251,    76,   272,
-          254,    92,   -78,  -152,  -115,   -23,    52,    71,
-           41,     0,   -24,   -23,    -9,     0,     0,     0,
-
-            0,     0,   -13,   -25,   -20,    10,    51,    71,
-           38,   -47,  -133,  -145,   -42,   139,   277,   242,
-            0,  -329,  -511,  -351,   144,   698,   895,   450,
-         -535, -1510, -1697,  -521,  1999,  5138,  7737,  8744,
-         7737,  5138,  1999,  -521, -1697, -1510,  -535,   450,
-          895,   698,   144,  -351,  -511,  -329,     0,   242,
-          277,   139,   -42,  -145,  -133,   -47,    38,    71,
-           51,    10,   -20,   -25,   -13,     0,     0,     0,
-
-            0,     0,    -9,   -23,   -24,     0,    41,    71,
-           52,   -23,  -115,  -152,   -78,    92,   254,   272,
-           76,  -251,  -493,  -427,     0,   576,   900,   624,
-         -262, -1309, -1763,  -954,  1272,  4356,  7203,  8679,
-         8169,  5886,  2767,     0, -1542, -1660,  -809,   240,
-          848,   796,   292,  -252,  -507,  -398,   -82,   199,
-          288,   183,     0,  -130,  -145,   -71,    20,    69,
-           60,    20,   -15,   -26,   -17,    -3,     0,     0,
-
-            0,     0,    -6,   -20,   -26,    -8,    31,    67,
-           62,     0,   -94,  -152,  -108,    45,   223,   287,
-          143,  -167,  -454,  -480,  -134,   439,   866,   758,
-            0, -1071, -1748, -1295,   601,  3559,  6580,  8485,
-         8485,  6580,  3559,   601, -1295, -1748, -1071,     0,
-          758,   866,   439,  -134,  -480,  -454,  -167,   143,
-          287,   223,    45,  -108,  -152,   -94,     0,    62,
-           67,    31,    -8,   -26,   -20,    -6,     0,     0,
-
-            0,     0,    -3,   -17,   -26,   -15,    20,    60,
-           69,    20,   -71,  -145,  -130,     0,   183,   288,
-          199,   -82,  -398,  -507,  -252,   292,   796,   848,
-          240,  -809, -1660, -1542,     0,  2767,  5886,  8169,
-         8679,  7203,  4356,  1272,  -954, -1763, -1309,  -262,
-          624,   900,   576,     0,  -427,  -493,  -251,    76,
-          272,   254,    92,   -78,  -152,  -115,   -23,    52,
-           71,    41,     0,   -24,   -23,    -9,     0,     0,
-    };
-
-    arm_resample_x192k_12k8(
-        4, (const int16x2_t *)h, hp50, (int16x2_t *)x, y, n);
-}
-#endif /* resample_48k_12k8 */
-
-/**
- * Return vector of correlations
- */
-#ifndef correlate
-#define correlate arm_correlate
-static void arm_correlate(
-    const int16_t *a, const int16_t *b, int n, float *y, int nc)
-{
-    /* --- Check alignment of `b` --- */
-
-    if ((uintptr_t)b & 3)
-        *(y++) = dot(a, b--, n), nc--;
-
-    /* --- Processing by pair --- */
-
-    for ( ; nc >= 2; nc -= 2) {
-        const int16x2_t *an = (const int16x2_t *)(a  );
-        const int16x2_t *bn = (const int16x2_t *)(b--);
-
-        int16x2_t ax, b0, b1;
-        int64_t v0 = 0, v1 = 0;
-
-        b1 = (int16x2_t)*(b--) << 16;
-
-        for (int i = 0; i < (n >> 4); i++ )
-            for (int j = 0; j < 4; j++) {
-
-                ax = *(an++), b0 = *(bn++);
-                v0 = __smlald (ax, b0, v0);
-                v1 = __smlaldx(ax, __pkhbt(b0, b1), v1);
-
-                ax = *(an++), b1 = *(bn++);
-                v0 = __smlald (ax, b1, v0);
-                v1 = __smlaldx(ax, __pkhbt(b1, b0), v1);
-            }
-
-        *(y++) = (float)((int32_t)((v0 + (1 << 5)) >> 6));
-        *(y++) = (float)((int32_t)((v1 + (1 << 5)) >> 6));
-    }
-
-    /* --- Odd element count --- */
-
-    if (nc > 0)
-        *(y++) = dot(a, b, n);
-}
-#endif /* correlate */
-
-#endif /* __ARM_FEATURE_SIMD32 */
diff --git a/system/embdrv/lc3/src/ltpf_neon.h b/system/embdrv/lc3/src/ltpf_neon.h
deleted file mode 100644
index 240b8c7..0000000
--- a/system/embdrv/lc3/src/ltpf_neon.h
+++ /dev/null
@@ -1,256 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#if __ARM_NEON && __ARM_ARCH_ISA_A64
-
-#ifndef TEST_NEON
-#include <arm_neon.h>
-#endif /* TEST_NEON */
-
-
-/**
- * Import
- */
-
-static inline int32_t filter_hp50(struct lc3_ltpf_hp50_state *, int32_t);
-
-
-/**
- * Resample from 16 Khz to 12.8 KHz
- */
-#ifndef resample_16k_12k8
-#define resample_16k_12k8 neon_resample_16k_12k8
-LC3_HOT static void neon_resample_16k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    static const int16_t h[4][20] = {
-
-    {   -61,   214,  -398,   417,     0, -1052,  2686, -4529,  5997, 26233,
-       5997, -4529,  2686, -1052,     0,   417,  -398,   214,   -61,     0 },
-
-    {   -79,   180,  -213,     0,   598, -1522,  2389, -2427,     0, 24506,
-      13068, -5289,  1873,     0,  -752,   763,  -457,   156,     0,   -28 },
-
-    {   -61,    92,     0,  -323,   861, -1361,  1317,     0, -3885, 19741,
-      19741, -3885,     0,  1317, -1361,   861,  -323,     0,    92,   -61 },
-
-    {   -28,     0,   156,  -457,   763,  -752,     0,  1873, -5289, 13068,
-      24506,     0, -2427,  2389, -1522,   598,     0,  -213,   180,   -79 },
-
-    };
-
-    x -= 20 - 1;
-
-    for (int i = 0; i < 5*n; i += 5) {
-        const int16_t *hn = h[i & 3];
-        const int16_t *xn = x + (i >> 2);
-        int32x4_t un;
-
-        un = vmull_s16(    vld1_s16(xn), vld1_s16(hn)), xn += 4, hn += 4;
-        un = vmlal_s16(un, vld1_s16(xn), vld1_s16(hn)), xn += 4, hn += 4;
-        un = vmlal_s16(un, vld1_s16(xn), vld1_s16(hn)), xn += 4, hn += 4;
-        un = vmlal_s16(un, vld1_s16(xn), vld1_s16(hn)), xn += 4, hn += 4;
-        un = vmlal_s16(un, vld1_s16(xn), vld1_s16(hn)), xn += 4, hn += 4;
-
-        int32_t yn = filter_hp50(hp50, vaddvq_s32(un));
-        *(y++) = (yn + (1 << 15)) >> 16;
-    }
-}
-#endif /* resample_16k_12k8 */
-
-/**
- * Resample from 32 Khz to 12.8 KHz
- */
-#ifndef resample_32k_12k8
-#define resample_32k_12k8 neon_resample_32k_12k8
-LC3_HOT static void neon_resample_32k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    x -= 40 - 1;
-
-    static const int16_t h[2][40] = {
-
-    {   -30,   -31,    46,   107,     0,  -199,  -162,   209,   430,     0,
-       -681,  -526,   658,  1343,     0, -2264, -1943,  2999,  9871, 13116,
-       9871,  2999, -1943, -2264,     0,  1343,   658,  -526,  -681,     0,
-        430,   209,  -162,  -199,     0,   107,    46,   -31,   -30,     0 },
-
-    {   -14,   -39,     0,    90,    78,  -106,  -229,     0,   382,   299,
-       -376,  -761,     0,  1194,   937, -1214, -2644,     0,  6534, 12253,
-      12253,  6534,     0, -2644, -1214,   937,  1194,     0,  -761,  -376,
-        299,   382,     0,  -229,  -106,    78,    90,     0,   -39,   -14 },
-
-    };
-
-    for (int i = 0; i < 5*n; i += 5) {
-        const int16_t *hn = h[i & 1];
-        const int16_t *xn = x + (i >> 1);
-
-        int32x4_t un = vmull_s16(vld1_s16(xn), vld1_s16(hn));
-        xn += 4, hn += 4;
-
-        for (int i = 1; i < 10; i++)
-            un = vmlal_s16(un, vld1_s16(xn), vld1_s16(hn)), xn += 4, hn += 4;
-
-        int32_t yn = filter_hp50(hp50, vaddvq_s32(un));
-        *(y++) = (yn + (1 << 15)) >> 16;
-    }
-}
-#endif /* resample_32k_12k8 */
-
-/**
- * Resample from 48 Khz to 12.8 KHz
- */
-#ifndef resample_48k_12k8
-#define resample_48k_12k8 neon_resample_48k_12k8
-LC3_HOT static void neon_resample_48k_12k8(
-    struct lc3_ltpf_hp50_state *hp50, const int16_t *x, int16_t *y, int n)
-{
-    static const int16_t alignas(16) h[4][64] = {
-
-    {  -13,   -25,   -20,    10,    51,    71,    38,   -47,  -133,  -145,
-       -42,   139,   277,   242,     0,  -329,  -511,  -351,   144,   698,
-       895,   450,  -535, -1510, -1697,  -521,  1999,  5138,  7737,  8744,
-      7737,  5138,  1999,  -521, -1697, -1510,  -535,   450,   895,   698,
-       144,  -351,  -511,  -329,     0,   242,   277,   139,   -42,  -145,
-      -133,   -47,    38,    71,    51,    10,   -20,   -25,   -13,     0 },
-
-    {   -9,   -23,   -24,     0,    41,    71,    52,   -23,  -115,  -152,
-       -78,    92,   254,   272,    76,  -251,  -493,  -427,     0,   576,
-       900,   624,  -262, -1309, -1763,  -954,  1272,  4356,  7203,  8679,
-      8169,  5886,  2767,     0, -1542, -1660,  -809,   240,   848,   796,
-       292,  -252,  -507,  -398,   -82,   199,   288,   183,     0,  -130,
-      -145,   -71,    20,    69,    60,    20,   -15,   -26,   -17,    -3 },
-
-    {   -6,   -20,   -26,    -8,    31,    67,    62,     0,   -94,  -152,
-      -108,    45,   223,   287,   143,  -167,  -454,  -480,  -134,   439,
-       866,   758,     0, -1071, -1748, -1295,   601,  3559,  6580,  8485,
-      8485,  6580,  3559,   601, -1295, -1748, -1071,     0,   758,   866,
-       439,  -134,  -480,  -454,  -167,   143,   287,   223,    45,  -108,
-      -152,   -94,     0,    62,    67,    31,    -8,   -26,   -20,    -6 },
-
-    {   -3,   -17,   -26,   -15,    20,    60,    69,    20,   -71,  -145,
-      -130,     0,   183,   288,   199,   -82,  -398,  -507,  -252,   292,
-       796,   848,   240,  -809, -1660, -1542,     0,  2767,  5886,  8169,
-      8679,  7203,  4356,  1272,  -954, -1763, -1309,  -262,   624,   900,
-       576,     0,  -427,  -493,  -251,    76,   272,   254,    92,   -78,
-      -152,  -115,   -23,    52,    71,    41,     0,   -24,   -23,    -9 },
-
-    };
-
-    x -= 60 - 1;
-
-    for (int i = 0; i < 15*n; i += 15) {
-        const int16_t *hn = h[i & 3];
-        const int16_t *xn = x + (i >> 2);
-
-        int32x4_t un = vmull_s16(vld1_s16(xn), vld1_s16(hn));
-        xn += 4, hn += 4;
-
-        for (int i = 1; i < 15; i++)
-            un = vmlal_s16(un, vld1_s16(xn), vld1_s16(hn)), xn += 4, hn += 4;
-
-        int32_t yn = filter_hp50(hp50, vaddvq_s32(un));
-        *(y++) = (yn + (1 << 15)) >> 16;
-    }
-}
-#endif /* resample_48k_12k8 */
-
-/**
- * Return dot product of 2 vectors
- */
-#ifndef dot
-#define dot neon_dot
-LC3_HOT static inline float neon_dot(const int16_t *a, const int16_t *b, int n)
-{
-    int64x2_t v = vmovq_n_s64(0);
-
-    for (int i = 0; i < (n >> 4); i++) {
-        int32x4_t u;
-
-        u = vmull_s16(   vld1_s16(a), vld1_s16(b)), a += 4, b += 4;
-        u = vmlal_s16(u, vld1_s16(a), vld1_s16(b)), a += 4, b += 4;
-        v = vpadalq_s32(v, u);
-
-        u = vmull_s16(   vld1_s16(a), vld1_s16(b)), a += 4, b += 4;
-        u = vmlal_s16(u, vld1_s16(a), vld1_s16(b)), a += 4, b += 4;
-        v = vpadalq_s32(v, u);
-    }
-
-    int32_t v32 = (vaddvq_s64(v) + (1 << 5)) >> 6;
-    return (float)v32;
-}
-#endif /* dot */
-
-/**
- * Return vector of correlations
- */
-#ifndef correlate
-#define correlate neon_correlate
-LC3_HOT static void neon_correlate(
-    const int16_t *a, const int16_t *b, int n, float *y, int nc)
-{
-    for ( ; nc >= 4; nc -= 4, b -= 4) {
-        const int16_t *an = (const int16_t *)a;
-        const int16_t *bn = (const int16_t *)b;
-
-        int64x2_t v0 = vmovq_n_s64(0), v1 = v0, v2 = v0, v3 = v0;
-        int16x4_t ax, b0, b1;
-
-        b0 = vld1_s16(bn-4);
-
-        for (int i=0; i < (n >> 4); i++ )
-            for (int j = 0; j < 2; j++) {
-                int32x4_t u0, u1, u2, u3;
-
-                b1 = b0;
-                b0 = vld1_s16(bn), bn += 4;
-                ax = vld1_s16(an), an += 4;
-
-                u0 = vmull_s16(ax, b0);
-                u1 = vmull_s16(ax, vext_s16(b1, b0, 3));
-                u2 = vmull_s16(ax, vext_s16(b1, b0, 2));
-                u3 = vmull_s16(ax, vext_s16(b1, b0, 1));
-
-                b1 = b0;
-                b0 = vld1_s16(bn), bn += 4;
-                ax = vld1_s16(an), an += 4;
-
-                u0 = vmlal_s16(u0, ax, b0);
-                u1 = vmlal_s16(u1, ax, vext_s16(b1, b0, 3));
-                u2 = vmlal_s16(u2, ax, vext_s16(b1, b0, 2));
-                u3 = vmlal_s16(u3, ax, vext_s16(b1, b0, 1));
-
-                v0 = vpadalq_s32(v0, u0);
-                v1 = vpadalq_s32(v1, u1);
-                v2 = vpadalq_s32(v2, u2);
-                v3 = vpadalq_s32(v3, u3);
-            }
-
-        *(y++) = (float)((int32_t)((vaddvq_s64(v0) + (1 << 5)) >> 6));
-        *(y++) = (float)((int32_t)((vaddvq_s64(v1) + (1 << 5)) >> 6));
-        *(y++) = (float)((int32_t)((vaddvq_s64(v2) + (1 << 5)) >> 6));
-        *(y++) = (float)((int32_t)((vaddvq_s64(v3) + (1 << 5)) >> 6));
-    }
-
-    for ( ; nc > 0; nc--)
-        *(y++) = neon_dot(a, b--, n);
-}
-#endif /* correlate */
-
-#endif /* __ARM_NEON && __ARM_ARCH_ISA_A64 */
diff --git a/system/embdrv/lc3/src/mdct.c b/system/embdrv/lc3/src/mdct.c
deleted file mode 100644
index 58c7d78..0000000
--- a/system/embdrv/lc3/src/mdct.c
+++ /dev/null
@@ -1,451 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "tables.h"
-
-#include "mdct_neon.h"
-
-
-/* ----------------------------------------------------------------------------
- *  FFT processing
- * -------------------------------------------------------------------------- */
-
-/**
- * FFT 5 Points
- * x, y            Input and output coefficients, of size 5xn
- * n               Number of interleaved transform to perform (n % 2 = 0)
- */
-#ifndef fft_5
-LC3_HOT static inline void fft_5(
-    const struct lc3_complex *x, struct lc3_complex *y, int n)
-{
-    static const float cos1 =  0.3090169944;  /* cos(-2Pi 1/5) */
-    static const float cos2 = -0.8090169944;  /* cos(-2Pi 2/5) */
-
-    static const float sin1 = -0.9510565163;  /* sin(-2Pi 1/5) */
-    static const float sin2 = -0.5877852523;  /* sin(-2Pi 2/5) */
-
-    for (int i = 0; i < n; i++, x++, y+= 5) {
-
-        struct lc3_complex s14 =
-            { x[1*n].re + x[4*n].re, x[1*n].im + x[4*n].im };
-        struct lc3_complex d14 =
-            { x[1*n].re - x[4*n].re, x[1*n].im - x[4*n].im };
-
-        struct lc3_complex s23 =
-            { x[2*n].re + x[3*n].re, x[2*n].im + x[3*n].im };
-        struct lc3_complex d23 =
-            { x[2*n].re - x[3*n].re, x[2*n].im - x[3*n].im };
-
-        y[0].re = x[0].re + s14.re + s23.re;
-
-        y[0].im = x[0].im + s14.im + s23.im;
-
-        y[1].re = x[0].re + s14.re * cos1 - d14.im * sin1
-                          + s23.re * cos2 - d23.im * sin2;
-
-        y[1].im = x[0].im + s14.im * cos1 + d14.re * sin1
-                          + s23.im * cos2 + d23.re * sin2;
-
-        y[2].re = x[0].re + s14.re * cos2 - d14.im * sin2
-                          + s23.re * cos1 + d23.im * sin1;
-
-        y[2].im = x[0].im + s14.im * cos2 + d14.re * sin2
-                          + s23.im * cos1 - d23.re * sin1;
-
-        y[3].re = x[0].re + s14.re * cos2 + d14.im * sin2
-                          + s23.re * cos1 - d23.im * sin1;
-
-        y[3].im = x[0].im + s14.im * cos2 - d14.re * sin2
-                          + s23.im * cos1 + d23.re * sin1;
-
-        y[4].re = x[0].re + s14.re * cos1 + d14.im * sin1
-                          + s23.re * cos2 + d23.im * sin2;
-
-        y[4].im = x[0].im + s14.im * cos1 - d14.re * sin1
-                          + s23.im * cos2 - d23.re * sin2;
-    }
-}
-#endif /* fft_5 */
-
-/**
- * FFT Butterfly 3 Points
- * x, y            Input and output coefficients
- * twiddles        Twiddles factors, determine size of transform
- * n               Number of interleaved transforms
- */
-#ifndef fft_bf3
-LC3_HOT static inline void fft_bf3(
-    const struct lc3_fft_bf3_twiddles *twiddles,
-    const struct lc3_complex *x, struct lc3_complex *y, int n)
-{
-    int n3 = twiddles->n3;
-    const struct lc3_complex (*w0)[2] = twiddles->t;
-    const struct lc3_complex (*w1)[2] = w0 + n3, (*w2)[2] = w1 + n3;
-
-    const struct lc3_complex *x0 = x, *x1 = x0 + n*n3, *x2 = x1 + n*n3;
-    struct lc3_complex *y0 = y, *y1 = y0 + n3, *y2 = y1 + n3;
-
-    for (int i = 0; i < n; i++, y0 += 3*n3, y1 += 3*n3, y2 += 3*n3)
-        for (int j = 0; j < n3; j++, x0++, x1++, x2++) {
-
-            y0[j].re = x0->re + x1->re * w0[j][0].re - x1->im * w0[j][0].im
-                              + x2->re * w0[j][1].re - x2->im * w0[j][1].im;
-
-            y0[j].im = x0->im + x1->im * w0[j][0].re + x1->re * w0[j][0].im
-                              + x2->im * w0[j][1].re + x2->re * w0[j][1].im;
-
-            y1[j].re = x0->re + x1->re * w1[j][0].re - x1->im * w1[j][0].im
-                              + x2->re * w1[j][1].re - x2->im * w1[j][1].im;
-
-            y1[j].im = x0->im + x1->im * w1[j][0].re + x1->re * w1[j][0].im
-                              + x2->im * w1[j][1].re + x2->re * w1[j][1].im;
-
-            y2[j].re = x0->re + x1->re * w2[j][0].re - x1->im * w2[j][0].im
-                              + x2->re * w2[j][1].re - x2->im * w2[j][1].im;
-
-            y2[j].im = x0->im + x1->im * w2[j][0].re + x1->re * w2[j][0].im
-                              + x2->im * w2[j][1].re + x2->re * w2[j][1].im;
-        }
-}
-#endif /* fft_bf3 */
-
-/**
- * FFT Butterfly 2 Points
- * twiddles        Twiddles factors, determine size of transform
- * x, y            Input and output coefficients
- * n               Number of interleaved transforms
- */
-#ifndef fft_bf2
-LC3_HOT static inline void fft_bf2(
-    const struct lc3_fft_bf2_twiddles *twiddles,
-    const struct lc3_complex *x, struct lc3_complex *y, int n)
-{
-    int n2 = twiddles->n2;
-    const struct lc3_complex *w = twiddles->t;
-
-    const struct lc3_complex *x0 = x, *x1 = x0 + n*n2;
-    struct lc3_complex *y0 = y, *y1 = y0 + n2;
-
-    for (int i = 0; i < n; i++, y0 += 2*n2, y1 += 2*n2) {
-
-        for (int j = 0; j < n2; j++, x0++, x1++) {
-
-            y0[j].re = x0->re + x1->re * w[j].re - x1->im * w[j].im;
-            y0[j].im = x0->im + x1->im * w[j].re + x1->re * w[j].im;
-
-            y1[j].re = x0->re - x1->re * w[j].re + x1->im * w[j].im;
-            y1[j].im = x0->im - x1->im * w[j].re - x1->re * w[j].im;
-        }
-    }
-}
-#endif /* fft_bf2 */
-
-/**
- * Perform FFT
- * x, y0, y1       Input, and 2 scratch buffers of size `n`
- * n               Number of points 30, 40, 60, 80, 90, 120, 160, 180, 240
- * return          The buffer `y0` or `y1` that hold the result
- *
- * Input `x` can be the same as the `y0` second scratch buffer
- */
-static struct lc3_complex *fft(const struct lc3_complex *x, int n,
-    struct lc3_complex *y0, struct lc3_complex *y1)
-{
-    struct lc3_complex *y[2] = { y1, y0 };
-    int i2, i3, is = 0;
-
-    /* The number of points `n` can be decomposed as :
-     *
-     *   n = 5^1 * 3^n3 * 2^n2
-     *
-     *   for n = 40, 80, 160        n3 = 0, n2 = [3..5]
-     *       n = 30, 60, 120, 240   n3 = 1, n2 = [1..4]
-     *       n = 90, 180            n3 = 2, n2 = [1..2]
-     *
-     * Note that the expression `n & (n-1) == 0` is equivalent
-     * to the check that `n` is a power of 2. */
-
-    fft_5(x, y[is], n /= 5);
-
-    for (i3 = 0; n & (n-1); i3++, is ^= 1)
-        fft_bf3(lc3_fft_twiddles_bf3[i3], y[is], y[is ^ 1], n /= 3);
-
-    for (i2 = 0; n > 1; i2++, is ^= 1)
-        fft_bf2(lc3_fft_twiddles_bf2[i2][i3], y[is], y[is ^ 1], n >>= 1);
-
-    return y[is];
-}
-
-
-/* ----------------------------------------------------------------------------
- *  MDCT processing
- * -------------------------------------------------------------------------- */
-
-/**
- * Windowing of samples before MDCT
- * dt, sr          Duration and samplerate (size of the transform)
- * x, y            Input current and delayed samples
- * y, d            Output windowed samples, and delayed ones
- */
-LC3_HOT static void mdct_window(enum lc3_dt dt, enum lc3_srate sr,
-    const float *x, float *d, float *y)
-{
-    int ns = LC3_NS(dt, sr), nd = LC3_ND(dt, sr);
-
-    const float *w0 = lc3_mdct_win[dt][sr], *w1 = w0 + ns;
-    const float *w2 = w1, *w3 = w2 + nd;
-
-    const float *x0 = x + ns-nd, *x1 = x0;
-    float *y0 = y + ns/2, *y1 = y0;
-    float *d0 = d, *d1 = d + nd;
-
-    while (x1 > x) {
-        *(--y0) = *d0 * *(w0++) - *(--x1) * *(--w1);
-        *(y1++) = (*(d0++) = *(x0++)) * *(w2++);
-
-        *(--y0) = *d0 * *(w0++) - *(--x1) * *(--w1);
-        *(y1++) = (*(d0++) = *(x0++)) * *(w2++);
-    }
-
-    for (x1 += ns; x0 < x1; ) {
-        *(--y0) = *d0 * *(w0++) - *(--d1) * *(--w1);
-        *(y1++) = (*(d0++) = *(x0++)) * *(w2++) + (*d1 = *(--x1)) * *(--w3);
-
-        *(--y0) = *d0 * *(w0++) - *(--d1) * *(--w1);
-        *(y1++) = (*(d0++) = *(x0++)) * *(w2++) + (*d1 = *(--x1)) * *(--w3);
-    }
-}
-
-/**
- * Pre-rotate MDCT coefficients of N/2 points, before FFT N/4 points FFT
- * def             Size and twiddles factors
- * x, y            Input and output coefficients
- *
- * `x` and y` can be the same buffer
- */
-LC3_HOT static void mdct_pre_fft(const struct lc3_mdct_rot_def *def,
-    const float *x, struct lc3_complex *y)
-{
-    int n4 = def->n4;
-
-    const float *x0 = x, *x1 = x0 + 2*n4;
-    const struct lc3_complex *w0 = def->w, *w1 = w0 + n4;
-    struct lc3_complex *y0 = y, *y1 = y0 + n4;
-
-    while (x0 < x1) {
-        struct lc3_complex u, uw = *(w0++);
-        u.re = - *(--x1) * uw.re + *x0 * uw.im;
-        u.im =   *(x0++) * uw.re + *x1 * uw.im;
-
-        struct lc3_complex v, vw = *(--w1);
-        v.re = - *(--x1) * vw.im + *x0 * vw.re;
-        v.im = - *(x0++) * vw.im - *x1 * vw.re;
-
-        *(y0++) = u;
-        *(--y1) = v;
-    }
-}
-
-/**
- * Post-rotate FFT N/4 points coefficients, resulting MDCT N points
- * def             Size and twiddles factors
- * x, y            Input and output coefficients
- * scale           Scale on output coefficients
- *
- * `x` and y` can be the same buffer
- */
-LC3_HOT static void mdct_post_fft(const struct lc3_mdct_rot_def *def,
-    const struct lc3_complex *x, float *y, float scale)
-{
-    int n4 = def->n4, n8 = n4 >> 1;
-
-    const struct lc3_complex *w0 = def->w + n8, *w1 = w0 - 1;
-    const struct lc3_complex *x0 = x + n8, *x1 = x0 - 1;
-
-    float *y0 = y + n4, *y1 = y0;
-
-    for ( ; y1 > y; x0++, x1--, w0++, w1--) {
-
-        float u0 = (x0->im * w0->im + x0->re * w0->re) * scale;
-        float u1 = (x1->re * w1->im - x1->im * w1->re) * scale;
-
-        float v0 = (x0->re * w0->im - x0->im * w0->re) * scale;
-        float v1 = (x1->im * w1->im + x1->re * w1->re) * scale;
-
-        *(y0++) = u0;  *(y0++) = u1;
-        *(--y1) = v0;  *(--y1) = v1;
-    }
-}
-
-/**
- * Pre-rotate IMDCT coefficients of N points, before FFT N/4 points FFT
- * def             Size and twiddles factors
- * x, y            Input and output coefficients
- *
- * `x` and `y` can be the same buffer
- * The real and imaginary parts of `y` are swapped,
- * to operate on FFT instead of IFFT
- */
-LC3_HOT static void imdct_pre_fft(const struct lc3_mdct_rot_def *def,
-    const float *x, struct lc3_complex *y)
-{
-    int n4 = def->n4;
-
-    const float *x0 = x, *x1 = x0 + 2*n4;
-
-    const struct lc3_complex *w0 = def->w, *w1 = w0 + n4;
-    struct lc3_complex *y0 = y, *y1 = y0 + n4;
-
-    while (x0 < x1) {
-        float u0 = *(x0++), u1 = *(--x1);
-        float v0 = *(x0++), v1 = *(--x1);
-        struct lc3_complex uw = *(w0++), vw = *(--w1);
-
-        (y0  )->re = - u0 * uw.re - u1 * uw.im;
-        (y0++)->im = - u1 * uw.re + u0 * uw.im;
-
-        (--y1)->re = - v1 * vw.re - v0 * vw.im;
-        (  y1)->im = - v0 * vw.re + v1 * vw.im;
-    }
-}
-
-/**
- * Post-rotate FFT N/4 points coefficients, resulting IMDCT N points
- * def             Size and twiddles factors
- * x, y            Input and output coefficients
- * scale           Scale on output coefficients
- *
- * `x` and y` can be the same buffer
- * The real and imaginary parts of `x` are swapped,
- * to operate on FFT instead of IFFT
- */
-LC3_HOT static void imdct_post_fft(const struct lc3_mdct_rot_def *def,
-    const struct lc3_complex *x, float *y, float scale)
-{
-    int n4 = def->n4;
-
-    const struct lc3_complex *w0 = def->w, *w1 = w0 + n4;
-    const struct lc3_complex *x0 = x, *x1 = x0 + n4;
-
-    float *y0 = y, *y1 = y0 + 2*n4;
-
-    while (x0 < x1) {
-        struct lc3_complex uz = *(x0++), vz = *(--x1);
-        struct lc3_complex uw = *(w0++), vw = *(--w1);
-
-        *(y0++) = (uz.re * uw.im - uz.im * uw.re) * scale;
-        *(--y1) = (uz.re * uw.re + uz.im * uw.im) * scale;
-
-        *(--y1) = (vz.re * vw.im - vz.im * vw.re) * scale;
-        *(y0++) = (vz.re * vw.re + vz.im * vw.im) * scale;
-    }
-}
-
-/**
- * Apply windowing of samples
- * dt, sr          Duration and samplerate
- * x, d            Middle half of IMDCT coefficients and delayed samples
- * y, d            Output samples and delayed ones
- */
-LC3_HOT static void imdct_window(enum lc3_dt dt, enum lc3_srate sr,
-    const float *x, float *d, float *y)
-{
-    /* The full MDCT coefficients is given by symmetry :
-     *   T[   0 ..  n/4-1] = -half[n/4-1 .. 0    ]
-     *   T[ n/4 ..  n/2-1] =  half[0     .. n/4-1]
-     *   T[ n/2 .. 3n/4-1] =  half[n/4   .. n/2-1]
-     *   T[3n/4 ..    n-1] =  half[n/2-1 .. n/4  ]  */
-
-    int n4 = LC3_NS(dt, sr) >> 1, nd = LC3_ND(dt, sr);
-    const float *w2 = lc3_mdct_win[dt][sr], *w0 = w2 + 3*n4, *w1 = w0;
-
-    const float *x0 = d + nd-n4, *x1 = x0;
-    float *y0 = y + nd-n4, *y1 = y0, *y2 = d + nd, *y3 = d;
-
-    while (y0 > y) {
-        *(--y0) = *(--x0) - *(x  ) * *(w1++);
-        *(y1++) = *(x1++) + *(x++) * *(--w0);
-
-        *(--y0) = *(--x0) - *(x  ) * *(w1++);
-        *(y1++) = *(x1++) + *(x++) * *(--w0);
-    }
-
-    while (y1 < y + nd) {
-        *(y1++) = *(x1++) + *(x++) * *(--w0);
-        *(y1++) = *(x1++) + *(x++) * *(--w0);
-    }
-
-    while (y1 < y + 2*n4) {
-        *(y1++) = *(x  ) * *(--w0);
-        *(--y2) = *(x++) * *(w2++);
-
-        *(y1++) = *(x  ) * *(--w0);
-        *(--y2) = *(x++) * *(w2++);
-    }
-
-    while (y2 > y3) {
-        *(y3++) = *(x  ) * *(--w0);
-        *(--y2) = *(x++) * *(w2++);
-
-        *(y3++) = *(x  ) * *(--w0);
-        *(--y2) = *(x++) * *(w2++);
-    }
-}
-
-/**
- * Forward MDCT transformation
- */
-void lc3_mdct_forward(enum lc3_dt dt, enum lc3_srate sr,
-    enum lc3_srate sr_dst, const float *x, float *d, float *y)
-{
-    const struct lc3_mdct_rot_def *rot = lc3_mdct_rot[dt][sr];
-    int nf = LC3_NS(dt, sr_dst);
-    int ns = LC3_NS(dt, sr);
-
-    struct lc3_complex buffer[ns/2];
-    struct lc3_complex *z = (struct lc3_complex *)y;
-    union { float *f; struct lc3_complex *z; } u = { .z = buffer };
-
-    mdct_window(dt, sr, x, d, u.f);
-
-    mdct_pre_fft(rot, u.f, u.z);
-    u.z = fft(u.z, ns/2, u.z, z);
-    mdct_post_fft(rot, u.z, y, sqrtf( (2.f*nf) / (ns*ns) ));
-}
-
-/**
- * Inverse MDCT transformation
- */
-void lc3_mdct_inverse(enum lc3_dt dt, enum lc3_srate sr,
-    enum lc3_srate sr_src, const float *x, float *d, float *y)
-{
-    const struct lc3_mdct_rot_def *rot = lc3_mdct_rot[dt][sr];
-    int nf = LC3_NS(dt, sr_src);
-    int ns = LC3_NS(dt, sr);
-
-    struct lc3_complex buffer[ns/2];
-    struct lc3_complex *z = (struct lc3_complex *)y;
-    union { float *f; struct lc3_complex *z; } u = { .z = buffer };
-
-    imdct_pre_fft(rot, x, z);
-    z = fft(z, ns/2, z, u.z);
-    imdct_post_fft(rot, z, u.f, sqrtf(2.f / nf));
-
-    imdct_window(dt, sr, u.f, d, y);
-}
diff --git a/system/embdrv/lc3/src/mdct.h b/system/embdrv/lc3/src/mdct.h
deleted file mode 100644
index 03ae801..0000000
--- a/system/embdrv/lc3/src/mdct.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Compute LD-MDCT (Low Delay Modified Discret Cosinus Transform)
- *
- * Reference : Low Complexity Communication Codec (LC3)
- *             Bluetooth Specification v1.0
- */
-
-#ifndef __LC3_MDCT_H
-#define __LC3_MDCT_H
-
-#include "common.h"
-
-
-/**
- * Forward MDCT transformation
- * dt, sr          Duration and samplerate (size of the transform)
- * sr_dst          Samplerate destination, scale transforam accordingly
- * x, d            Temporal samples and delayed buffer
- * y, d            Output `ns` coefficients and `nd` delayed samples
- *
- * `x` and `y` can be the same buffer
- */
-void lc3_mdct_forward(enum lc3_dt dt, enum lc3_srate sr,
-    enum lc3_srate sr_dst, const float *x, float *d, float *y);
-
-/**
- * Inverse MDCT transformation
- * dt, sr          Duration and samplerate (size of the transform)
- * sr_src          Samplerate source, scale transforam accordingly
- * x, d            Frequency coefficients and delayed buffer
- * y, d            Output `ns` samples and `nd` delayed ones
- *
- * `x` and `y` can be the same buffer
- */
-void lc3_mdct_inverse(enum lc3_dt dt, enum lc3_srate sr,
-    enum lc3_srate sr_src, const float *x, float *d, float *y);
-
-
-#endif /* __LC3_MDCT_H */
diff --git a/system/embdrv/lc3/src/mdct_neon.h b/system/embdrv/lc3/src/mdct_neon.h
deleted file mode 100644
index 950e750..0000000
--- a/system/embdrv/lc3/src/mdct_neon.h
+++ /dev/null
@@ -1,280 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#if __ARM_NEON && __ARM_ARCH_ISA_A64
-
-#ifndef TEST_NEON
-#include <arm_neon.h>
-#endif /* TEST_NEON */
-
-
-/**
- * FFT 5 Points
- * The number of interleaved transform `n` assumed to be even
- */
-#ifndef fft_5
-#define fft_5 neon_fft_5
-LC3_HOT static inline void neon_fft_5(
-    const struct lc3_complex *x, struct lc3_complex *y, int n)
-{
-    static const union { float f[2]; uint64_t u64; }
-        __cos1 = { {  0.3090169944,  0.3090169944 } },
-        __cos2 = { { -0.8090169944, -0.8090169944 } },
-        __sin1 = { {  0.9510565163, -0.9510565163 } },
-        __sin2 = { {  0.5877852523, -0.5877852523 } };
-
-    float32x2_t sin1 = vcreate_f32(__sin1.u64);
-    float32x2_t sin2 = vcreate_f32(__sin2.u64);
-    float32x2_t cos1 = vcreate_f32(__cos1.u64);
-    float32x2_t cos2 = vcreate_f32(__cos2.u64);
-
-    float32x4_t sin1q = vcombine_f32(sin1, sin1);
-    float32x4_t sin2q = vcombine_f32(sin2, sin2);
-    float32x4_t cos1q = vcombine_f32(cos1, cos1);
-    float32x4_t cos2q = vcombine_f32(cos2, cos2);
-
-    for (int i = 0; i < n; i += 2, x += 2, y += 10) {
-
-        float32x4_t y0, y1, y2, y3, y4;
-
-        float32x4_t x0 = vld1q_f32( (float *)(x + 0*n) );
-        float32x4_t x1 = vld1q_f32( (float *)(x + 1*n) );
-        float32x4_t x2 = vld1q_f32( (float *)(x + 2*n) );
-        float32x4_t x3 = vld1q_f32( (float *)(x + 3*n) );
-        float32x4_t x4 = vld1q_f32( (float *)(x + 4*n) );
-
-        float32x4_t s14 = vaddq_f32(x1, x4);
-        float32x4_t s23 = vaddq_f32(x2, x3);
-
-        float32x4_t d14 = vrev64q_f32( vsubq_f32(x1, x4) );
-        float32x4_t d23 = vrev64q_f32( vsubq_f32(x2, x3) );
-
-        y0 = vaddq_f32( x0, vaddq_f32(s14, s23) );
-
-        y4 = vfmaq_f32( x0, s14, cos1q );
-        y4 = vfmaq_f32( y4, s23, cos2q );
-
-        y1 = vfmaq_f32( y4, d14, sin1q );
-        y1 = vfmaq_f32( y1, d23, sin2q );
-
-        y4 = vfmsq_f32( y4, d14, sin1q );
-        y4 = vfmsq_f32( y4, d23, sin2q );
-
-        y3 = vfmaq_f32( x0, s14, cos2q );
-        y3 = vfmaq_f32( y3, s23, cos1q );
-
-        y2 = vfmaq_f32( y3, d14, sin2q );
-        y2 = vfmsq_f32( y2, d23, sin1q );
-
-        y3 = vfmsq_f32( y3, d14, sin2q );
-        y3 = vfmaq_f32( y3, d23, sin1q );
-
-        vst1_f32( (float *)(y + 0), vget_low_f32(y0) );
-        vst1_f32( (float *)(y + 1), vget_low_f32(y1) );
-        vst1_f32( (float *)(y + 2), vget_low_f32(y2) );
-        vst1_f32( (float *)(y + 3), vget_low_f32(y3) );
-        vst1_f32( (float *)(y + 4), vget_low_f32(y4) );
-
-        vst1_f32( (float *)(y + 5), vget_high_f32(y0) );
-        vst1_f32( (float *)(y + 6), vget_high_f32(y1) );
-        vst1_f32( (float *)(y + 7), vget_high_f32(y2) );
-        vst1_f32( (float *)(y + 8), vget_high_f32(y3) );
-        vst1_f32( (float *)(y + 9), vget_high_f32(y4) );
-    }
-}
-#endif /* fft_5 */
-
-/**
- * FFT Butterfly 3 Points
- */
-#ifndef fft_bf3
-#define fft_bf3 neon_fft_bf3
-LC3_HOT static inline void neon_fft_bf3(
-    const struct lc3_fft_bf3_twiddles *twiddles,
-    const struct lc3_complex *x, struct lc3_complex *y, int n)
-{
-    int n3 = twiddles->n3;
-    const struct lc3_complex (*w0_ptr)[2] = twiddles->t;
-    const struct lc3_complex (*w1_ptr)[2] = w0_ptr + n3;
-    const struct lc3_complex (*w2_ptr)[2] = w1_ptr + n3;
-
-    const struct lc3_complex *x0_ptr = x;
-    const struct lc3_complex *x1_ptr = x0_ptr + n*n3;
-    const struct lc3_complex *x2_ptr = x1_ptr + n*n3;
-
-    struct lc3_complex *y0_ptr = y;
-    struct lc3_complex *y1_ptr = y0_ptr + n3;
-    struct lc3_complex *y2_ptr = y1_ptr + n3;
-
-    for (int j, i = 0; i < n; i++,
-            y0_ptr += 3*n3, y1_ptr += 3*n3, y2_ptr += 3*n3) {
-
-        /* --- Process by pair --- */
-
-        for (j = 0; j < (n3 >> 1); j++,
-                x0_ptr += 2, x1_ptr += 2, x2_ptr += 2) {
-
-            float32x4_t x0 = vld1q_f32( (float *)x0_ptr );
-            float32x4_t x1 = vld1q_f32( (float *)x1_ptr );
-            float32x4_t x2 = vld1q_f32( (float *)x2_ptr );
-
-            float32x4_t x1r = vtrn1q_f32( vrev64q_f32(vnegq_f32(x1)), x1 );
-            float32x4_t x2r = vtrn1q_f32( vrev64q_f32(vnegq_f32(x2)), x2 );
-
-            float32x4x2_t wn;
-            float32x4_t yn;
-
-            wn = vld2q_f32( (float *)(w0_ptr + 2*j) );
-
-            yn = vfmaq_f32( x0, x1 , vtrn1q_f32(wn.val[0], wn.val[0]) );
-            yn = vfmaq_f32( yn, x1r, vtrn1q_f32(wn.val[1], wn.val[1]) );
-            yn = vfmaq_f32( yn, x2 , vtrn2q_f32(wn.val[0], wn.val[0]) );
-            yn = vfmaq_f32( yn, x2r, vtrn2q_f32(wn.val[1], wn.val[1]) );
-            vst1q_f32( (float *)(y0_ptr + 2*j), yn );
-
-            wn = vld2q_f32( (float *)(w1_ptr + 2*j) );
-
-            yn = vfmaq_f32( x0, x1 , vtrn1q_f32(wn.val[0], wn.val[0]) );
-            yn = vfmaq_f32( yn, x1r, vtrn1q_f32(wn.val[1], wn.val[1]) );
-            yn = vfmaq_f32( yn, x2 , vtrn2q_f32(wn.val[0], wn.val[0]) );
-            yn = vfmaq_f32( yn, x2r, vtrn2q_f32(wn.val[1], wn.val[1]) );
-            vst1q_f32( (float *)(y1_ptr + 2*j), yn );
-
-            wn = vld2q_f32( (float *)(w2_ptr + 2*j) );
-
-            yn = vfmaq_f32( x0, x1 , vtrn1q_f32(wn.val[0], wn.val[0]) );
-            yn = vfmaq_f32( yn, x1r, vtrn1q_f32(wn.val[1], wn.val[1]) );
-            yn = vfmaq_f32( yn, x2 , vtrn2q_f32(wn.val[0], wn.val[0]) );
-            yn = vfmaq_f32( yn, x2r, vtrn2q_f32(wn.val[1], wn.val[1]) );
-            vst1q_f32( (float *)(y2_ptr + 2*j), yn );
-
-        }
-
-        /* --- Last iteration --- */
-
-        if (n3 & 1) {
-
-            float32x2x2_t wn;
-            float32x2_t yn;
-
-            float32x2_t x0 = vld1_f32( (float *)(x0_ptr++) );
-            float32x2_t x1 = vld1_f32( (float *)(x1_ptr++) );
-            float32x2_t x2 = vld1_f32( (float *)(x2_ptr++) );
-
-            float32x2_t x1r = vtrn1_f32( vrev64_f32(vneg_f32(x1)), x1 );
-            float32x2_t x2r = vtrn1_f32( vrev64_f32(vneg_f32(x2)), x2 );
-
-            wn = vld2_f32( (float *)(w0_ptr + 2*j) );
-
-            yn = vfma_f32( x0, x1 , vtrn1_f32(wn.val[0], wn.val[0]) );
-            yn = vfma_f32( yn, x1r, vtrn1_f32(wn.val[1], wn.val[1]) );
-            yn = vfma_f32( yn, x2 , vtrn2_f32(wn.val[0], wn.val[0]) );
-            yn = vfma_f32( yn, x2r, vtrn2_f32(wn.val[1], wn.val[1]) );
-            vst1_f32( (float *)(y0_ptr + 2*j), yn );
-
-            wn = vld2_f32( (float *)(w1_ptr + 2*j) );
-
-            yn = vfma_f32( x0, x1 , vtrn1_f32(wn.val[0], wn.val[0]) );
-            yn = vfma_f32( yn, x1r, vtrn1_f32(wn.val[1], wn.val[1]) );
-            yn = vfma_f32( yn, x2 , vtrn2_f32(wn.val[0], wn.val[0]) );
-            yn = vfma_f32( yn, x2r, vtrn2_f32(wn.val[1], wn.val[1]) );
-            vst1_f32( (float *)(y1_ptr + 2*j), yn );
-
-            wn = vld2_f32( (float *)(w2_ptr + 2*j) );
-
-            yn = vfma_f32( x0, x1 , vtrn1_f32(wn.val[0], wn.val[0]) );
-            yn = vfma_f32( yn, x1r, vtrn1_f32(wn.val[1], wn.val[1]) );
-            yn = vfma_f32( yn, x2 , vtrn2_f32(wn.val[0], wn.val[0]) );
-            yn = vfma_f32( yn, x2r, vtrn2_f32(wn.val[1], wn.val[1]) );
-            vst1_f32( (float *)(y2_ptr + 2*j), yn );
-        }
-
-    }
-}
-#endif /* fft_bf3 */
-
-/**
- * FFT Butterfly 2 Points
- */
-#ifndef fft_bf2
-#define fft_bf2 neon_fft_bf2
-LC3_HOT static inline void neon_fft_bf2(
-    const struct lc3_fft_bf2_twiddles *twiddles,
-    const struct lc3_complex *x, struct lc3_complex *y, int n)
-{
-    int n2 = twiddles->n2;
-    const struct lc3_complex *w_ptr = twiddles->t;
-
-    const struct lc3_complex *x0_ptr = x;
-    const struct lc3_complex *x1_ptr = x0_ptr + n*n2;
-
-    struct lc3_complex *y0_ptr = y;
-    struct lc3_complex *y1_ptr = y0_ptr + n2;
-
-    for (int j, i = 0; i < n; i++, y0_ptr += 2*n2, y1_ptr += 2*n2) {
-
-        /* --- Process by pair --- */
-
-        for (j = 0; j < (n2 >> 1); j++, x0_ptr += 2, x1_ptr += 2) {
-
-            float32x4_t x0 = vld1q_f32( (float *)x0_ptr );
-            float32x4_t x1 = vld1q_f32( (float *)x1_ptr );
-            float32x4_t y0, y1;
-
-            float32x4_t x1r = vtrn1q_f32( vrev64q_f32(vnegq_f32(x1)), x1 );
-
-            float32x4_t w = vld1q_f32( (float *)(w_ptr + 2*j) );
-            float32x4_t w_re = vtrn1q_f32(w, w);
-            float32x4_t w_im = vtrn2q_f32(w, w);
-
-            y0 = vfmaq_f32( x0, x1 , w_re );
-            y0 = vfmaq_f32( y0, x1r, w_im );
-            vst1q_f32( (float *)(y0_ptr + 2*j), y0 );
-
-            y1 = vfmsq_f32( x0, x1 , w_re );
-            y1 = vfmsq_f32( y1, x1r, w_im );
-            vst1q_f32( (float *)(y1_ptr + 2*j), y1 );
-        }
-
-        /* --- Last iteration --- */
-
-        if (n2 & 1) {
-
-            float32x2_t x0 = vld1_f32( (float *)(x0_ptr++) );
-            float32x2_t x1 = vld1_f32( (float *)(x1_ptr++) );
-            float32x2_t y0, y1;
-
-            float32x2_t x1r = vtrn1_f32( vrev64_f32(vneg_f32(x1)), x1 );
-
-            float32x2_t w = vld1_f32( (float *)(w_ptr + 2*j) );
-            float32x2_t w_re = vtrn1_f32(w, w);
-            float32x2_t w_im = vtrn2_f32(w, w);
-
-            y0 = vfma_f32( x0, x1 , w_re );
-            y0 = vfma_f32( y0, x1r, w_im );
-            vst1_f32( (float *)(y0_ptr + 2*j), y0 );
-
-            y1 = vfms_f32( x0, x1 , w_re );
-            y1 = vfms_f32( y1, x1r, w_im );
-            vst1_f32( (float *)(y1_ptr + 2*j), y1 );
-        }
-    }
-}
-#endif /* fft_bf2 */
-
-#endif /* __ARM_NEON && __ARM_ARCH_ISA_A64 */
diff --git a/system/embdrv/lc3/src/plc.c b/system/embdrv/lc3/src/plc.c
deleted file mode 100644
index 03911b4..0000000
--- a/system/embdrv/lc3/src/plc.c
+++ /dev/null
@@ -1,61 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "plc.h"
-
-
-/**
- * Reset Packet Loss Concealment state
- */
-void lc3_plc_reset(struct lc3_plc_state *plc)
-{
-    plc->seed = 24607;
-    lc3_plc_suspend(plc);
-}
-
-/**
- * Suspend PLC execution (Good frame received)
- */
-void lc3_plc_suspend(struct lc3_plc_state *plc)
-{
-    plc->count = 1;
-    plc->alpha = 1.0f;
-}
-
-/**
- * Synthesis of a PLC frame
- */
-void lc3_plc_synthesize(enum lc3_dt dt, enum lc3_srate sr,
-    struct lc3_plc_state *plc, const float *x, float *y)
-{
-    uint16_t seed = plc->seed;
-    float alpha = plc->alpha;
-    int ne = LC3_NE(dt, sr);
-
-    alpha *= (plc->count < 4 ? 1.0f :
-              plc->count < 8 ? 0.9f : 0.85f);
-
-    for (int i = 0; i < ne; i++) {
-        seed = (16831 + seed * 12821) & 0xffff;
-        y[i] = alpha * (seed & 0x8000 ? -x[i] : x[i]);
-    }
-
-    plc->seed = seed;
-    plc->alpha = alpha;
-    plc->count++;
-}
diff --git a/system/embdrv/lc3/src/plc.h b/system/embdrv/lc3/src/plc.h
deleted file mode 100644
index 6fda5b5..0000000
--- a/system/embdrv/lc3/src/plc.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Packet Loss Concealment
- *
- * Reference : Low Complexity Communication Codec (LC3)
- *             Bluetooth Specification v1.0
- */
-
-#ifndef __LC3_PLC_H
-#define __LC3_PLC_H
-
-#include "common.h"
-
-
-/**
- * Reset PLC state
- * plc             PLC State to reset
- */
-void lc3_plc_reset(lc3_plc_state_t *plc);
-
-/**
- * Suspend PLC synthesis (Error-free frame decoded)
- * plc             PLC State
- */
-void lc3_plc_suspend(lc3_plc_state_t *plc);
-
-/**
- * Synthesis of a PLC frame
- * dt, sr          Duration and samplerate of the frame
- * plc             PLC State
- * x               Last good spectral coefficients
- * y               Return emulated ones
- *
- * `x` and `y` can be the same buffer
- */
-void lc3_plc_synthesize(enum lc3_dt dt, enum lc3_srate sr,
-    lc3_plc_state_t *plc, const float *x, float *y);
-
-
-#endif /* __LC3_PLC_H */
diff --git a/system/embdrv/lc3/src/sns.c b/system/embdrv/lc3/src/sns.c
deleted file mode 100644
index 56a893c..0000000
--- a/system/embdrv/lc3/src/sns.c
+++ /dev/null
@@ -1,880 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "sns.h"
-#include "tables.h"
-
-
-/* ----------------------------------------------------------------------------
- *  DCT-16
- * -------------------------------------------------------------------------- */
-
-/**
- * Matrix of DCT-16 coefficients
- *
- * M[n][k] = 2f cos( Pi k (2n + 1) / 2N )
- *
- *   k = [0..N-1], n = [0..N-1], N = 16
- *   f = sqrt(1/4N) for k=0, sqrt(1/2N) otherwise
- */
-static const float dct16_m[16][16] = {
-
-    {  2.50000000e-01,  3.51850934e-01,  3.46759961e-01,  3.38329500e-01,
-       3.26640741e-01,  3.11806253e-01,  2.93968901e-01,  2.73300467e-01,
-       2.50000000e-01,  2.24291897e-01,  1.96423740e-01,  1.66663915e-01,
-       1.35299025e-01,  1.02631132e-01,  6.89748448e-02,  3.46542923e-02 },
-
-    {  2.50000000e-01,  3.38329500e-01,  2.93968901e-01,  2.24291897e-01,
-       1.35299025e-01,  3.46542923e-02, -6.89748448e-02, -1.66663915e-01,
-      -2.50000000e-01, -3.11806253e-01, -3.46759961e-01, -3.51850934e-01,
-      -3.26640741e-01, -2.73300467e-01, -1.96423740e-01, -1.02631132e-01 },
-
-     { 2.50000000e-01,  3.11806253e-01,  1.96423740e-01,  3.46542923e-02,
-      -1.35299025e-01, -2.73300467e-01, -3.46759961e-01, -3.38329500e-01,
-      -2.50000000e-01, -1.02631132e-01,  6.89748448e-02,  2.24291897e-01,
-       3.26640741e-01,  3.51850934e-01,  2.93968901e-01,  1.66663915e-01 },
-
-     { 2.50000000e-01,  2.73300467e-01,  6.89748448e-02, -1.66663915e-01,
-      -3.26640741e-01, -3.38329500e-01, -1.96423740e-01,  3.46542923e-02,
-       2.50000000e-01,  3.51850934e-01,  2.93968901e-01,  1.02631132e-01,
-      -1.35299025e-01, -3.11806253e-01, -3.46759961e-01, -2.24291897e-01 },
-
-    {  2.50000000e-01,  2.24291897e-01, -6.89748448e-02, -3.11806253e-01,
-      -3.26640741e-01, -1.02631132e-01,  1.96423740e-01,  3.51850934e-01,
-       2.50000000e-01, -3.46542923e-02, -2.93968901e-01, -3.38329500e-01,
-      -1.35299025e-01,  1.66663915e-01,  3.46759961e-01,  2.73300467e-01 },
-
-    {  2.50000000e-01,  1.66663915e-01, -1.96423740e-01, -3.51850934e-01,
-      -1.35299025e-01,  2.24291897e-01,  3.46759961e-01,  1.02631132e-01,
-      -2.50000000e-01, -3.38329500e-01, -6.89748448e-02,  2.73300467e-01,
-       3.26640741e-01,  3.46542923e-02, -2.93968901e-01, -3.11806253e-01 },
-
-    {  2.50000000e-01,  1.02631132e-01, -2.93968901e-01, -2.73300467e-01,
-       1.35299025e-01,  3.51850934e-01,  6.89748448e-02, -3.11806253e-01,
-      -2.50000000e-01,  1.66663915e-01,  3.46759961e-01,  3.46542923e-02,
-      -3.26640741e-01, -2.24291897e-01,  1.96423740e-01,  3.38329500e-01 },
-
-    {  2.50000000e-01,  3.46542923e-02, -3.46759961e-01, -1.02631132e-01,
-       3.26640741e-01,  1.66663915e-01, -2.93968901e-01, -2.24291897e-01,
-       2.50000000e-01,  2.73300467e-01, -1.96423740e-01, -3.11806253e-01,
-       1.35299025e-01,  3.38329500e-01, -6.89748448e-02, -3.51850934e-01 },
-
-    {  2.50000000e-01, -3.46542923e-02, -3.46759961e-01,  1.02631132e-01,
-       3.26640741e-01, -1.66663915e-01, -2.93968901e-01,  2.24291897e-01,
-       2.50000000e-01, -2.73300467e-01, -1.96423740e-01,  3.11806253e-01,
-       1.35299025e-01, -3.38329500e-01, -6.89748448e-02,  3.51850934e-01 },
-
-    {  2.50000000e-01, -1.02631132e-01, -2.93968901e-01,  2.73300467e-01,
-       1.35299025e-01, -3.51850934e-01,  6.89748448e-02,  3.11806253e-01,
-      -2.50000000e-01, -1.66663915e-01,  3.46759961e-01, -3.46542923e-02,
-      -3.26640741e-01,  2.24291897e-01,  1.96423740e-01, -3.38329500e-01 },
-
-    {  2.50000000e-01, -1.66663915e-01, -1.96423740e-01,  3.51850934e-01,
-      -1.35299025e-01, -2.24291897e-01,  3.46759961e-01, -1.02631132e-01,
-      -2.50000000e-01,  3.38329500e-01, -6.89748448e-02, -2.73300467e-01,
-       3.26640741e-01, -3.46542923e-02, -2.93968901e-01,  3.11806253e-01 },
-
-    {  2.50000000e-01, -2.24291897e-01, -6.89748448e-02,  3.11806253e-01,
-      -3.26640741e-01,  1.02631132e-01,  1.96423740e-01, -3.51850934e-01,
-       2.50000000e-01,  3.46542923e-02, -2.93968901e-01,  3.38329500e-01,
-      -1.35299025e-01, -1.66663915e-01,  3.46759961e-01, -2.73300467e-01 },
-
-    {  2.50000000e-01, -2.73300467e-01,  6.89748448e-02,  1.66663915e-01,
-      -3.26640741e-01,  3.38329500e-01, -1.96423740e-01, -3.46542923e-02,
-       2.50000000e-01, -3.51850934e-01,  2.93968901e-01, -1.02631132e-01,
-      -1.35299025e-01,  3.11806253e-01, -3.46759961e-01,  2.24291897e-01 },
-
-    {  2.50000000e-01, -3.11806253e-01,  1.96423740e-01, -3.46542923e-02,
-      -1.35299025e-01,  2.73300467e-01, -3.46759961e-01,  3.38329500e-01,
-      -2.50000000e-01,  1.02631132e-01,  6.89748448e-02, -2.24291897e-01,
-       3.26640741e-01, -3.51850934e-01,  2.93968901e-01, -1.66663915e-01 },
-
-    {  2.50000000e-01, -3.38329500e-01,  2.93968901e-01, -2.24291897e-01,
-       1.35299025e-01, -3.46542923e-02, -6.89748448e-02,  1.66663915e-01,
-      -2.50000000e-01,  3.11806253e-01, -3.46759961e-01,  3.51850934e-01,
-      -3.26640741e-01,  2.73300467e-01, -1.96423740e-01,  1.02631132e-01 },
-
-    {  2.50000000e-01, -3.51850934e-01,  3.46759961e-01, -3.38329500e-01,
-       3.26640741e-01, -3.11806253e-01,  2.93968901e-01, -2.73300467e-01,
-       2.50000000e-01, -2.24291897e-01,  1.96423740e-01, -1.66663915e-01,
-       1.35299025e-01, -1.02631132e-01,  6.89748448e-02, -3.46542923e-02 },
-
-};
-
-/**
- * Forward DCT-16 transformation
- * x, y            Input and output 16 values
- */
-LC3_HOT static void dct16_forward(const float *x, float *y)
-{
-    for (int i = 0, j; i < 16; i++)
-        for (y[i] = 0, j = 0; j < 16; j++)
-            y[i] += x[j] * dct16_m[j][i];
-}
-
-/**
- * Inverse DCT-16 transformation
- * x, y            Input and output 16 values
- */
-LC3_HOT static void dct16_inverse(const float *x, float *y)
-{
-    for (int i = 0, j; i < 16; i++)
-        for (y[i] = 0, j = 0; j < 16; j++)
-            y[i] += x[j] * dct16_m[i][j];
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Scale factors
- * -------------------------------------------------------------------------- */
-
-/**
- * Scale factors
- * dt, sr          Duration and samplerate of the frame
- * eb              Energy estimation per bands
- * att             1: Attack detected  0: Otherwise
- * scf             Output 16 scale factors
- */
-LC3_HOT static void compute_scale_factors(
-    enum lc3_dt dt, enum lc3_srate sr,
-    const float *eb, bool att, float *scf)
-{
-    /* Pre-emphasis gain table :
-     * Ge[b] = 10 ^ (b * g_tilt) / 630 , b = [0..63] */
-
-    static const float ge_table[LC3_NUM_SRATE][LC3_NUM_BANDS] = {
-
-        [LC3_SRATE_8K] = { /* g_tilt = 14 */
-            1.00000000e+00, 1.05250029e+00, 1.10775685e+00, 1.16591440e+00,
-            1.22712524e+00, 1.29154967e+00, 1.35935639e+00, 1.43072299e+00,
-            1.50583635e+00, 1.58489319e+00, 1.66810054e+00, 1.75567629e+00,
-            1.84784980e+00, 1.94486244e+00, 2.04696827e+00, 2.15443469e+00,
-            2.26754313e+00, 2.38658979e+00, 2.51188643e+00, 2.64376119e+00,
-            2.78255940e+00, 2.92864456e+00, 3.08239924e+00, 3.24422608e+00,
-            3.41454887e+00, 3.59381366e+00, 3.78248991e+00, 3.98107171e+00,
-            4.19007911e+00, 4.41005945e+00, 4.64158883e+00, 4.88527357e+00,
-            5.14175183e+00, 5.41169527e+00, 5.69581081e+00, 5.99484250e+00,
-            6.30957344e+00, 6.64082785e+00, 6.98947321e+00, 7.35642254e+00,
-            7.74263683e+00, 8.14912747e+00, 8.57695899e+00, 9.02725178e+00,
-            9.50118507e+00, 1.00000000e+01, 1.05250029e+01, 1.10775685e+01,
-            1.16591440e+01, 1.22712524e+01, 1.29154967e+01, 1.35935639e+01,
-            1.43072299e+01, 1.50583635e+01, 1.58489319e+01, 1.66810054e+01,
-            1.75567629e+01, 1.84784980e+01, 1.94486244e+01, 2.04696827e+01,
-            2.15443469e+01, 2.26754313e+01, 2.38658979e+01, 2.51188643e+01 },
-
-        [LC3_SRATE_16K] = { /* g_tilt = 18 */
-            1.00000000e+00, 1.06800043e+00, 1.14062492e+00, 1.21818791e+00,
-            1.30102522e+00, 1.38949549e+00, 1.48398179e+00, 1.58489319e+00,
-            1.69266662e+00, 1.80776868e+00, 1.93069773e+00, 2.06198601e+00,
-            2.20220195e+00, 2.35195264e+00, 2.51188643e+00, 2.68269580e+00,
-            2.86512027e+00, 3.05994969e+00, 3.26802759e+00, 3.49025488e+00,
-            3.72759372e+00, 3.98107171e+00, 4.25178630e+00, 4.54090961e+00,
-            4.84969343e+00, 5.17947468e+00, 5.53168120e+00, 5.90783791e+00,
-            6.30957344e+00, 6.73862717e+00, 7.19685673e+00, 7.68624610e+00,
-            8.20891416e+00, 8.76712387e+00, 9.36329209e+00, 1.00000000e+01,
-            1.06800043e+01, 1.14062492e+01, 1.21818791e+01, 1.30102522e+01,
-            1.38949549e+01, 1.48398179e+01, 1.58489319e+01, 1.69266662e+01,
-            1.80776868e+01, 1.93069773e+01, 2.06198601e+01, 2.20220195e+01,
-            2.35195264e+01, 2.51188643e+01, 2.68269580e+01, 2.86512027e+01,
-            3.05994969e+01, 3.26802759e+01, 3.49025488e+01, 3.72759372e+01,
-            3.98107171e+01, 4.25178630e+01, 4.54090961e+01, 4.84969343e+01,
-            5.17947468e+01, 5.53168120e+01, 5.90783791e+01, 6.30957344e+01 },
-
-        [LC3_SRATE_24K] = { /* g_tilt = 22 */
-            1.00000000e+00, 1.08372885e+00, 1.17446822e+00, 1.27280509e+00,
-            1.37937560e+00, 1.49486913e+00, 1.62003281e+00, 1.75567629e+00,
-            1.90267705e+00, 2.06198601e+00, 2.23463373e+00, 2.42173704e+00,
-            2.62450630e+00, 2.84425319e+00, 3.08239924e+00, 3.34048498e+00,
-            3.62017995e+00, 3.92329345e+00, 4.25178630e+00, 4.60778348e+00,
-            4.99358789e+00, 5.41169527e+00, 5.86481029e+00, 6.35586411e+00,
-            6.88803330e+00, 7.46476041e+00, 8.08977621e+00, 8.76712387e+00,
-            9.50118507e+00, 1.02967084e+01, 1.11588399e+01, 1.20931568e+01,
-            1.31057029e+01, 1.42030283e+01, 1.53922315e+01, 1.66810054e+01,
-            1.80776868e+01, 1.95913107e+01, 2.12316686e+01, 2.30093718e+01,
-            2.49359200e+01, 2.70237760e+01, 2.92864456e+01, 3.17385661e+01,
-            3.43959997e+01, 3.72759372e+01, 4.03970086e+01, 4.37794036e+01,
-            4.74450028e+01, 5.14175183e+01, 5.57226480e+01, 6.03882412e+01,
-            6.54444792e+01, 7.09240702e+01, 7.68624610e+01, 8.32980665e+01,
-            9.02725178e+01, 9.78309319e+01, 1.06022203e+02, 1.14899320e+02,
-            1.24519708e+02, 1.34945600e+02, 1.46244440e+02, 1.58489319e+02 },
-
-        [LC3_SRATE_32K] = { /* g_tilt = 26 */
-            1.00000000e+00, 1.09968890e+00, 1.20931568e+00, 1.32987103e+00,
-            1.46244440e+00, 1.60823388e+00, 1.76855694e+00, 1.94486244e+00,
-            2.13874364e+00, 2.35195264e+00, 2.58641621e+00, 2.84425319e+00,
-            3.12779366e+00, 3.43959997e+00, 3.78248991e+00, 4.15956216e+00,
-            4.57422434e+00, 5.03022373e+00, 5.53168120e+00, 6.08312841e+00,
-            6.68954879e+00, 7.35642254e+00, 8.08977621e+00, 8.89623710e+00,
-            9.78309319e+00, 1.07583590e+01, 1.18308480e+01, 1.30102522e+01,
-            1.43072299e+01, 1.57335019e+01, 1.73019574e+01, 1.90267705e+01,
-            2.09235283e+01, 2.30093718e+01, 2.53031508e+01, 2.78255940e+01,
-            3.05994969e+01, 3.36499270e+01, 3.70044512e+01, 4.06933843e+01,
-            4.47500630e+01, 4.92111475e+01, 5.41169527e+01, 5.95118121e+01,
-            6.54444792e+01, 7.19685673e+01, 7.91430346e+01, 8.70327166e+01,
-            9.57089124e+01, 1.05250029e+02, 1.15742288e+02, 1.27280509e+02,
-            1.39968963e+02, 1.53922315e+02, 1.69266662e+02, 1.86140669e+02,
-            2.04696827e+02, 2.25102829e+02, 2.47543082e+02, 2.72220379e+02,
-            2.99357729e+02, 3.29200372e+02, 3.62017995e+02, 3.98107171e+02 },
-
-        [LC3_SRATE_48K] = { /* g_tilt = 30 */
-            1.00000000e+00, 1.11588399e+00, 1.24519708e+00, 1.38949549e+00,
-            1.55051578e+00, 1.73019574e+00, 1.93069773e+00, 2.15443469e+00,
-            2.40409918e+00, 2.68269580e+00, 2.99357729e+00, 3.34048498e+00,
-            3.72759372e+00, 4.15956216e+00, 4.64158883e+00, 5.17947468e+00,
-            5.77969288e+00, 6.44946677e+00, 7.19685673e+00, 8.03085722e+00,
-            8.96150502e+00, 1.00000000e+01, 1.11588399e+01, 1.24519708e+01,
-            1.38949549e+01, 1.55051578e+01, 1.73019574e+01, 1.93069773e+01,
-            2.15443469e+01, 2.40409918e+01, 2.68269580e+01, 2.99357729e+01,
-            3.34048498e+01, 3.72759372e+01, 4.15956216e+01, 4.64158883e+01,
-            5.17947468e+01, 5.77969288e+01, 6.44946677e+01, 7.19685673e+01,
-            8.03085722e+01, 8.96150502e+01, 1.00000000e+02, 1.11588399e+02,
-            1.24519708e+02, 1.38949549e+02, 1.55051578e+02, 1.73019574e+02,
-            1.93069773e+02, 2.15443469e+02, 2.40409918e+02, 2.68269580e+02,
-            2.99357729e+02, 3.34048498e+02, 3.72759372e+02, 4.15956216e+02,
-            4.64158883e+02, 5.17947468e+02, 5.77969288e+02, 6.44946677e+02,
-            7.19685673e+02, 8.03085722e+02, 8.96150502e+02, 1.00000000e+03 },
-    };
-
-    float e[LC3_NUM_BANDS];
-
-    /* --- Copy and padding --- */
-
-    int nb = LC3_MIN(lc3_band_lim[dt][sr][LC3_NUM_BANDS], LC3_NUM_BANDS);
-    int n2 = LC3_NUM_BANDS - nb;
-
-    for (int i2 = 0; i2 < n2; i2++)
-        e[2*i2 + 0] = e[2*i2 + 1] = eb[i2];
-
-    memcpy(e + 2*n2, eb + n2, (nb - n2) * sizeof(float));
-
-    /* --- Smoothing, pre-emphasis and logarithm --- */
-
-    const float *ge = ge_table[sr];
-
-    float e0 = e[0], e1 = e[0], e2;
-    float e_sum = 0;
-
-    for (int i = 0; i < LC3_NUM_BANDS-1; ) {
-        e[i] = (e0 * 0.25f + e1 * 0.5f + (e2 = e[i+1]) * 0.25f) * ge[i];
-        e_sum += e[i++];
-
-        e[i] = (e1 * 0.25f + e2 * 0.5f + (e0 = e[i+1]) * 0.25f) * ge[i];
-        e_sum += e[i++];
-
-        e[i] = (e2 * 0.25f + e0 * 0.5f + (e1 = e[i+1]) * 0.25f) * ge[i];
-        e_sum += e[i++];
-    }
-
-    e[LC3_NUM_BANDS-1] = (e0 * 0.25f + e1 * 0.75f) * ge[LC3_NUM_BANDS-1];
-    e_sum += e[LC3_NUM_BANDS-1];
-
-    float noise_floor = fmaxf(e_sum * (1e-4f / 64), 0x1p-32f);
-
-    for (int i = 0; i < LC3_NUM_BANDS; i++)
-        e[i] = fast_log2f(fmaxf(e[i], noise_floor)) * 0.5f;
-
-    /* --- Grouping & scaling --- */
-
-    float scf_sum;
-
-    scf[0] = (e[0] + e[4]) * 1.f/12 +
-             (e[0] + e[3]) * 2.f/12 +
-             (e[1] + e[2]) * 3.f/12  ;
-    scf_sum = scf[0];
-
-    for (int i = 1; i < 15; i++) {
-        scf[i] = (e[4*i-1] + e[4*i+4]) * 1.f/12 +
-                 (e[4*i  ] + e[4*i+3]) * 2.f/12 +
-                 (e[4*i+1] + e[4*i+2]) * 3.f/12  ;
-        scf_sum += scf[i];
-    }
-
-    scf[15] = (e[59] + e[63]) * 1.f/12 +
-              (e[60] + e[63]) * 2.f/12 +
-              (e[61] + e[62]) * 3.f/12  ;
-    scf_sum += scf[15];
-
-    for (int i = 0; i < 16; i++)
-        scf[i] = 0.85f * (scf[i] - scf_sum * 1.f/16);
-
-    /* --- Attack handling --- */
-
-    if (!att)
-        return;
-
-    float s0, s1 = scf[0], s2 = scf[1], s3 = scf[2], s4 = scf[3];
-    float sn = s1 + s2;
-
-    scf[0] = (sn += s3) * 1.f/3;
-    scf[1] = (sn += s4) * 1.f/4;
-    scf_sum = scf[0] + scf[1];
-
-    for (int i = 2; i < 14; i++, sn -= s0) {
-        s0 = s1, s1 = s2, s2 = s3, s3 = s4, s4 = scf[i+2];
-        scf[i] = (sn += s4) * 1.f/5;
-        scf_sum += scf[i];
-    }
-
-    scf[14] = (sn      ) * 1.f/4;
-    scf[15] = (sn -= s1) * 1.f/3;
-    scf_sum += scf[14] + scf[15];
-
-    for (int i = 0; i < 16; i++)
-        scf[i] = (dt == LC3_DT_7M5 ? 0.3f : 0.5f) *
-                 (scf[i] - scf_sum * 1.f/16);
-}
-
-/**
- * Codebooks
- * scf             Input 16 scale factors
- * lf/hfcb_idx     Output the low and high frequency codebooks index
- */
-LC3_HOT static void resolve_codebooks(
-    const float *scf, int *lfcb_idx, int *hfcb_idx)
-{
-    float dlfcb_max = 0, dhfcb_max = 0;
-    *lfcb_idx = *hfcb_idx = 0;
-
-    for (int icb = 0; icb < 32; icb++) {
-        const float *lfcb = lc3_sns_lfcb[icb];
-        const float *hfcb = lc3_sns_hfcb[icb];
-        float dlfcb = 0, dhfcb = 0;
-
-        for (int i = 0; i < 8; i++) {
-            dlfcb += (scf[  i] - lfcb[i]) * (scf[  i] - lfcb[i]);
-            dhfcb += (scf[8+i] - hfcb[i]) * (scf[8+i] - hfcb[i]);
-        }
-
-        if (icb == 0 || dlfcb < dlfcb_max)
-            *lfcb_idx = icb, dlfcb_max = dlfcb;
-
-        if (icb == 0 || dhfcb < dhfcb_max)
-            *hfcb_idx = icb, dhfcb_max = dhfcb;
-    }
-}
-
-/**
- * Unit energy normalize pulse configuration
- * c               Pulse configuration
- * cn              Normalized pulse configuration
- */
-LC3_HOT static void normalize(const int *c, float *cn)
-{
-    int c2_sum = 0;
-    for (int i = 0; i < 16; i++)
-        c2_sum += c[i] * c[i];
-
-    float c_norm = 1.f / sqrtf(c2_sum);
-
-    for (int i = 0; i < 16; i++)
-        cn[i] = c[i] * c_norm;
-}
-
-/**
- * Sub-procedure of `quantize()`, add unit pulse
- * x, y, n         Transformed residual, and vector of pulses with length
- * start, end      Current number of pulses, limit to reach
- * corr, energy    Correlation (x,y) and y energy, updated at output
- */
-LC3_HOT static void add_pulse(const float *x, int *y, int n,
-    int start, int end, float *corr, float *energy)
-{
-    for (int k = start; k < end; k++) {
-        float best_c2 = (*corr + x[0]) * (*corr + x[0]);
-        float best_e = *energy + 2*y[0] + 1;
-        int nbest = 0;
-
-        for (int i = 1; i < n; i++) {
-            float c2 = (*corr + x[i]) * (*corr + x[i]);
-            float e  = *energy + 2*y[i] + 1;
-
-            if (c2 * best_e > e * best_c2)
-                best_c2 = c2, best_e = e, nbest = i;
-        }
-
-        *corr += x[nbest];
-        *energy += 2*y[nbest] + 1;
-        y[nbest]++;
-    }
-}
-
-/**
- * Quantization of codebooks residual
- * scf             Input 16 scale factors, output quantized version
- * lf/hfcb_idx     Codebooks index
- * c, cn           Output 4 pulse configurations candidates, normalized
- * shape/gain_idx  Output selected shape/gain indexes
- */
-LC3_HOT static void quantize(const float *scf, int lfcb_idx, int hfcb_idx,
-    int (*c)[16], float (*cn)[16], int *shape_idx, int *gain_idx)
-{
-    /* --- Residual --- */
-
-    const float *lfcb = lc3_sns_lfcb[lfcb_idx];
-    const float *hfcb = lc3_sns_hfcb[hfcb_idx];
-    float r[16], x[16];
-
-    for (int i = 0; i < 8; i++) {
-        r[  i] = scf[  i] - lfcb[i];
-        r[8+i] = scf[8+i] - hfcb[i];
-    }
-
-    dct16_forward(r, x);
-
-    /* --- Shape 3 candidate ---
-     * Project to or below pyramid N = 16, K = 6,
-     * then add unit pulses until you reach K = 6, over N = 16 */
-
-    float xm[16];
-    float xm_sum = 0;
-
-    for (int i = 0; i < 16; i++) {
-        xm[i] = fabsf(x[i]);
-        xm_sum += xm[i];
-    }
-
-    float proj_factor = (6 - 1) / fmaxf(xm_sum, 1e-31f);
-    float corr = 0, energy = 0;
-    int npulses = 0;
-
-    for (int i = 0; i < 16; i++) {
-        c[3][i] = floorf(xm[i] * proj_factor);
-        npulses += c[3][i];
-        corr    += c[3][i] * xm[i];
-        energy  += c[3][i] * c[3][i];
-    }
-
-    add_pulse(xm, c[3], 16, npulses, 6, &corr, &energy);
-    npulses = 6;
-
-    /* --- Shape 2 candidate ---
-     * Add unit pulses until you reach K = 8 on shape 3 */
-
-    memcpy(c[2], c[3], sizeof(c[2]));
-
-    add_pulse(xm, c[2], 16, npulses, 8, &corr, &energy);
-    npulses = 8;
-
-    /* --- Shape 1 candidate ---
-     * Remove any unit pulses from shape 2 that are not part of 0 to 9
-     * Update energy and correlation terms accordingly
-     * Add unit pulses until you reach K = 10, over N = 10 */
-
-    memcpy(c[1], c[2], sizeof(c[1]));
-
-    for (int i = 10; i < 16; i++) {
-        c[1][i] = 0;
-        npulses -= c[2][i];
-        corr    -= c[2][i] * xm[i];
-        energy  -= c[2][i] * c[2][i];
-    }
-
-    add_pulse(xm, c[1], 10, npulses, 10, &corr, &energy);
-    npulses = 10;
-
-    /* --- Shape 0 candidate ---
-     * Add unit pulses until you reach K = 1, on shape 1 */
-
-    memcpy(c[0], c[1], sizeof(c[0]));
-
-    add_pulse(xm + 10, c[0] + 10, 6, 0, 1, &corr, &energy);
-
-    /* --- Add sign and unit energy normalize --- */
-
-    for (int j = 0; j < 16; j++)
-        for (int i = 0; i < 4; i++)
-            c[i][j] = x[j] < 0 ? -c[i][j] : c[i][j];
-
-    for (int i = 0; i < 4; i++)
-        normalize(c[i], cn[i]);
-
-    /* --- Determe shape & gain index ---
-     * Search the Mean Square Error, within (shape, gain) combinations */
-
-    float mse_min = INFINITY;
-    *shape_idx = *gain_idx = 0;
-
-    for (int ic = 0; ic < 4; ic++) {
-        const struct lc3_sns_vq_gains *cgains = lc3_sns_vq_gains + ic;
-        float cmse_min = INFINITY;
-        int cgain_idx = 0;
-
-        for (int ig = 0; ig < cgains->count; ig++) {
-            float g = cgains->v[ig];
-
-            float mse = 0;
-            for (int i = 0; i < 16; i++)
-                mse += (x[i] - g * cn[ic][i]) * (x[i] - g * cn[ic][i]);
-
-            if (mse < cmse_min) {
-                cgain_idx = ig,
-                cmse_min = mse;
-            }
-        }
-
-        if (cmse_min < mse_min) {
-            *shape_idx = ic, *gain_idx = cgain_idx;
-            mse_min = cmse_min;
-        }
-    }
-}
-
-/**
- * Unquantization of codebooks residual
- * lf/hfcb_idx     Low and high frequency codebooks index
- * c               Table of normalized pulse configuration
- * shape/gain      Selected shape/gain indexes
- * scf             Return unquantized scale factors
- */
-LC3_HOT static void unquantize(int lfcb_idx, int hfcb_idx,
-    const float *c, int shape, int gain, float *scf)
-{
-    const float *lfcb = lc3_sns_lfcb[lfcb_idx];
-    const float *hfcb = lc3_sns_hfcb[hfcb_idx];
-    float g = lc3_sns_vq_gains[shape].v[gain];
-
-    dct16_inverse(c, scf);
-
-    for (int i = 0; i < 8; i++)
-        scf[i] = lfcb[i] + g * scf[i];
-
-    for (int i = 8; i < 16; i++)
-        scf[i] = hfcb[i-8] + g * scf[i];
-}
-
-/**
- * Sub-procedure of `sns_enumerate()`, enumeration of a vector
- * c, n            Table of pulse configuration, and length
- * idx, ls         Return enumeration set
- */
-static void enum_mvpq(const int *c, int n, int *idx, bool *ls)
-{
-    int ci, i, j;
-
-    /* --- Scan for 1st significant coeff --- */
-
-    for (i = 0, c += n; (ci = *(--c)) == 0 ; i++);
-
-    *idx = 0;
-    *ls = ci < 0;
-
-    /* --- Scan remaining coefficients --- */
-
-    for (i++, j = LC3_ABS(ci); i < n; i++, j += LC3_ABS(ci)) {
-
-        if ((ci = *(--c)) != 0) {
-            *idx = (*idx << 1) | *ls;
-            *ls = ci < 0;
-        }
-
-        *idx += lc3_sns_mpvq_offsets[i][j];
-    }
-}
-
-/**
- * Sub-procedure of `sns_deenumerate()`, deenumeration of a vector
- * idx, ls         Enumeration set
- * npulses         Number of pulses in the set
- * c, n            Table of pulses configuration, and length
- */
-static void deenum_mvpq(int idx, bool ls, int npulses, int *c, int n)
-{
-    int i;
-
-    /* --- Scan for coefficients --- */
-
-    for (i = n-1; i >= 0 && idx; i--) {
-
-        int ci = 0;
-
-        for (ci = 0; idx < lc3_sns_mpvq_offsets[i][npulses - ci]; ci++);
-        idx -= lc3_sns_mpvq_offsets[i][npulses - ci];
-
-        *(c++) = ls ? -ci : ci;
-        npulses -= ci;
-        if (ci > 0) {
-            ls = idx & 1;
-            idx >>= 1;
-        }
-    }
-
-    /* --- Set last significant --- */
-
-    int ci = npulses;
-
-    if (i-- >= 0)
-        *(c++) = ls ? -ci : ci;
-
-    while (i-- >= 0)
-        *(c++) = 0;
-}
-
-/**
- * SNS Enumeration of PVQ configuration
- * shape           Selected shape index
- * c               Selected pulse configuration
- * idx_a, ls_a     Return enumeration set A
- * idx_b, ls_b     Return enumeration set B (shape = 0)
- */
-static void enumerate(int shape, const int *c,
-    int *idx_a, bool *ls_a, int *idx_b, bool *ls_b)
-{
-    enum_mvpq(c, shape < 2 ? 10 : 16, idx_a, ls_a);
-
-    if (shape == 0)
-        enum_mvpq(c + 10, 6, idx_b, ls_b);
-}
-
-/**
- * SNS Deenumeration of PVQ configuration
- * shape           Selected shape index
- * idx_a, ls_a     enumeration set A
- * idx_b, ls_b     enumeration set B (shape = 0)
- * c               Return pulse configuration
- */
-static void deenumerate(int shape,
-    int idx_a, bool ls_a, int idx_b, bool ls_b, int *c)
-{
-    int npulses_a = (const int []){ 10, 10, 8, 6 }[shape];
-
-    deenum_mvpq(idx_a, ls_a, npulses_a, c, shape < 2 ? 10 : 16);
-
-    if (shape == 0)
-        deenum_mvpq(idx_b, ls_b, 1, c + 10, 6);
-    else if (shape == 1)
-        memset(c + 10, 0, 6 * sizeof(*c));
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Filtering
- * -------------------------------------------------------------------------- */
-
-/**
- * Spectral shaping
- * dt, sr          Duration and samplerate of the frame
- * scf_q           Quantized scale factors
- * inv             True on inverse shaping, False otherwise
- * x               Spectral coefficients
- * y               Return shapped coefficients
- *
- * `x` and `y` can be the same buffer
- */
-LC3_HOT static void spectral_shaping(enum lc3_dt dt, enum lc3_srate sr,
-    const float *scf_q, bool inv, const float *x, float *y)
-{
-    /* --- Interpolate scale factors --- */
-
-    float scf[LC3_NUM_BANDS];
-    float s0, s1 = inv ? -scf_q[0] : scf_q[0];
-
-    scf[0] = scf[1] = s1;
-    for (int i = 0; i < 15; i++) {
-        s0 = s1, s1 = inv ? -scf_q[i+1] : scf_q[i+1];
-        scf[4*i+2] = s0 + 0.125f * (s1 - s0);
-        scf[4*i+3] = s0 + 0.375f * (s1 - s0);
-        scf[4*i+4] = s0 + 0.625f * (s1 - s0);
-        scf[4*i+5] = s0 + 0.875f * (s1 - s0);
-    }
-    scf[62] = s1 + 0.125f * (s1 - s0);
-    scf[63] = s1 + 0.375f * (s1 - s0);
-
-    int nb = LC3_MIN(lc3_band_lim[dt][sr][LC3_NUM_BANDS], LC3_NUM_BANDS);
-    int n2 = LC3_NUM_BANDS - nb;
-
-    for (int i2 = 0; i2 < n2; i2++)
-        scf[i2] = 0.5f * (scf[2*i2] + scf[2*i2+1]);
-
-    if (n2 > 0)
-        memmove(scf + n2, scf + 2*n2, (nb - n2) * sizeof(float));
-
-    /* --- Spectral shaping --- */
-
-    const int *lim = lc3_band_lim[dt][sr];
-
-    for (int i = 0, ib = 0; ib < nb; ib++) {
-        float g_sns = fast_exp2f(-scf[ib]);
-
-        for ( ; i < lim[ib+1]; i++)
-            y[i] = x[i] * g_sns;
-    }
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Interface
- * -------------------------------------------------------------------------- */
-
-/**
- * SNS analysis
- */
-void lc3_sns_analyze(enum lc3_dt dt, enum lc3_srate sr,
-    const float *eb, bool att, struct lc3_sns_data *data,
-    const float *x, float *y)
-{
-    /* Processing steps :
-     * - Determine 16 scale factors from bands energy estimation
-     * - Get codebooks indexes that match thoses scale factors
-     * - Quantize the residual with the selected codebook
-     * - The pulse configuration `c[]` is enumerated
-     * - Finally shape the spectrum coefficients accordingly */
-
-    float scf[16], cn[4][16];
-    int c[4][16];
-
-    compute_scale_factors(dt, sr, eb, att, scf);
-
-    resolve_codebooks(scf, &data->lfcb, &data->hfcb);
-
-    quantize(scf, data->lfcb, data->hfcb,
-        c, cn, &data->shape, &data->gain);
-
-    unquantize(data->lfcb, data->hfcb,
-        cn[data->shape], data->shape, data->gain, scf);
-
-    enumerate(data->shape, c[data->shape],
-        &data->idx_a, &data->ls_a, &data->idx_b, &data->ls_b);
-
-    spectral_shaping(dt, sr, scf, false, x, y);
-}
-
-/**
- * SNS synthesis
- */
-void lc3_sns_synthesize(enum lc3_dt dt, enum lc3_srate sr,
-    const lc3_sns_data_t *data, const float *x, float *y)
-{
-    float scf[16], cn[16];
-    int c[16];
-
-    deenumerate(data->shape,
-        data->idx_a, data->ls_a, data->idx_b, data->ls_b, c);
-
-    normalize(c, cn);
-
-    unquantize(data->lfcb, data->hfcb, cn, data->shape, data->gain, scf);
-
-    spectral_shaping(dt, sr, scf, true, x, y);
-}
-
-/**
- * Return number of bits coding the bitstream data
- */
-int lc3_sns_get_nbits(void)
-{
-    return 38;
-}
-
-/**
- * Put bitstream data
- */
-void lc3_sns_put_data(lc3_bits_t *bits, const struct lc3_sns_data *data)
-{
-    /* --- Codebooks --- */
-
-    lc3_put_bits(bits, data->lfcb, 5);
-    lc3_put_bits(bits, data->hfcb, 5);
-
-    /* --- Shape, gain and vectors --- *
-     * Write MSB bit of shape index, next LSB bits of shape and gain,
-     * and MVPQ vectors indexes are muxed */
-
-    int shape_msb = data->shape >> 1;
-    lc3_put_bit(bits, shape_msb);
-
-    if (shape_msb == 0) {
-        const int size_a = 2390004;
-        int submode = data->shape & 1;
-
-        int mux_high = submode == 0 ?
-            2 * (data->idx_b + 1) + data->ls_b : data->gain & 1;
-        int mux_code = mux_high * size_a + data->idx_a;
-
-        lc3_put_bits(bits, data->gain >> submode, 1);
-        lc3_put_bits(bits, data->ls_a, 1);
-        lc3_put_bits(bits, mux_code, 25);
-
-    } else {
-        const int size_a = 15158272;
-        int submode = data->shape & 1;
-
-        int mux_code = submode == 0 ?
-            data->idx_a : size_a + 2 * data->idx_a + (data->gain & 1);
-
-        lc3_put_bits(bits, data->gain >> submode, 2);
-        lc3_put_bits(bits, data->ls_a, 1);
-        lc3_put_bits(bits, mux_code, 24);
-    }
-}
-
-/**
- * Get bitstream data
- */
-int lc3_sns_get_data(lc3_bits_t *bits, struct lc3_sns_data *data)
-{
-    /* --- Codebooks --- */
-
-    *data = (struct lc3_sns_data){
-        .lfcb = lc3_get_bits(bits, 5),
-        .hfcb = lc3_get_bits(bits, 5)
-    };
-
-    /* --- Shape, gain and vectors --- */
-
-    int shape_msb = lc3_get_bit(bits);
-    data->gain = lc3_get_bits(bits, 1 + shape_msb);
-    data->ls_a = lc3_get_bit(bits);
-
-    int mux_code = lc3_get_bits(bits, 25 - shape_msb);
-
-    if (shape_msb == 0) {
-        const int size_a = 2390004;
-
-        if (mux_code >= size_a * 14)
-            return -1;
-
-        data->idx_a = mux_code % size_a;
-        mux_code = mux_code / size_a;
-
-        data->shape = (mux_code < 2);
-
-        if (data->shape == 0) {
-            data->idx_b = (mux_code - 2) / 2;
-            data->ls_b  = (mux_code - 2) % 2;
-        } else {
-            data->gain = (data->gain << 1) + (mux_code % 2);
-        }
-
-    } else {
-        const int size_a = 15158272;
-
-        if (mux_code >= size_a + 1549824)
-            return -1;
-
-        data->shape = 2 + (mux_code >= size_a);
-        if (data->shape == 2) {
-            data->idx_a = mux_code;
-        } else {
-            mux_code -= size_a;
-            data->idx_a = mux_code / 2;
-            data->gain = (data->gain << 1) + (mux_code % 2);
-        }
-    }
-
-    return 0;
-}
diff --git a/system/embdrv/lc3/src/sns.h b/system/embdrv/lc3/src/sns.h
deleted file mode 100644
index 432223c..0000000
--- a/system/embdrv/lc3/src/sns.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Spectral Noise Shaping
- *
- * Reference : Low Complexity Communication Codec (LC3)
- *             Bluetooth Specification v1.0
- */
-
-#ifndef __LC3_SNS_H
-#define __LC3_SNS_H
-
-#include "common.h"
-#include "bits.h"
-
-
-/**
- * Bitstream data
- */
-
-typedef struct lc3_sns_data {
-    int lfcb, hfcb;
-    int shape, gain;
-    int idx_a, idx_b;
-    bool ls_a, ls_b;
-} lc3_sns_data_t;
-
-
-/* ----------------------------------------------------------------------------
- *  Encoding
- * -------------------------------------------------------------------------- */
-
-/**
- * SNS analysis
- * dt, sr          Duration and samplerate of the frame
- * eb              Energy estimation per bands, and count of bands
- * att             1: Attack detected  0: Otherwise
- * data            Return bitstream data
- * x               Spectral coefficients
- * y               Return shapped coefficients
- *
- * `x` and `y` can be the same buffer
- */
-void lc3_sns_analyze(enum lc3_dt dt, enum lc3_srate sr,
-    const float *eb, bool att, lc3_sns_data_t *data,
-    const float *x, float *y);
-
-/**
- * Return number of bits coding the bitstream data
- * return          Bit consumption
- */
-int lc3_sns_get_nbits(void);
-
-/**
- * Put bitstream data
- * bits            Bitstream context
- * data            Bitstream data
- */
-void lc3_sns_put_data(lc3_bits_t *bits, const lc3_sns_data_t *data);
-
-
-/* ----------------------------------------------------------------------------
- *  Decoding
- * -------------------------------------------------------------------------- */
-
-/**
- * Get bitstream data
- * bits            Bitstream context
- * data            Return SNS data
- * return          0: Ok  -1: Invalid SNS data
- */
-int lc3_sns_get_data(lc3_bits_t *bits, lc3_sns_data_t *data);
-
-/**
- * SNS synthesis
- * dt, sr          Duration and samplerate of the frame
- * data            Bitstream data
- * x               Spectral coefficients
- * y               Return shapped coefficients
- *
- * `x` and `y` can be the same buffer
- */
-void lc3_sns_synthesize(enum lc3_dt dt, enum lc3_srate sr,
-    const lc3_sns_data_t *data, const float *x, float *y);
-
-
-#endif /* __LC3_SNS_H */
diff --git a/system/embdrv/lc3/src/spec.c b/system/embdrv/lc3/src/spec.c
deleted file mode 100644
index 7ecf43b..0000000
--- a/system/embdrv/lc3/src/spec.c
+++ /dev/null
@@ -1,904 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "spec.h"
-#include "bits.h"
-#include "tables.h"
-
-
-/* ----------------------------------------------------------------------------
- *  Global Gain / Quantization
- * -------------------------------------------------------------------------- */
-
-/**
- * Resolve quantized gain index offset
- * sr, nbytes      Samplerate and size of the frame
- * return          Gain index offset
- */
-static int resolve_gain_offset(enum lc3_srate sr, int nbytes)
-{
-    int g_off = (nbytes * 8) / (10 * (1 + sr));
-    return 105 + 5*(1 + sr) + LC3_MIN(g_off, 115);
-}
-
-/**
- * Global Gain Estimation
- * dt, sr          Duration and samplerate of the frame
- * x               Spectral coefficients
- * nbits_budget    Number of bits available coding the spectrum
- * nbits_off       Offset on the available bits, temporarily smoothed
- * g_off           Gain index offset
- * reset_off       Return True when the nbits_off must be reset
- * return          The quantized gain value
- */
-LC3_HOT static int estimate_gain(
-    enum lc3_dt dt, enum lc3_srate sr, const float *x,
-    int nbits_budget, float nbits_off, int g_off, bool *reset_off)
-{
-    int ne = LC3_NE(dt, sr) >> 2;
-    int e[ne];
-
-    /* --- Energy (dB) by 4 MDCT blocks --- */
-
-    float x2_max = 0;
-
-    for (int i = 0; i < ne; i++, x += 4) {
-        float x0 = x[0] * x[0];
-        float x1 = x[1] * x[1];
-        float x2 = x[2] * x[2];
-        float x3 = x[3] * x[3];
-
-        x2_max = fmaxf(x2_max, x0);
-        x2_max = fmaxf(x2_max, x1);
-        x2_max = fmaxf(x2_max, x2);
-        x2_max = fmaxf(x2_max, x3);
-
-        e[i] = fast_db_q16(fmaxf(x0 + x1 + x2 + x3, 1e-10f));
-    }
-
-    /* --- Determine gain index --- */
-
-    int nbits = nbits_budget + nbits_off + 0.5f;
-    int g_int = 255 - g_off;
-
-    const int k_20_28 = 20.f/28 * 0x1p16f + 0.5f;
-    const int k_2u7 = 2.7f * 0x1p16f + 0.5f;
-    const int k_1u4 = 1.4f * 0x1p16f + 0.5f;
-
-    for (int i = 128, j, j0 = ne-1, j1 ; i > 0; i >>= 1) {
-        int gn = (g_int - i) * k_20_28;
-        int v = 0;
-
-        for (j = j0; j >= 0 && e[j] < gn; j--);
-
-        for (j1 = j; j >= 0; j--) {
-            int e_diff = e[j] - gn;
-
-            v += e_diff < 0 ? k_2u7 :
-                 e_diff < 43 << 16 ?   e_diff + ( 7 << 16)
-                                   : 2*e_diff - (36 << 16);
-        }
-
-        if (v > nbits * k_1u4)
-            j0 = j1;
-        else
-            g_int = g_int - i;
-    }
-
-    /* --- Limit gain index --- */
-
-    int g_min = x2_max == 0 ? -g_off :
-        ceilf(28 * log10f(sqrtf(x2_max) / (32768 - 0.375f)));
-
-    *reset_off = g_int < g_min || x2_max == 0;
-    if (*reset_off)
-        g_int = g_min;
-
-    return g_int;
-}
-
-/**
- * Global Gain Adjustment
- * sr              Samplerate of the frame
- * g_idx           The estimated quantized gain index
- * nbits           Computed number of bits coding the spectrum
- * nbits_budget    Number of bits available for coding the spectrum
- * return          Gain adjust value (-1 to 2)
- */
-LC3_HOT static int adjust_gain(
-    enum lc3_srate sr, int g_idx, int nbits, int nbits_budget)
-{
-    /* --- Compute delta threshold --- */
-
-    const int *t = (const int [LC3_NUM_SRATE][3]){
-        {  80,  500,  850 }, { 230, 1025, 1700 }, { 380, 1550, 2550 },
-        { 530, 2075, 3400 }, { 680, 2600, 4250 }
-    }[sr];
-
-    int delta, den = 48;
-
-    if (nbits < t[0]) {
-        delta = 3*(nbits + 48);
-
-    } else if (nbits < t[1]) {
-        int n0 = 3*(t[0] + 48), range = t[1] - t[0];
-        delta = n0 * range + (nbits - t[0]) * (t[1] - n0);
-        den *= range;
-
-    } else {
-        delta = LC3_MIN(nbits, t[2]);
-    }
-
-    delta = (delta + den/2) / den;
-
-    /* --- Adjust gain --- */
-
-    if (nbits < nbits_budget - (delta + 2))
-        return -(g_idx > 0);
-
-    if (nbits > nbits_budget)
-        return (g_idx < 255) + (g_idx < 254 && nbits >= nbits_budget + delta);
-
-    return 0;
-}
-
-/**
- * Unquantize gain
- * g_int           Quantization gain value
- * return          Unquantized gain value
- */
-static float unquantize_gain(int g_int)
-{
-    /* Unquantization gain table :
-     * G[i] = 10 ^ (i / 28) , i = [0..64] */
-
-    static const float iq_table[] = {
-        1.00000000e+00, 1.08571112e+00, 1.17876863e+00, 1.27980221e+00,
-        1.38949549e+00, 1.50859071e+00, 1.63789371e+00, 1.77827941e+00,
-        1.93069773e+00, 2.09617999e+00, 2.27584593e+00, 2.47091123e+00,
-        2.68269580e+00, 2.91263265e+00, 3.16227766e+00, 3.43332002e+00,
-        3.72759372e+00, 4.04708995e+00, 4.39397056e+00, 4.77058270e+00,
-        5.17947468e+00, 5.62341325e+00, 6.10540230e+00, 6.62870316e+00,
-        7.19685673e+00, 7.81370738e+00, 8.48342898e+00, 9.21055318e+00,
-        1.00000000e+01, 1.08571112e+01, 1.17876863e+01, 1.27980221e+01,
-        1.38949549e+01, 1.50859071e+01, 1.63789371e+01, 1.77827941e+01,
-        1.93069773e+01, 2.09617999e+01, 2.27584593e+01, 2.47091123e+01,
-        2.68269580e+01, 2.91263265e+01, 3.16227766e+01, 3.43332002e+01,
-        3.72759372e+01, 4.04708995e+01, 4.39397056e+01, 4.77058270e+01,
-        5.17947468e+01, 5.62341325e+01, 6.10540230e+01, 6.62870316e+01,
-        7.19685673e+01, 7.81370738e+01, 8.48342898e+01, 9.21055318e+01,
-        1.00000000e+02, 1.08571112e+02, 1.17876863e+02, 1.27980221e+02,
-        1.38949549e+02, 1.50859071e+02, 1.63789371e+02, 1.77827941e+02,
-        1.93069773e+02
-    };
-
-    float g = iq_table[LC3_ABS(g_int) & 0x3f];
-    for(int n64 = LC3_ABS(g_int) >> 6; n64--; )
-        g *= iq_table[64];
-
-    return g_int >= 0 ? g : 1 / g;
-}
-
-/**
- * Spectrum quantization
- * dt, sr          Duration and samplerate of the frame
- * g_int           Quantization gain value
- * x               Spectral coefficients, scaled as output
- * xq, nq          Output spectral quantized coefficients, and count
- *
- * The spectral coefficients `xq` are stored as :
- *   b0       0:positive or zero  1:negative
- *   b15..b1  Absolute value
- */
-LC3_HOT static void quantize(enum lc3_dt dt, enum lc3_srate sr,
-    int g_int, float *x, uint16_t *xq, int *nq)
-{
-    float g_inv = 1 / unquantize_gain(g_int);
-    int ne = LC3_NE(dt, sr);
-
-    *nq = ne;
-
-    for (int i = 0; i < ne; i += 2) {
-        uint16_t x0, x1;
-
-        x[i+0] *= g_inv;
-        x[i+1] *= g_inv;
-
-        x0 = fminf(fabsf(x[i+0]) + 6.f/16, INT16_MAX);
-        x1 = fminf(fabsf(x[i+1]) + 6.f/16, INT16_MAX);
-
-        xq[i+0] = (x0 << 1) + ((x0 > 0) & (x[i+0] < 0));
-        xq[i+1] = (x1 << 1) + ((x1 > 0) & (x[i+1] < 0));
-
-        *nq = x0 || x1 ? ne : *nq - 2;
-    }
-}
-
-/**
- * Spectrum quantization inverse
- * dt, sr          Duration and samplerate of the frame
- * g_int           Quantization gain value
- * x, nq           Spectral quantized, and count of significants
- * return          Unquantized gain value
- */
-LC3_HOT static float unquantize(enum lc3_dt dt, enum lc3_srate sr,
-    int g_int, float *x, int nq)
-{
-    float g = unquantize_gain(g_int);
-    int i, ne = LC3_NE(dt, sr);
-
-    for (i = 0; i < nq; i++)
-        x[i] = x[i] * g;
-
-    for ( ; i < ne; i++)
-        x[i] = 0;
-
-    return g;
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Spectrum coding
- * -------------------------------------------------------------------------- */
-
-/**
- * Resolve High-bitrate mode according size of the frame
- * sr, nbytes      Samplerate and size of the frame
- * return          True when High-Rate mode enabled
- */
-static int resolve_high_rate(enum lc3_srate sr, int nbytes)
-{
-    return nbytes > 20 * (1 + (int)sr);
-}
-
-/**
- * Bit consumption
- * dt, sr, nbytes  Duration, samplerate and size of the frame
- * x               Spectral quantized coefficients
- * n               Count of significant coefficients, updated on truncation
- * nbits_budget    Truncate to stay in budget, when not zero
- * p_lsb_mode      Return True when LSB's are not AC coded, or NULL
- * return          The number of bits coding the spectrum
- *
- * The spectral coefficients `x` storage is :
- *   b0       0:positive or zero  1:negative
- *   b15..b1  Absolute value
- */
-LC3_HOT static int compute_nbits(
-    enum lc3_dt dt, enum lc3_srate sr, int nbytes,
-    const uint16_t *x, int *n, int nbits_budget, bool *p_lsb_mode)
-{
-    int ne = LC3_NE(dt, sr);
-
-    /* --- Mode and rate --- */
-
-    bool lsb_mode  = nbytes >= 20 * (3 + (int)sr);
-    bool high_rate = resolve_high_rate(sr, nbytes);
-
-    /* --- Loop on quantized coefficients --- */
-
-    int nbits = 0, nbits_lsb = 0;
-    uint8_t state = 0;
-
-    int nbits_end = 0;
-    int n_end = 0;
-
-    nbits_budget = nbits_budget ? nbits_budget * 2048 : INT_MAX;
-
-    for (int i = 0, h = 0; h < 2; h++) {
-        const uint8_t (*lut_coeff)[4] = lc3_spectrum_lookup[high_rate][h];
-
-        for ( ; i < LC3_MIN(*n, (ne + 2) >> (1 - h))
-                && nbits <= nbits_budget; i += 2) {
-
-            const uint8_t *lut = lut_coeff[state];
-            uint16_t a = x[i] >> 1, b = x[i+1] >> 1;
-
-            /* --- Sign values --- */
-
-            int s = (a > 0) + (b > 0);
-            nbits += s * 2048;
-
-            /* --- LSB values Reduce to 2*2 bits MSB values ---
-             * Reduce to 2x2 bits MSB values. The LSB's pair are arithmetic
-             * coded with an escape code followed by 1 bit for each values.
-             * The LSB mode does not arthmetic code the first LSB,
-             * add the sign of the LSB when one of pair was at value 1 */
-
-            int k = 0;
-            int m = (a | b) >> 2;
-
-            if (m) {
-
-                if (lsb_mode) {
-                    nbits += lc3_spectrum_bits[lut[k++]][16] - 2*2048;
-                    nbits_lsb += 2 + (a == 1) + (b == 1);
-                }
-
-                for (m >>= lsb_mode; m; m >>= 1, k++)
-                    nbits += lc3_spectrum_bits[lut[LC3_MIN(k, 3)]][16];
-
-                nbits += k * 2*2048;
-                a >>= k;
-                b >>= k;
-
-                k = LC3_MIN(k, 3);
-            }
-
-            /* --- MSB values --- */
-
-            nbits += lc3_spectrum_bits[lut[k]][a + 4*b];
-
-            /* --- Update state --- */
-
-            if (s && nbits <= nbits_budget) {
-                n_end = i + 2;
-                nbits_end = nbits;
-            }
-
-            state = (state << 4) + (k > 1 ? 12 + k : 1 + (a + b) * (k + 1));
-        }
-    }
-
-    /* --- Return --- */
-
-    *n = n_end;
-
-    if (p_lsb_mode)
-        *p_lsb_mode = lsb_mode &&
-            nbits_end + nbits_lsb * 2048 > nbits_budget;
-
-    if (nbits_budget >= INT_MAX)
-        nbits_end += nbits_lsb * 2048;
-
-    return (nbits_end + 2047) / 2048;
-}
-
-/**
- * Put quantized spectrum
- * bits            Bitstream context
- * dt, sr, nbytes  Duration, samplerate and size of the frame
- * x               Spectral quantized
- * nq, lsb_mode    Count of significants, and LSB discard indication
- *
- * The spectral coefficients `x` storage is :
- *   b0       0:positive or zero  1:negative
- *   b15..b1  Absolute value
- */
-LC3_HOT static void put_quantized(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_srate sr, int nbytes,
-    const uint16_t *x, int nq, bool lsb_mode)
-{
-    int ne = LC3_NE(dt, sr);
-    bool high_rate = resolve_high_rate(sr, nbytes);
-
-    /* --- Loop on quantized coefficients --- */
-
-    uint8_t state = 0;
-
-    for (int i = 0, h = 0; h < 2; h++) {
-        const uint8_t (*lut_coeff)[4] = lc3_spectrum_lookup[high_rate][h];
-
-        for ( ; i < LC3_MIN(nq, (ne + 2) >> (1 - h)); i += 2) {
-
-            const uint8_t *lut = lut_coeff[state];
-            uint16_t a = x[i] >> 1, b = x[i+1] >> 1;
-
-            /* --- LSB values Reduce to 2*2 bits MSB values ---
-             * Reduce to 2x2 bits MSB values. The LSB's pair are arithmetic
-             * coded with an escape code and 1 bits for each values.
-             * The LSB mode discard the first LSB (at this step) */
-
-            int m = (a | b) >> 2;
-            int k = 0, shr = 0;
-
-            if (m) {
-
-                if (lsb_mode)
-                    lc3_put_symbol(bits,
-                        lc3_spectrum_models + lut[k++], 16);
-
-                for (m >>= lsb_mode; m; m >>= 1, k++) {
-                    lc3_put_bit(bits, (a >> k) & 1);
-                    lc3_put_bit(bits, (b >> k) & 1);
-                    lc3_put_symbol(bits,
-                        lc3_spectrum_models + lut[LC3_MIN(k, 3)], 16);
-                }
-
-                a >>= lsb_mode;
-                b >>= lsb_mode;
-
-                shr = k - lsb_mode;
-                k = LC3_MIN(k, 3);
-            }
-
-            /* --- Sign values --- */
-
-            if (a) lc3_put_bit(bits, x[i+0] & 1);
-            if (b) lc3_put_bit(bits, x[i+1] & 1);
-
-            /* --- MSB values --- */
-
-            a >>= shr;
-            b >>= shr;
-
-            lc3_put_symbol(bits, lc3_spectrum_models + lut[k], a + 4*b);
-
-            /* --- Update state --- */
-
-            state = (state << 4) + (k > 1 ? 12 + k : 1 + (a + b) * (k + 1));
-        }
-    }
-}
-
-/**
- * Get quantized spectrum
- * bits            Bitstream context
- * dt, sr, nbytes  Duration, samplerate and size of the frame
- * nq, lsb_mode    Count of significants, and LSB discard indication
- * xq              Return `nq` spectral quantized coefficients
- * nf_seed         Return the noise factor seed associated
- * return          0: Ok  -1: Invalid bitstream data
- */
-LC3_HOT static int get_quantized(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_srate sr, int nbytes,
-    int nq, bool lsb_mode, float *xq, uint16_t *nf_seed)
-{
-    int ne = LC3_NE(dt, sr);
-    bool high_rate = resolve_high_rate(sr, nbytes);
-
-     *nf_seed = 0;
-
-    /* --- Loop on quantized coefficients --- */
-
-    uint8_t state = 0;
-
-    for (int i = 0, h = 0; h < 2; h++) {
-        const uint8_t (*lut_coeff)[4] = lc3_spectrum_lookup[high_rate][h];
-
-        for ( ; i < LC3_MIN(nq, (ne + 2) >> (1 - h)); i += 2) {
-
-            const uint8_t *lut = lut_coeff[state];
-
-            /* --- LSB values ---
-             * Until the symbol read indicates the escape value 16,
-             * read an LSB bit for each values.
-             * The LSB mode discard the first LSB (at this step) */
-
-            int u = 0, v = 0;
-            int k = 0, shl = 0;
-
-            unsigned s = lc3_get_symbol(bits, lc3_spectrum_models + lut[k]);
-
-            if (lsb_mode && s >= 16) {
-                s = lc3_get_symbol(bits, lc3_spectrum_models + lut[++k]);
-                shl++;
-            }
-
-            for ( ; s >= 16 && shl < 14; shl++) {
-                u |= lc3_get_bit(bits) << shl;
-                v |= lc3_get_bit(bits) << shl;
-
-                k += (k < 3);
-                s  = lc3_get_symbol(bits, lc3_spectrum_models + lut[k]);
-            }
-
-            if (s >= 16)
-                return -1;
-
-            /* --- MSB & sign values --- */
-
-            int a = s % 4;
-            int b = s / 4;
-
-            u |= a << shl;
-            v |= b << shl;
-
-            xq[i  ] = u && lc3_get_bit(bits) ? -u : u;
-            xq[i+1] = v && lc3_get_bit(bits) ? -v : v;
-
-            *nf_seed = (*nf_seed + u * i + v * (i+1)) & 0xffff;
-
-            /* --- Update state --- */
-
-            state = (state << 4) + (k > 1 ? 12 + k : 1 + (a + b) * (k + 1));
-        }
-    }
-
-    return 0;
-}
-
-/**
- * Put residual bits of quantization
- * bits            Bitstream context
- * nbits           Maximum number of bits to output
- * x, n            Spectral quantized, and count of significants
- * xf              Scaled spectral coefficients
- *
- * The spectral coefficients `x` storage is :
- *   b0       0:positive or zero  1:negative
- *   b15..b1  Absolute value
- */
-LC3_HOT static void put_residual(
-    lc3_bits_t *bits, int nbits, const uint16_t *x, int n, const float *xf)
-{
-    for (int i = 0; i < n && nbits > 0; i++) {
-
-        if (x[i] == 0)
-            continue;
-
-        float xq = x[i] & 1 ? -(x[i] >> 1) : (x[i] >> 1);
-
-        lc3_put_bit(bits, xf[i] >= xq);
-        nbits--;
-    }
-}
-
-/**
- * Get residual bits of quantization
- * bits            Bitstream context
- * nbits           Maximum number of bits to output
- * x, nq           Spectral quantized, and count of significants
- */
-LC3_HOT static void get_residual(
-    lc3_bits_t *bits, int nbits, float *x, int nq)
-{
-    for (int i = 0; i < nq && nbits > 0; i++) {
-
-        if (x[i] == 0)
-            continue;
-
-        if (lc3_get_bit(bits) == 0)
-            x[i] -= x[i] < 0 ? 5.f/16 : 3.f/16;
-        else
-            x[i] += x[i] > 0 ? 5.f/16 : 3.f/16;
-
-        nbits--;
-    }
-}
-
-/**
- * Put LSB values of quantized spectrum values
- * bits            Bitstream context
- * nbits           Maximum number of bits to output
- * x, n            Spectral quantized, and count of significants
- *
- * The spectral coefficients `x` storage is :
- *   b0       0:positive or zero  1:negative
- *   b15..b1  Absolute value
- */
-LC3_HOT static void put_lsb(
-    lc3_bits_t *bits, int nbits, const uint16_t *x, int n)
-{
-    for (int i = 0; i < n && nbits > 0; i += 2) {
-        uint16_t a = x[i] >> 1, b = x[i+1] >> 1;
-        int a_neg = x[i] & 1, b_neg = x[i+1] & 1;
-
-        if ((a | b) >> 2 == 0)
-            continue;
-
-        if (nbits-- > 0)
-            lc3_put_bit(bits, a & 1);
-
-        if (a == 1 && nbits-- > 0)
-            lc3_put_bit(bits, a_neg);
-
-        if (nbits-- > 0)
-            lc3_put_bit(bits, b & 1);
-
-        if (b == 1 && nbits-- > 0)
-            lc3_put_bit(bits, b_neg);
-    }
-}
-
-/**
- * Get LSB values of quantized spectrum values
- * bits            Bitstream context
- * nbits           Maximum number of bits to output
- * x, nq           Spectral quantized, and count of significants
- * nf_seed         Update the noise factor seed according
- */
-LC3_HOT static void get_lsb(lc3_bits_t *bits,
-    int nbits, float *x, int nq, uint16_t *nf_seed)
-{
-    for (int i = 0; i < nq && nbits > 0; i += 2) {
-
-        float a = fabsf(x[i]), b = fabsf(x[i+1]);
-
-        if (fmaxf(a, b) < 4)
-            continue;
-
-        if (nbits-- > 0 && lc3_get_bit(bits)) {
-            if (a) {
-                x[i] += x[i] < 0 ? -1 : 1;
-                *nf_seed = (*nf_seed + i) & 0xffff;
-            } else if (nbits-- > 0) {
-                x[i] = lc3_get_bit(bits) ? -1 : 1;
-                *nf_seed = (*nf_seed + i) & 0xffff;
-            }
-        }
-
-        if (nbits-- > 0 && lc3_get_bit(bits)) {
-            if (b) {
-                x[i+1] += x[i+1] < 0 ? -1 : 1;
-                *nf_seed = (*nf_seed + i+1) & 0xffff;
-            } else if (nbits-- > 0) {
-                x[i+1] = lc3_get_bit(bits) ? -1 : 1;
-                *nf_seed = (*nf_seed + i+1) & 0xffff;
-            }
-        }
-    }
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Noise coding
- * -------------------------------------------------------------------------- */
-
-/**
- * Estimate noise level
- * dt, bw          Duration and bandwidth of the frame
- * xq, nq          Quantized spectral coefficients
- * x               Quantization scaled spectrum coefficients
- * return          Noise factor (0 to 7)
- *
- * The spectral coefficients `x` storage is :
- *   b0       0:positive or zero  1:negative
- *   b15..b1  Absolute value
- */
-LC3_HOT static int estimate_noise(enum lc3_dt dt, enum lc3_bandwidth bw,
-    const uint16_t *xq, int nq, const float *x)
-{
-    int bw_stop = (dt == LC3_DT_7M5 ? 60 : 80) * (1 + bw);
-    int w = 2 + dt;
-
-    float sum = 0;
-    int i, n = 0, z = 0;
-
-    for (i = 6*(3 + dt) - w; i < LC3_MIN(nq, bw_stop); i++) {
-        z = xq[i] ? 0 : z + 1;
-        if (z > 2*w)
-            sum += fabsf(x[i - w]), n++;
-    }
-
-    for ( ; i < bw_stop + w; i++)
-        if (++z > 2*w)
-            sum += fabsf(x[i - w]), n++;
-
-    int nf = n ? 8 - (int)((16 * sum) / n + 0.5f) : 0;
-
-    return LC3_CLIP(nf, 0, 7);
-}
-
-/**
- * Noise filling
- * dt, bw          Duration and bandwidth of the frame
- * nf, nf_seed     The noise factor and pseudo-random seed
- * g               Quantization gain
- * x, nq           Spectral quantized, and count of significants
- */
-LC3_HOT static void fill_noise(enum lc3_dt dt, enum lc3_bandwidth bw,
-    int nf, uint16_t nf_seed, float g, float *x, int nq)
-{
-    int bw_stop = (dt == LC3_DT_7M5 ? 60 : 80) * (1 + bw);
-    int w = 2 + dt;
-
-    float s = g * (float)(8 - nf) / 16;
-    int i, z = 0;
-
-    for (i = 6*(3 + dt) - w; i < LC3_MIN(nq, bw_stop); i++) {
-        z = x[i] ? 0 : z + 1;
-        if (z > 2*w) {
-            nf_seed = (13849 + nf_seed*31821) & 0xffff;
-            x[i - w] = nf_seed & 0x8000 ? -s : s;
-        }
-    }
-
-    for ( ; i < bw_stop + w; i++)
-        if (++z > 2*w) {
-            nf_seed = (13849 + nf_seed*31821) & 0xffff;
-            x[i - w] = nf_seed & 0x8000 ? -s : s;
-        }
-}
-
-/**
- * Put noise factor
- * bits            Bitstream context
- * nf              Noise factor (0 to 7)
- */
-static void put_noise_factor(lc3_bits_t *bits, int nf)
-{
-    lc3_put_bits(bits, nf, 3);
-}
-
-/**
- * Get noise factor
- * bits            Bitstream context
- * return          Noise factor (0 to 7)
- */
-static int get_noise_factor(lc3_bits_t *bits)
-{
-    return lc3_get_bits(bits, 3);
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Encoding
- * -------------------------------------------------------------------------- */
-
-/**
- * Bit consumption of the number of coded coefficients
- * dt, sr          Duration, samplerate of the frame
- * return          Bit consumpution of the number of coded coefficients
- */
-static int get_nbits_nq(enum lc3_dt dt, enum lc3_srate sr)
-{
-    int ne = LC3_NE(dt, sr);
-    return 4 + (ne > 32) + (ne > 64) + (ne > 128) + (ne > 256);
-}
-
-/**
- * Bit consumption of the arithmetic coder
- * dt, sr, nbytes  Duration, samplerate and size of the frame
- * return          Bit consumption of bitstream data
- */
-static int get_nbits_ac(enum lc3_dt dt, enum lc3_srate sr, int nbytes)
-{
-    return get_nbits_nq(dt, sr) + 3 + LC3_MIN((nbytes-1) / 160, 2);
-}
-
-/**
- * Spectrum analysis
- */
-void lc3_spec_analyze(enum lc3_dt dt, enum lc3_srate sr,
-    int nbytes, bool pitch, const lc3_tns_data_t *tns,
-    struct lc3_spec_analysis *spec, float *x,
-    uint16_t *xq, struct lc3_spec_side *side)
-{
-    bool reset_off;
-
-    /* --- Bit budget --- */
-
-    const int nbits_gain = 8;
-    const int nbits_nf = 3;
-
-    int nbits_budget = 8*nbytes - get_nbits_ac(dt, sr, nbytes) -
-        lc3_bwdet_get_nbits(sr) - lc3_ltpf_get_nbits(pitch) -
-        lc3_sns_get_nbits() - lc3_tns_get_nbits(tns) - nbits_gain - nbits_nf;
-
-    /* --- Global gain --- */
-
-    float nbits_off = spec->nbits_off + spec->nbits_spare;
-    nbits_off = fminf(fmaxf(nbits_off, -40), 40);
-    nbits_off = 0.8f * spec->nbits_off + 0.2f * nbits_off;
-
-    int g_off = resolve_gain_offset(sr, nbytes);
-
-    int g_int = estimate_gain(dt, sr,
-        x, nbits_budget, nbits_off, g_off, &reset_off);
-
-    /* --- Quantization --- */
-
-    quantize(dt, sr, g_int, x, xq, &side->nq);
-
-    int nbits = compute_nbits(dt, sr, nbytes, xq, &side->nq, 0, NULL);
-
-    spec->nbits_off = reset_off ? 0 : nbits_off;
-    spec->nbits_spare = reset_off ? 0 : nbits_budget - nbits;
-
-    /* --- Adjust gain and requantize --- */
-
-    int g_adj = adjust_gain(sr, g_int + g_off, nbits, nbits_budget);
-
-    if (g_adj)
-        quantize(dt, sr, g_adj, x, xq, &side->nq);
-
-    side->g_idx = g_int + g_adj + g_off;
-    nbits = compute_nbits(dt, sr, nbytes,
-        xq, &side->nq, nbits_budget, &side->lsb_mode);
-}
-
-/**
- * Put spectral quantization side data
- */
-void lc3_spec_put_side(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_srate sr, const struct lc3_spec_side *side)
-{
-    int nbits_nq = get_nbits_nq(dt, sr);
-
-    lc3_put_bits(bits, LC3_MAX(side->nq >> 1, 1) - 1, nbits_nq);
-    lc3_put_bits(bits, side->lsb_mode, 1);
-    lc3_put_bits(bits, side->g_idx, 8);
-}
-
-/**
- * Encode spectral coefficients
- */
-void lc3_spec_encode(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_srate sr, enum lc3_bandwidth bw, int nbytes,
-    const uint16_t *xq, const lc3_spec_side_t *side, const float *x)
-{
-    bool lsb_mode = side->lsb_mode;
-    int nq = side->nq;
-
-    put_noise_factor(bits, estimate_noise(dt, bw, xq, nq, x));
-
-    put_quantized(bits, dt, sr, nbytes, xq, nq, lsb_mode);
-
-    int nbits_left = lc3_get_bits_left(bits);
-
-    if (lsb_mode)
-        put_lsb(bits, nbits_left, xq, nq);
-    else
-        put_residual(bits, nbits_left, xq, nq, x);
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Decoding
- * -------------------------------------------------------------------------- */
-
-/**
- * Get spectral quantization side data
- */
-int lc3_spec_get_side(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_srate sr, struct lc3_spec_side *side)
-{
-    int nbits_nq = get_nbits_nq(dt, sr);
-    int ne = LC3_NE(dt, sr);
-
-    side->nq = (lc3_get_bits(bits, nbits_nq) + 1) << 1;
-    side->lsb_mode = lc3_get_bit(bits);
-    side->g_idx = lc3_get_bits(bits, 8);
-
-    return side->nq > ne ? (side->nq = ne), -1 : 0;
-}
-
-/**
- * Decode spectral coefficients
- */
-int lc3_spec_decode(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_srate sr, enum lc3_bandwidth bw,
-    int nbytes, const lc3_spec_side_t *side, float *x)
-{
-    bool lsb_mode = side->lsb_mode;
-    int nq = side->nq;
-    int ret = 0;
-
-    int nf = get_noise_factor(bits);
-    uint16_t nf_seed;
-
-    if ((ret = get_quantized(bits, dt, sr, nbytes,
-                    nq, lsb_mode, x, &nf_seed)) < 0)
-        return ret;
-
-    int nbits_left = lc3_get_bits_left(bits);
-
-    if (lsb_mode)
-        get_lsb(bits, nbits_left, x, nq, &nf_seed);
-    else
-        get_residual(bits, nbits_left, x, nq);
-
-    int g_int = side->g_idx - resolve_gain_offset(sr, nbytes);
-    float g = unquantize(dt, sr, g_int, x, nq);
-
-    if (nq > 2 || x[0] || x[1] || side->g_idx > 0 || nf < 7)
-        fill_noise(dt, bw, nf, nf_seed, g, x, nq);
-
-    return 0;
-}
diff --git a/system/embdrv/lc3/src/spec.h b/system/embdrv/lc3/src/spec.h
deleted file mode 100644
index 091d25f..0000000
--- a/system/embdrv/lc3/src/spec.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Spectral coefficients encoding/decoding
- *
- * Reference : Low Complexity Communication Codec (LC3)
- *             Bluetooth Specification v1.0
- */
-
-#ifndef __LC3_SPEC_H
-#define __LC3_SPEC_H
-
-#include "common.h"
-#include "tables.h"
-#include "bwdet.h"
-#include "ltpf.h"
-#include "tns.h"
-#include "sns.h"
-
-
-/**
- * Spectral quantization side data
- */
-typedef struct lc3_spec_side {
-    int g_idx, nq;
-    bool lsb_mode;
-} lc3_spec_side_t;
-
-
-/* ----------------------------------------------------------------------------
- *  Encoding
- * -------------------------------------------------------------------------- */
-
-/**
- * Spectrum analysis
- * dt, sr, nbytes  Duration, samplerate and size of the frame
- * pitch, tns      Pitch present indication and TNS bistream data
- * spec            Context of analysis
- * x               Spectral coefficients, scaled as output
- * xq, side        Return quantization data
- *
- * The spectral coefficients `xq` storage is :
- *   b0       0:positive or zero  1:negative
- *   b15..b1  Absolute value
- */
-void lc3_spec_analyze(enum lc3_dt dt, enum lc3_srate sr,
-    int nbytes, bool pitch, const lc3_tns_data_t *tns,
-    lc3_spec_analysis_t *spec, float *x, uint16_t *xq, lc3_spec_side_t *side);
-
-/**
- * Put spectral quantization side data
- * bits            Bitstream context
- * dt, sr          Duration and samplerate of the frame
- * side            Spectral quantization side data
- */
-void lc3_spec_put_side(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_srate sr, const lc3_spec_side_t *side);
-
-/**
- * Encode spectral coefficients
- * bits            Bitstream context
- * dt, sr, bw      Duration, samplerate, bandwidth
- * nbytes          and size of the frame
- * xq, side        Quantization data
- * x               Scaled spectral coefficients
- *
- * The spectral coefficients `xq` storage is :
- *   b0       0:positive or zero  1:negative
- *   b15..b1  Absolute value
- */
-void lc3_spec_encode(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_srate sr, enum lc3_bandwidth bw, int nbytes,
-    const uint16_t *xq, const lc3_spec_side_t *side, const float *x);
-
-
-/* ----------------------------------------------------------------------------
- *  Decoding
- * -------------------------------------------------------------------------- */
-
-/**
- * Get spectral quantization side data
- * bits            Bitstream context
- * dt, sr          Duration and samplerate of the frame
- * side            Return quantization side data
- * return          0: Ok  -1: Invalid bandwidth indication
- */
-int lc3_spec_get_side(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_srate sr, lc3_spec_side_t *side);
-
-/**
- * Decode spectral coefficients
- * bits            Bitstream context
- * dt, sr, bw      Duration, samplerate, bandwidth
- * nbytes          and size of the frame
- * side            Quantization side data
- * x               Spectral coefficients
- * return          0: Ok  -1: Invalid bitstream data
- */
-int lc3_spec_decode(lc3_bits_t *bits, enum lc3_dt dt, enum lc3_srate sr,
-    enum lc3_bandwidth bw, int nbytes, const lc3_spec_side_t *side, float *x);
-
-
-#endif /* __LC3_SPEC_H */
diff --git a/system/embdrv/lc3/src/tables.c b/system/embdrv/lc3/src/tables.c
deleted file mode 100644
index aef6fc1..0000000
--- a/system/embdrv/lc3/src/tables.c
+++ /dev/null
@@ -1,3457 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "tables.h"
-
-
-/**
- * Twiddles FFT 3 points
- *
- * T[0..N-1] =
- *   { cos(-2Pi *  i/N) + j sin(-2Pi *  i/N),
- *     cos(-2Pi * 2i/N) + j sin(-2Pi * 2i/N) } , N=15, 45
- */
-
-static const struct lc3_fft_bf3_twiddles fft_twiddles_15 = {
-    .n3 = 15/3, .t = (const struct lc3_complex [][2]){
-        { {  1.0000000e+0, -0.0000000e+0 }, {  1.0000000e+0, -0.0000000e+0 } },
-        { {  9.1354546e-1, -4.0673664e-1 }, {  6.6913061e-1, -7.4314483e-1 } },
-        { {  6.6913061e-1, -7.4314483e-1 }, { -1.0452846e-1, -9.9452190e-1 } },
-        { {  3.0901699e-1, -9.5105652e-1 }, { -8.0901699e-1, -5.8778525e-1 } },
-        { { -1.0452846e-1, -9.9452190e-1 }, { -9.7814760e-1,  2.0791169e-1 } },
-        { { -5.0000000e-1, -8.6602540e-1 }, { -5.0000000e-1,  8.6602540e-1 } },
-        { { -8.0901699e-1, -5.8778525e-1 }, {  3.0901699e-1,  9.5105652e-1 } },
-        { { -9.7814760e-1, -2.0791169e-1 }, {  9.1354546e-1,  4.0673664e-1 } },
-        { { -9.7814760e-1,  2.0791169e-1 }, {  9.1354546e-1, -4.0673664e-1 } },
-        { { -8.0901699e-1,  5.8778525e-1 }, {  3.0901699e-1, -9.5105652e-1 } },
-        { { -5.0000000e-1,  8.6602540e-1 }, { -5.0000000e-1, -8.6602540e-1 } },
-        { { -1.0452846e-1,  9.9452190e-1 }, { -9.7814760e-1, -2.0791169e-1 } },
-        { {  3.0901699e-1,  9.5105652e-1 }, { -8.0901699e-1,  5.8778525e-1 } },
-        { {  6.6913061e-1,  7.4314483e-1 }, { -1.0452846e-1,  9.9452190e-1 } },
-        { {  9.1354546e-1,  4.0673664e-1 }, {  6.6913061e-1,  7.4314483e-1 } },
-    }
-};
-
-static const struct lc3_fft_bf3_twiddles fft_twiddles_45 = {
-    .n3 = 45/3, .t = (const struct lc3_complex [][2]){
-        { {  1.0000000e+0, -0.0000000e+0 }, {  1.0000000e+0, -0.0000000e+0 } },
-        { {  9.9026807e-1, -1.3917310e-1 }, {  9.6126170e-1, -2.7563736e-1 } },
-        { {  9.6126170e-1, -2.7563736e-1 }, {  8.4804810e-1, -5.2991926e-1 } },
-        { {  9.1354546e-1, -4.0673664e-1 }, {  6.6913061e-1, -7.4314483e-1 } },
-        { {  8.4804810e-1, -5.2991926e-1 }, {  4.3837115e-1, -8.9879405e-1 } },
-        { {  7.6604444e-1, -6.4278761e-1 }, {  1.7364818e-1, -9.8480775e-1 } },
-        { {  6.6913061e-1, -7.4314483e-1 }, { -1.0452846e-1, -9.9452190e-1 } },
-        { {  5.5919290e-1, -8.2903757e-1 }, { -3.7460659e-1, -9.2718385e-1 } },
-        { {  4.3837115e-1, -8.9879405e-1 }, { -6.1566148e-1, -7.8801075e-1 } },
-        { {  3.0901699e-1, -9.5105652e-1 }, { -8.0901699e-1, -5.8778525e-1 } },
-        { {  1.7364818e-1, -9.8480775e-1 }, { -9.3969262e-1, -3.4202014e-1 } },
-        { {  3.4899497e-2, -9.9939083e-1 }, { -9.9756405e-1, -6.9756474e-2 } },
-        { { -1.0452846e-1, -9.9452190e-1 }, { -9.7814760e-1,  2.0791169e-1 } },
-        { { -2.4192190e-1, -9.7029573e-1 }, { -8.8294759e-1,  4.6947156e-1 } },
-        { { -3.7460659e-1, -9.2718385e-1 }, { -7.1933980e-1,  6.9465837e-1 } },
-        { { -5.0000000e-1, -8.6602540e-1 }, { -5.0000000e-1,  8.6602540e-1 } },
-        { { -6.1566148e-1, -7.8801075e-1 }, { -2.4192190e-1,  9.7029573e-1 } },
-        { { -7.1933980e-1, -6.9465837e-1 }, {  3.4899497e-2,  9.9939083e-1 } },
-        { { -8.0901699e-1, -5.8778525e-1 }, {  3.0901699e-1,  9.5105652e-1 } },
-        { { -8.8294759e-1, -4.6947156e-1 }, {  5.5919290e-1,  8.2903757e-1 } },
-        { { -9.3969262e-1, -3.4202014e-1 }, {  7.6604444e-1,  6.4278761e-1 } },
-        { { -9.7814760e-1, -2.0791169e-1 }, {  9.1354546e-1,  4.0673664e-1 } },
-        { { -9.9756405e-1, -6.9756474e-2 }, {  9.9026807e-1,  1.3917310e-1 } },
-        { { -9.9756405e-1,  6.9756474e-2 }, {  9.9026807e-1, -1.3917310e-1 } },
-        { { -9.7814760e-1,  2.0791169e-1 }, {  9.1354546e-1, -4.0673664e-1 } },
-        { { -9.3969262e-1,  3.4202014e-1 }, {  7.6604444e-1, -6.4278761e-1 } },
-        { { -8.8294759e-1,  4.6947156e-1 }, {  5.5919290e-1, -8.2903757e-1 } },
-        { { -8.0901699e-1,  5.8778525e-1 }, {  3.0901699e-1, -9.5105652e-1 } },
-        { { -7.1933980e-1,  6.9465837e-1 }, {  3.4899497e-2, -9.9939083e-1 } },
-        { { -6.1566148e-1,  7.8801075e-1 }, { -2.4192190e-1, -9.7029573e-1 } },
-        { { -5.0000000e-1,  8.6602540e-1 }, { -5.0000000e-1, -8.6602540e-1 } },
-        { { -3.7460659e-1,  9.2718385e-1 }, { -7.1933980e-1, -6.9465837e-1 } },
-        { { -2.4192190e-1,  9.7029573e-1 }, { -8.8294759e-1, -4.6947156e-1 } },
-        { { -1.0452846e-1,  9.9452190e-1 }, { -9.7814760e-1, -2.0791169e-1 } },
-        { {  3.4899497e-2,  9.9939083e-1 }, { -9.9756405e-1,  6.9756474e-2 } },
-        { {  1.7364818e-1,  9.8480775e-1 }, { -9.3969262e-1,  3.4202014e-1 } },
-        { {  3.0901699e-1,  9.5105652e-1 }, { -8.0901699e-1,  5.8778525e-1 } },
-        { {  4.3837115e-1,  8.9879405e-1 }, { -6.1566148e-1,  7.8801075e-1 } },
-        { {  5.5919290e-1,  8.2903757e-1 }, { -3.7460659e-1,  9.2718385e-1 } },
-        { {  6.6913061e-1,  7.4314483e-1 }, { -1.0452846e-1,  9.9452190e-1 } },
-        { {  7.6604444e-1,  6.4278761e-1 }, {  1.7364818e-1,  9.8480775e-1 } },
-        { {  8.4804810e-1,  5.2991926e-1 }, {  4.3837115e-1,  8.9879405e-1 } },
-        { {  9.1354546e-1,  4.0673664e-1 }, {  6.6913061e-1,  7.4314483e-1 } },
-        { {  9.6126170e-1,  2.7563736e-1 }, {  8.4804810e-1,  5.2991926e-1 } },
-        { {  9.9026807e-1,  1.3917310e-1 }, {  9.6126170e-1,  2.7563736e-1 } },
-    }
-};
-
-const struct lc3_fft_bf3_twiddles *lc3_fft_twiddles_bf3[] =
-    { &fft_twiddles_15, &fft_twiddles_45 };
-
-
-/**
- * Twiddles FFT 2 points
- *
- * T[0..N/2-1] =
- *   cos(-2Pi * i/N) + j sin(-2Pi * i/N) , N=10, 20, ...
- */
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_10 = {
-    .n2 = 10/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  8.0901699e-01, -5.8778525e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, { -3.0901699e-01, -9.5105652e-01 },
-        { -8.0901699e-01, -5.8778525e-01 },
-    }
-};
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_20 = {
-    .n2 = 20/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  9.5105652e-01, -3.0901699e-01 },
-        {  8.0901699e-01, -5.8778525e-01 }, {  5.8778525e-01, -8.0901699e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, {  6.1232340e-17, -1.0000000e+00 },
-        { -3.0901699e-01, -9.5105652e-01 }, { -5.8778525e-01, -8.0901699e-01 },
-        { -8.0901699e-01, -5.8778525e-01 }, { -9.5105652e-01, -3.0901699e-01 },
-    }
-};
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_30 = {
-    .n2 = 30/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  9.7814760e-01, -2.0791169e-01 },
-        {  9.1354546e-01, -4.0673664e-01 }, {  8.0901699e-01, -5.8778525e-01 },
-        {  6.6913061e-01, -7.4314483e-01 }, {  5.0000000e-01, -8.6602540e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, {  1.0452846e-01, -9.9452190e-01 },
-        { -1.0452846e-01, -9.9452190e-01 }, { -3.0901699e-01, -9.5105652e-01 },
-        { -5.0000000e-01, -8.6602540e-01 }, { -6.6913061e-01, -7.4314483e-01 },
-        { -8.0901699e-01, -5.8778525e-01 }, { -9.1354546e-01, -4.0673664e-01 },
-        { -9.7814760e-01, -2.0791169e-01 },
-    }
-};
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_40 = {
-    .n2 = 40/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  9.8768834e-01, -1.5643447e-01 },
-        {  9.5105652e-01, -3.0901699e-01 }, {  8.9100652e-01, -4.5399050e-01 },
-        {  8.0901699e-01, -5.8778525e-01 }, {  7.0710678e-01, -7.0710678e-01 },
-        {  5.8778525e-01, -8.0901699e-01 }, {  4.5399050e-01, -8.9100652e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, {  1.5643447e-01, -9.8768834e-01 },
-        {  6.1232340e-17, -1.0000000e+00 }, { -1.5643447e-01, -9.8768834e-01 },
-        { -3.0901699e-01, -9.5105652e-01 }, { -4.5399050e-01, -8.9100652e-01 },
-        { -5.8778525e-01, -8.0901699e-01 }, { -7.0710678e-01, -7.0710678e-01 },
-        { -8.0901699e-01, -5.8778525e-01 }, { -8.9100652e-01, -4.5399050e-01 },
-        { -9.5105652e-01, -3.0901699e-01 }, { -9.8768834e-01, -1.5643447e-01 },
-    }
-};
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_60 = {
-    .n2 = 60/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  9.9452190e-01, -1.0452846e-01 },
-        {  9.7814760e-01, -2.0791169e-01 }, {  9.5105652e-01, -3.0901699e-01 },
-        {  9.1354546e-01, -4.0673664e-01 }, {  8.6602540e-01, -5.0000000e-01 },
-        {  8.0901699e-01, -5.8778525e-01 }, {  7.4314483e-01, -6.6913061e-01 },
-        {  6.6913061e-01, -7.4314483e-01 }, {  5.8778525e-01, -8.0901699e-01 },
-        {  5.0000000e-01, -8.6602540e-01 }, {  4.0673664e-01, -9.1354546e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, {  2.0791169e-01, -9.7814760e-01 },
-        {  1.0452846e-01, -9.9452190e-01 }, {  2.8327694e-16, -1.0000000e+00 },
-        { -1.0452846e-01, -9.9452190e-01 }, { -2.0791169e-01, -9.7814760e-01 },
-        { -3.0901699e-01, -9.5105652e-01 }, { -4.0673664e-01, -9.1354546e-01 },
-        { -5.0000000e-01, -8.6602540e-01 }, { -5.8778525e-01, -8.0901699e-01 },
-        { -6.6913061e-01, -7.4314483e-01 }, { -7.4314483e-01, -6.6913061e-01 },
-        { -8.0901699e-01, -5.8778525e-01 }, { -8.6602540e-01, -5.0000000e-01 },
-        { -9.1354546e-01, -4.0673664e-01 }, { -9.5105652e-01, -3.0901699e-01 },
-        { -9.7814760e-01, -2.0791169e-01 }, { -9.9452190e-01, -1.0452846e-01 },
-    }
-};
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_80 = {
-    .n2 = 80/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  9.9691733e-01, -7.8459096e-02 },
-        {  9.8768834e-01, -1.5643447e-01 }, {  9.7236992e-01, -2.3344536e-01 },
-        {  9.5105652e-01, -3.0901699e-01 }, {  9.2387953e-01, -3.8268343e-01 },
-        {  8.9100652e-01, -4.5399050e-01 }, {  8.5264016e-01, -5.2249856e-01 },
-        {  8.0901699e-01, -5.8778525e-01 }, {  7.6040597e-01, -6.4944805e-01 },
-        {  7.0710678e-01, -7.0710678e-01 }, {  6.4944805e-01, -7.6040597e-01 },
-        {  5.8778525e-01, -8.0901699e-01 }, {  5.2249856e-01, -8.5264016e-01 },
-        {  4.5399050e-01, -8.9100652e-01 }, {  3.8268343e-01, -9.2387953e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, {  2.3344536e-01, -9.7236992e-01 },
-        {  1.5643447e-01, -9.8768834e-01 }, {  7.8459096e-02, -9.9691733e-01 },
-        {  6.1232340e-17, -1.0000000e+00 }, { -7.8459096e-02, -9.9691733e-01 },
-        { -1.5643447e-01, -9.8768834e-01 }, { -2.3344536e-01, -9.7236992e-01 },
-        { -3.0901699e-01, -9.5105652e-01 }, { -3.8268343e-01, -9.2387953e-01 },
-        { -4.5399050e-01, -8.9100652e-01 }, { -5.2249856e-01, -8.5264016e-01 },
-        { -5.8778525e-01, -8.0901699e-01 }, { -6.4944805e-01, -7.6040597e-01 },
-        { -7.0710678e-01, -7.0710678e-01 }, { -7.6040597e-01, -6.4944805e-01 },
-        { -8.0901699e-01, -5.8778525e-01 }, { -8.5264016e-01, -5.2249856e-01 },
-        { -8.9100652e-01, -4.5399050e-01 }, { -9.2387953e-01, -3.8268343e-01 },
-        { -9.5105652e-01, -3.0901699e-01 }, { -9.7236992e-01, -2.3344536e-01 },
-        { -9.8768834e-01, -1.5643447e-01 }, { -9.9691733e-01, -7.8459096e-02 },
-    }
-};
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_90 = {
-    .n2 = 90/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  9.9756405e-01, -6.9756474e-02 },
-        {  9.9026807e-01, -1.3917310e-01 }, {  9.7814760e-01, -2.0791169e-01 },
-        {  9.6126170e-01, -2.7563736e-01 }, {  9.3969262e-01, -3.4202014e-01 },
-        {  9.1354546e-01, -4.0673664e-01 }, {  8.8294759e-01, -4.6947156e-01 },
-        {  8.4804810e-01, -5.2991926e-01 }, {  8.0901699e-01, -5.8778525e-01 },
-        {  7.6604444e-01, -6.4278761e-01 }, {  7.1933980e-01, -6.9465837e-01 },
-        {  6.6913061e-01, -7.4314483e-01 }, {  6.1566148e-01, -7.8801075e-01 },
-        {  5.5919290e-01, -8.2903757e-01 }, {  5.0000000e-01, -8.6602540e-01 },
-        {  4.3837115e-01, -8.9879405e-01 }, {  3.7460659e-01, -9.2718385e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, {  2.4192190e-01, -9.7029573e-01 },
-        {  1.7364818e-01, -9.8480775e-01 }, {  1.0452846e-01, -9.9452190e-01 },
-        {  3.4899497e-02, -9.9939083e-01 }, { -3.4899497e-02, -9.9939083e-01 },
-        { -1.0452846e-01, -9.9452190e-01 }, { -1.7364818e-01, -9.8480775e-01 },
-        { -2.4192190e-01, -9.7029573e-01 }, { -3.0901699e-01, -9.5105652e-01 },
-        { -3.7460659e-01, -9.2718385e-01 }, { -4.3837115e-01, -8.9879405e-01 },
-        { -5.0000000e-01, -8.6602540e-01 }, { -5.5919290e-01, -8.2903757e-01 },
-        { -6.1566148e-01, -7.8801075e-01 }, { -6.6913061e-01, -7.4314483e-01 },
-        { -7.1933980e-01, -6.9465837e-01 }, { -7.6604444e-01, -6.4278761e-01 },
-        { -8.0901699e-01, -5.8778525e-01 }, { -8.4804810e-01, -5.2991926e-01 },
-        { -8.8294759e-01, -4.6947156e-01 }, { -9.1354546e-01, -4.0673664e-01 },
-        { -9.3969262e-01, -3.4202014e-01 }, { -9.6126170e-01, -2.7563736e-01 },
-        { -9.7814760e-01, -2.0791169e-01 }, { -9.9026807e-01, -1.3917310e-01 },
-        { -9.9756405e-01, -6.9756474e-02 },
-    }
-};
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_120 = {
-    .n2 = 120/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  9.9862953e-01, -5.2335956e-02 },
-        {  9.9452190e-01, -1.0452846e-01 }, {  9.8768834e-01, -1.5643447e-01 },
-        {  9.7814760e-01, -2.0791169e-01 }, {  9.6592583e-01, -2.5881905e-01 },
-        {  9.5105652e-01, -3.0901699e-01 }, {  9.3358043e-01, -3.5836795e-01 },
-        {  9.1354546e-01, -4.0673664e-01 }, {  8.9100652e-01, -4.5399050e-01 },
-        {  8.6602540e-01, -5.0000000e-01 }, {  8.3867057e-01, -5.4463904e-01 },
-        {  8.0901699e-01, -5.8778525e-01 }, {  7.7714596e-01, -6.2932039e-01 },
-        {  7.4314483e-01, -6.6913061e-01 }, {  7.0710678e-01, -7.0710678e-01 },
-        {  6.6913061e-01, -7.4314483e-01 }, {  6.2932039e-01, -7.7714596e-01 },
-        {  5.8778525e-01, -8.0901699e-01 }, {  5.4463904e-01, -8.3867057e-01 },
-        {  5.0000000e-01, -8.6602540e-01 }, {  4.5399050e-01, -8.9100652e-01 },
-        {  4.0673664e-01, -9.1354546e-01 }, {  3.5836795e-01, -9.3358043e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, {  2.5881905e-01, -9.6592583e-01 },
-        {  2.0791169e-01, -9.7814760e-01 }, {  1.5643447e-01, -9.8768834e-01 },
-        {  1.0452846e-01, -9.9452190e-01 }, {  5.2335956e-02, -9.9862953e-01 },
-        {  2.8327694e-16, -1.0000000e+00 }, { -5.2335956e-02, -9.9862953e-01 },
-        { -1.0452846e-01, -9.9452190e-01 }, { -1.5643447e-01, -9.8768834e-01 },
-        { -2.0791169e-01, -9.7814760e-01 }, { -2.5881905e-01, -9.6592583e-01 },
-        { -3.0901699e-01, -9.5105652e-01 }, { -3.5836795e-01, -9.3358043e-01 },
-        { -4.0673664e-01, -9.1354546e-01 }, { -4.5399050e-01, -8.9100652e-01 },
-        { -5.0000000e-01, -8.6602540e-01 }, { -5.4463904e-01, -8.3867057e-01 },
-        { -5.8778525e-01, -8.0901699e-01 }, { -6.2932039e-01, -7.7714596e-01 },
-        { -6.6913061e-01, -7.4314483e-01 }, { -7.0710678e-01, -7.0710678e-01 },
-        { -7.4314483e-01, -6.6913061e-01 }, { -7.7714596e-01, -6.2932039e-01 },
-        { -8.0901699e-01, -5.8778525e-01 }, { -8.3867057e-01, -5.4463904e-01 },
-        { -8.6602540e-01, -5.0000000e-01 }, { -8.9100652e-01, -4.5399050e-01 },
-        { -9.1354546e-01, -4.0673664e-01 }, { -9.3358043e-01, -3.5836795e-01 },
-        { -9.5105652e-01, -3.0901699e-01 }, { -9.6592583e-01, -2.5881905e-01 },
-        { -9.7814760e-01, -2.0791169e-01 }, { -9.8768834e-01, -1.5643447e-01 },
-        { -9.9452190e-01, -1.0452846e-01 }, { -9.9862953e-01, -5.2335956e-02 },
-    }
-};
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_160 = {
-    .n2 = 160/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  9.9922904e-01, -3.9259816e-02 },
-        {  9.9691733e-01, -7.8459096e-02 }, {  9.9306846e-01, -1.1753740e-01 },
-        {  9.8768834e-01, -1.5643447e-01 }, {  9.8078528e-01, -1.9509032e-01 },
-        {  9.7236992e-01, -2.3344536e-01 }, {  9.6245524e-01, -2.7144045e-01 },
-        {  9.5105652e-01, -3.0901699e-01 }, {  9.3819134e-01, -3.4611706e-01 },
-        {  9.2387953e-01, -3.8268343e-01 }, {  9.0814317e-01, -4.1865974e-01 },
-        {  8.9100652e-01, -4.5399050e-01 }, {  8.7249601e-01, -4.8862124e-01 },
-        {  8.5264016e-01, -5.2249856e-01 }, {  8.3146961e-01, -5.5557023e-01 },
-        {  8.0901699e-01, -5.8778525e-01 }, {  7.8531693e-01, -6.1909395e-01 },
-        {  7.6040597e-01, -6.4944805e-01 }, {  7.3432251e-01, -6.7880075e-01 },
-        {  7.0710678e-01, -7.0710678e-01 }, {  6.7880075e-01, -7.3432251e-01 },
-        {  6.4944805e-01, -7.6040597e-01 }, {  6.1909395e-01, -7.8531693e-01 },
-        {  5.8778525e-01, -8.0901699e-01 }, {  5.5557023e-01, -8.3146961e-01 },
-        {  5.2249856e-01, -8.5264016e-01 }, {  4.8862124e-01, -8.7249601e-01 },
-        {  4.5399050e-01, -8.9100652e-01 }, {  4.1865974e-01, -9.0814317e-01 },
-        {  3.8268343e-01, -9.2387953e-01 }, {  3.4611706e-01, -9.3819134e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, {  2.7144045e-01, -9.6245524e-01 },
-        {  2.3344536e-01, -9.7236992e-01 }, {  1.9509032e-01, -9.8078528e-01 },
-        {  1.5643447e-01, -9.8768834e-01 }, {  1.1753740e-01, -9.9306846e-01 },
-        {  7.8459096e-02, -9.9691733e-01 }, {  3.9259816e-02, -9.9922904e-01 },
-        {  6.1232340e-17, -1.0000000e+00 }, { -3.9259816e-02, -9.9922904e-01 },
-        { -7.8459096e-02, -9.9691733e-01 }, { -1.1753740e-01, -9.9306846e-01 },
-        { -1.5643447e-01, -9.8768834e-01 }, { -1.9509032e-01, -9.8078528e-01 },
-        { -2.3344536e-01, -9.7236992e-01 }, { -2.7144045e-01, -9.6245524e-01 },
-        { -3.0901699e-01, -9.5105652e-01 }, { -3.4611706e-01, -9.3819134e-01 },
-        { -3.8268343e-01, -9.2387953e-01 }, { -4.1865974e-01, -9.0814317e-01 },
-        { -4.5399050e-01, -8.9100652e-01 }, { -4.8862124e-01, -8.7249601e-01 },
-        { -5.2249856e-01, -8.5264016e-01 }, { -5.5557023e-01, -8.3146961e-01 },
-        { -5.8778525e-01, -8.0901699e-01 }, { -6.1909395e-01, -7.8531693e-01 },
-        { -6.4944805e-01, -7.6040597e-01 }, { -6.7880075e-01, -7.3432251e-01 },
-        { -7.0710678e-01, -7.0710678e-01 }, { -7.3432251e-01, -6.7880075e-01 },
-        { -7.6040597e-01, -6.4944805e-01 }, { -7.8531693e-01, -6.1909395e-01 },
-        { -8.0901699e-01, -5.8778525e-01 }, { -8.3146961e-01, -5.5557023e-01 },
-        { -8.5264016e-01, -5.2249856e-01 }, { -8.7249601e-01, -4.8862124e-01 },
-        { -8.9100652e-01, -4.5399050e-01 }, { -9.0814317e-01, -4.1865974e-01 },
-        { -9.2387953e-01, -3.8268343e-01 }, { -9.3819134e-01, -3.4611706e-01 },
-        { -9.5105652e-01, -3.0901699e-01 }, { -9.6245524e-01, -2.7144045e-01 },
-        { -9.7236992e-01, -2.3344536e-01 }, { -9.8078528e-01, -1.9509032e-01 },
-        { -9.8768834e-01, -1.5643447e-01 }, { -9.9306846e-01, -1.1753740e-01 },
-        { -9.9691733e-01, -7.8459096e-02 }, { -9.9922904e-01, -3.9259816e-02 },
-    }
-};
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_180 = {
-    .n2 = 180/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  9.9939083e-01, -3.4899497e-02 },
-        {  9.9756405e-01, -6.9756474e-02 }, {  9.9452190e-01, -1.0452846e-01 },
-        {  9.9026807e-01, -1.3917310e-01 }, {  9.8480775e-01, -1.7364818e-01 },
-        {  9.7814760e-01, -2.0791169e-01 }, {  9.7029573e-01, -2.4192190e-01 },
-        {  9.6126170e-01, -2.7563736e-01 }, {  9.5105652e-01, -3.0901699e-01 },
-        {  9.3969262e-01, -3.4202014e-01 }, {  9.2718385e-01, -3.7460659e-01 },
-        {  9.1354546e-01, -4.0673664e-01 }, {  8.9879405e-01, -4.3837115e-01 },
-        {  8.8294759e-01, -4.6947156e-01 }, {  8.6602540e-01, -5.0000000e-01 },
-        {  8.4804810e-01, -5.2991926e-01 }, {  8.2903757e-01, -5.5919290e-01 },
-        {  8.0901699e-01, -5.8778525e-01 }, {  7.8801075e-01, -6.1566148e-01 },
-        {  7.6604444e-01, -6.4278761e-01 }, {  7.4314483e-01, -6.6913061e-01 },
-        {  7.1933980e-01, -6.9465837e-01 }, {  6.9465837e-01, -7.1933980e-01 },
-        {  6.6913061e-01, -7.4314483e-01 }, {  6.4278761e-01, -7.6604444e-01 },
-        {  6.1566148e-01, -7.8801075e-01 }, {  5.8778525e-01, -8.0901699e-01 },
-        {  5.5919290e-01, -8.2903757e-01 }, {  5.2991926e-01, -8.4804810e-01 },
-        {  5.0000000e-01, -8.6602540e-01 }, {  4.6947156e-01, -8.8294759e-01 },
-        {  4.3837115e-01, -8.9879405e-01 }, {  4.0673664e-01, -9.1354546e-01 },
-        {  3.7460659e-01, -9.2718385e-01 }, {  3.4202014e-01, -9.3969262e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, {  2.7563736e-01, -9.6126170e-01 },
-        {  2.4192190e-01, -9.7029573e-01 }, {  2.0791169e-01, -9.7814760e-01 },
-        {  1.7364818e-01, -9.8480775e-01 }, {  1.3917310e-01, -9.9026807e-01 },
-        {  1.0452846e-01, -9.9452190e-01 }, {  6.9756474e-02, -9.9756405e-01 },
-        {  3.4899497e-02, -9.9939083e-01 }, {  6.1232340e-17, -1.0000000e+00 },
-        { -3.4899497e-02, -9.9939083e-01 }, { -6.9756474e-02, -9.9756405e-01 },
-        { -1.0452846e-01, -9.9452190e-01 }, { -1.3917310e-01, -9.9026807e-01 },
-        { -1.7364818e-01, -9.8480775e-01 }, { -2.0791169e-01, -9.7814760e-01 },
-        { -2.4192190e-01, -9.7029573e-01 }, { -2.7563736e-01, -9.6126170e-01 },
-        { -3.0901699e-01, -9.5105652e-01 }, { -3.4202014e-01, -9.3969262e-01 },
-        { -3.7460659e-01, -9.2718385e-01 }, { -4.0673664e-01, -9.1354546e-01 },
-        { -4.3837115e-01, -8.9879405e-01 }, { -4.6947156e-01, -8.8294759e-01 },
-        { -5.0000000e-01, -8.6602540e-01 }, { -5.2991926e-01, -8.4804810e-01 },
-        { -5.5919290e-01, -8.2903757e-01 }, { -5.8778525e-01, -8.0901699e-01 },
-        { -6.1566148e-01, -7.8801075e-01 }, { -6.4278761e-01, -7.6604444e-01 },
-        { -6.6913061e-01, -7.4314483e-01 }, { -6.9465837e-01, -7.1933980e-01 },
-        { -7.1933980e-01, -6.9465837e-01 }, { -7.4314483e-01, -6.6913061e-01 },
-        { -7.6604444e-01, -6.4278761e-01 }, { -7.8801075e-01, -6.1566148e-01 },
-        { -8.0901699e-01, -5.8778525e-01 }, { -8.2903757e-01, -5.5919290e-01 },
-        { -8.4804810e-01, -5.2991926e-01 }, { -8.6602540e-01, -5.0000000e-01 },
-        { -8.8294759e-01, -4.6947156e-01 }, { -8.9879405e-01, -4.3837115e-01 },
-        { -9.1354546e-01, -4.0673664e-01 }, { -9.2718385e-01, -3.7460659e-01 },
-        { -9.3969262e-01, -3.4202014e-01 }, { -9.5105652e-01, -3.0901699e-01 },
-        { -9.6126170e-01, -2.7563736e-01 }, { -9.7029573e-01, -2.4192190e-01 },
-        { -9.7814760e-01, -2.0791169e-01 }, { -9.8480775e-01, -1.7364818e-01 },
-        { -9.9026807e-01, -1.3917310e-01 }, { -9.9452190e-01, -1.0452846e-01 },
-        { -9.9756405e-01, -6.9756474e-02 }, { -9.9939083e-01, -3.4899497e-02 },
-    }
-};
-
-static const struct lc3_fft_bf2_twiddles fft_twiddles_240 = {
-    .n2 = 240/2, .t = (const struct lc3_complex []){
-        {  1.0000000e+00, -0.0000000e+00 }, {  9.9965732e-01, -2.6176948e-02 },
-        {  9.9862953e-01, -5.2335956e-02 }, {  9.9691733e-01, -7.8459096e-02 },
-        {  9.9452190e-01, -1.0452846e-01 }, {  9.9144486e-01, -1.3052619e-01 },
-        {  9.8768834e-01, -1.5643447e-01 }, {  9.8325491e-01, -1.8223553e-01 },
-        {  9.7814760e-01, -2.0791169e-01 }, {  9.7236992e-01, -2.3344536e-01 },
-        {  9.6592583e-01, -2.5881905e-01 }, {  9.5881973e-01, -2.8401534e-01 },
-        {  9.5105652e-01, -3.0901699e-01 }, {  9.4264149e-01, -3.3380686e-01 },
-        {  9.3358043e-01, -3.5836795e-01 }, {  9.2387953e-01, -3.8268343e-01 },
-        {  9.1354546e-01, -4.0673664e-01 }, {  9.0258528e-01, -4.3051110e-01 },
-        {  8.9100652e-01, -4.5399050e-01 }, {  8.7881711e-01, -4.7715876e-01 },
-        {  8.6602540e-01, -5.0000000e-01 }, {  8.5264016e-01, -5.2249856e-01 },
-        {  8.3867057e-01, -5.4463904e-01 }, {  8.2412619e-01, -5.6640624e-01 },
-        {  8.0901699e-01, -5.8778525e-01 }, {  7.9335334e-01, -6.0876143e-01 },
-        {  7.7714596e-01, -6.2932039e-01 }, {  7.6040597e-01, -6.4944805e-01 },
-        {  7.4314483e-01, -6.6913061e-01 }, {  7.2537437e-01, -6.8835458e-01 },
-        {  7.0710678e-01, -7.0710678e-01 }, {  6.8835458e-01, -7.2537437e-01 },
-        {  6.6913061e-01, -7.4314483e-01 }, {  6.4944805e-01, -7.6040597e-01 },
-        {  6.2932039e-01, -7.7714596e-01 }, {  6.0876143e-01, -7.9335334e-01 },
-        {  5.8778525e-01, -8.0901699e-01 }, {  5.6640624e-01, -8.2412619e-01 },
-        {  5.4463904e-01, -8.3867057e-01 }, {  5.2249856e-01, -8.5264016e-01 },
-        {  5.0000000e-01, -8.6602540e-01 }, {  4.7715876e-01, -8.7881711e-01 },
-        {  4.5399050e-01, -8.9100652e-01 }, {  4.3051110e-01, -9.0258528e-01 },
-        {  4.0673664e-01, -9.1354546e-01 }, {  3.8268343e-01, -9.2387953e-01 },
-        {  3.5836795e-01, -9.3358043e-01 }, {  3.3380686e-01, -9.4264149e-01 },
-        {  3.0901699e-01, -9.5105652e-01 }, {  2.8401534e-01, -9.5881973e-01 },
-        {  2.5881905e-01, -9.6592583e-01 }, {  2.3344536e-01, -9.7236992e-01 },
-        {  2.0791169e-01, -9.7814760e-01 }, {  1.8223553e-01, -9.8325491e-01 },
-        {  1.5643447e-01, -9.8768834e-01 }, {  1.3052619e-01, -9.9144486e-01 },
-        {  1.0452846e-01, -9.9452190e-01 }, {  7.8459096e-02, -9.9691733e-01 },
-        {  5.2335956e-02, -9.9862953e-01 }, {  2.6176948e-02, -9.9965732e-01 },
-        {  2.8327694e-16, -1.0000000e+00 }, { -2.6176948e-02, -9.9965732e-01 },
-        { -5.2335956e-02, -9.9862953e-01 }, { -7.8459096e-02, -9.9691733e-01 },
-        { -1.0452846e-01, -9.9452190e-01 }, { -1.3052619e-01, -9.9144486e-01 },
-        { -1.5643447e-01, -9.8768834e-01 }, { -1.8223553e-01, -9.8325491e-01 },
-        { -2.0791169e-01, -9.7814760e-01 }, { -2.3344536e-01, -9.7236992e-01 },
-        { -2.5881905e-01, -9.6592583e-01 }, { -2.8401534e-01, -9.5881973e-01 },
-        { -3.0901699e-01, -9.5105652e-01 }, { -3.3380686e-01, -9.4264149e-01 },
-        { -3.5836795e-01, -9.3358043e-01 }, { -3.8268343e-01, -9.2387953e-01 },
-        { -4.0673664e-01, -9.1354546e-01 }, { -4.3051110e-01, -9.0258528e-01 },
-        { -4.5399050e-01, -8.9100652e-01 }, { -4.7715876e-01, -8.7881711e-01 },
-        { -5.0000000e-01, -8.6602540e-01 }, { -5.2249856e-01, -8.5264016e-01 },
-        { -5.4463904e-01, -8.3867057e-01 }, { -5.6640624e-01, -8.2412619e-01 },
-        { -5.8778525e-01, -8.0901699e-01 }, { -6.0876143e-01, -7.9335334e-01 },
-        { -6.2932039e-01, -7.7714596e-01 }, { -6.4944805e-01, -7.6040597e-01 },
-        { -6.6913061e-01, -7.4314483e-01 }, { -6.8835458e-01, -7.2537437e-01 },
-        { -7.0710678e-01, -7.0710678e-01 }, { -7.2537437e-01, -6.8835458e-01 },
-        { -7.4314483e-01, -6.6913061e-01 }, { -7.6040597e-01, -6.4944805e-01 },
-        { -7.7714596e-01, -6.2932039e-01 }, { -7.9335334e-01, -6.0876143e-01 },
-        { -8.0901699e-01, -5.8778525e-01 }, { -8.2412619e-01, -5.6640624e-01 },
-        { -8.3867057e-01, -5.4463904e-01 }, { -8.5264016e-01, -5.2249856e-01 },
-        { -8.6602540e-01, -5.0000000e-01 }, { -8.7881711e-01, -4.7715876e-01 },
-        { -8.9100652e-01, -4.5399050e-01 }, { -9.0258528e-01, -4.3051110e-01 },
-        { -9.1354546e-01, -4.0673664e-01 }, { -9.2387953e-01, -3.8268343e-01 },
-        { -9.3358043e-01, -3.5836795e-01 }, { -9.4264149e-01, -3.3380686e-01 },
-        { -9.5105652e-01, -3.0901699e-01 }, { -9.5881973e-01, -2.8401534e-01 },
-        { -9.6592583e-01, -2.5881905e-01 }, { -9.7236992e-01, -2.3344536e-01 },
-        { -9.7814760e-01, -2.0791169e-01 }, { -9.8325491e-01, -1.8223553e-01 },
-        { -9.8768834e-01, -1.5643447e-01 }, { -9.9144486e-01, -1.3052619e-01 },
-        { -9.9452190e-01, -1.0452846e-01 }, { -9.9691733e-01, -7.8459096e-02 },
-        { -9.9862953e-01, -5.2335956e-02 }, { -9.9965732e-01, -2.6176948e-02 },
-    }
-};
-
-const struct lc3_fft_bf2_twiddles *lc3_fft_twiddles_bf2[][3] = {
-    { &fft_twiddles_10 , &fft_twiddles_30 , &fft_twiddles_90  },
-    { &fft_twiddles_20 , &fft_twiddles_60 , &fft_twiddles_180 },
-    { &fft_twiddles_40 , &fft_twiddles_120 },
-    { &fft_twiddles_80 , &fft_twiddles_240 },
-    { &fft_twiddles_160  }
-};
-
-
-/**
- * MDCT Rotation twiddles
- *
- *            2Pi (n + 1/8) / N
- *   W[n] = e                   , n = [0..N/4-1]
- */
-
-static const struct lc3_mdct_rot_def mdct_rot_120 = {
-    .n4 = 120/4, .w = (const struct lc3_complex []){
-        { 9.9997858e-01, 6.5449380e-03 }, { 9.9826561e-01, 5.8870804e-02 },
-        { 9.9381646e-01, 1.1103531e-01 }, { 9.8664333e-01, 1.6289547e-01 },
-        { 9.7676588e-01, 2.1430915e-01 }, { 9.6421118e-01, 2.6513543e-01 },
-        { 9.4901365e-01, 3.1523498e-01 }, { 9.3121493e-01, 3.6447050e-01 },
-        { 9.1086382e-01, 4.1270703e-01 }, { 8.8801610e-01, 4.5981236e-01 },
-        { 8.6273439e-01, 5.0565737e-01 }, { 8.3508798e-01, 5.5011642e-01 },
-        { 8.0515265e-01, 5.9306763e-01 }, { 7.7301045e-01, 6.3439328e-01 },
-        { 7.3874949e-01, 6.7398011e-01 }, { 7.0246367e-01, 7.1171961e-01 },
-        { 6.6425244e-01, 7.4750833e-01 }, { 6.2422054e-01, 7.8124818e-01 },
-        { 5.8247770e-01, 8.1284668e-01 }, { 5.3913832e-01, 8.4221723e-01 },
-        { 4.9432121e-01, 8.6927932e-01 }, { 4.4814919e-01, 8.9395878e-01 },
-        { 4.0074883e-01, 9.1618796e-01 }, { 3.5225005e-01, 9.3590593e-01 },
-        { 3.0278577e-01, 9.5305864e-01 }, { 2.5249158e-01, 9.6759909e-01 },
-        { 2.0150532e-01, 9.7948742e-01 }, { 1.4996676e-01, 9.8869104e-01 },
-        { 9.8017140e-02, 9.9518473e-01 }, { 4.5798867e-02, 9.9895068e-01 },
-    }
-};
-
-static const struct lc3_mdct_rot_def mdct_rot_160 = {
-    .n4 = 160/4, .w = (const struct lc3_complex []){
-        { 9.9998795e-01, 4.9087188e-03 }, { 9.9902428e-01, 4.4164277e-02 },
-        { 9.9652019e-01, 8.3351737e-02 }, { 9.9247953e-01, 1.2241068e-01 },
-        { 9.8690855e-01, 1.6128086e-01 }, { 9.7981582e-01, 1.9990237e-01 },
-        { 9.7121229e-01, 2.3821564e-01 }, { 9.6111122e-01, 2.7616160e-01 },
-        { 9.4952818e-01, 3.1368174e-01 }, { 9.3648104e-01, 3.5071820e-01 },
-        { 9.2198992e-01, 3.8721389e-01 }, { 9.0607715e-01, 4.2311251e-01 },
-        { 8.8876728e-01, 4.5835873e-01 }, { 8.7008699e-01, 4.9289819e-01 },
-        { 8.5006509e-01, 5.2667764e-01 }, { 8.2873246e-01, 5.5964499e-01 },
-        { 8.0612197e-01, 5.9174941e-01 }, { 7.8226851e-01, 6.2294139e-01 },
-        { 7.5720885e-01, 6.5317284e-01 }, { 7.3098162e-01, 6.8239715e-01 },
-        { 7.0362727e-01, 7.1056925e-01 }, { 6.7518798e-01, 7.3764570e-01 },
-        { 6.4570760e-01, 7.6358476e-01 }, { 6.1523159e-01, 7.8834643e-01 },
-        { 5.8380693e-01, 8.1189252e-01 }, { 5.5148209e-01, 8.3418673e-01 },
-        { 5.1830690e-01, 8.5519469e-01 }, { 4.8433252e-01, 8.7488400e-01 },
-        { 4.4961133e-01, 8.9322430e-01 }, { 4.1419687e-01, 9.1018732e-01 },
-        { 3.7814376e-01, 9.2574689e-01 }, { 3.4150757e-01, 9.3987902e-01 },
-        { 3.0434480e-01, 9.5256194e-01 }, { 2.6671276e-01, 9.6377607e-01 },
-        { 2.2866946e-01, 9.7350412e-01 }, { 1.9027357e-01, 9.8173111e-01 },
-        { 1.5158430e-01, 9.8844433e-01 }, { 1.1266129e-01, 9.9363345e-01 },
-        { 7.3564564e-02, 9.9729046e-01 }, { 3.4354408e-02, 9.9940971e-01 },
-    }
-};
-
-static const struct lc3_mdct_rot_def mdct_rot_240 = {
-    .n4 = 240/4, .w = (const struct lc3_complex []){
-        { 9.9999465e-01, 3.2724865e-03 }, { 9.9956631e-01, 2.9448173e-02 },
-        { 9.9845292e-01, 5.5603678e-02 }, { 9.9665524e-01, 8.1721074e-02 },
-        { 9.9417450e-01, 1.0778246e-01 }, { 9.9101241e-01, 1.3376998e-01 },
-        { 9.8717112e-01, 1.5966582e-01 }, { 9.8265328e-01, 1.8545224e-01 },
-        { 9.7746197e-01, 2.1111155e-01 }, { 9.7160077e-01, 2.3662618e-01 },
-        { 9.6507367e-01, 2.6197864e-01 }, { 9.5788516e-01, 2.8715155e-01 },
-        { 9.5004017e-01, 3.1212766e-01 }, { 9.4154407e-01, 3.3688985e-01 },
-        { 9.3240267e-01, 3.6142116e-01 }, { 9.2262226e-01, 3.8570477e-01 },
-        { 9.1220953e-01, 4.0972403e-01 }, { 9.0117161e-01, 4.3346249e-01 },
-        { 8.8951608e-01, 4.5690388e-01 }, { 8.7725091e-01, 4.8003212e-01 },
-        { 8.6438452e-01, 5.0283138e-01 }, { 8.5092573e-01, 5.2528602e-01 },
-        { 8.3688375e-01, 5.4738066e-01 }, { 8.2226822e-01, 5.6910015e-01 },
-        { 8.0708914e-01, 5.9042960e-01 }, { 7.9135693e-01, 6.1135441e-01 },
-        { 7.7508236e-01, 6.3186022e-01 }, { 7.5827658e-01, 6.5193299e-01 },
-        { 7.4095113e-01, 6.7155895e-01 }, { 7.2311786e-01, 6.9072467e-01 },
-        { 7.0478900e-01, 7.0941699e-01 }, { 6.8597711e-01, 7.2762312e-01 },
-        { 6.6669509e-01, 7.4533057e-01 }, { 6.4695615e-01, 7.6252720e-01 },
-        { 6.2677382e-01, 7.7920124e-01 }, { 6.0616193e-01, 7.9534126e-01 },
-        { 5.8513461e-01, 8.1093618e-01 }, { 5.6370626e-01, 8.2597533e-01 },
-        { 5.4189158e-01, 8.4044840e-01 }, { 5.1970551e-01, 8.5434547e-01 },
-        { 4.9716327e-01, 8.6765701e-01 }, { 4.7428029e-01, 8.8037390e-01 },
-        { 4.5107226e-01, 8.9248743e-01 }, { 4.2755509e-01, 9.0398929e-01 },
-        { 4.0374490e-01, 9.1487161e-01 }, { 3.7965800e-01, 9.2512691e-01 },
-        { 3.5531090e-01, 9.3474818e-01 }, { 3.3072029e-01, 9.4372882e-01 },
-        { 3.0590302e-01, 9.5206268e-01 }, { 2.8087610e-01, 9.5974404e-01 },
-        { 2.5565668e-01, 9.6676764e-01 }, { 2.3026205e-01, 9.7312866e-01 },
-        { 2.0470960e-01, 9.7882275e-01 }, { 1.7901686e-01, 9.8384601e-01 },
-        { 1.5320143e-01, 9.8819498e-01 }, { 1.2728100e-01, 9.9186670e-01 },
-        { 1.0127334e-01, 9.9485864e-01 }, { 7.5196277e-02, 9.9716875e-01 },
-        { 4.9067674e-02, 9.9879546e-01 }, { 2.2905443e-02, 9.9973764e-01 },
-    }
-};
-
-static const struct lc3_mdct_rot_def mdct_rot_320 = {
-    .n4 = 320/4, .w = (const struct lc3_complex []){
-        { 9.9999699e-01, 2.4543668e-03 }, { 9.9975604e-01, 2.2087527e-02 },
-        { 9.9912967e-01, 4.1712172e-02 }, { 9.9811811e-01, 6.1320736e-02 },
-        { 9.9672176e-01, 8.0905660e-02 }, { 9.9494116e-01, 1.0045939e-01 },
-        { 9.9277699e-01, 1.1997440e-01 }, { 9.9023008e-01, 1.3944315e-01 },
-        { 9.8730142e-01, 1.5885814e-01 }, { 9.8399213e-01, 1.7821189e-01 },
-        { 9.8030350e-01, 1.9749694e-01 }, { 9.7623695e-01, 2.1670585e-01 },
-        { 9.7179403e-01, 2.3583121e-01 }, { 9.6697647e-01, 2.5486566e-01 },
-        { 9.6178612e-01, 2.7380185e-01 }, { 9.5622499e-01, 2.9263249e-01 },
-        { 9.5029521e-01, 3.1135031e-01 }, { 9.4399908e-01, 3.2994809e-01 },
-        { 9.3733901e-01, 3.4841868e-01 }, { 9.3031759e-01, 3.6675495e-01 },
-        { 9.2293750e-01, 3.8494982e-01 }, { 9.1520161e-01, 4.0299629e-01 },
-        { 9.0711289e-01, 4.2088739e-01 }, { 8.9867447e-01, 4.3861624e-01 },
-        { 8.8988958e-01, 4.5617599e-01 }, { 8.8076163e-01, 4.7355988e-01 },
-        { 8.7129412e-01, 4.9076120e-01 }, { 8.6149072e-01, 5.0777332e-01 },
-        { 8.5135519e-01, 5.2458968e-01 }, { 8.4089145e-01, 5.4120381e-01 },
-        { 8.3010353e-01, 5.5760929e-01 }, { 8.1899560e-01, 5.7379980e-01 },
-        { 8.0757192e-01, 5.8976911e-01 }, { 7.9583690e-01, 6.0551104e-01 },
-        { 7.8379508e-01, 6.2101954e-01 }, { 7.7145109e-01, 6.3628862e-01 },
-        { 7.5880969e-01, 6.5131241e-01 }, { 7.4587576e-01, 6.6608510e-01 },
-        { 7.3265427e-01, 6.8060100e-01 }, { 7.1915033e-01, 6.9485451e-01 },
-        { 7.0536915e-01, 7.0884015e-01 }, { 6.9131604e-01, 7.2255252e-01 },
-        { 6.7699640e-01, 7.3598632e-01 }, { 6.6241578e-01, 7.4913639e-01 },
-        { 6.4757978e-01, 7.6199766e-01 }, { 6.3249412e-01, 7.7456516e-01 },
-        { 6.1716463e-01, 7.8683405e-01 }, { 6.0159721e-01, 7.9879960e-01 },
-        { 5.8579786e-01, 8.1045720e-01 }, { 5.6977267e-01, 8.2180235e-01 },
-        { 5.5352783e-01, 8.3283068e-01 }, { 5.3706959e-01, 8.4353794e-01 },
-        { 5.2040430e-01, 8.5392000e-01 }, { 5.0353838e-01, 8.6397286e-01 },
-        { 4.8647834e-01, 8.7369263e-01 }, { 4.6923076e-01, 8.8307559e-01 },
-        { 4.5180228e-01, 8.9211810e-01 }, { 4.3419961e-01, 9.0081668e-01 },
-        { 4.1642956e-01, 9.0916798e-01 }, { 3.9849896e-01, 9.1716878e-01 },
-        { 3.8041474e-01, 9.2481600e-01 }, { 3.6218386e-01, 9.3210667e-01 },
-        { 3.4381335e-01, 9.3903801e-01 }, { 3.2531029e-01, 9.4560733e-01 },
-        { 3.0668182e-01, 9.5181209e-01 }, { 2.8793512e-01, 9.5764992e-01 },
-        { 2.6907741e-01, 9.6311855e-01 }, { 2.5011597e-01, 9.6821588e-01 },
-        { 2.3105811e-01, 9.7293995e-01 }, { 2.1191117e-01, 9.7728893e-01 },
-        { 1.9268253e-01, 9.8126115e-01 }, { 1.7337961e-01, 9.8485507e-01 },
-        { 1.5400984e-01, 9.8806931e-01 }, { 1.3458071e-01, 9.9090264e-01 },
-        { 1.1509969e-01, 9.9335395e-01 }, { 9.5574297e-02, 9.9542230e-01 },
-        { 7.6012059e-02, 9.9710690e-01 }, { 5.6420516e-02, 9.9840709e-01 },
-        { 3.6807223e-02, 9.9932238e-01 }, { 1.7179740e-02, 9.9985242e-01 },
-    }
-};
-
-static const struct lc3_mdct_rot_def mdct_rot_360 = {
-    .n4 = 360/4, .w = (const struct lc3_complex []){
-        { 9.9999762e-01, 2.1816598e-03 }, { 9.9980724e-01, 1.9633692e-02 },
-        { 9.9931231e-01, 3.7079744e-02 }, { 9.9851298e-01, 5.4514502e-02 },
-        { 9.9740949e-01, 7.1932653e-02 }, { 9.9600218e-01, 8.9328893e-02 },
-        { 9.9429148e-01, 1.0669792e-01 }, { 9.9227791e-01, 1.2403445e-01 },
-        { 9.8996208e-01, 1.4133320e-01 }, { 9.8734470e-01, 1.5858889e-01 },
-        { 9.8442657e-01, 1.7579628e-01 }, { 9.8120857e-01, 1.9295012e-01 },
-        { 9.7769168e-01, 2.1004518e-01 }, { 9.7387698e-01, 2.2707626e-01 },
-        { 9.6976563e-01, 2.4403818e-01 }, { 9.6535887e-01, 2.6092575e-01 },
-        { 9.6065806e-01, 2.7773385e-01 }, { 9.5566462e-01, 2.9445734e-01 },
-        { 9.5038008e-01, 3.1109114e-01 }, { 9.4480605e-01, 3.2763018e-01 },
-        { 9.3894421e-01, 3.4406942e-01 }, { 9.3279637e-01, 3.6040385e-01 },
-        { 9.2636438e-01, 3.7662850e-01 }, { 9.1965022e-01, 3.9273843e-01 },
-        { 9.1265592e-01, 4.0872872e-01 }, { 9.0538362e-01, 4.2459451e-01 },
-        { 8.9783553e-01, 4.4033097e-01 }, { 8.9001395e-01, 4.5593329e-01 },
-        { 8.8192126e-01, 4.7139674e-01 }, { 8.7355994e-01, 4.8671659e-01 },
-        { 8.6493251e-01, 5.0188818e-01 }, { 8.5604162e-01, 5.1690690e-01 },
-        { 8.4688997e-01, 5.3176816e-01 }, { 8.3748035e-01, 5.4646743e-01 },
-        { 8.2781563e-01, 5.6100025e-01 }, { 8.1789875e-01, 5.7536218e-01 },
-        { 8.0773272e-01, 5.8954885e-01 }, { 7.9732065e-01, 6.0355594e-01 },
-        { 7.8666571e-01, 6.1737918e-01 }, { 7.7577115e-01, 6.3101436e-01 },
-        { 7.6464028e-01, 6.4445733e-01 }, { 7.5327649e-01, 6.5770399e-01 },
-        { 7.4168324e-01, 6.7075030e-01 }, { 7.2986407e-01, 6.8359230e-01 },
-        { 7.1782258e-01, 6.9622607e-01 }, { 7.0556243e-01, 7.0864776e-01 },
-        { 6.9308736e-01, 7.2085360e-01 }, { 6.8040117e-01, 7.3283985e-01 },
-        { 6.6750772e-01, 7.4460287e-01 }, { 6.5441095e-01, 7.5613908e-01 },
-        { 6.4111483e-01, 7.6744496e-01 }, { 6.2762343e-01, 7.7851708e-01 },
-        { 6.1394084e-01, 7.8935204e-01 }, { 6.0007124e-01, 7.9994657e-01 },
-        { 5.8601885e-01, 8.1029742e-01 }, { 5.7178796e-01, 8.2040144e-01 },
-        { 5.5738289e-01, 8.3025557e-01 }, { 5.4280804e-01, 8.3985679e-01 },
-        { 5.2806785e-01, 8.4920218e-01 }, { 5.1316680e-01, 8.5828890e-01 },
-        { 4.9810944e-01, 8.6711417e-01 }, { 4.8290034e-01, 8.7567532e-01 },
-        { 4.6754415e-01, 8.8396972e-01 }, { 4.5204555e-01, 8.9199486e-01 },
-        { 4.3640924e-01, 8.9974828e-01 }, { 4.2064000e-01, 9.0722764e-01 },
-        { 4.0474263e-01, 9.1443064e-01 }, { 3.8872197e-01, 9.2135511e-01 },
-        { 3.7258290e-01, 9.2799891e-01 }, { 3.5633034e-01, 9.3436004e-01 },
-        { 3.3996924e-01, 9.4043656e-01 }, { 3.2350458e-01, 9.4622660e-01 },
-        { 3.0694138e-01, 9.5172842e-01 }, { 2.9028468e-01, 9.5694034e-01 },
-        { 2.7353955e-01, 9.6186076e-01 }, { 2.5671111e-01, 9.6648818e-01 },
-        { 2.3980446e-01, 9.7082121e-01 }, { 2.2282477e-01, 9.7485851e-01 },
-        { 2.0577721e-01, 9.7859887e-01 }, { 1.8866696e-01, 9.8204113e-01 },
-        { 1.7149925e-01, 9.8518425e-01 }, { 1.5427929e-01, 9.8802728e-01 },
-        { 1.3701234e-01, 9.9056934e-01 }, { 1.1970366e-01, 9.9280967e-01 },
-        { 1.0235851e-01, 9.9474757e-01 }, { 8.4982177e-02, 9.9638247e-01 },
-        { 6.7579962e-02, 9.9771386e-01 }, { 5.0157162e-02, 9.9874134e-01 },
-        { 3.2719083e-02, 9.9946459e-01 }, { 1.5271037e-02, 9.9988339e-01 },
-    }
-};
-
-static const struct lc3_mdct_rot_def mdct_rot_480 = {
-    .n4 = 480/4, .w = (const struct lc3_complex []){
-        { 9.9999866e-01, 1.6362454e-03 }, { 9.9989157e-01, 1.4725683e-02 },
-        { 9.9961315e-01, 2.7812598e-02 }, { 9.9916346e-01, 4.0894747e-02 },
-        { 9.9854256e-01, 5.3969889e-02 }, { 9.9775057e-01, 6.7035784e-02 },
-        { 9.9678762e-01, 8.0090192e-02 }, { 9.9565388e-01, 9.3130877e-02 },
-        { 9.9434953e-01, 1.0615561e-01 }, { 9.9287481e-01, 1.1916214e-01 },
-        { 9.9122996e-01, 1.3214826e-01 }, { 9.8941527e-01, 1.4511174e-01 },
-        { 9.8743105e-01, 1.5805036e-01 }, { 9.8527764e-01, 1.7096189e-01 },
-        { 9.8295541e-01, 1.8384413e-01 }, { 9.8046475e-01, 1.9669487e-01 },
-        { 9.7780610e-01, 2.0951190e-01 }, { 9.7497990e-01, 2.2229304e-01 },
-        { 9.7198664e-01, 2.3503609e-01 }, { 9.6882685e-01, 2.4773886e-01 },
-        { 9.6550104e-01, 2.6039919e-01 }, { 9.6200980e-01, 2.7301490e-01 },
-        { 9.5835373e-01, 2.8558383e-01 }, { 9.5453345e-01, 2.9810383e-01 },
-        { 9.5054962e-01, 3.1057274e-01 }, { 9.4640291e-01, 3.2298845e-01 },
-        { 9.4209404e-01, 3.3534881e-01 }, { 9.3762375e-01, 3.4765171e-01 },
-        { 9.3299280e-01, 3.5989504e-01 }, { 9.2820199e-01, 3.7207670e-01 },
-        { 9.2325213e-01, 3.8419461e-01 }, { 9.1814408e-01, 3.9624670e-01 },
-        { 9.1287871e-01, 4.0823088e-01 }, { 9.0745693e-01, 4.2014512e-01 },
-        { 9.0187965e-01, 4.3198737e-01 }, { 8.9614785e-01, 4.4375560e-01 },
-        { 8.9026249e-01, 4.5544780e-01 }, { 8.8422459e-01, 4.6706195e-01 },
-        { 8.7803519e-01, 4.7859608e-01 }, { 8.7169533e-01, 4.9004821e-01 },
-        { 8.6520612e-01, 5.0141636e-01 }, { 8.5856866e-01, 5.1269860e-01 },
-        { 8.5178409e-01, 5.2389299e-01 }, { 8.4485357e-01, 5.3499762e-01 },
-        { 8.3777828e-01, 5.4601058e-01 }, { 8.3055945e-01, 5.5692998e-01 },
-        { 8.2319831e-01, 5.6775395e-01 }, { 8.1569611e-01, 5.7848064e-01 },
-        { 8.0805415e-01, 5.8910822e-01 }, { 8.0027373e-01, 5.9963485e-01 },
-        { 7.9235620e-01, 6.1005873e-01 }, { 7.8430289e-01, 6.2037809e-01 },
-        { 7.7611520e-01, 6.3059115e-01 }, { 7.6779452e-01, 6.4069616e-01 },
-        { 7.5934229e-01, 6.5069139e-01 }, { 7.5075995e-01, 6.6057513e-01 },
-        { 7.4204897e-01, 6.7034568e-01 }, { 7.3321084e-01, 6.8000137e-01 },
-        { 7.2424708e-01, 6.8954054e-01 }, { 7.1515923e-01, 6.9896157e-01 },
-        { 7.0594883e-01, 7.0826283e-01 }, { 6.9661748e-01, 7.1744274e-01 },
-        { 6.8716676e-01, 7.2649972e-01 }, { 6.7759830e-01, 7.3543221e-01 },
-        { 6.6791374e-01, 7.4423869e-01 }, { 6.5811474e-01, 7.5291765e-01 },
-        { 6.4820297e-01, 7.6146760e-01 }, { 6.3818013e-01, 7.6988708e-01 },
-        { 6.2804795e-01, 7.7817464e-01 }, { 6.1780815e-01, 7.8632887e-01 },
-        { 6.0746249e-01, 7.9434836e-01 }, { 5.9701275e-01, 8.0223175e-01 },
-        { 5.8646072e-01, 8.0997767e-01 }, { 5.7580819e-01, 8.1758481e-01 },
-        { 5.6505701e-01, 8.2505187e-01 }, { 5.5420900e-01, 8.3237755e-01 },
-        { 5.4326604e-01, 8.3956061e-01 }, { 5.3222998e-01, 8.4659981e-01 },
-        { 5.2110274e-01, 8.5349396e-01 }, { 5.0988620e-01, 8.6024186e-01 },
-        { 4.9858230e-01, 8.6684237e-01 }, { 4.8719297e-01, 8.7329434e-01 },
-        { 4.7572016e-01, 8.7959669e-01 }, { 4.6416584e-01, 8.8574831e-01 },
-        { 4.5253199e-01, 8.9174817e-01 }, { 4.4082059e-01, 8.9759523e-01 },
-        { 4.2903367e-01, 9.0328850e-01 }, { 4.1717323e-01, 9.0882699e-01 },
-        { 4.0524131e-01, 9.1420976e-01 }, { 3.9323996e-01, 9.1943588e-01 },
-        { 3.8117123e-01, 9.2450446e-01 }, { 3.6903718e-01, 9.2941463e-01 },
-        { 3.5683990e-01, 9.3416555e-01 }, { 3.4458148e-01, 9.3875641e-01 },
-        { 3.3226402e-01, 9.4318642e-01 }, { 3.1988962e-01, 9.4745482e-01 },
-        { 3.0746042e-01, 9.5156087e-01 }, { 2.9497853e-01, 9.5550388e-01 },
-        { 2.8244610e-01, 9.5928317e-01 }, { 2.6986527e-01, 9.6289809e-01 },
-        { 2.5723821e-01, 9.6634802e-01 }, { 2.4456706e-01, 9.6963238e-01 },
-        { 2.3185402e-01, 9.7275059e-01 }, { 2.1910124e-01, 9.7570213e-01 },
-        { 2.0631092e-01, 9.7848649e-01 }, { 1.9348526e-01, 9.8110318e-01 },
-        { 1.8062644e-01, 9.8355177e-01 }, { 1.6773667e-01, 9.8583184e-01 },
-        { 1.5481816e-01, 9.8794298e-01 }, { 1.4187312e-01, 9.8988485e-01 },
-        { 1.2890377e-01, 9.9165711e-01 }, { 1.1591234e-01, 9.9325945e-01 },
-        { 1.0290104e-01, 9.9469160e-01 }, { 8.9872115e-02, 9.9595331e-01 },
-        { 7.6827789e-02, 9.9704438e-01 }, { 6.3770300e-02, 9.9796460e-01 },
-        { 5.0701883e-02, 9.9871383e-01 }, { 3.7624779e-02, 9.9929194e-01 },
-        { 2.4541229e-02, 9.9969882e-01 }, { 1.1453473e-02, 9.9993441e-01 },
-    }
-};
-
-static const struct lc3_mdct_rot_def mdct_rot_640 = {
-    .n4 = 640/4, .w = (const struct lc3_complex []){
-        { 9.9999925e-01, 1.2271843e-03 }, { 9.9993901e-01, 1.1044437e-02 },
-        { 9.9978239e-01, 2.0860625e-02 }, { 9.9952942e-01, 3.0674803e-02 },
-        { 9.9918010e-01, 4.0486024e-02 }, { 9.9873449e-01, 5.0293344e-02 },
-        { 9.9819261e-01, 6.0095815e-02 }, { 9.9755453e-01, 6.9892495e-02 },
-        { 9.9682030e-01, 7.9682438e-02 }, { 9.9598999e-01, 8.9464701e-02 },
-        { 9.9506369e-01, 9.9238342e-02 }, { 9.9404148e-01, 1.0900242e-01 },
-        { 9.9292347e-01, 1.1875599e-01 }, { 9.9170975e-01, 1.2849811e-01 },
-        { 9.9040046e-01, 1.3822785e-01 }, { 9.8899570e-01, 1.4794427e-01 },
-        { 9.8749562e-01, 1.5764642e-01 }, { 9.8590037e-01, 1.6733339e-01 },
-        { 9.8421009e-01, 1.7700422e-01 }, { 9.8242496e-01, 1.8665800e-01 },
-        { 9.8054513e-01, 1.9629378e-01 }, { 9.7857080e-01, 2.0591064e-01 },
-        { 9.7650215e-01, 2.1550766e-01 }, { 9.7433938e-01, 2.2508391e-01 },
-        { 9.7208271e-01, 2.3463847e-01 }, { 9.6973234e-01, 2.4417040e-01 },
-        { 9.6728851e-01, 2.5367881e-01 }, { 9.6475145e-01, 2.6316276e-01 },
-        { 9.6212140e-01, 2.7262136e-01 }, { 9.5939863e-01, 2.8205367e-01 },
-        { 9.5658338e-01, 2.9145880e-01 }, { 9.5367594e-01, 3.0083584e-01 },
-        { 9.5067658e-01, 3.1018388e-01 }, { 9.4758559e-01, 3.1950203e-01 },
-        { 9.4440327e-01, 3.2878938e-01 }, { 9.4112993e-01, 3.3804505e-01 },
-        { 9.3776588e-01, 3.4726813e-01 }, { 9.3431145e-01, 3.5645774e-01 },
-        { 9.3076696e-01, 3.6561300e-01 }, { 9.2713277e-01, 3.7473302e-01 },
-        { 9.2340921e-01, 3.8381691e-01 }, { 9.1959666e-01, 3.9286382e-01 },
-        { 9.1569547e-01, 4.0187286e-01 }, { 9.1170603e-01, 4.1084317e-01 },
-        { 9.0762872e-01, 4.1977388e-01 }, { 9.0346392e-01, 4.2866413e-01 },
-        { 8.9921205e-01, 4.3751307e-01 }, { 8.9487351e-01, 4.4631984e-01 },
-        { 8.9044872e-01, 4.5508359e-01 }, { 8.8593811e-01, 4.6380348e-01 },
-        { 8.8134211e-01, 4.7247866e-01 }, { 8.7666116e-01, 4.8110831e-01 },
-        { 8.7189572e-01, 4.8969159e-01 }, { 8.6704625e-01, 4.9822767e-01 },
-        { 8.6211320e-01, 5.0671573e-01 }, { 8.5709707e-01, 5.1515495e-01 },
-        { 8.5199832e-01, 5.2354452e-01 }, { 8.4681746e-01, 5.3188363e-01 },
-        { 8.4155498e-01, 5.4017147e-01 }, { 8.3621139e-01, 5.4840726e-01 },
-        { 8.3078720e-01, 5.5659018e-01 }, { 8.2528294e-01, 5.6471946e-01 },
-        { 8.1969914e-01, 5.7279431e-01 }, { 8.1403633e-01, 5.8081396e-01 },
-        { 8.0829506e-01, 5.8877762e-01 }, { 8.0247589e-01, 5.9668454e-01 },
-        { 7.9657938e-01, 6.0453395e-01 }, { 7.9060609e-01, 6.1232509e-01 },
-        { 7.8455660e-01, 6.2005721e-01 }, { 7.7843149e-01, 6.2772957e-01 },
-        { 7.7223135e-01, 6.3534143e-01 }, { 7.6595679e-01, 6.4289206e-01 },
-        { 7.5960840e-01, 6.5038072e-01 }, { 7.5318680e-01, 6.5780669e-01 },
-        { 7.4669260e-01, 6.6516927e-01 }, { 7.4012644e-01, 6.7246773e-01 },
-        { 7.3348894e-01, 6.7970138e-01 }, { 7.2678075e-01, 6.8686952e-01 },
-        { 7.2000251e-01, 6.9397146e-01 }, { 7.1315487e-01, 7.0100651e-01 },
-        { 7.0623850e-01, 7.0797400e-01 }, { 6.9925406e-01, 7.1487325e-01 },
-        { 6.9220222e-01, 7.2170360e-01 }, { 6.8508367e-01, 7.2846439e-01 },
-        { 6.7789909e-01, 7.3515497e-01 }, { 6.7064917e-01, 7.4177469e-01 },
-        { 6.6333461e-01, 7.4832292e-01 }, { 6.5595612e-01, 7.5479903e-01 },
-        { 6.4851440e-01, 7.6120239e-01 }, { 6.4101018e-01, 7.6753238e-01 },
-        { 6.3344418e-01, 7.7378839e-01 }, { 6.2581713e-01, 7.7996982e-01 },
-        { 6.1812975e-01, 7.8607608e-01 }, { 6.1038281e-01, 7.9210658e-01 },
-        { 6.0257703e-01, 7.9806073e-01 }, { 5.9471317e-01, 8.0393796e-01 },
-        { 5.8679200e-01, 8.0973771e-01 }, { 5.7881426e-01, 8.1545941e-01 },
-        { 5.7078075e-01, 8.2110251e-01 }, { 5.6269221e-01, 8.2666648e-01 },
-        { 5.5454945e-01, 8.3215077e-01 }, { 5.4635323e-01, 8.3755486e-01 },
-        { 5.3810436e-01, 8.4287822e-01 }, { 5.2980362e-01, 8.4812034e-01 },
-        { 5.2145182e-01, 8.5328072e-01 }, { 5.1304977e-01, 8.5835886e-01 },
-        { 5.0459826e-01, 8.6335427e-01 }, { 4.9609812e-01, 8.6826647e-01 },
-        { 4.8755016e-01, 8.7309498e-01 }, { 4.7895521e-01, 8.7783934e-01 },
-        { 4.7031410e-01, 8.8249909e-01 }, { 4.6162766e-01, 8.8707379e-01 },
-        { 4.5289673e-01, 8.9156298e-01 }, { 4.4412214e-01, 8.9596625e-01 },
-        { 4.3530476e-01, 9.0028316e-01 }, { 4.2644541e-01, 9.0451330e-01 },
-        { 4.1754496e-01, 9.0865626e-01 }, { 4.0860427e-01, 9.1271165e-01 },
-        { 3.9962420e-01, 9.1667906e-01 }, { 3.9060561e-01, 9.2055812e-01 },
-        { 3.8154937e-01, 9.2434846e-01 }, { 3.7245636e-01, 9.2804971e-01 },
-        { 3.6332745e-01, 9.3166151e-01 }, { 3.5416353e-01, 9.3518351e-01 },
-        { 3.4496546e-01, 9.3861538e-01 }, { 3.3573415e-01, 9.4195678e-01 },
-        { 3.2647048e-01, 9.4520740e-01 }, { 3.1717535e-01, 9.4836691e-01 },
-        { 3.0784964e-01, 9.5143502e-01 }, { 2.9849426e-01, 9.5441143e-01 },
-        { 2.8911012e-01, 9.5729585e-01 }, { 2.7969810e-01, 9.6008800e-01 },
-        { 2.7025914e-01, 9.6278762e-01 }, { 2.6079412e-01, 9.6539444e-01 },
-        { 2.5130396e-01, 9.6790822e-01 }, { 2.4178959e-01, 9.7032870e-01 },
-        { 2.3225191e-01, 9.7265567e-01 }, { 2.2269185e-01, 9.7488889e-01 },
-        { 2.1311032e-01, 9.7702814e-01 }, { 2.0350825e-01, 9.7907323e-01 },
-        { 1.9388657e-01, 9.8102395e-01 }, { 1.8424620e-01, 9.8288012e-01 },
-        { 1.7458807e-01, 9.8464156e-01 }, { 1.6491312e-01, 9.8630810e-01 },
-        { 1.5522227e-01, 9.8787957e-01 }, { 1.4551646e-01, 9.8935583e-01 },
-        { 1.3579663e-01, 9.9073673e-01 }, { 1.2606370e-01, 9.9202215e-01 },
-        { 1.1631863e-01, 9.9321195e-01 }, { 1.0656235e-01, 9.9430602e-01 },
-        { 9.6795791e-02, 9.9530426e-01 }, { 8.7019907e-02, 9.9620657e-01 },
-        { 7.7235635e-02, 9.9701287e-01 }, { 6.7443920e-02, 9.9772307e-01 },
-        { 5.7645703e-02, 9.9833710e-01 }, { 4.7841931e-02, 9.9885492e-01 },
-        { 3.8033548e-02, 9.9927646e-01 }, { 2.8221499e-02, 9.9960169e-01 },
-        { 1.8406730e-02, 9.9983058e-01 }, { 8.5901868e-03, 9.9996310e-01 },
-    }
-};
-
-static const struct lc3_mdct_rot_def mdct_rot_720 = {
-    .n4 = 720/4, .w = (const struct lc3_complex []){
-        { 9.9999941e-01, 1.0908306e-03 }, { 9.9995181e-01, 9.8173193e-03 },
-        { 9.9982806e-01, 1.8543060e-02 }, { 9.9962818e-01, 2.7267389e-02 },
-        { 9.9935216e-01, 3.5989642e-02 }, { 9.9900005e-01, 4.4709154e-02 },
-        { 9.9857185e-01, 5.3425261e-02 }, { 9.9806761e-01, 6.2137299e-02 },
-        { 9.9748736e-01, 7.0844606e-02 }, { 9.9683116e-01, 7.9546517e-02 },
-        { 9.9609903e-01, 8.8242371e-02 }, { 9.9529105e-01, 9.6931504e-02 },
-        { 9.9440728e-01, 1.0561326e-01 }, { 9.9344778e-01, 1.1428696e-01 },
-        { 9.9241262e-01, 1.2295197e-01 }, { 9.9130189e-01, 1.3160761e-01 },
-        { 9.9011567e-01, 1.4025323e-01 }, { 9.8885404e-01, 1.4888817e-01 },
-        { 9.8751711e-01, 1.5751177e-01 }, { 9.8610498e-01, 1.6612338e-01 },
-        { 9.8461775e-01, 1.7472233e-01 }, { 9.8305553e-01, 1.8330798e-01 },
-        { 9.8141846e-01, 1.9187967e-01 }, { 9.7970664e-01, 2.0043675e-01 },
-        { 9.7792022e-01, 2.0897856e-01 }, { 9.7605933e-01, 2.1750446e-01 },
-        { 9.7412410e-01, 2.2601379e-01 }, { 9.7211469e-01, 2.3450592e-01 },
-        { 9.7003125e-01, 2.4298018e-01 }, { 9.6787394e-01, 2.5143594e-01 },
-        { 9.6564292e-01, 2.5987255e-01 }, { 9.6333837e-01, 2.6828937e-01 },
-        { 9.6096045e-01, 2.7668577e-01 }, { 9.5850935e-01, 2.8506109e-01 },
-        { 9.5598526e-01, 2.9341470e-01 }, { 9.5338836e-01, 3.0174596e-01 },
-        { 9.5071887e-01, 3.1005425e-01 }, { 9.4797697e-01, 3.1833893e-01 },
-        { 9.4516287e-01, 3.2659936e-01 }, { 9.4227680e-01, 3.3483492e-01 },
-        { 9.3931898e-01, 3.4304499e-01 }, { 9.3628962e-01, 3.5122892e-01 },
-        { 9.3318895e-01, 3.5938611e-01 }, { 9.3001722e-01, 3.6751594e-01 },
-        { 9.2677467e-01, 3.7561777e-01 }, { 9.2346154e-01, 3.8369100e-01 },
-        { 9.2007808e-01, 3.9173501e-01 }, { 9.1662456e-01, 3.9974919e-01 },
-        { 9.1310123e-01, 4.0773292e-01 }, { 9.0950837e-01, 4.1568561e-01 },
-        { 9.0584624e-01, 4.2360664e-01 }, { 9.0211513e-01, 4.3149541e-01 },
-        { 8.9831532e-01, 4.3935132e-01 }, { 8.9444710e-01, 4.4717377e-01 },
-        { 8.9051077e-01, 4.5496217e-01 }, { 8.8650662e-01, 4.6271592e-01 },
-        { 8.8243495e-01, 4.7043443e-01 }, { 8.7829609e-01, 4.7811712e-01 },
-        { 8.7409034e-01, 4.8576339e-01 }, { 8.6981803e-01, 4.9337268e-01 },
-        { 8.6547947e-01, 5.0094439e-01 }, { 8.6107501e-01, 5.0847795e-01 },
-        { 8.5660497e-01, 5.1597279e-01 }, { 8.5206970e-01, 5.2342834e-01 },
-        { 8.4746954e-01, 5.3084403e-01 }, { 8.4280484e-01, 5.3821929e-01 },
-        { 8.3807596e-01, 5.4555356e-01 }, { 8.3328326e-01, 5.5284629e-01 },
-        { 8.2842709e-01, 5.6009691e-01 }, { 8.2350785e-01, 5.6730488e-01 },
-        { 8.1852588e-01, 5.7446965e-01 }, { 8.1348159e-01, 5.8159067e-01 },
-        { 8.0837534e-01, 5.8866740e-01 }, { 8.0320753e-01, 5.9569930e-01 },
-        { 7.9797856e-01, 6.0268584e-01 }, { 7.9268881e-01, 6.0962648e-01 },
-        { 7.8733870e-01, 6.1652070e-01 }, { 7.8192863e-01, 6.2336796e-01 },
-        { 7.7645902e-01, 6.3016775e-01 }, { 7.7093027e-01, 6.3691955e-01 },
-        { 7.6534281e-01, 6.4362285e-01 }, { 7.5969708e-01, 6.5027714e-01 },
-        { 7.5399348e-01, 6.5688190e-01 }, { 7.4823247e-01, 6.6343664e-01 },
-        { 7.4241448e-01, 6.6994085e-01 }, { 7.3653994e-01, 6.7639405e-01 },
-        { 7.3060932e-01, 6.8279574e-01 }, { 7.2462306e-01, 6.8914543e-01 },
-        { 7.1858162e-01, 6.9544264e-01 }, { 7.1248545e-01, 7.0168688e-01 },
-        { 7.0633503e-01, 7.0787769e-01 }, { 7.0013081e-01, 7.1401460e-01 },
-        { 6.9387328e-01, 7.2009713e-01 }, { 6.8756291e-01, 7.2612482e-01 },
-        { 6.8120017e-01, 7.3209721e-01 }, { 6.7478556e-01, 7.3801385e-01 },
-        { 6.6831956e-01, 7.4387429e-01 }, { 6.6180267e-01, 7.4967808e-01 },
-        { 6.5523538e-01, 7.5542478e-01 }, { 6.4861819e-01, 7.6111395e-01 },
-        { 6.4195160e-01, 7.6674516e-01 }, { 6.3523613e-01, 7.7231798e-01 },
-        { 6.2847228e-01, 7.7783198e-01 }, { 6.2166057e-01, 7.8328675e-01 },
-        { 6.1480152e-01, 7.8868187e-01 }, { 6.0789565e-01, 7.9401692e-01 },
-        { 6.0094349e-01, 7.9929151e-01 }, { 5.9394556e-01, 8.0450523e-01 },
-        { 5.8690240e-01, 8.0965769e-01 }, { 5.7981455e-01, 8.1474848e-01 },
-        { 5.7268254e-01, 8.1977723e-01 }, { 5.6550692e-01, 8.2474355e-01 },
-        { 5.5828823e-01, 8.2964706e-01 }, { 5.5102703e-01, 8.3448740e-01 },
-        { 5.4372386e-01, 8.3926418e-01 }, { 5.3637929e-01, 8.4397705e-01 },
-        { 5.2899387e-01, 8.4862564e-01 }, { 5.2156817e-01, 8.5320961e-01 },
-        { 5.1410274e-01, 8.5772861e-01 }, { 5.0659817e-01, 8.6218229e-01 },
-        { 4.9905502e-01, 8.6657030e-01 }, { 4.9147386e-01, 8.7089233e-01 },
-        { 4.8385527e-01, 8.7514803e-01 }, { 4.7619984e-01, 8.7933709e-01 },
-        { 4.6850814e-01, 8.8345918e-01 }, { 4.6078076e-01, 8.8751399e-01 },
-        { 4.5301829e-01, 8.9150122e-01 }, { 4.4522133e-01, 8.9542056e-01 },
-        { 4.3739045e-01, 8.9927170e-01 }, { 4.2952627e-01, 9.0305436e-01 },
-        { 4.2162938e-01, 9.0676825e-01 }, { 4.1370038e-01, 9.1041309e-01 },
-        { 4.0573988e-01, 9.1398859e-01 }, { 3.9774847e-01, 9.1749450e-01 },
-        { 3.8972678e-01, 9.2093053e-01 }, { 3.8167541e-01, 9.2429643e-01 },
-        { 3.7359497e-01, 9.2759194e-01 }, { 3.6548608e-01, 9.3081681e-01 },
-        { 3.5734936e-01, 9.3397079e-01 }, { 3.4918542e-01, 9.3705365e-01 },
-        { 3.4099489e-01, 9.4006515e-01 }, { 3.3277840e-01, 9.4300506e-01 },
-        { 3.2453656e-01, 9.4587315e-01 }, { 3.1627001e-01, 9.4866922e-01 },
-        { 3.0797937e-01, 9.5139304e-01 }, { 2.9966528e-01, 9.5404440e-01 },
-        { 2.9132836e-01, 9.5662311e-01 }, { 2.8296927e-01, 9.5912898e-01 },
-        { 2.7458862e-01, 9.6156180e-01 }, { 2.6618706e-01, 9.6392139e-01 },
-        { 2.5776523e-01, 9.6620758e-01 }, { 2.4932377e-01, 9.6842019e-01 },
-        { 2.4086332e-01, 9.7055904e-01 }, { 2.3238453e-01, 9.7262399e-01 },
-        { 2.2388805e-01, 9.7461487e-01 }, { 2.1537451e-01, 9.7653153e-01 },
-        { 2.0684457e-01, 9.7837382e-01 }, { 1.9829888e-01, 9.8014160e-01 },
-        { 1.8973809e-01, 9.8183474e-01 }, { 1.8116285e-01, 9.8345311e-01 },
-        { 1.7257382e-01, 9.8499659e-01 }, { 1.6397164e-01, 9.8646505e-01 },
-        { 1.5535697e-01, 9.8785840e-01 }, { 1.4673047e-01, 9.8917651e-01 },
-        { 1.3809280e-01, 9.9041929e-01 }, { 1.2944462e-01, 9.9158665e-01 },
-        { 1.2078657e-01, 9.9267850e-01 }, { 1.1211933e-01, 9.9369475e-01 },
-        { 1.0344355e-01, 9.9463533e-01 }, { 9.4759887e-02, 9.9550016e-01 },
-        { 8.6069011e-02, 9.9628918e-01 }, { 7.7371581e-02, 9.9700233e-01 },
-        { 6.8668259e-02, 9.9763955e-01 }, { 5.9959707e-02, 9.9820080e-01 },
-        { 5.1246589e-02, 9.9868603e-01 }, { 4.2529569e-02, 9.9909521e-01 },
-        { 3.3809310e-02, 9.9942830e-01 }, { 2.5086476e-02, 9.9968528e-01 },
-        { 1.6361732e-02, 9.9986614e-01 }, { 7.6357413e-03, 9.9997085e-01 },
-    }
-};
-
-static const struct lc3_mdct_rot_def mdct_rot_960 = {
-    .n4 = 960/4, .w = (const struct lc3_complex []){
-        { 9.9999967e-01, 8.1812300e-04 }, { 9.9997289e-01, 7.3630412e-03 },
-        { 9.9990328e-01, 1.3907644e-02 }, { 9.9979084e-01, 2.0451651e-02 },
-        { 9.9963557e-01, 2.6994782e-02 }, { 9.9943748e-01, 3.3536757e-02 },
-        { 9.9919658e-01, 4.0077295e-02 }, { 9.9891288e-01, 4.6616116e-02 },
-        { 9.9858638e-01, 5.3152941e-02 }, { 9.9821711e-01, 5.9687488e-02 },
-        { 9.9780508e-01, 6.6219479e-02 }, { 9.9735031e-01, 7.2748633e-02 },
-        { 9.9685281e-01, 7.9274670e-02 }, { 9.9631261e-01, 8.5797312e-02 },
-        { 9.9572973e-01, 9.2316279e-02 }, { 9.9510420e-01, 9.8831291e-02 },
-        { 9.9443605e-01, 1.0534207e-01 }, { 9.9372529e-01, 1.1184834e-01 },
-        { 9.9297196e-01, 1.1834981e-01 }, { 9.9217610e-01, 1.2484622e-01 },
-        { 9.9133774e-01, 1.3133727e-01 }, { 9.9045692e-01, 1.3782270e-01 },
-        { 9.8953366e-01, 1.4430223e-01 }, { 9.8856802e-01, 1.5077558e-01 },
-        { 9.8756003e-01, 1.5724246e-01 }, { 9.8650973e-01, 1.6370261e-01 },
-        { 9.8541718e-01, 1.7015575e-01 }, { 9.8428242e-01, 1.7660160e-01 },
-        { 9.8310549e-01, 1.8303989e-01 }, { 9.8188645e-01, 1.8947033e-01 },
-        { 9.8062534e-01, 1.9589266e-01 }, { 9.7932224e-01, 2.0230660e-01 },
-        { 9.7797718e-01, 2.0871187e-01 }, { 9.7659022e-01, 2.1510820e-01 },
-        { 9.7516144e-01, 2.2149531e-01 }, { 9.7369088e-01, 2.2787294e-01 },
-        { 9.7217861e-01, 2.3424080e-01 }, { 9.7062469e-01, 2.4059864e-01 },
-        { 9.6902920e-01, 2.4694616e-01 }, { 9.6739220e-01, 2.5328311e-01 },
-        { 9.6571376e-01, 2.5960920e-01 }, { 9.6399395e-01, 2.6592418e-01 },
-        { 9.6223284e-01, 2.7222777e-01 }, { 9.6043052e-01, 2.7851969e-01 },
-        { 9.5858705e-01, 2.8479968e-01 }, { 9.5670253e-01, 2.9106748e-01 },
-        { 9.5477702e-01, 2.9732280e-01 }, { 9.5281061e-01, 3.0356539e-01 },
-        { 9.5080338e-01, 3.0979497e-01 }, { 9.4875543e-01, 3.1601129e-01 },
-        { 9.4666684e-01, 3.2221406e-01 }, { 9.4453769e-01, 3.2840304e-01 },
-        { 9.4236808e-01, 3.3457794e-01 }, { 9.4015810e-01, 3.4073852e-01 },
-        { 9.3790786e-01, 3.4688450e-01 }, { 9.3561743e-01, 3.5301562e-01 },
-        { 9.3328693e-01, 3.5913161e-01 }, { 9.3091644e-01, 3.6523223e-01 },
-        { 9.2850608e-01, 3.7131719e-01 }, { 9.2605595e-01, 3.7738626e-01 },
-        { 9.2356614e-01, 3.8343915e-01 }, { 9.2103677e-01, 3.8947562e-01 },
-        { 9.1846795e-01, 3.9549541e-01 }, { 9.1585979e-01, 4.0149825e-01 },
-        { 9.1321239e-01, 4.0748390e-01 }, { 9.1052587e-01, 4.1345209e-01 },
-        { 9.0780035e-01, 4.1940257e-01 }, { 9.0503595e-01, 4.2533508e-01 },
-        { 9.0223277e-01, 4.3124938e-01 }, { 8.9939095e-01, 4.3714520e-01 },
-        { 8.9651059e-01, 4.4302229e-01 }, { 8.9359184e-01, 4.4888041e-01 },
-        { 8.9063481e-01, 4.5471930e-01 }, { 8.8763962e-01, 4.6053871e-01 },
-        { 8.8460641e-01, 4.6633839e-01 }, { 8.8153531e-01, 4.7211810e-01 },
-        { 8.7842644e-01, 4.7787758e-01 }, { 8.7527995e-01, 4.8361659e-01 },
-        { 8.7209596e-01, 4.8933489e-01 }, { 8.6887462e-01, 4.9503222e-01 },
-        { 8.6561605e-01, 5.0070835e-01 }, { 8.6232041e-01, 5.0636303e-01 },
-        { 8.5898782e-01, 5.1199602e-01 }, { 8.5561844e-01, 5.1760707e-01 },
-        { 8.5221241e-01, 5.2319595e-01 }, { 8.4876987e-01, 5.2876243e-01 },
-        { 8.4529098e-01, 5.3430625e-01 }, { 8.4177587e-01, 5.3982718e-01 },
-        { 8.3822471e-01, 5.4532499e-01 }, { 8.3463763e-01, 5.5079944e-01 },
-        { 8.3101481e-01, 5.5625029e-01 }, { 8.2735639e-01, 5.6167732e-01 },
-        { 8.2366252e-01, 5.6708028e-01 }, { 8.1993338e-01, 5.7245896e-01 },
-        { 8.1616911e-01, 5.7781311e-01 }, { 8.1236987e-01, 5.8314251e-01 },
-        { 8.0853584e-01, 5.8844693e-01 }, { 8.0466718e-01, 5.9372614e-01 },
-        { 8.0076404e-01, 5.9897992e-01 }, { 7.9682660e-01, 6.0420805e-01 },
-        { 7.9285503e-01, 6.0941029e-01 }, { 7.8884950e-01, 6.1458642e-01 },
-        { 7.8481017e-01, 6.1973623e-01 }, { 7.8073723e-01, 6.2485949e-01 },
-        { 7.7663084e-01, 6.2995598e-01 }, { 7.7249118e-01, 6.3502549e-01 },
-        { 7.6831844e-01, 6.4006780e-01 }, { 7.6411277e-01, 6.4508268e-01 },
-        { 7.5987438e-01, 6.5006994e-01 }, { 7.5560344e-01, 6.5502934e-01 },
-        { 7.5130013e-01, 6.5996069e-01 }, { 7.4696464e-01, 6.6486377e-01 },
-        { 7.4259715e-01, 6.6973837e-01 }, { 7.3819784e-01, 6.7458427e-01 },
-        { 7.3376692e-01, 6.7940128e-01 }, { 7.2930457e-01, 6.8418919e-01 },
-        { 7.2481097e-01, 6.8894779e-01 }, { 7.2028632e-01, 6.9367688e-01 },
-        { 7.1573083e-01, 6.9837625e-01 }, { 7.1114467e-01, 7.0304571e-01 },
-        { 7.0652804e-01, 7.0768504e-01 }, { 7.0188116e-01, 7.1229407e-01 },
-        { 6.9720420e-01, 7.1687258e-01 }, { 6.9249738e-01, 7.2142039e-01 },
-        { 6.8776090e-01, 7.2593729e-01 }, { 6.8299495e-01, 7.3042309e-01 },
-        { 6.7819975e-01, 7.3487761e-01 }, { 6.7337550e-01, 7.3930064e-01 },
-        { 6.6852240e-01, 7.4369201e-01 }, { 6.6364066e-01, 7.4805152e-01 },
-        { 6.5873050e-01, 7.5237898e-01 }, { 6.5379211e-01, 7.5667422e-01 },
-        { 6.4882573e-01, 7.6093704e-01 }, { 6.4383154e-01, 7.6516727e-01 },
-        { 6.3880978e-01, 7.6936471e-01 }, { 6.3376065e-01, 7.7352921e-01 },
-        { 6.2868438e-01, 7.7766056e-01 }, { 6.2358117e-01, 7.8175861e-01 },
-        { 6.1845126e-01, 7.8582316e-01 }, { 6.1329485e-01, 7.8985406e-01 },
-        { 6.0811216e-01, 7.9385112e-01 }, { 6.0290343e-01, 7.9781417e-01 },
-        { 5.9766888e-01, 8.0174305e-01 }, { 5.9240872e-01, 8.0563758e-01 },
-        { 5.8712318e-01, 8.0949760e-01 }, { 5.8181249e-01, 8.1332295e-01 },
-        { 5.7647688e-01, 8.1711346e-01 }, { 5.7111658e-01, 8.2086896e-01 },
-        { 5.6573181e-01, 8.2458930e-01 }, { 5.6032281e-01, 8.2827432e-01 },
-        { 5.5488980e-01, 8.3192386e-01 }, { 5.4943303e-01, 8.3553776e-01 },
-        { 5.4395272e-01, 8.3911587e-01 }, { 5.3844911e-01, 8.4265803e-01 },
-        { 5.3292243e-01, 8.4616410e-01 }, { 5.2737292e-01, 8.4963392e-01 },
-        { 5.2180083e-01, 8.5306735e-01 }, { 5.1620638e-01, 8.5646423e-01 },
-        { 5.1058981e-01, 8.5982442e-01 }, { 5.0495138e-01, 8.6314779e-01 },
-        { 4.9929132e-01, 8.6643418e-01 }, { 4.9360987e-01, 8.6968345e-01 },
-        { 4.8790727e-01, 8.7289547e-01 }, { 4.8218377e-01, 8.7607009e-01 },
-        { 4.7643962e-01, 8.7920719e-01 }, { 4.7067506e-01, 8.8230663e-01 },
-        { 4.6489034e-01, 8.8536827e-01 }, { 4.5908570e-01, 8.8839199e-01 },
-        { 4.5326139e-01, 8.9137765e-01 }, { 4.4741768e-01, 8.9432512e-01 },
-        { 4.4155479e-01, 8.9723429e-01 }, { 4.3567299e-01, 9.0010502e-01 },
-        { 4.2977253e-01, 9.0293719e-01 }, { 4.2385365e-01, 9.0573069e-01 },
-        { 4.1791662e-01, 9.0848539e-01 }, { 4.1196169e-01, 9.1120117e-01 },
-        { 4.0598911e-01, 9.1387791e-01 }, { 3.9999914e-01, 9.1651551e-01 },
-        { 3.9399204e-01, 9.1911385e-01 }, { 3.8796806e-01, 9.2167282e-01 },
-        { 3.8192746e-01, 9.2419231e-01 }, { 3.7587050e-01, 9.2667220e-01 },
-        { 3.6979743e-01, 9.2911240e-01 }, { 3.6370853e-01, 9.3151280e-01 },
-        { 3.5760405e-01, 9.3387330e-01 }, { 3.5148424e-01, 9.3619380e-01 },
-        { 3.4534939e-01, 9.3847419e-01 }, { 3.3919973e-01, 9.4071438e-01 },
-        { 3.3303555e-01, 9.4291427e-01 }, { 3.2685710e-01, 9.4507377e-01 },
-        { 3.2066465e-01, 9.4719279e-01 }, { 3.1445847e-01, 9.4927123e-01 },
-        { 3.0823881e-01, 9.5130901e-01 }, { 3.0200595e-01, 9.5330604e-01 },
-        { 2.9576015e-01, 9.5526223e-01 }, { 2.8950169e-01, 9.5717750e-01 },
-        { 2.8323082e-01, 9.5905177e-01 }, { 2.7694782e-01, 9.6088496e-01 },
-        { 2.7065295e-01, 9.6267699e-01 }, { 2.6434649e-01, 9.6442777e-01 },
-        { 2.5802871e-01, 9.6613725e-01 }, { 2.5169988e-01, 9.6780534e-01 },
-        { 2.4536026e-01, 9.6943197e-01 }, { 2.3901013e-01, 9.7101707e-01 },
-        { 2.3264977e-01, 9.7256058e-01 }, { 2.2627944e-01, 9.7406243e-01 },
-        { 2.1989941e-01, 9.7552255e-01 }, { 2.1350997e-01, 9.7694089e-01 },
-        { 2.0711138e-01, 9.7831737e-01 }, { 2.0070391e-01, 9.7965195e-01 },
-        { 1.9428785e-01, 9.8094456e-01 }, { 1.8786347e-01, 9.8219515e-01 },
-        { 1.8143104e-01, 9.8340367e-01 }, { 1.7499084e-01, 9.8457006e-01 },
-        { 1.6854314e-01, 9.8569428e-01 }, { 1.6208822e-01, 9.8677627e-01 },
-        { 1.5562636e-01, 9.8781599e-01 }, { 1.4915783e-01, 9.8881340e-01 },
-        { 1.4268292e-01, 9.8976845e-01 }, { 1.3620189e-01, 9.9068110e-01 },
-        { 1.2971502e-01, 9.9155132e-01 }, { 1.2322260e-01, 9.9237906e-01 },
-        { 1.1672491e-01, 9.9316428e-01 }, { 1.1022221e-01, 9.9390697e-01 },
-        { 1.0371479e-01, 9.9460708e-01 }, { 9.7202924e-02, 9.9526458e-01 },
-        { 9.0686897e-02, 9.9587945e-01 }, { 8.4166986e-02, 9.9645166e-01 },
-        { 7.7643468e-02, 9.9698119e-01 }, { 7.1116625e-02, 9.9746801e-01 },
-        { 6.4586736e-02, 9.9791210e-01 }, { 5.8054080e-02, 9.9831344e-01 },
-        { 5.1518937e-02, 9.9867202e-01 }, { 4.4981587e-02, 9.9898782e-01 },
-        { 3.8442310e-02, 9.9926082e-01 }, { 3.1901387e-02, 9.9949102e-01 },
-        { 2.5359097e-02, 9.9967841e-01 }, { 1.8815721e-02, 9.9982297e-01 },
-        { 1.2271538e-02, 9.9992470e-01 }, { 5.7268303e-03, 9.9998360e-01 },
-    }
-};
-
-const struct lc3_mdct_rot_def * lc3_mdct_rot[LC3_NUM_DT][LC3_NUM_SRATE] = {
-    [LC3_DT_7M5] = { &mdct_rot_120, &mdct_rot_240, &mdct_rot_360,
-                     &mdct_rot_480, &mdct_rot_720                },
-    [LC3_DT_10M] = { &mdct_rot_160, &mdct_rot_320, &mdct_rot_480,
-                     &mdct_rot_640, &mdct_rot_960                }
-};
-
-
-/**
- * Low delay MDCT windows (cf. 3.7.3)
- */
-
-static const float mdct_win_10m_80[80+50] = {
-    -7.07854671e-04, -2.09819773e-03, -4.52519808e-03, -8.23397633e-03,
-    -1.33771310e-02, -1.99972156e-02, -2.80090946e-02, -3.72150208e-02,
-    -4.73176826e-02, -5.79465483e-02, -6.86760675e-02, -7.90464744e-02,
-    -8.85970547e-02, -9.68830362e-02, -1.03496124e-01, -1.08076646e-01,
-    -1.10324226e-01, -1.09980985e-01, -1.06817214e-01, -1.00619042e-01,
-    -9.11645251e-02, -7.82061748e-02, -6.14668812e-02, -4.06336286e-02,
-    -1.53632952e-02,  1.47015507e-02,  4.98973651e-02,  9.05036926e-02,
-     1.36691102e-01,  1.88468639e-01,  2.45645680e-01,  3.07778908e-01,
-     3.74164237e-01,  4.43811480e-01,  5.15473546e-01,  5.87666172e-01,
-     6.58761977e-01,  7.27057670e-01,  7.90875299e-01,  8.48664336e-01,
-     8.99132024e-01,  9.41334815e-01,  9.74763483e-01,  9.99411473e-01,
-     1.01576037e+00,  1.02473616e+00,  1.02763429e+00,  1.02599149e+00,
-     1.02142721e+00,  1.01543986e+00,  1.00936693e+00,  1.00350816e+00,
-     9.98889821e-01,  9.95313390e-01,  9.92594392e-01,  9.90577196e-01,
-     9.89137162e-01,  9.88179075e-01,  9.87624927e-01,  9.87405628e-01,
-     9.87452485e-01,  9.87695113e-01,  9.88064062e-01,  9.88492687e-01,
-     9.88923003e-01,  9.89307497e-01,  9.89614633e-01,  9.89831927e-01,
-     9.89969310e-01,  9.90060335e-01,  9.90157502e-01,  9.90325529e-01,
-     9.90630379e-01,  9.91129889e-01,  9.91866549e-01,  9.92861973e-01,
-     9.94115607e-01,  9.95603378e-01,  9.97279311e-01,  9.99078484e-01,
-     1.00092237e+00,  1.00272811e+00,  1.00441604e+00,  1.00591922e+00,
-     1.00718935e+00,  1.00820015e+00,  1.00894949e+00,  1.00945824e+00,
-     1.00976898e+00,  1.00994034e+00,  1.01003945e+00,  1.01013232e+00,
-     1.01027252e+00,  1.01049435e+00,  1.01080807e+00,  1.01120107e+00,
-     1.01164127e+00,  1.01208013e+00,  1.01245818e+00,  1.01270696e+00,
-     1.01275501e+00,  1.01253013e+00,  1.01196233e+00,  1.01098214e+00,
-     1.00951244e+00,  1.00746086e+00,  1.00470868e+00,  1.00111141e+00,
-     9.96504102e-01,  9.90720000e-01,  9.82376587e-01,  9.70882175e-01,
-     9.54673298e-01,  9.32155386e-01,  9.01800368e-01,  8.62398408e-01,
-     8.13281737e-01,  7.54455197e-01,  6.86658072e-01,  6.11348804e-01,
-     5.30618165e-01,  4.47130985e-01,  3.63911468e-01,  2.84164703e-01,
-     2.11020945e-01,  1.47228797e-01,  9.48266535e-02,  5.48243661e-02,
-     2.70146141e-02,  9.99674359e-03,
-};
-
-static const float mdct_win_10m_160[160+100] = {
-    -4.61989875e-04, -9.74716672e-04, -1.66447310e-03, -2.59710692e-03,
-    -3.80628516e-03, -5.32460872e-03, -7.17588528e-03, -9.38248086e-03,
-    -1.19527030e-02, -1.48952816e-02, -1.82066640e-02, -2.18757093e-02,
-    -2.58847194e-02, -3.02086274e-02, -3.48159779e-02, -3.96706799e-02,
-    -4.47269805e-02, -4.99422586e-02, -5.52633479e-02, -6.06371724e-02,
-    -6.60096152e-02, -7.13196627e-02, -7.65117823e-02, -8.15296401e-02,
-    -8.63113754e-02, -9.08041129e-02, -9.49537776e-02, -9.87073651e-02,
-    -1.02020268e-01, -1.04843883e-01, -1.07138231e-01, -1.08869014e-01,
-    -1.09996966e-01, -1.10489847e-01, -1.10322584e-01, -1.09462175e-01,
-    -1.07883429e-01, -1.05561251e-01, -1.02465016e-01, -9.85701457e-02,
-    -9.38468492e-02, -8.82630999e-02, -8.17879272e-02, -7.43878560e-02,
-    -6.60218980e-02, -5.66565564e-02, -4.62445689e-02, -3.47458578e-02,
-    -2.21158161e-02, -8.31042570e-03,  6.71769764e-03,  2.30064206e-02,
-     4.06010646e-02,  5.95323909e-02,  7.98335419e-02,  1.01523314e-01,
-     1.24617139e-01,  1.49115252e-01,  1.75006740e-01,  2.02269985e-01,
-     2.30865538e-01,  2.60736512e-01,  2.91814469e-01,  3.24009570e-01,
-     3.57217518e-01,  3.91314689e-01,  4.26157164e-01,  4.61592545e-01,
-     4.97447159e-01,  5.33532682e-01,  5.69654673e-01,  6.05608382e-01,
-     6.41183084e-01,  6.76165350e-01,  7.10340055e-01,  7.43494372e-01,
-     7.75428189e-01,  8.05943723e-01,  8.34858937e-01,  8.62010834e-01,
-     8.87259971e-01,  9.10486312e-01,  9.31596250e-01,  9.50522086e-01,
-     9.67236671e-01,  9.81739750e-01,  9.94055718e-01,  1.00424751e+00,
-     1.01240743e+00,  1.01865099e+00,  1.02311884e+00,  1.02597245e+00,
-     1.02739752e+00,  1.02758583e+00,  1.02673867e+00,  1.02506178e+00,
-     1.02275651e+00,  1.02000914e+00,  1.01699650e+00,  1.01391595e+00,
-     1.01104487e+00,  1.00777386e+00,  1.00484875e+00,  1.00224501e+00,
-     9.99939317e-01,  9.97905542e-01,  9.96120338e-01,  9.94559753e-01,
-     9.93203161e-01,  9.92029727e-01,  9.91023065e-01,  9.90166895e-01,
-     9.89448837e-01,  9.88855636e-01,  9.88377852e-01,  9.88005163e-01,
-     9.87729546e-01,  9.87541274e-01,  9.87432981e-01,  9.87394992e-01,
-     9.87419705e-01,  9.87497321e-01,  9.87620124e-01,  9.87778192e-01,
-     9.87963798e-01,  9.88167801e-01,  9.88383520e-01,  9.88602222e-01,
-     9.88818277e-01,  9.89024798e-01,  9.89217866e-01,  9.89392368e-01,
-     9.89546334e-01,  9.89677201e-01,  9.89785920e-01,  9.89872536e-01,
-     9.89941079e-01,  9.89994556e-01,  9.90039402e-01,  9.90081472e-01,
-     9.90129379e-01,  9.90190227e-01,  9.90273445e-01,  9.90386228e-01,
-     9.90537983e-01,  9.90734883e-01,  9.90984259e-01,  9.91290512e-01,
-     9.91658694e-01,  9.92090615e-01,  9.92588721e-01,  9.93151653e-01,
-     9.93779087e-01,  9.94466818e-01,  9.95211663e-01,  9.96006862e-01,
-     9.96846133e-01,  9.97720337e-01,  9.98621352e-01,  9.99538258e-01,
-     1.00046196e+00,  1.00138055e+00,  1.00228487e+00,  1.00316385e+00,
-     1.00400915e+00,  1.00481138e+00,  1.00556397e+00,  1.00625986e+00,
-     1.00689557e+00,  1.00746662e+00,  1.00797244e+00,  1.00841147e+00,
-     1.00878601e+00,  1.00909776e+00,  1.00935176e+00,  1.00955240e+00,
-     1.00970709e+00,  1.00982209e+00,  1.00990696e+00,  1.00996902e+00,
-     1.01001789e+00,  1.01006081e+00,  1.01010656e+00,  1.01016113e+00,
-     1.01023108e+00,  1.01031948e+00,  1.01043047e+00,  1.01056410e+00,
-     1.01072136e+00,  1.01089966e+00,  1.01109699e+00,  1.01130817e+00,
-     1.01152919e+00,  1.01175301e+00,  1.01197388e+00,  1.01218284e+00,
-     1.01237303e+00,  1.01253506e+00,  1.01266098e+00,  1.01274058e+00,
-     1.01276592e+00,  1.01272696e+00,  1.01261590e+00,  1.01242289e+00,
-     1.01214046e+00,  1.01175881e+00,  1.01126996e+00,  1.01066368e+00,
-     1.00993075e+00,  1.00905825e+00,  1.00803431e+00,  1.00684335e+00,
-     1.00547001e+00,  1.00389477e+00,  1.00209885e+00,  1.00006069e+00,
-     9.97760020e-01,  9.95174643e-01,  9.92286108e-01,  9.89075787e-01,
-     9.84736245e-01,  9.79861353e-01,  9.74137862e-01,  9.67333198e-01,
-     9.59253976e-01,  9.49698408e-01,  9.38463416e-01,  9.25356797e-01,
-     9.10198679e-01,  8.92833832e-01,  8.73143784e-01,  8.51042044e-01,
-     8.26483991e-01,  7.99468149e-01,  7.70043128e-01,  7.38302860e-01,
-     7.04381434e-01,  6.68461648e-01,  6.30775533e-01,  5.91579959e-01,
-     5.51170316e-01,  5.09891542e-01,  4.68101711e-01,  4.26177297e-01,
-     3.84517234e-01,  3.43522867e-01,  3.03600465e-01,  2.65143468e-01,
-     2.28528397e-01,  1.94102191e-01,  1.62173542e-01,  1.33001524e-01,
-     1.06784043e-01,  8.36505724e-02,  6.36518811e-02,  4.67653841e-02,
-     3.28807275e-02,  2.18305756e-02,  1.33638143e-02,  6.75812489e-03,
-};
-
-static const float mdct_win_10m_240[240+150] = {
-    -3.61349642e-04, -7.07854671e-04, -1.07444364e-03, -1.53347854e-03,
-    -2.09819773e-03, -2.77842087e-03, -3.58412992e-03, -4.52519808e-03,
-    -5.60932724e-03, -6.84323454e-03, -8.23397633e-03, -9.78531476e-03,
-    -1.14988030e-02, -1.33771310e-02, -1.54218168e-02, -1.76297991e-02,
-    -1.99972156e-02, -2.25208056e-02, -2.51940630e-02, -2.80090946e-02,
-    -3.09576509e-02, -3.40299627e-02, -3.72150208e-02, -4.05005325e-02,
-    -4.38721922e-02, -4.73176826e-02, -5.08232534e-02, -5.43716664e-02,
-    -5.79465483e-02, -6.15342620e-02, -6.51170816e-02, -6.86760675e-02,
-    -7.21944781e-02, -7.56569598e-02, -7.90464744e-02, -8.23444256e-02,
-    -8.55332458e-02, -8.85970547e-02, -9.15209110e-02, -9.42884745e-02,
-    -9.68830362e-02, -9.92912326e-02, -1.01500847e-01, -1.03496124e-01,
-    -1.05263700e-01, -1.06793998e-01, -1.08076646e-01, -1.09099730e-01,
-    -1.09852449e-01, -1.10324226e-01, -1.10508462e-01, -1.10397741e-01,
-    -1.09980985e-01, -1.09249277e-01, -1.08197423e-01, -1.06817214e-01,
-    -1.05099580e-01, -1.03036011e-01, -1.00619042e-01, -9.78412002e-02,
-    -9.46930422e-02, -9.11645251e-02, -8.72464453e-02, -8.29304391e-02,
-    -7.82061748e-02, -7.30614243e-02, -6.74846818e-02, -6.14668812e-02,
-    -5.49949726e-02, -4.80544442e-02, -4.06336286e-02, -3.27204559e-02,
-    -2.43012258e-02, -1.53632952e-02, -5.89143427e-03,  4.12659586e-03,
-     1.47015507e-02,  2.58473819e-02,  3.75765277e-02,  4.98973651e-02,
-     6.28203403e-02,  7.63539773e-02,  9.05036926e-02,  1.05274712e-01,
-     1.20670347e-01,  1.36691102e-01,  1.53334389e-01,  1.70595471e-01,
-     1.88468639e-01,  2.06944996e-01,  2.26009300e-01,  2.45645680e-01,
-     2.65834602e-01,  2.86554381e-01,  3.07778908e-01,  3.29476944e-01,
-     3.51617148e-01,  3.74164237e-01,  3.97073959e-01,  4.20304305e-01,
-     4.43811480e-01,  4.67544229e-01,  4.91449863e-01,  5.15473546e-01,
-     5.39555764e-01,  5.63639982e-01,  5.87666172e-01,  6.11569531e-01,
-     6.35289059e-01,  6.58761977e-01,  6.81923097e-01,  7.04709282e-01,
-     7.27057670e-01,  7.48906896e-01,  7.70199019e-01,  7.90875299e-01,
-     8.10878869e-01,  8.30157914e-01,  8.48664336e-01,  8.66354816e-01,
-     8.83189685e-01,  8.99132024e-01,  9.14154056e-01,  9.28228255e-01,
-     9.41334815e-01,  9.53461939e-01,  9.64604825e-01,  9.74763483e-01,
-     9.83943539e-01,  9.92152910e-01,  9.99411473e-01,  1.00574608e+00,
-     1.01118397e+00,  1.01576037e+00,  1.01951507e+00,  1.02249094e+00,
-     1.02473616e+00,  1.02630410e+00,  1.02725098e+00,  1.02763429e+00,
-     1.02751106e+00,  1.02694280e+00,  1.02599149e+00,  1.02471615e+00,
-     1.02317598e+00,  1.02142721e+00,  1.01952157e+00,  1.01751012e+00,
-     1.01543986e+00,  1.01346092e+00,  1.01165490e+00,  1.00936693e+00,
-     1.00726318e+00,  1.00531319e+00,  1.00350816e+00,  1.00184079e+00,
-     1.00030393e+00,  9.98889821e-01,  9.97591528e-01,  9.96401528e-01,
-     9.95313390e-01,  9.94320108e-01,  9.93415896e-01,  9.92594392e-01,
-     9.91851028e-01,  9.91179799e-01,  9.90577196e-01,  9.90038105e-01,
-     9.89559439e-01,  9.89137162e-01,  9.88768437e-01,  9.88449792e-01,
-     9.88179075e-01,  9.87952836e-01,  9.87769137e-01,  9.87624927e-01,
-     9.87517995e-01,  9.87445813e-01,  9.87405628e-01,  9.87395112e-01,
-     9.87411537e-01,  9.87452485e-01,  9.87514989e-01,  9.87596889e-01,
-     9.87695113e-01,  9.87807582e-01,  9.87931200e-01,  9.88064062e-01,
-     9.88203257e-01,  9.88347108e-01,  9.88492687e-01,  9.88638659e-01,
-     9.88782558e-01,  9.88923003e-01,  9.89058172e-01,  9.89186767e-01,
-     9.89307497e-01,  9.89419640e-01,  9.89522076e-01,  9.89614633e-01,
-     9.89697035e-01,  9.89769260e-01,  9.89831927e-01,  9.89885257e-01,
-     9.89930764e-01,  9.89969310e-01,  9.90002569e-01,  9.90032156e-01,
-     9.90060335e-01,  9.90088981e-01,  9.90120659e-01,  9.90157502e-01,
-     9.90202395e-01,  9.90257541e-01,  9.90325529e-01,  9.90408791e-01,
-     9.90509649e-01,  9.90630379e-01,  9.90772711e-01,  9.90938744e-01,
-     9.91129889e-01,  9.91347632e-01,  9.91592856e-01,  9.91866549e-01,
-     9.92169132e-01,  9.92501085e-01,  9.92861973e-01,  9.93251918e-01,
-     9.93670021e-01,  9.94115607e-01,  9.94587315e-01,  9.95083740e-01,
-     9.95603378e-01,  9.96143992e-01,  9.96703453e-01,  9.97279311e-01,
-     9.97869086e-01,  9.98469709e-01,  9.99078484e-01,  9.99691901e-01,
-     1.00030819e+00,  1.00092237e+00,  1.00153264e+00,  1.00213546e+00,
-     1.00272811e+00,  1.00330745e+00,  1.00387093e+00,  1.00441604e+00,
-     1.00494055e+00,  1.00544214e+00,  1.00591922e+00,  1.00637030e+00,
-     1.00679393e+00,  1.00718935e+00,  1.00755557e+00,  1.00789267e+00,
-     1.00820015e+00,  1.00847842e+00,  1.00872788e+00,  1.00894949e+00,
-     1.00914411e+00,  1.00931322e+00,  1.00945824e+00,  1.00958128e+00,
-     1.00968409e+00,  1.00976898e+00,  1.00983831e+00,  1.00989455e+00,
-     1.00994034e+00,  1.00997792e+00,  1.01001023e+00,  1.01003945e+00,
-     1.01006820e+00,  1.01009839e+00,  1.01013232e+00,  1.01017166e+00,
-     1.01021810e+00,  1.01027252e+00,  1.01033649e+00,  1.01041022e+00,
-     1.01049435e+00,  1.01058887e+00,  1.01069350e+00,  1.01080807e+00,
-     1.01093144e+00,  1.01106288e+00,  1.01120107e+00,  1.01134470e+00,
-     1.01149190e+00,  1.01164127e+00,  1.01179028e+00,  1.01193757e+00,
-     1.01208013e+00,  1.01221624e+00,  1.01234291e+00,  1.01245818e+00,
-     1.01255888e+00,  1.01264286e+00,  1.01270696e+00,  1.01274895e+00,
-     1.01276580e+00,  1.01275501e+00,  1.01271380e+00,  1.01263978e+00,
-     1.01253013e+00,  1.01238231e+00,  1.01219407e+00,  1.01196233e+00,
-     1.01168517e+00,  1.01135914e+00,  1.01098214e+00,  1.01055072e+00,
-     1.01006213e+00,  1.00951244e+00,  1.00889869e+00,  1.00821592e+00,
-     1.00746086e+00,  1.00662774e+00,  1.00571234e+00,  1.00470868e+00,
-     1.00361147e+00,  1.00241429e+00,  1.00111141e+00,  9.99696165e-01,
-     9.98162595e-01,  9.96504102e-01,  9.94714888e-01,  9.92789191e-01,
-     9.90720000e-01,  9.88479371e-01,  9.85534766e-01,  9.82376587e-01,
-     9.78974733e-01,  9.75162381e-01,  9.70882175e-01,  9.66080552e-01,
-     9.60697640e-01,  9.54673298e-01,  9.47947935e-01,  9.40460905e-01,
-     9.32155386e-01,  9.22977548e-01,  9.12874535e-01,  9.01800368e-01,
-     8.89716328e-01,  8.76590897e-01,  8.62398408e-01,  8.47120080e-01,
-     8.30747973e-01,  8.13281737e-01,  7.94729145e-01,  7.75110884e-01,
-     7.54455197e-01,  7.32796355e-01,  7.10179084e-01,  6.86658072e-01,
-     6.62296243e-01,  6.37168412e-01,  6.11348804e-01,  5.84920660e-01,
-     5.57974743e-01,  5.30618165e-01,  5.02952396e-01,  4.75086883e-01,
-     4.47130985e-01,  4.19204992e-01,  3.91425291e-01,  3.63911468e-01,
-     3.36783777e-01,  3.10162784e-01,  2.84164703e-01,  2.58903371e-01,
-     2.34488060e-01,  2.11020945e-01,  1.88599764e-01,  1.67310081e-01,
-     1.47228797e-01,  1.28422307e-01,  1.10942255e-01,  9.48266535e-02,
-     8.00991437e-02,  6.67676585e-02,  5.48243661e-02,  4.42458885e-02,
-     3.49936100e-02,  2.70146141e-02,  2.02437018e-02,  1.46079676e-02,
-     9.99674359e-03,  5.30523510e-03,
-};
-
-static const float mdct_win_10m_320[320+200] = {
-    -3.02115349e-04, -5.86773749e-04, -8.36650400e-04, -1.12663536e-03,
-    -1.47049294e-03, -1.87347339e-03, -2.33929236e-03, -2.87200807e-03,
-    -3.47625639e-03, -4.15596382e-03, -4.91456379e-03, -5.75517250e-03,
-    -6.68062338e-03, -7.69381692e-03, -8.79676075e-03, -9.99050307e-03,
-    -1.12757412e-02, -1.26533415e-02, -1.41243899e-02, -1.56888962e-02,
-    -1.73451209e-02, -1.90909737e-02, -2.09254671e-02, -2.28468479e-02,
-    -2.48520772e-02, -2.69374670e-02, -2.90995249e-02, -3.13350463e-02,
-    -3.36396073e-02, -3.60082097e-02, -3.84360174e-02, -4.09174603e-02,
-    -4.34465489e-02, -4.60178672e-02, -4.86259851e-02, -5.12647420e-02,
-    -5.39264475e-02, -5.66038431e-02, -5.92911675e-02, -6.19826820e-02,
-    -6.46702555e-02, -6.73454222e-02, -7.00009902e-02, -7.26305701e-02,
-    -7.52278496e-02, -7.77852594e-02, -8.02948025e-02, -8.27492454e-02,
-    -8.51412546e-02, -8.74637912e-02, -8.97106934e-02, -9.18756408e-02,
-    -9.39517698e-02, -9.59313774e-02, -9.78084326e-02, -9.95785130e-02,
-    -1.01236117e-01, -1.02774104e-01, -1.04186122e-01, -1.05468025e-01,
-    -1.06616088e-01, -1.07625538e-01, -1.08491230e-01, -1.09208742e-01,
-    -1.09773615e-01, -1.10180886e-01, -1.10427188e-01, -1.10510836e-01,
-    -1.10428147e-01, -1.10173922e-01, -1.09743736e-01, -1.09135313e-01,
-    -1.08346734e-01, -1.07373994e-01, -1.06213016e-01, -1.04860615e-01,
-    -1.03313240e-01, -1.01567316e-01, -9.96200551e-02, -9.74680323e-02,
-    -9.51072362e-02, -9.25330338e-02, -8.97412522e-02, -8.67287769e-02,
-    -8.34921384e-02, -8.00263990e-02, -7.63267954e-02, -7.23880616e-02,
-    -6.82057680e-02, -6.37761143e-02, -5.90938600e-02, -5.41531632e-02,
-    -4.89481272e-02, -4.34734711e-02, -3.77246130e-02, -3.16958761e-02,
-    -2.53817983e-02, -1.87768910e-02, -1.18746138e-02, -4.66909925e-03,
-     2.84409675e-03,  1.06697612e-02,  1.88135595e-02,  2.72815601e-02,
-     3.60781047e-02,  4.52070276e-02,  5.46723880e-02,  6.44786605e-02,
-     7.46286220e-02,  8.51249057e-02,  9.59698399e-02,  1.07165078e-01,
-     1.18711585e-01,  1.30610107e-01,  1.42859645e-01,  1.55458473e-01,
-     1.68404161e-01,  1.81694789e-01,  1.95327388e-01,  2.09296321e-01,
-     2.23594564e-01,  2.38216022e-01,  2.53152972e-01,  2.68396157e-01,
-     2.83936139e-01,  2.99762426e-01,  3.15861908e-01,  3.32221055e-01,
-     3.48826468e-01,  3.65664038e-01,  3.82715297e-01,  3.99961186e-01,
-     4.17384327e-01,  4.34966962e-01,  4.52687640e-01,  4.70524201e-01,
-     4.88453925e-01,  5.06454555e-01,  5.24500675e-01,  5.42567437e-01,
-     5.60631204e-01,  5.78667265e-01,  5.96647704e-01,  6.14545890e-01,
-     6.32336194e-01,  6.49992632e-01,  6.67487403e-01,  6.84793267e-01,
-     7.01883546e-01,  7.18732254e-01,  7.35312821e-01,  7.51600199e-01,
-     7.67569925e-01,  7.83197457e-01,  7.98458386e-01,  8.13329535e-01,
-     8.27789227e-01,  8.41817856e-01,  8.55396130e-01,  8.68506898e-01,
-     8.81133444e-01,  8.93259678e-01,  9.04874884e-01,  9.15965761e-01,
-     9.26521530e-01,  9.36533999e-01,  9.45997703e-01,  9.54908841e-01,
-     9.63265812e-01,  9.71068890e-01,  9.78320416e-01,  9.85022676e-01,
-     9.91179208e-01,  9.96798994e-01,  1.00189402e+00,  1.00647434e+00,
-     1.01055206e+00,  1.01414254e+00,  1.01726259e+00,  1.01992884e+00,
-     1.02215987e+00,  1.02397632e+00,  1.02540073e+00,  1.02645534e+00,
-     1.02716451e+00,  1.02755273e+00,  1.02764446e+00,  1.02746325e+00,
-     1.02703590e+00,  1.02638907e+00,  1.02554820e+00,  1.02453713e+00,
-     1.02338080e+00,  1.02210370e+00,  1.02072836e+00,  1.01927533e+00,
-     1.01776518e+00,  1.01621736e+00,  1.01466531e+00,  1.01324907e+00,
-     1.01194801e+00,  1.01018909e+00,  1.00855796e+00,  1.00701129e+00,
-     1.00554876e+00,  1.00416842e+00,  1.00286727e+00,  1.00164177e+00,
-     1.00048907e+00,  9.99406080e-01,  9.98389887e-01,  9.97437085e-01,
-     9.96544484e-01,  9.95709855e-01,  9.94930241e-01,  9.94202405e-01,
-     9.93524160e-01,  9.92893043e-01,  9.92306810e-01,  9.91763378e-01,
-     9.91259764e-01,  9.90795450e-01,  9.90367789e-01,  9.89975161e-01,
-     9.89616034e-01,  9.89289016e-01,  9.88992851e-01,  9.88726033e-01,
-     9.88486872e-01,  9.88275104e-01,  9.88089217e-01,  9.87927711e-01,
-     9.87789826e-01,  9.87674344e-01,  9.87580750e-01,  9.87507202e-01,
-     9.87452945e-01,  9.87416974e-01,  9.87398469e-01,  9.87395830e-01,
-     9.87408003e-01,  9.87434340e-01,  9.87473624e-01,  9.87524314e-01,
-     9.87585620e-01,  9.87656379e-01,  9.87735892e-01,  9.87822558e-01,
-     9.87915097e-01,  9.88013273e-01,  9.88115695e-01,  9.88221131e-01,
-     9.88328903e-01,  9.88437831e-01,  9.88547679e-01,  9.88656841e-01,
-     9.88764587e-01,  9.88870854e-01,  9.88974432e-01,  9.89074727e-01,
-     9.89171004e-01,  9.89263102e-01,  9.89350722e-01,  9.89433065e-01,
-     9.89509692e-01,  9.89581081e-01,  9.89646747e-01,  9.89706737e-01,
-     9.89760693e-01,  9.89809448e-01,  9.89853013e-01,  9.89891471e-01,
-     9.89925419e-01,  9.89955420e-01,  9.89982449e-01,  9.90006512e-01,
-     9.90028481e-01,  9.90049748e-01,  9.90070956e-01,  9.90092836e-01,
-     9.90116392e-01,  9.90142748e-01,  9.90173428e-01,  9.90208733e-01,
-     9.90249864e-01,  9.90298369e-01,  9.90354850e-01,  9.90420508e-01,
-     9.90495930e-01,  9.90582515e-01,  9.90681257e-01,  9.90792209e-01,
-     9.90916546e-01,  9.91055074e-01,  9.91208461e-01,  9.91376861e-01,
-     9.91560583e-01,  9.91760421e-01,  9.91976718e-01,  9.92209110e-01,
-     9.92457914e-01,  9.92723123e-01,  9.93004954e-01,  9.93302728e-01,
-     9.93616108e-01,  9.93945371e-01,  9.94289515e-01,  9.94648168e-01,
-     9.95020303e-01,  9.95405817e-01,  9.95803871e-01,  9.96213027e-01,
-     9.96632469e-01,  9.97061531e-01,  9.97499058e-01,  9.97943743e-01,
-     9.98394057e-01,  9.98849312e-01,  9.99308343e-01,  9.99768922e-01,
-     1.00023113e+00,  1.00069214e+00,  1.00115201e+00,  1.00160853e+00,
-     1.00206049e+00,  1.00250721e+00,  1.00294713e+00,  1.00337891e+00,
-     1.00380137e+00,  1.00421381e+00,  1.00461539e+00,  1.00500462e+00,
-     1.00538063e+00,  1.00574328e+00,  1.00609151e+00,  1.00642491e+00,
-     1.00674243e+00,  1.00704432e+00,  1.00733022e+00,  1.00759940e+00,
-     1.00785206e+00,  1.00808818e+00,  1.00830803e+00,  1.00851125e+00,
-     1.00869814e+00,  1.00886952e+00,  1.00902566e+00,  1.00916672e+00,
-     1.00929336e+00,  1.00940640e+00,  1.00950702e+00,  1.00959526e+00,
-     1.00967215e+00,  1.00973908e+00,  1.00979668e+00,  1.00984614e+00,
-     1.00988808e+00,  1.00992409e+00,  1.00995538e+00,  1.00998227e+00,
-     1.01000630e+00,  1.01002862e+00,  1.01005025e+00,  1.01007195e+00,
-     1.01009437e+00,  1.01011892e+00,  1.01014650e+00,  1.01017711e+00,
-     1.01021176e+00,  1.01025100e+00,  1.01029547e+00,  1.01034523e+00,
-     1.01040032e+00,  1.01046156e+00,  1.01052862e+00,  1.01060152e+00,
-     1.01067979e+00,  1.01076391e+00,  1.01085343e+00,  1.01094755e+00,
-     1.01104595e+00,  1.01114849e+00,  1.01125440e+00,  1.01136308e+00,
-     1.01147330e+00,  1.01158500e+00,  1.01169742e+00,  1.01180892e+00,
-     1.01191926e+00,  1.01202724e+00,  1.01213215e+00,  1.01223273e+00,
-     1.01232756e+00,  1.01241638e+00,  1.01249789e+00,  1.01257043e+00,
-     1.01263330e+00,  1.01268528e+00,  1.01272556e+00,  1.01275258e+00,
-     1.01276506e+00,  1.01276236e+00,  1.01274338e+00,  1.01270648e+00,
-     1.01265084e+00,  1.01257543e+00,  1.01247947e+00,  1.01236111e+00,
-     1.01221981e+00,  1.01205436e+00,  1.01186400e+00,  1.01164722e+00,
-     1.01140252e+00,  1.01112965e+00,  1.01082695e+00,  1.01049292e+00,
-     1.01012635e+00,  1.00972589e+00,  1.00929006e+00,  1.00881730e+00,
-     1.00830503e+00,  1.00775283e+00,  1.00715783e+00,  1.00651805e+00,
-     1.00583140e+00,  1.00509559e+00,  1.00430863e+00,  1.00346750e+00,
-     1.00256950e+00,  1.00161271e+00,  1.00059427e+00,  9.99511170e-01,
-     9.98360922e-01,  9.97140929e-01,  9.95848886e-01,  9.94481854e-01,
-     9.93037528e-01,  9.91514656e-01,  9.89913680e-01,  9.88193062e-01,
-     9.85942259e-01,  9.83566790e-01,  9.81142303e-01,  9.78521444e-01,
-     9.75663604e-01,  9.72545344e-01,  9.69145663e-01,  9.65440618e-01,
-     9.61404362e-01,  9.57011307e-01,  9.52236767e-01,  9.47054884e-01,
-     9.41440374e-01,  9.35369161e-01,  9.28819009e-01,  9.21766289e-01,
-     9.14189628e-01,  9.06069468e-01,  8.97389168e-01,  8.88133200e-01,
-     8.78289389e-01,  8.67846957e-01,  8.56797064e-01,  8.45133465e-01,
-     8.32854281e-01,  8.19959478e-01,  8.06451101e-01,  7.92334648e-01,
-     7.77620449e-01,  7.62320618e-01,  7.46448649e-01,  7.30020573e-01,
-     7.13056738e-01,  6.95580544e-01,  6.77617323e-01,  6.59195531e-01,
-     6.40348643e-01,  6.21107220e-01,  6.01504928e-01,  5.81578761e-01,
-     5.61367451e-01,  5.40918863e-01,  5.20273683e-01,  4.99478073e-01,
-     4.78577418e-01,  4.57617260e-01,  4.36649021e-01,  4.15722146e-01,
-     3.94885659e-01,  3.74190319e-01,  3.53686890e-01,  3.33426002e-01,
-     3.13458647e-01,  2.93833790e-01,  2.74599264e-01,  2.55803064e-01,
-     2.37490219e-01,  2.19703603e-01,  2.02485542e-01,  1.85874992e-01,
-     1.69906780e-01,  1.54613227e-01,  1.40023821e-01,  1.26163740e-01,
-     1.13053443e-01,  1.00708497e-01,  8.91402439e-02,  7.83561210e-02,
-     6.83582123e-02,  5.91421154e-02,  5.06989301e-02,  4.30171776e-02,
-     3.60802073e-02,  2.98631634e-02,  2.43372266e-02,  1.94767524e-02,
-     1.52571017e-02,  1.16378749e-02,  8.43308778e-03,  4.44966900e-03,
-};
-
-static const float mdct_win_10m_480[480+300] = {
-    -2.35303215e-04, -4.61989875e-04, -6.26293154e-04, -7.92918043e-04,
-    -9.74716672e-04, -1.18025689e-03, -1.40920904e-03, -1.66447310e-03,
-    -1.94659161e-03, -2.25708173e-03, -2.59710692e-03, -2.96760762e-03,
-    -3.37045488e-03, -3.80628516e-03, -4.27687377e-03, -4.78246990e-03,
-    -5.32460872e-03, -5.90340381e-03, -6.52041973e-03, -7.17588528e-03,
-    -7.87142282e-03, -8.60658604e-03, -9.38248086e-03, -1.01982718e-02,
-    -1.10552055e-02, -1.19527030e-02, -1.28920591e-02, -1.38726348e-02,
-    -1.48952816e-02, -1.59585662e-02, -1.70628856e-02, -1.82066640e-02,
-    -1.93906598e-02, -2.06135542e-02, -2.18757093e-02, -2.31752632e-02,
-    -2.45122745e-02, -2.58847194e-02, -2.72926374e-02, -2.87339090e-02,
-    -3.02086274e-02, -3.17144037e-02, -3.32509886e-02, -3.48159779e-02,
-    -3.64089241e-02, -3.80274232e-02, -3.96706799e-02, -4.13357542e-02,
-    -4.30220337e-02, -4.47269805e-02, -4.64502229e-02, -4.81889149e-02,
-    -4.99422586e-02, -5.17069080e-02, -5.34816204e-02, -5.52633479e-02,
-    -5.70512315e-02, -5.88427175e-02, -6.06371724e-02, -6.24310403e-02,
-    -6.42230355e-02, -6.60096152e-02, -6.77896227e-02, -6.95599687e-02,
-    -7.13196627e-02, -7.30658127e-02, -7.47975891e-02, -7.65117823e-02,
-    -7.82071142e-02, -7.98801069e-02, -8.15296401e-02, -8.31523735e-02,
-    -8.47472895e-02, -8.63113754e-02, -8.78437445e-02, -8.93416436e-02,
-    -9.08041129e-02, -9.22279576e-02, -9.36123287e-02, -9.49537776e-02,
-    -9.62515531e-02, -9.75028462e-02, -9.87073651e-02, -9.98627129e-02,
-    -1.00968022e-01, -1.02020268e-01, -1.03018380e-01, -1.03959636e-01,
-    -1.04843883e-01, -1.05668684e-01, -1.06434282e-01, -1.07138231e-01,
-    -1.07779996e-01, -1.08357063e-01, -1.08869014e-01, -1.09313559e-01,
-    -1.09690356e-01, -1.09996966e-01, -1.10233226e-01, -1.10397281e-01,
-    -1.10489847e-01, -1.10508642e-01, -1.10453743e-01, -1.10322584e-01,
-    -1.10114583e-01, -1.09827693e-01, -1.09462175e-01, -1.09016396e-01,
-    -1.08490885e-01, -1.07883429e-01, -1.07193718e-01, -1.06419636e-01,
-    -1.05561251e-01, -1.04616281e-01, -1.03584904e-01, -1.02465016e-01,
-    -1.01256900e-01, -9.99586457e-02, -9.85701457e-02, -9.70891114e-02,
-    -9.55154582e-02, -9.38468492e-02, -9.20830006e-02, -9.02217102e-02,
-    -8.82630999e-02, -8.62049382e-02, -8.40474215e-02, -8.17879272e-02,
-    -7.94262503e-02, -7.69598078e-02, -7.43878560e-02, -7.17079700e-02,
-    -6.89199478e-02, -6.60218980e-02, -6.30134942e-02, -5.98919191e-02,
-    -5.66565564e-02, -5.33040616e-02, -4.98342724e-02, -4.62445689e-02,
-    -4.25345569e-02, -3.87019577e-02, -3.47458578e-02, -3.06634152e-02,
-    -2.64542508e-02, -2.21158161e-02, -1.76474054e-02, -1.30458136e-02,
-    -8.31042570e-03, -3.43826866e-03,  1.57031548e-03,  6.71769764e-03,
-     1.20047702e-02,  1.74339832e-02,  2.30064206e-02,  2.87248142e-02,
-     3.45889635e-02,  4.06010646e-02,  4.67610292e-02,  5.30713391e-02,
-     5.95323909e-02,  6.61464781e-02,  7.29129318e-02,  7.98335419e-02,
-     8.69080741e-02,  9.41381377e-02,  1.01523314e-01,  1.09065152e-01,
-     1.16762655e-01,  1.24617139e-01,  1.32627295e-01,  1.40793819e-01,
-     1.49115252e-01,  1.57592141e-01,  1.66222480e-01,  1.75006740e-01,
-     1.83943194e-01,  1.93031818e-01,  2.02269985e-01,  2.11656743e-01,
-     2.21188852e-01,  2.30865538e-01,  2.40683799e-01,  2.50642064e-01,
-     2.60736512e-01,  2.70965907e-01,  2.81325902e-01,  2.91814469e-01,
-     3.02427028e-01,  3.13160350e-01,  3.24009570e-01,  3.34971959e-01,
-     3.46042294e-01,  3.57217518e-01,  3.68491565e-01,  3.79859512e-01,
-     3.91314689e-01,  4.02853287e-01,  4.14468833e-01,  4.26157164e-01,
-     4.37911390e-01,  4.49725632e-01,  4.61592545e-01,  4.73506703e-01,
-     4.85460018e-01,  4.97447159e-01,  5.09459723e-01,  5.21490984e-01,
-     5.33532682e-01,  5.45578981e-01,  5.57621716e-01,  5.69654673e-01,
-     5.81668558e-01,  5.93656062e-01,  6.05608382e-01,  6.17519206e-01,
-     6.29379661e-01,  6.41183084e-01,  6.52920354e-01,  6.64584079e-01,
-     6.76165350e-01,  6.87657395e-01,  6.99051154e-01,  7.10340055e-01,
-     7.21514933e-01,  7.32569177e-01,  7.43494372e-01,  7.54284633e-01,
-     7.64931365e-01,  7.75428189e-01,  7.85767017e-01,  7.95941465e-01,
-     8.05943723e-01,  8.15768707e-01,  8.25408622e-01,  8.34858937e-01,
-     8.44112583e-01,  8.53165119e-01,  8.62010834e-01,  8.70645634e-01,
-     8.79063156e-01,  8.87259971e-01,  8.95231329e-01,  9.02975168e-01,
-     9.10486312e-01,  9.17762555e-01,  9.24799743e-01,  9.31596250e-01,
-     9.38149486e-01,  9.44458839e-01,  9.50522086e-01,  9.56340292e-01,
-     9.61911452e-01,  9.67236671e-01,  9.72315664e-01,  9.77150119e-01,
-     9.81739750e-01,  9.86086587e-01,  9.90190638e-01,  9.94055718e-01,
-     9.97684240e-01,  1.00108096e+00,  1.00424751e+00,  1.00718858e+00,
-     1.00990665e+00,  1.01240743e+00,  1.01469470e+00,  1.01677466e+00,
-     1.01865099e+00,  1.02033046e+00,  1.02181733e+00,  1.02311884e+00,
-     1.02424026e+00,  1.02518972e+00,  1.02597245e+00,  1.02659694e+00,
-     1.02706918e+00,  1.02739752e+00,  1.02758790e+00,  1.02764895e+00,
-     1.02758583e+00,  1.02740852e+00,  1.02712299e+00,  1.02673867e+00,
-     1.02626166e+00,  1.02570100e+00,  1.02506178e+00,  1.02435398e+00,
-     1.02358239e+00,  1.02275651e+00,  1.02188060e+00,  1.02096387e+00,
-     1.02000914e+00,  1.01902729e+00,  1.01801944e+00,  1.01699650e+00,
-     1.01595743e+00,  1.01492344e+00,  1.01391595e+00,  1.01304757e+00,
-     1.01221613e+00,  1.01104487e+00,  1.00991459e+00,  1.00882489e+00,
-     1.00777386e+00,  1.00676170e+00,  1.00578665e+00,  1.00484875e+00,
-     1.00394608e+00,  1.00307885e+00,  1.00224501e+00,  1.00144473e+00,
-     1.00067619e+00,  9.99939317e-01,  9.99232085e-01,  9.98554813e-01,
-     9.97905542e-01,  9.97284268e-01,  9.96689095e-01,  9.96120338e-01,
-     9.95576126e-01,  9.95056572e-01,  9.94559753e-01,  9.94086038e-01,
-     9.93633779e-01,  9.93203161e-01,  9.92792187e-01,  9.92401518e-01,
-     9.92029727e-01,  9.91676778e-01,  9.91340877e-01,  9.91023065e-01,
-     9.90721643e-01,  9.90436680e-01,  9.90166895e-01,  9.89913101e-01,
-     9.89673564e-01,  9.89448837e-01,  9.89237484e-01,  9.89040193e-01,
-     9.88855636e-01,  9.88684347e-01,  9.88524761e-01,  9.88377852e-01,
-     9.88242327e-01,  9.88118564e-01,  9.88005163e-01,  9.87903202e-01,
-     9.87811174e-01,  9.87729546e-01,  9.87657198e-01,  9.87594984e-01,
-     9.87541274e-01,  9.87496906e-01,  9.87460625e-01,  9.87432981e-01,
-     9.87412641e-01,  9.87400475e-01,  9.87394992e-01,  9.87396916e-01,
-     9.87404906e-01,  9.87419705e-01,  9.87439972e-01,  9.87466328e-01,
-     9.87497321e-01,  9.87533893e-01,  9.87574654e-01,  9.87620124e-01,
-     9.87668980e-01,  9.87722156e-01,  9.87778192e-01,  9.87837649e-01,
-     9.87899199e-01,  9.87963798e-01,  9.88030030e-01,  9.88098468e-01,
-     9.88167801e-01,  9.88239030e-01,  9.88310769e-01,  9.88383520e-01,
-     9.88456016e-01,  9.88529420e-01,  9.88602222e-01,  9.88674940e-01,
-     9.88746626e-01,  9.88818277e-01,  9.88888248e-01,  9.88957438e-01,
-     9.89024798e-01,  9.89091125e-01,  9.89155170e-01,  9.89217866e-01,
-     9.89277956e-01,  9.89336519e-01,  9.89392368e-01,  9.89446283e-01,
-     9.89497212e-01,  9.89546334e-01,  9.89592362e-01,  9.89636265e-01,
-     9.89677201e-01,  9.89716220e-01,  9.89752029e-01,  9.89785920e-01,
-     9.89817027e-01,  9.89846207e-01,  9.89872536e-01,  9.89897514e-01,
-     9.89920005e-01,  9.89941079e-01,  9.89960061e-01,  9.89978226e-01,
-     9.89994556e-01,  9.90010350e-01,  9.90024832e-01,  9.90039402e-01,
-     9.90053211e-01,  9.90067475e-01,  9.90081472e-01,  9.90096693e-01,
-     9.90112245e-01,  9.90129379e-01,  9.90147465e-01,  9.90168060e-01,
-     9.90190227e-01,  9.90215190e-01,  9.90242442e-01,  9.90273445e-01,
-     9.90307127e-01,  9.90344891e-01,  9.90386228e-01,  9.90432448e-01,
-     9.90482565e-01,  9.90537983e-01,  9.90598060e-01,  9.90664037e-01,
-     9.90734883e-01,  9.90812038e-01,  9.90894786e-01,  9.90984259e-01,
-     9.91079525e-01,  9.91181924e-01,  9.91290512e-01,  9.91406471e-01,
-     9.91528801e-01,  9.91658694e-01,  9.91795272e-01,  9.91939622e-01,
-     9.92090615e-01,  9.92249503e-01,  9.92415240e-01,  9.92588721e-01,
-     9.92768871e-01,  9.92956911e-01,  9.93151653e-01,  9.93353924e-01,
-     9.93562689e-01,  9.93779087e-01,  9.94001643e-01,  9.94231202e-01,
-     9.94466818e-01,  9.94709344e-01,  9.94957285e-01,  9.95211663e-01,
-     9.95471264e-01,  9.95736795e-01,  9.96006862e-01,  9.96282303e-01,
-     9.96561799e-01,  9.96846133e-01,  9.97133827e-01,  9.97425669e-01,
-     9.97720337e-01,  9.98018509e-01,  9.98318587e-01,  9.98621352e-01,
-     9.98925543e-01,  9.99231731e-01,  9.99538258e-01,  9.99846116e-01,
-     1.00015391e+00,  1.00046196e+00,  1.00076886e+00,  1.00107561e+00,
-     1.00138055e+00,  1.00168424e+00,  1.00198543e+00,  1.00228487e+00,
-     1.00258098e+00,  1.00287441e+00,  1.00316385e+00,  1.00345006e+00,
-     1.00373157e+00,  1.00400915e+00,  1.00428146e+00,  1.00454934e+00,
-     1.00481138e+00,  1.00506827e+00,  1.00531880e+00,  1.00556397e+00,
-     1.00580227e+00,  1.00603455e+00,  1.00625986e+00,  1.00647902e+00,
-     1.00669054e+00,  1.00689557e+00,  1.00709305e+00,  1.00728380e+00,
-     1.00746662e+00,  1.00764273e+00,  1.00781104e+00,  1.00797244e+00,
-     1.00812588e+00,  1.00827260e+00,  1.00841147e+00,  1.00854357e+00,
-     1.00866802e+00,  1.00878601e+00,  1.00889653e+00,  1.00900077e+00,
-     1.00909776e+00,  1.00918888e+00,  1.00927316e+00,  1.00935176e+00,
-     1.00942394e+00,  1.00949118e+00,  1.00955240e+00,  1.00960889e+00,
-     1.00965997e+00,  1.00970709e+00,  1.00974924e+00,  1.00978774e+00,
-     1.00982209e+00,  1.00985371e+00,  1.00988150e+00,  1.00990696e+00,
-     1.00992957e+00,  1.00995057e+00,  1.00996902e+00,  1.00998650e+00,
-     1.01000236e+00,  1.01001789e+00,  1.01003217e+00,  1.01004672e+00,
-     1.01006081e+00,  1.01007567e+00,  1.01009045e+00,  1.01010656e+00,
-     1.01012323e+00,  1.01014176e+00,  1.01016113e+00,  1.01018264e+00,
-     1.01020559e+00,  1.01023108e+00,  1.01025795e+00,  1.01028773e+00,
-     1.01031948e+00,  1.01035408e+00,  1.01039064e+00,  1.01043047e+00,
-     1.01047227e+00,  1.01051710e+00,  1.01056410e+00,  1.01061427e+00,
-     1.01066629e+00,  1.01072136e+00,  1.01077842e+00,  1.01083825e+00,
-     1.01089966e+00,  1.01096373e+00,  1.01102919e+00,  1.01109699e+00,
-     1.01116586e+00,  1.01123661e+00,  1.01130817e+00,  1.01138145e+00,
-     1.01145479e+00,  1.01152919e+00,  1.01160368e+00,  1.01167880e+00,
-     1.01175301e+00,  1.01182748e+00,  1.01190094e+00,  1.01197388e+00,
-     1.01204489e+00,  1.01211499e+00,  1.01218284e+00,  1.01224902e+00,
-     1.01231210e+00,  1.01237303e+00,  1.01243046e+00,  1.01248497e+00,
-     1.01253506e+00,  1.01258168e+00,  1.01262347e+00,  1.01266098e+00,
-     1.01269276e+00,  1.01271979e+00,  1.01274058e+00,  1.01275575e+00,
-     1.01276395e+00,  1.01276592e+00,  1.01276030e+00,  1.01274782e+00,
-     1.01272696e+00,  1.01269861e+00,  1.01266140e+00,  1.01261590e+00,
-     1.01256083e+00,  1.01249705e+00,  1.01242289e+00,  1.01233923e+00,
-     1.01224492e+00,  1.01214046e+00,  1.01202430e+00,  1.01189756e+00,
-     1.01175881e+00,  1.01160845e+00,  1.01144516e+00,  1.01126996e+00,
-     1.01108126e+00,  1.01087961e+00,  1.01066368e+00,  1.01043418e+00,
-     1.01018968e+00,  1.00993075e+00,  1.00965566e+00,  1.00936525e+00,
-     1.00905825e+00,  1.00873476e+00,  1.00839308e+00,  1.00803431e+00,
-     1.00765666e+00,  1.00726014e+00,  1.00684335e+00,  1.00640701e+00,
-     1.00594915e+00,  1.00547001e+00,  1.00496799e+00,  1.00444353e+00,
-     1.00389477e+00,  1.00332190e+00,  1.00272313e+00,  1.00209885e+00,
-     1.00144728e+00,  1.00076851e+00,  1.00006069e+00,  9.99324268e-01,
-     9.98557350e-01,  9.97760020e-01,  9.96930604e-01,  9.96069427e-01,
-     9.95174643e-01,  9.94246644e-01,  9.93283713e-01,  9.92286108e-01,
-     9.91252309e-01,  9.90182742e-01,  9.89075787e-01,  9.87931302e-01,
-     9.86355322e-01,  9.84736245e-01,  9.83175095e-01,  9.81558334e-01,
-     9.79861353e-01,  9.78061749e-01,  9.76157432e-01,  9.74137862e-01,
-     9.71999011e-01,  9.69732741e-01,  9.67333198e-01,  9.64791512e-01,
-     9.62101150e-01,  9.59253976e-01,  9.56242718e-01,  9.53060091e-01,
-     9.49698408e-01,  9.46149812e-01,  9.42407161e-01,  9.38463416e-01,
-     9.34311297e-01,  9.29944987e-01,  9.25356797e-01,  9.20540463e-01,
-     9.15489628e-01,  9.10198679e-01,  9.04662060e-01,  8.98875519e-01,
-     8.92833832e-01,  8.86533719e-01,  8.79971272e-01,  8.73143784e-01,
-     8.66047653e-01,  8.58681252e-01,  8.51042044e-01,  8.43129723e-01,
-     8.34943514e-01,  8.26483991e-01,  8.17750537e-01,  8.08744982e-01,
-     7.99468149e-01,  7.89923516e-01,  7.80113773e-01,  7.70043128e-01,
-     7.59714574e-01,  7.49133097e-01,  7.38302860e-01,  7.27229876e-01,
-     7.15920192e-01,  7.04381434e-01,  6.92619693e-01,  6.80643883e-01,
-     6.68461648e-01,  6.56083014e-01,  6.43517927e-01,  6.30775533e-01,
-     6.17864165e-01,  6.04795463e-01,  5.91579959e-01,  5.78228937e-01,
-     5.64753589e-01,  5.51170316e-01,  5.37490509e-01,  5.23726350e-01,
-     5.09891542e-01,  4.96000807e-01,  4.82066294e-01,  4.68101711e-01,
-     4.54121700e-01,  4.40142182e-01,  4.26177297e-01,  4.12241789e-01,
-     3.98349961e-01,  3.84517234e-01,  3.70758372e-01,  3.57088679e-01,
-     3.43522867e-01,  3.30076376e-01,  3.16764033e-01,  3.03600465e-01,
-     2.90599616e-01,  2.77775850e-01,  2.65143468e-01,  2.52716188e-01,
-     2.40506985e-01,  2.28528397e-01,  2.16793343e-01,  2.05313990e-01,
-     1.94102191e-01,  1.83168087e-01,  1.72522195e-01,  1.62173542e-01,
-     1.52132068e-01,  1.42405280e-01,  1.33001524e-01,  1.23926066e-01,
-     1.15185830e-01,  1.06784043e-01,  9.87263751e-02,  9.10137900e-02,
-     8.36505724e-02,  7.66350831e-02,  6.99703341e-02,  6.36518811e-02,
-     5.76817602e-02,  5.20524422e-02,  4.67653841e-02,  4.18095054e-02,
-     3.71864025e-02,  3.28807275e-02,  2.88954850e-02,  2.52098057e-02,
-     2.18305756e-02,  1.87289619e-02,  1.59212782e-02,  1.33638143e-02,
-     1.10855888e-02,  8.94347419e-03,  6.75812489e-03,  3.50443813e-03,
-};
-
-static const float mdct_win_7m5_60[60+46] = {
-     2.95060859e-03,  7.17541132e-03,  1.37695374e-02,  2.30953556e-02,
-     3.54036230e-02,  5.08289304e-02,  6.94696293e-02,  9.13884278e-02,
-     1.16604575e-01,  1.45073546e-01,  1.76711174e-01,  2.11342953e-01,
-     2.48768614e-01,  2.88701102e-01,  3.30823871e-01,  3.74814544e-01,
-     4.20308013e-01,  4.66904918e-01,  5.14185341e-01,  5.61710041e-01,
-     6.09026346e-01,  6.55671016e-01,  7.01218384e-01,  7.45240679e-01,
-     7.87369206e-01,  8.27223833e-01,  8.64513675e-01,  8.98977415e-01,
-     9.30407518e-01,  9.58599937e-01,  9.83447719e-01,  1.00488283e+00,
-     1.02285381e+00,  1.03740495e+00,  1.04859791e+00,  1.05656184e+00,
-     1.06149371e+00,  1.06362578e+00,  1.06325973e+00,  1.06074505e+00,
-     1.05643590e+00,  1.05069500e+00,  1.04392435e+00,  1.03647725e+00,
-     1.02872867e+00,  1.02106486e+00,  1.01400658e+00,  1.00727455e+00,
-     1.00172250e+00,  9.97309592e-01,  9.93985158e-01,  9.91683335e-01,
-     9.90325325e-01,  9.89822613e-01,  9.90074734e-01,  9.90975314e-01,
-     9.92412851e-01,  9.94273149e-01,  9.96439157e-01,  9.98791616e-01,
-     1.00120985e+00,  1.00357357e+00,  1.00575984e+00,  1.00764515e+00,
-     1.00910687e+00,  1.01002476e+00,  1.01028203e+00,  1.00976919e+00,
-     1.00838641e+00,  1.00605124e+00,  1.00269767e+00,  9.98280464e-01,
-     9.92777987e-01,  9.86186892e-01,  9.77634164e-01,  9.67447270e-01,
-     9.55129725e-01,  9.40389877e-01,  9.22959280e-01,  9.02607350e-01,
-     8.79202689e-01,  8.52641750e-01,  8.22881272e-01,  7.89971715e-01,
-     7.54030328e-01,  7.15255742e-01,  6.73936911e-01,  6.30414716e-01,
-     5.85078858e-01,  5.38398518e-01,  4.90833753e-01,  4.42885823e-01,
-     3.95091024e-01,  3.48004343e-01,  3.02196710e-01,  2.58227431e-01,
-     2.16641416e-01,  1.77922122e-01,  1.42480547e-01,  1.10652194e-01,
-     8.26995967e-02,  5.88334516e-02,  3.92030848e-02,  2.38629107e-02,
-     1.26976223e-02,  5.35665361e-03,
-};
-
-static const float mdct_win_7m5_120[120+92] = {
-     2.20824874e-03,  3.81014420e-03,  5.91552473e-03,  8.58361457e-03,
-     1.18759723e-02,  1.58335301e-02,  2.04918652e-02,  2.58883593e-02,
-     3.20415894e-02,  3.89616721e-02,  4.66742169e-02,  5.51849337e-02,
-     6.45038384e-02,  7.46411071e-02,  8.56000162e-02,  9.73846703e-02,
-     1.09993603e-01,  1.23419277e-01,  1.37655457e-01,  1.52690437e-01,
-     1.68513363e-01,  1.85093105e-01,  2.02410419e-01,  2.20450365e-01,
-     2.39167941e-01,  2.58526168e-01,  2.78498539e-01,  2.99038432e-01,
-     3.20104862e-01,  3.41658622e-01,  3.63660034e-01,  3.86062695e-01,
-     4.08815272e-01,  4.31871046e-01,  4.55176988e-01,  4.78676593e-01,
-     5.02324813e-01,  5.26060916e-01,  5.49831283e-01,  5.73576883e-01,
-     5.97241338e-01,  6.20770242e-01,  6.44099662e-01,  6.67176382e-01,
-     6.89958854e-01,  7.12379980e-01,  7.34396372e-01,  7.55966688e-01,
-     7.77036981e-01,  7.97558114e-01,  8.17490856e-01,  8.36796950e-01,
-     8.55447310e-01,  8.73400798e-01,  8.90635719e-01,  9.07128770e-01,
-     9.22848784e-01,  9.37763323e-01,  9.51860206e-01,  9.65130600e-01,
-     9.77556541e-01,  9.89126209e-01,  9.99846919e-01,  1.00970073e+00,
-     1.01868229e+00,  1.02681455e+00,  1.03408981e+00,  1.04051196e+00,
-     1.04610837e+00,  1.05088565e+00,  1.05486289e+00,  1.05807221e+00,
-     1.06053414e+00,  1.06227662e+00,  1.06333815e+00,  1.06375557e+00,
-     1.06356632e+00,  1.06282156e+00,  1.06155996e+00,  1.05981709e+00,
-     1.05765876e+00,  1.05512006e+00,  1.05223985e+00,  1.04908779e+00,
-     1.04569860e+00,  1.04210831e+00,  1.03838099e+00,  1.03455276e+00,
-     1.03067200e+00,  1.02679167e+00,  1.02295558e+00,  1.01920733e+00,
-     1.01587289e+00,  1.01221017e+00,  1.00884559e+00,  1.00577851e+00,
-     1.00300262e+00,  1.00051460e+00,  9.98309229e-01,  9.96378601e-01,
-     9.94718132e-01,  9.93316216e-01,  9.92166957e-01,  9.91258603e-01,
-     9.90581104e-01,  9.90123118e-01,  9.89873712e-01,  9.89818707e-01,
-     9.89946800e-01,  9.90243175e-01,  9.90695564e-01,  9.91288540e-01,
-     9.92009469e-01,  9.92842693e-01,  9.93775067e-01,  9.94790398e-01,
-     9.95875534e-01,  9.97014367e-01,  9.98192871e-01,  9.99394506e-01,
-     1.00060586e+00,  1.00181040e+00,  1.00299457e+00,  1.00414155e+00,
-     1.00523688e+00,  1.00626393e+00,  1.00720890e+00,  1.00805489e+00,
-     1.00878802e+00,  1.00939182e+00,  1.00985296e+00,  1.01015529e+00,
-     1.01028602e+00,  1.01022988e+00,  1.00997541e+00,  1.00950846e+00,
-     1.00881848e+00,  1.00789488e+00,  1.00672876e+00,  1.00530991e+00,
-     1.00363456e+00,  1.00169363e+00,  9.99485663e-01,  9.97006370e-01,
-     9.94254687e-01,  9.91231967e-01,  9.87937115e-01,  9.84375125e-01,
-     9.79890963e-01,  9.75269879e-01,  9.70180498e-01,  9.64580027e-01,
-     9.58425534e-01,  9.51684014e-01,  9.44320232e-01,  9.36290624e-01,
-     9.27580507e-01,  9.18153414e-01,  9.07976524e-01,  8.97050058e-01,
-     8.85351360e-01,  8.72857927e-01,  8.59579819e-01,  8.45502615e-01,
-     8.30619943e-01,  8.14946648e-01,  7.98489378e-01,  7.81262450e-01,
-     7.63291769e-01,  7.44590843e-01,  7.25199287e-01,  7.05153668e-01,
-     6.84490545e-01,  6.63245210e-01,  6.41477162e-01,  6.19235334e-01,
-     5.96559133e-01,  5.73519989e-01,  5.50173851e-01,  5.26568538e-01,
-     5.02781159e-01,  4.78860889e-01,  4.54877894e-01,  4.30898123e-01,
-     4.06993964e-01,  3.83234031e-01,  3.59680098e-01,  3.36408100e-01,
-     3.13496418e-01,  2.91010565e-01,  2.69019585e-01,  2.47584348e-01,
-     2.26788433e-01,  2.06677771e-01,  1.87310343e-01,  1.68739644e-01,
-     1.51012382e-01,  1.34171842e-01,  1.18254662e-01,  1.03290734e-01,
-     8.93117360e-02,  7.63429787e-02,  6.44077291e-02,  5.35243715e-02,
-     4.37084453e-02,  3.49667099e-02,  2.72984629e-02,  2.06895808e-02,
-     1.51125125e-02,  1.05228754e-02,  6.85547314e-03,  4.02351119e-03,
-};
-
-static const float mdct_win_7m5_180[180+138] = {
-     1.97084908e-03,  2.95060859e-03,  4.12447721e-03,  5.52688664e-03,
-     7.17541132e-03,  9.08757730e-03,  1.12819105e-02,  1.37695374e-02,
-     1.65600266e-02,  1.96650895e-02,  2.30953556e-02,  2.68612894e-02,
-     3.09632560e-02,  3.54036230e-02,  4.01915610e-02,  4.53331403e-02,
-     5.08289304e-02,  5.66815448e-02,  6.28935304e-02,  6.94696293e-02,
-     7.64106314e-02,  8.37160016e-02,  9.13884278e-02,  9.94294008e-02,
-     1.07834725e-01,  1.16604575e-01,  1.25736503e-01,  1.35226811e-01,
-     1.45073546e-01,  1.55273819e-01,  1.65822194e-01,  1.76711174e-01,
-     1.87928776e-01,  1.99473180e-01,  2.11342953e-01,  2.23524554e-01,
-     2.36003100e-01,  2.48768614e-01,  2.61813811e-01,  2.75129161e-01,
-     2.88701102e-01,  3.02514034e-01,  3.16558805e-01,  3.30823871e-01,
-     3.45295567e-01,  3.59963992e-01,  3.74814544e-01,  3.89831817e-01,
-     4.05001010e-01,  4.20308013e-01,  4.35739515e-01,  4.51277817e-01,
-     4.66904918e-01,  4.82609041e-01,  4.98375466e-01,  5.14185341e-01,
-     5.30021478e-01,  5.45869352e-01,  5.61710041e-01,  5.77528151e-01,
-     5.93304696e-01,  6.09026346e-01,  6.24674189e-01,  6.40227555e-01,
-     6.55671016e-01,  6.70995935e-01,  6.86184559e-01,  7.01218384e-01,
-     7.16078449e-01,  7.30756084e-01,  7.45240679e-01,  7.59515122e-01,
-     7.73561955e-01,  7.87369206e-01,  8.00923138e-01,  8.14211386e-01,
-     8.27223833e-01,  8.39952374e-01,  8.52386102e-01,  8.64513675e-01,
-     8.76324079e-01,  8.87814288e-01,  8.98977415e-01,  9.09803319e-01,
-     9.20284312e-01,  9.30407518e-01,  9.40169652e-01,  9.49567795e-01,
-     9.58599937e-01,  9.67260260e-01,  9.75545166e-01,  9.83447719e-01,
-     9.90971957e-01,  9.98119269e-01,  1.00488283e+00,  1.01125773e+00,
-     1.01724436e+00,  1.02285381e+00,  1.02808734e+00,  1.03293706e+00,
-     1.03740495e+00,  1.04150164e+00,  1.04523236e+00,  1.04859791e+00,
-     1.05160340e+00,  1.05425505e+00,  1.05656184e+00,  1.05853400e+00,
-     1.06017414e+00,  1.06149371e+00,  1.06249943e+00,  1.06320577e+00,
-     1.06362578e+00,  1.06376487e+00,  1.06363778e+00,  1.06325973e+00,
-     1.06264695e+00,  1.06180496e+00,  1.06074505e+00,  1.05948492e+00,
-     1.05804533e+00,  1.05643590e+00,  1.05466218e+00,  1.05274047e+00,
-     1.05069500e+00,  1.04853894e+00,  1.04627898e+00,  1.04392435e+00,
-     1.04149540e+00,  1.03901003e+00,  1.03647725e+00,  1.03390793e+00,
-     1.03131989e+00,  1.02872867e+00,  1.02614832e+00,  1.02358988e+00,
-     1.02106486e+00,  1.01856262e+00,  1.01655770e+00,  1.01400658e+00,
-     1.01162953e+00,  1.00938590e+00,  1.00727455e+00,  1.00529616e+00,
-     1.00344526e+00,  1.00172250e+00,  1.00012792e+00,  9.98657533e-01,
-     9.97309592e-01,  9.96083571e-01,  9.94976569e-01,  9.93985158e-01,
-     9.93107530e-01,  9.92341305e-01,  9.91683335e-01,  9.91130070e-01,
-     9.90678325e-01,  9.90325325e-01,  9.90067562e-01,  9.89901282e-01,
-     9.89822613e-01,  9.89827845e-01,  9.89913241e-01,  9.90074734e-01,
-     9.90308256e-01,  9.90609852e-01,  9.90975314e-01,  9.91400330e-01,
-     9.91880966e-01,  9.92412851e-01,  9.92991779e-01,  9.93613381e-01,
-     9.94273149e-01,  9.94966958e-01,  9.95690370e-01,  9.96439157e-01,
-     9.97208572e-01,  9.97994275e-01,  9.98791616e-01,  9.99596062e-01,
-     1.00040410e+00,  1.00120985e+00,  1.00200976e+00,  1.00279924e+00,
-     1.00357357e+00,  1.00432828e+00,  1.00505850e+00,  1.00575984e+00,
-     1.00642767e+00,  1.00705768e+00,  1.00764515e+00,  1.00818549e+00,
-     1.00867427e+00,  1.00910687e+00,  1.00947916e+00,  1.00978659e+00,
-     1.01002476e+00,  1.01018954e+00,  1.01027669e+00,  1.01028203e+00,
-     1.01020174e+00,  1.01003208e+00,  1.00976919e+00,  1.00940939e+00,
-     1.00894931e+00,  1.00838641e+00,  1.00771780e+00,  1.00694031e+00,
-     1.00605124e+00,  1.00504879e+00,  1.00393183e+00,  1.00269767e+00,
-     1.00134427e+00,  9.99872092e-01,  9.98280464e-01,  9.96566569e-01,
-     9.94731737e-01,  9.92777987e-01,  9.90701374e-01,  9.88504165e-01,
-     9.86186892e-01,  9.83711989e-01,  9.80584643e-01,  9.77634164e-01,
-     9.74455033e-01,  9.71062916e-01,  9.67447270e-01,  9.63593926e-01,
-     9.59491398e-01,  9.55129725e-01,  9.50501326e-01,  9.45592810e-01,
-     9.40389877e-01,  9.34886760e-01,  9.29080559e-01,  9.22959280e-01,
-     9.16509579e-01,  9.09724456e-01,  9.02607350e-01,  8.95155084e-01,
-     8.87356154e-01,  8.79202689e-01,  8.70699698e-01,  8.61847424e-01,
-     8.52641750e-01,  8.43077833e-01,  8.33154905e-01,  8.22881272e-01,
-     8.12257597e-01,  8.01285439e-01,  7.89971715e-01,  7.78318177e-01,
-     7.66337710e-01,  7.54030328e-01,  7.41407991e-01,  7.28477501e-01,
-     7.15255742e-01,  7.01751739e-01,  6.87975632e-01,  6.73936911e-01,
-     6.59652573e-01,  6.45139489e-01,  6.30414716e-01,  6.15483622e-01,
-     6.00365852e-01,  5.85078858e-01,  5.69649536e-01,  5.54084810e-01,
-     5.38398518e-01,  5.22614738e-01,  5.06756805e-01,  4.90833753e-01,
-     4.74866033e-01,  4.58876566e-01,  4.42885823e-01,  4.26906539e-01,
-     4.10970973e-01,  3.95091024e-01,  3.79291327e-01,  3.63587417e-01,
-     3.48004343e-01,  3.32563201e-01,  3.17287485e-01,  3.02196710e-01,
-     2.87309403e-01,  2.72643992e-01,  2.58227431e-01,  2.44072856e-01,
-     2.30208977e-01,  2.16641416e-01,  2.03398481e-01,  1.90486162e-01,
-     1.77922122e-01,  1.65726674e-01,  1.53906397e-01,  1.42480547e-01,
-     1.31453980e-01,  1.20841778e-01,  1.10652194e-01,  1.00891734e-01,
-     9.15718851e-02,  8.26995967e-02,  7.42815529e-02,  6.63242382e-02,
-     5.88334516e-02,  5.18140676e-02,  4.52698346e-02,  3.92030848e-02,
-     3.36144159e-02,  2.85023308e-02,  2.38629107e-02,  1.96894227e-02,
-     1.59720527e-02,  1.26976223e-02,  9.84937739e-03,  7.40724463e-03,
-     5.35665361e-03,  3.83226552e-03,
-};
-
-static const float mdct_win_7m5_240[240+184] = {
-     1.84833037e-03,  2.56481839e-03,  3.36762118e-03,  4.28736617e-03,
-     5.33830143e-03,  6.52679223e-03,  7.86112587e-03,  9.34628179e-03,
-     1.09916868e-02,  1.28011172e-02,  1.47805911e-02,  1.69307043e-02,
-     1.92592307e-02,  2.17696937e-02,  2.44685983e-02,  2.73556543e-02,
-     3.04319230e-02,  3.36980464e-02,  3.71583577e-02,  4.08148180e-02,
-     4.46708068e-02,  4.87262995e-02,  5.29820633e-02,  5.74382470e-02,
-     6.20968580e-02,  6.69609767e-02,  7.20298364e-02,  7.73039146e-02,
-     8.27825574e-02,  8.84682102e-02,  9.43607566e-02,  1.00460272e-01,
-     1.06763824e-01,  1.13273679e-01,  1.19986420e-01,  1.26903521e-01,
-     1.34020853e-01,  1.41339557e-01,  1.48857211e-01,  1.56573685e-01,
-     1.64484622e-01,  1.72589077e-01,  1.80879090e-01,  1.89354320e-01,
-     1.98012244e-01,  2.06854141e-01,  2.15875319e-01,  2.25068672e-01,
-     2.34427407e-01,  2.43948314e-01,  2.53627993e-01,  2.63464061e-01,
-     2.73450494e-01,  2.83582189e-01,  2.93853469e-01,  3.04257373e-01,
-     3.14790914e-01,  3.25449123e-01,  3.36227410e-01,  3.47118760e-01,
-     3.58120177e-01,  3.69224663e-01,  3.80427793e-01,  3.91720023e-01,
-     4.03097022e-01,  4.14551955e-01,  4.26081719e-01,  4.37676318e-01,
-     4.49330196e-01,  4.61034855e-01,  4.72786043e-01,  4.84576777e-01,
-     4.96401707e-01,  5.08252458e-01,  5.20122078e-01,  5.32002077e-01,
-     5.43888090e-01,  5.55771601e-01,  5.67645739e-01,  5.79502786e-01,
-     5.91335035e-01,  6.03138367e-01,  6.14904172e-01,  6.26623941e-01,
-     6.38288834e-01,  6.49893375e-01,  6.61432360e-01,  6.72902514e-01,
-     6.84293750e-01,  6.95600460e-01,  7.06811784e-01,  7.17923425e-01,
-     7.28931386e-01,  7.39832773e-01,  7.50618982e-01,  7.61284053e-01,
-     7.71818919e-01,  7.82220992e-01,  7.92481330e-01,  8.02599448e-01,
-     8.12565230e-01,  8.22377129e-01,  8.32030518e-01,  8.41523208e-01,
-     8.50848313e-01,  8.60002412e-01,  8.68979881e-01,  8.77778347e-01,
-     8.86395904e-01,  8.94829421e-01,  9.03077626e-01,  9.11132652e-01,
-     9.18993585e-01,  9.26652937e-01,  9.34111420e-01,  9.41364344e-01,
-     9.48412967e-01,  9.55255630e-01,  9.61892013e-01,  9.68316363e-01,
-     9.74530156e-01,  9.80528338e-01,  9.86313928e-01,  9.91886049e-01,
-     9.97246345e-01,  1.00239190e+00,  1.00731946e+00,  1.01202707e+00,
-     1.01651654e+00,  1.02079430e+00,  1.02486082e+00,  1.02871471e+00,
-     1.03235170e+00,  1.03577375e+00,  1.03898432e+00,  1.04198786e+00,
-     1.04478564e+00,  1.04737818e+00,  1.04976743e+00,  1.05195405e+00,
-     1.05394290e+00,  1.05573463e+00,  1.05734177e+00,  1.05875726e+00,
-     1.05998674e+00,  1.06103672e+00,  1.06190651e+00,  1.06260369e+00,
-     1.06313289e+00,  1.06350237e+00,  1.06370981e+00,  1.06376322e+00,
-     1.06366765e+00,  1.06343012e+00,  1.06305656e+00,  1.06255421e+00,
-     1.06192235e+00,  1.06116702e+00,  1.06029469e+00,  1.05931469e+00,
-     1.05823465e+00,  1.05705891e+00,  1.05578948e+00,  1.05442979e+00,
-     1.05298793e+00,  1.05147505e+00,  1.04989930e+00,  1.04826213e+00,
-     1.04656691e+00,  1.04481699e+00,  1.04302125e+00,  1.04118768e+00,
-     1.03932339e+00,  1.03743168e+00,  1.03551757e+00,  1.03358511e+00,
-     1.03164371e+00,  1.02969955e+00,  1.02775944e+00,  1.02582719e+00,
-     1.02390791e+00,  1.02200805e+00,  1.02013910e+00,  1.01826310e+00,
-     1.01687901e+00,  1.01492195e+00,  1.01309662e+00,  1.01134205e+00,
-     1.00965912e+00,  1.00805036e+00,  1.00651754e+00,  1.00505799e+00,
-     1.00366956e+00,  1.00235327e+00,  1.00110981e+00,  9.99937523e-01,
-     9.98834524e-01,  9.97800606e-01,  9.96835756e-01,  9.95938881e-01,
-     9.95108459e-01,  9.94343411e-01,  9.93642921e-01,  9.93005832e-01,
-     9.92430984e-01,  9.91917493e-01,  9.91463898e-01,  9.91068214e-01,
-     9.90729218e-01,  9.90446225e-01,  9.90217819e-01,  9.90041963e-01,
-     9.89917085e-01,  9.89841975e-01,  9.89815048e-01,  9.89834329e-01,
-     9.89898211e-01,  9.90005403e-01,  9.90154189e-01,  9.90342427e-01,
-     9.90568459e-01,  9.90830953e-01,  9.91128038e-01,  9.91457566e-01,
-     9.91817881e-01,  9.92207559e-01,  9.92624757e-01,  9.93067358e-01,
-     9.93533398e-01,  9.94021410e-01,  9.94529685e-01,  9.95055964e-01,
-     9.95598351e-01,  9.96155580e-01,  9.96725627e-01,  9.97306092e-01,
-     9.97895214e-01,  9.98491441e-01,  9.99092890e-01,  9.99697063e-01,
-     1.00030303e+00,  1.00090793e+00,  1.00151084e+00,  1.00210923e+00,
-     1.00270118e+00,  1.00328513e+00,  1.00385926e+00,  1.00442111e+00,
-     1.00496860e+00,  1.00550040e+00,  1.00601455e+00,  1.00650869e+00,
-     1.00698104e+00,  1.00743004e+00,  1.00785364e+00,  1.00824962e+00,
-     1.00861604e+00,  1.00895138e+00,  1.00925390e+00,  1.00952134e+00,
-     1.00975175e+00,  1.00994371e+00,  1.01009550e+00,  1.01020488e+00,
-     1.01027007e+00,  1.01028975e+00,  1.01026227e+00,  1.01018562e+00,
-     1.01005820e+00,  1.00987882e+00,  1.00964593e+00,  1.00935753e+00,
-     1.00901228e+00,  1.00860959e+00,  1.00814837e+00,  1.00762674e+00,
-     1.00704343e+00,  1.00639775e+00,  1.00568877e+00,  1.00491559e+00,
-     1.00407768e+00,  1.00317429e+00,  1.00220424e+00,  1.00116684e+00,
-     1.00006248e+00,  9.98891422e-01,  9.97652252e-01,  9.96343856e-01,
-     9.94967462e-01,  9.93524663e-01,  9.92013927e-01,  9.90433283e-01,
-     9.88785147e-01,  9.87072681e-01,  9.85297443e-01,  9.83401161e-01,
-     9.80949418e-01,  9.78782729e-01,  9.76468238e-01,  9.74042850e-01,
-     9.71498848e-01,  9.68829968e-01,  9.66030974e-01,  9.63095104e-01,
-     9.60018198e-01,  9.56795738e-01,  9.53426267e-01,  9.49903482e-01,
-     9.46222115e-01,  9.42375820e-01,  9.38361702e-01,  9.34177798e-01,
-     9.29823124e-01,  9.25292320e-01,  9.20580120e-01,  9.15679793e-01,
-     9.10590604e-01,  9.05315030e-01,  8.99852756e-01,  8.94199497e-01,
-     8.88350152e-01,  8.82301631e-01,  8.76054874e-01,  8.69612385e-01,
-     8.62972799e-01,  8.56135198e-01,  8.49098179e-01,  8.41857024e-01,
-     8.34414055e-01,  8.26774617e-01,  8.18939244e-01,  8.10904891e-01,
-     8.02675318e-01,  7.94253751e-01,  7.85641662e-01,  7.76838609e-01,
-     7.67853193e-01,  7.58685181e-01,  7.49330658e-01,  7.39809171e-01,
-     7.30109944e-01,  7.20247781e-01,  7.10224161e-01,  7.00044326e-01,
-     6.89711890e-01,  6.79231154e-01,  6.68608179e-01,  6.57850997e-01,
-     6.46965718e-01,  6.35959617e-01,  6.24840336e-01,  6.13603503e-01,
-     6.02265091e-01,  5.90829083e-01,  5.79309408e-01,  5.67711124e-01,
-     5.56037416e-01,  5.44293664e-01,  5.32489768e-01,  5.20636084e-01,
-     5.08743273e-01,  4.96811166e-01,  4.84849881e-01,  4.72868107e-01,
-     4.60875918e-01,  4.48881081e-01,  4.36891039e-01,  4.24912022e-01,
-     4.12960603e-01,  4.01035896e-01,  3.89157867e-01,  3.77322199e-01,
-     3.65543767e-01,  3.53832356e-01,  3.42196115e-01,  3.30644820e-01,
-     3.19187559e-01,  3.07833309e-01,  2.96588182e-01,  2.85463717e-01,
-     2.74462409e-01,  2.63609584e-01,  2.52883101e-01,  2.42323489e-01,
-     2.31925746e-01,  2.21690837e-01,  2.11638058e-01,  2.01766920e-01,
-     1.92082236e-01,  1.82589160e-01,  1.73305997e-01,  1.64229200e-01,
-     1.55362654e-01,  1.46717079e-01,  1.38299391e-01,  1.30105078e-01,
-     1.22145310e-01,  1.14423458e-01,  1.06941076e-01,  9.97025893e-02,
-     9.27124283e-02,  8.59737427e-02,  7.94893311e-02,  7.32616579e-02,
-     6.72934102e-02,  6.15874081e-02,  5.61458003e-02,  5.09700747e-02,
-     4.60617047e-02,  4.14220117e-02,  3.70514189e-02,  3.29494666e-02,
-     2.91153327e-02,  2.55476401e-02,  2.22437711e-02,  1.92000659e-02,
-     1.64122205e-02,  1.38747611e-02,  1.15806353e-02,  9.52213664e-03,
-     7.69137380e-03,  6.07207833e-03,  4.62581217e-03,  3.60685164e-03,
-};
-
-static const float mdct_win_7m5_360[360+276] = {
-     1.72152668e-03,  2.20824874e-03,  2.68901752e-03,  3.22613342e-03,
-     3.81014420e-03,  4.45371932e-03,  5.15369240e-03,  5.91552473e-03,
-     6.73869158e-03,  7.62861841e-03,  8.58361457e-03,  9.60938437e-03,
-     1.07060753e-02,  1.18759723e-02,  1.31190130e-02,  1.44390108e-02,
-     1.58335301e-02,  1.73063081e-02,  1.88584711e-02,  2.04918652e-02,
-     2.22061476e-02,  2.40057166e-02,  2.58883593e-02,  2.78552326e-02,
-     2.99059145e-02,  3.20415894e-02,  3.42610013e-02,  3.65680973e-02,
-     3.89616721e-02,  4.14435824e-02,  4.40140796e-02,  4.66742169e-02,
-     4.94214625e-02,  5.22588489e-02,  5.51849337e-02,  5.82005143e-02,
-     6.13059845e-02,  6.45038384e-02,  6.77913923e-02,  7.11707833e-02,
-     7.46411071e-02,  7.82028053e-02,  8.18549521e-02,  8.56000162e-02,
-     8.94357617e-02,  9.33642589e-02,  9.73846703e-02,  1.01496718e-01,
-     1.05698760e-01,  1.09993603e-01,  1.14378287e-01,  1.18853508e-01,
-     1.23419277e-01,  1.28075997e-01,  1.32820581e-01,  1.37655457e-01,
-     1.42578648e-01,  1.47590522e-01,  1.52690437e-01,  1.57878853e-01,
-     1.63152529e-01,  1.68513363e-01,  1.73957969e-01,  1.79484737e-01,
-     1.85093105e-01,  1.90784835e-01,  1.96556497e-01,  2.02410419e-01,
-     2.08345433e-01,  2.14359825e-01,  2.20450365e-01,  2.26617296e-01,
-     2.32856279e-01,  2.39167941e-01,  2.45550642e-01,  2.52003951e-01,
-     2.58526168e-01,  2.65118408e-01,  2.71775911e-01,  2.78498539e-01,
-     2.85284606e-01,  2.92132459e-01,  2.99038432e-01,  3.06004256e-01,
-     3.13026529e-01,  3.20104862e-01,  3.27237324e-01,  3.34423210e-01,
-     3.41658622e-01,  3.48944976e-01,  3.56279252e-01,  3.63660034e-01,
-     3.71085146e-01,  3.78554327e-01,  3.86062695e-01,  3.93610554e-01,
-     4.01195225e-01,  4.08815272e-01,  4.16468460e-01,  4.24155411e-01,
-     4.31871046e-01,  4.39614744e-01,  4.47384019e-01,  4.55176988e-01,
-     4.62990138e-01,  4.70824619e-01,  4.78676593e-01,  4.86545433e-01,
-     4.94428714e-01,  5.02324813e-01,  5.10229471e-01,  5.18142927e-01,
-     5.26060916e-01,  5.33982818e-01,  5.41906817e-01,  5.49831283e-01,
-     5.57751234e-01,  5.65667636e-01,  5.73576883e-01,  5.81476666e-01,
-     5.89364661e-01,  5.97241338e-01,  6.05102013e-01,  6.12946170e-01,
-     6.20770242e-01,  6.28572094e-01,  6.36348526e-01,  6.44099662e-01,
-     6.51820973e-01,  6.59513822e-01,  6.67176382e-01,  6.74806795e-01,
-     6.82400711e-01,  6.89958854e-01,  6.97475722e-01,  7.04950145e-01,
-     7.12379980e-01,  7.19765434e-01,  7.27103833e-01,  7.34396372e-01,
-     7.41638561e-01,  7.48829639e-01,  7.55966688e-01,  7.63049259e-01,
-     7.70072273e-01,  7.77036981e-01,  7.83941108e-01,  7.90781257e-01,
-     7.97558114e-01,  8.04271381e-01,  8.10914901e-01,  8.17490856e-01,
-     8.23997094e-01,  8.30432785e-01,  8.36796950e-01,  8.43089298e-01,
-     8.49305847e-01,  8.55447310e-01,  8.61511037e-01,  8.67496281e-01,
-     8.73400798e-01,  8.79227518e-01,  8.84972438e-01,  8.90635719e-01,
-     8.96217173e-01,  9.01716414e-01,  9.07128770e-01,  9.12456578e-01,
-     9.17697261e-01,  9.22848784e-01,  9.27909917e-01,  9.32882596e-01,
-     9.37763323e-01,  9.42553356e-01,  9.47252428e-01,  9.51860206e-01,
-     9.56376060e-01,  9.60800602e-01,  9.65130600e-01,  9.69366689e-01,
-     9.73508812e-01,  9.77556541e-01,  9.81507226e-01,  9.85364580e-01,
-     9.89126209e-01,  9.92794201e-01,  9.96367545e-01,  9.99846919e-01,
-     1.00322812e+00,  1.00651341e+00,  1.00970073e+00,  1.01279029e+00,
-     1.01578293e+00,  1.01868229e+00,  1.02148657e+00,  1.02419772e+00,
-     1.02681455e+00,  1.02933598e+00,  1.03176043e+00,  1.03408981e+00,
-     1.03632326e+00,  1.03846361e+00,  1.04051196e+00,  1.04246831e+00,
-     1.04433331e+00,  1.04610837e+00,  1.04779018e+00,  1.04938334e+00,
-     1.05088565e+00,  1.05229923e+00,  1.05362522e+00,  1.05486289e+00,
-     1.05601521e+00,  1.05708746e+00,  1.05807221e+00,  1.05897524e+00,
-     1.05979447e+00,  1.06053414e+00,  1.06119412e+00,  1.06177366e+00,
-     1.06227662e+00,  1.06270324e+00,  1.06305569e+00,  1.06333815e+00,
-     1.06354800e+00,  1.06368607e+00,  1.06375557e+00,  1.06375743e+00,
-     1.06369358e+00,  1.06356632e+00,  1.06337707e+00,  1.06312782e+00,
-     1.06282156e+00,  1.06245782e+00,  1.06203634e+00,  1.06155996e+00,
-     1.06102951e+00,  1.06044797e+00,  1.05981709e+00,  1.05914163e+00,
-     1.05842136e+00,  1.05765876e+00,  1.05685377e+00,  1.05600761e+00,
-     1.05512006e+00,  1.05419505e+00,  1.05323346e+00,  1.05223985e+00,
-     1.05121668e+00,  1.05016637e+00,  1.04908779e+00,  1.04798366e+00,
-     1.04685334e+00,  1.04569860e+00,  1.04452056e+00,  1.04332348e+00,
-     1.04210831e+00,  1.04087907e+00,  1.03963603e+00,  1.03838099e+00,
-     1.03711403e+00,  1.03583813e+00,  1.03455276e+00,  1.03326200e+00,
-     1.03196750e+00,  1.03067200e+00,  1.02937564e+00,  1.02808244e+00,
-     1.02679167e+00,  1.02550635e+00,  1.02422655e+00,  1.02295558e+00,
-     1.02169299e+00,  1.02044475e+00,  1.01920733e+00,  1.01799992e+00,
-     1.01716022e+00,  1.01587289e+00,  1.01461783e+00,  1.01339738e+00,
-     1.01221017e+00,  1.01105652e+00,  1.00993444e+00,  1.00884559e+00,
-     1.00778956e+00,  1.00676790e+00,  1.00577851e+00,  1.00482173e+00,
-     1.00389592e+00,  1.00300262e+00,  1.00214091e+00,  1.00131213e+00,
-     1.00051460e+00,  9.99748988e-01,  9.99013486e-01,  9.98309229e-01,
-     9.97634934e-01,  9.96991885e-01,  9.96378601e-01,  9.95795982e-01,
-     9.95242217e-01,  9.94718132e-01,  9.94222122e-01,  9.93755313e-01,
-     9.93316216e-01,  9.92905809e-01,  9.92522422e-01,  9.92166957e-01,
-     9.91837704e-01,  9.91535508e-01,  9.91258603e-01,  9.91007878e-01,
-     9.90781723e-01,  9.90581104e-01,  9.90404336e-01,  9.90252267e-01,
-     9.90123118e-01,  9.90017726e-01,  9.89934325e-01,  9.89873712e-01,
-     9.89834110e-01,  9.89816359e-01,  9.89818707e-01,  9.89841998e-01,
-     9.89884438e-01,  9.89946800e-01,  9.90027287e-01,  9.90126680e-01,
-     9.90243175e-01,  9.90377594e-01,  9.90528134e-01,  9.90695564e-01,
-     9.90878043e-01,  9.91076302e-01,  9.91288540e-01,  9.91515602e-01,
-     9.91755666e-01,  9.92009469e-01,  9.92275155e-01,  9.92553486e-01,
-     9.92842693e-01,  9.93143533e-01,  9.93454080e-01,  9.93775067e-01,
-     9.94104689e-01,  9.94443742e-01,  9.94790398e-01,  9.95145361e-01,
-     9.95506800e-01,  9.95875534e-01,  9.96249681e-01,  9.96629919e-01,
-     9.97014367e-01,  9.97403799e-01,  9.97796404e-01,  9.98192871e-01,
-     9.98591286e-01,  9.98992436e-01,  9.99394506e-01,  9.99798247e-01,
-     1.00020179e+00,  1.00060586e+00,  1.00100858e+00,  1.00141070e+00,
-     1.00181040e+00,  1.00220846e+00,  1.00260296e+00,  1.00299457e+00,
-     1.00338148e+00,  1.00376444e+00,  1.00414155e+00,  1.00451348e+00,
-     1.00487832e+00,  1.00523688e+00,  1.00558730e+00,  1.00593027e+00,
-     1.00626393e+00,  1.00658905e+00,  1.00690380e+00,  1.00720890e+00,
-     1.00750238e+00,  1.00778498e+00,  1.00805489e+00,  1.00831287e+00,
-     1.00855700e+00,  1.00878802e+00,  1.00900405e+00,  1.00920593e+00,
-     1.00939182e+00,  1.00956244e+00,  1.00971590e+00,  1.00985296e+00,
-     1.00997177e+00,  1.01007317e+00,  1.01015529e+00,  1.01021893e+00,
-     1.01026225e+00,  1.01028602e+00,  1.01028842e+00,  1.01027030e+00,
-     1.01022988e+00,  1.01016802e+00,  1.01008292e+00,  1.00997541e+00,
-     1.00984369e+00,  1.00968863e+00,  1.00950846e+00,  1.00930404e+00,
-     1.00907371e+00,  1.00881848e+00,  1.00853675e+00,  1.00822947e+00,
-     1.00789488e+00,  1.00753391e+00,  1.00714488e+00,  1.00672876e+00,
-     1.00628393e+00,  1.00581146e+00,  1.00530991e+00,  1.00478053e+00,
-     1.00422177e+00,  1.00363456e+00,  1.00301719e+00,  1.00237067e+00,
-     1.00169363e+00,  1.00098749e+00,  1.00025108e+00,  9.99485663e-01,
-     9.98689592e-01,  9.97863666e-01,  9.97006370e-01,  9.96119199e-01,
-     9.95201404e-01,  9.94254687e-01,  9.93277595e-01,  9.92270651e-01,
-     9.91231967e-01,  9.90163286e-01,  9.89064394e-01,  9.87937115e-01,
-     9.86779736e-01,  9.85592773e-01,  9.84375125e-01,  9.83129288e-01,
-     9.81348463e-01,  9.79890963e-01,  9.78400459e-01,  9.76860435e-01,
-     9.75269879e-01,  9.73627353e-01,  9.71931341e-01,  9.70180498e-01,
-     9.68372652e-01,  9.66506952e-01,  9.64580027e-01,  9.62592318e-01,
-     9.60540986e-01,  9.58425534e-01,  9.56244393e-01,  9.53998416e-01,
-     9.51684014e-01,  9.49301185e-01,  9.46846884e-01,  9.44320232e-01,
-     9.41718404e-01,  9.39042580e-01,  9.36290624e-01,  9.33464050e-01,
-     9.30560854e-01,  9.27580507e-01,  9.24519592e-01,  9.21378471e-01,
-     9.18153414e-01,  9.14844696e-01,  9.11451652e-01,  9.07976524e-01,
-     9.04417545e-01,  9.00776308e-01,  8.97050058e-01,  8.93238398e-01,
-     8.89338681e-01,  8.85351360e-01,  8.81274023e-01,  8.77109638e-01,
-     8.72857927e-01,  8.68519505e-01,  8.64092796e-01,  8.59579819e-01,
-     8.54976007e-01,  8.50285220e-01,  8.45502615e-01,  8.40630470e-01,
-     8.35667925e-01,  8.30619943e-01,  8.25482007e-01,  8.20258909e-01,
-     8.14946648e-01,  8.09546696e-01,  8.04059978e-01,  7.98489378e-01,
-     7.92831417e-01,  7.87090668e-01,  7.81262450e-01,  7.75353947e-01,
-     7.69363613e-01,  7.63291769e-01,  7.57139016e-01,  7.50901711e-01,
-     7.44590843e-01,  7.38205136e-01,  7.31738075e-01,  7.25199287e-01,
-     7.18588225e-01,  7.11905687e-01,  7.05153668e-01,  6.98332634e-01,
-     6.91444101e-01,  6.84490545e-01,  6.77470119e-01,  6.70388375e-01,
-     6.63245210e-01,  6.56045780e-01,  6.48788627e-01,  6.41477162e-01,
-     6.34114323e-01,  6.26702000e-01,  6.19235334e-01,  6.11720596e-01,
-     6.04161612e-01,  5.96559133e-01,  5.88914401e-01,  5.81234783e-01,
-     5.73519989e-01,  5.65770616e-01,  5.57988067e-01,  5.50173851e-01,
-     5.42330194e-01,  5.34460798e-01,  5.26568538e-01,  5.18656324e-01,
-     5.10728813e-01,  5.02781159e-01,  4.94819491e-01,  4.86845139e-01,
-     4.78860889e-01,  4.70869928e-01,  4.62875144e-01,  4.54877894e-01,
-     4.46882512e-01,  4.38889325e-01,  4.30898123e-01,  4.22918322e-01,
-     4.14950878e-01,  4.06993964e-01,  3.99052648e-01,  3.91134614e-01,
-     3.83234031e-01,  3.75354653e-01,  3.67502060e-01,  3.59680098e-01,
-     3.51887312e-01,  3.44130166e-01,  3.36408100e-01,  3.28728966e-01,
-     3.21090505e-01,  3.13496418e-01,  3.05951565e-01,  2.98454319e-01,
-     2.91010565e-01,  2.83621109e-01,  2.76285415e-01,  2.69019585e-01,
-     2.61812445e-01,  2.54659232e-01,  2.47584348e-01,  2.40578694e-01,
-     2.33647009e-01,  2.26788433e-01,  2.20001992e-01,  2.13301325e-01,
-     2.06677771e-01,  2.00140409e-01,  1.93683630e-01,  1.87310343e-01,
-     1.81027384e-01,  1.74839476e-01,  1.68739644e-01,  1.62737273e-01,
-     1.56825277e-01,  1.51012382e-01,  1.45298230e-01,  1.39687469e-01,
-     1.34171842e-01,  1.28762544e-01,  1.23455562e-01,  1.18254662e-01,
-     1.13159677e-01,  1.08171439e-01,  1.03290734e-01,  9.85202978e-02,
-     9.38600023e-02,  8.93117360e-02,  8.48752103e-02,  8.05523737e-02,
-     7.63429787e-02,  7.22489246e-02,  6.82699120e-02,  6.44077291e-02,
-     6.06620003e-02,  5.70343711e-02,  5.35243715e-02,  5.01334690e-02,
-     4.68610790e-02,  4.37084453e-02,  4.06748365e-02,  3.77612269e-02,
-     3.49667099e-02,  3.22919275e-02,  2.97357669e-02,  2.72984629e-02,
-     2.49787186e-02,  2.27762542e-02,  2.06895808e-02,  1.87178169e-02,
-     1.68593418e-02,  1.51125125e-02,  1.34757094e-02,  1.19462709e-02,
-     1.05228754e-02,  9.20130941e-03,  7.98124316e-03,  6.85547314e-03,
-     5.82657334e-03,  4.87838525e-03,  4.02351119e-03,  3.15418663e-03,
-};
-
-const float *lc3_mdct_win[LC3_NUM_DT][LC3_NUM_SRATE] = {
-
-    [LC3_DT_7M5] = {
-        [LC3_SRATE_8K ] = mdct_win_7m5_60,
-        [LC3_SRATE_16K] = mdct_win_7m5_120,
-        [LC3_SRATE_24K] = mdct_win_7m5_180,
-        [LC3_SRATE_32K] = mdct_win_7m5_240,
-        [LC3_SRATE_48K] = mdct_win_7m5_360,
-    },
-
-    [LC3_DT_10M] = {
-        [LC3_SRATE_8K ] = mdct_win_10m_80,
-        [LC3_SRATE_16K] = mdct_win_10m_160,
-        [LC3_SRATE_24K] = mdct_win_10m_240,
-        [LC3_SRATE_32K] = mdct_win_10m_320,
-        [LC3_SRATE_48K] = mdct_win_10m_480,
-    },
-};
-
-
-/**
- * Bands limits (cf. 3.7.1-2)
- */
-
-const int lc3_band_lim[LC3_NUM_DT][LC3_NUM_SRATE][LC3_NUM_BANDS+1] = {
-
-    [LC3_DT_7M5] = {
-
-        [LC3_SRATE_8K ] = {
-              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-             10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-             20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
-             30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
-             40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
-             50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
-             60,  60,  60,  60,  60                          },
-
-        [LC3_SRATE_16K] = {
-              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-             10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-             20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
-             30,  31,  32,  33,  34,  36,  38,  40,  42,  44,
-             46,  48,  50,  52,  54,  56,  58,  60,  62,  65,
-             68,  71,  74,  77,  80,  83,  86,  90,  94,  98,
-            102, 106, 110, 115, 120                          },
-
-        [LC3_SRATE_24K] = {
-              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-             10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-             20,  21,  22,  23,  24,  25,  26,  27,  29,  31,
-             33,  35,  37,  39,  41,  43,  45,  47,  49,  52,
-             55,  58,  61,  64,  67,  70,  74,  78,  82,  86,
-             90,  95, 100, 105, 110, 115, 121, 127, 134, 141,
-            148, 155, 163, 171, 180                          },
-
-        [LC3_SRATE_32K] = {
-              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-             10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-             20,  21,  22,  23,  24,  26,  28,  30,  32,  34,
-             36,  38,  40,  42,  45,  48,  51,  54,  57,  60,
-             63,  67,  71,  75,  79,  84,  89,  94,  99, 105,
-            111, 117, 124, 131, 138, 146, 154, 163, 172, 182,
-            192, 203, 215, 227, 240                          },
-
-        [LC3_SRATE_48K] = {
-              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-             10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-             20,  21,  22,  24,  26,  28,  30,  32,  34,  36,
-             38,  40,  43,  46,  49,  52,  55,  59,  63,  67,
-             71,  75,  80,  85,  90,  96, 102, 108, 115, 122,
-            129, 137, 146, 155, 165, 175, 186, 197, 209, 222,
-            236, 251, 266, 283, 300                          },
-    },
-
-    [LC3_DT_10M] = {
-
-        [LC3_SRATE_8K ] = {
-              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-             10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-             20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
-             30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
-             40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
-             51,  53,  55,  57,  59,  61,  63,  65,  67,  69,
-             71,  73,  75,  77,  80                          },
-
-        [LC3_SRATE_16K] = {
-              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-             10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-             20,  21,  22,  23,  24,  25,  26,  27,  28,  30,
-             32,  34,  36,  38,  40,  42,  44,  46,  48,  50,
-             52,  55,  58,  61,  64,  67,  70,  73,  76,  80,
-             84,  88,  92,  96, 101, 106, 111, 116, 121, 127,
-            133, 139, 146, 153, 160                          },
-
-        [LC3_SRATE_24K] = {
-              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-             10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-             20,  21,  22,  23,  25,  27,  29,  31,  33,  35,
-             37,  39,  41,  43,  46,  49,  52,  55,  58,  61,
-             64,  68,  72,  76,  80,  85,  90,  95, 100, 106,
-            112, 118, 125, 132, 139, 147, 155, 164, 173, 183,
-            193, 204, 215, 227, 240                          },
-
-        [LC3_SRATE_32K] = {
-              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-             10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-             20,  22,  24,  26,  28,  30,  32,  34,  36,  38,
-             41,  44,  47,  50,  53,  56,  60,  64,  68,  72,
-             76,  81,  86,  91,  97, 103, 109, 116, 123, 131,
-            139, 148, 157, 166, 176, 187, 199, 211, 224, 238,
-            252, 268, 284, 302, 320                          },
-
-        [LC3_SRATE_48K] = {
-              0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-             10,  11,  12,  13,  14,  15,  16,  17,  18,  20,
-             22,  24,  26,  28,  30,  32,  34,  36,  39,  42,
-             45,  48,  51,  55,  59,  63,  67,  71,  76,  81,
-             86,  92,  98, 105, 112, 119, 127, 135, 144, 154,
-            164, 175, 186, 198, 211, 225, 240, 256, 273, 291,
-            310, 330, 352, 375, 400                          },
-    }
-};
-
-
-/**
- * SNS Quantization (cf. 3.7.4)
- */
-
-const float lc3_sns_lfcb[32][8] = {
-
-    {  2.26283366e+00,  8.13311269e-01, -5.30193495e-01, -1.35664836e+00,
-      -1.59952177e+00, -1.44098768e+00, -1.14381648e+00, -7.55203768e-01 },
-
-    {  2.94516479e+00,  2.41143318e+00,  9.60455106e-01, -4.43226488e-01,
-      -1.22913612e+00, -1.55590039e+00, -1.49688656e+00, -1.11689987e+00 },
-
-    { -2.18610707e+00, -1.97152136e+00, -1.78718620e+00, -1.91865896e+00,
-      -1.79399122e+00, -1.35738404e+00, -7.05444279e-01, -4.78172945e-02 },
-
-    {  6.93688237e-01,  9.55609857e-01,  5.75230787e-01, -1.14603419e-01,
-      -6.46050637e-01, -9.52351370e-01, -1.07405247e+00, -7.58087707e-01 },
-
-    { -1.29752132e+00, -7.40369057e-01, -3.45372484e-01, -3.13285696e-01,
-      -4.02977243e-01, -3.72020853e-01, -7.83414177e-02,  9.70441304e-02 },
-
-    {  9.14652038e-01,  1.74293043e+00,  1.90906627e+00,  1.54408484e+00,
-       1.09344961e+00,  6.47479550e-01,  3.61790752e-02, -2.97092807e-01 },
-
-    { -2.51428813e+00, -2.89175271e+00, -2.00450667e+00, -7.50912274e-01,
-       4.41202105e-01,  1.20190988e+00,  1.32742857e+00,  1.22049081e+00 },
-
-    { -9.22188405e-01,  6.32495141e-01,  1.08736431e+00,  6.08628625e-01,
-       1.31174568e-01, -2.96149158e-01, -2.07013517e-01,  1.34924917e-01 },
-
-    {  7.90322288e-01,  6.28401262e-01,  3.93117924e-01,  4.80007711e-01,
-       4.47815138e-01,  2.09734215e-01,  6.56691996e-03, -8.61242342e-02 },
-
-    {  1.44775580e+00,  2.72399952e+00,  2.31083269e+00,  9.35051270e-01,
-      -2.74743911e-01, -9.02077697e-01, -9.40681512e-01, -6.33697039e-01 },
-
-    {  7.93354526e-01,  1.43931186e-02, -5.67834845e-01, -6.54760468e-01,
-      -4.79458998e-01, -1.73894662e-01,  6.80162706e-02,  2.95125948e-01 },
-
-    {  2.72425347e+00,  2.95947572e+00,  1.84953559e+00,  5.63284922e-01,
-       1.39917088e-01,  3.59641093e-01,  6.89461355e-01,  6.39790177e-01 },
-
-    { -5.30830198e-01, -2.12690683e-01,  5.76613628e-03,  4.24871484e-01,
-       4.73128952e-01,  8.58894199e-01,  1.19111161e+00,  9.96189670e-01 },
-
-    {  1.68728411e+00,  2.43614509e+00,  2.33019429e+00,  1.77983778e+00,
-       1.44411295e+00,  1.51995177e+00,  1.47199394e+00,  9.77682474e-01 },
-
-    { -2.95183273e+00, -1.59393497e+00, -1.09918773e-01,  3.88609073e-01,
-       5.12932650e-01,  6.28112597e-01,  8.22621796e-01,  8.75891425e-01 },
-
-    {  1.01878343e-01,  5.89857324e-01,  6.19047647e-01,  1.26731314e+00,
-       2.41961048e+00,  2.25174253e+00,  5.26537031e-01, -3.96591513e-01 },
-
-    {  2.68254575e+00,  1.32738011e+00,  1.30185274e-01, -3.38533089e-01,
-      -3.68219236e-01, -1.91689947e-01, -1.54782377e-01, -2.34207178e-01 },
-
-    {  4.82697924e+00,  3.11947804e+00,  1.39513671e+00,  2.50295316e-01,
-      -3.93613839e-01, -6.43458173e-01, -6.42570737e-01, -7.23193223e-01 },
-
-    {  8.78419936e-02, -5.69586840e-01, -1.14506016e+00, -1.66968488e+00,
-      -1.84534418e+00, -1.56468027e+00, -1.11746759e+00, -5.33981663e-01 },
-
-    {  1.39102308e+00,  1.98146479e+00,  1.11265796e+00, -2.20107509e-01,
-      -7.74965612e-01, -5.94063874e-01,  1.36937681e-01,  8.18242891e-01 },
-
-    {  3.84585894e-01, -1.60588786e-01, -5.39366810e-01, -5.29309079e-01,
-       1.90433547e-01,  2.56062918e+00,  2.81896398e+00,  6.56670876e-01 },
-
-    {  1.93227399e+00,  3.01030180e+00,  3.06543894e+00,  2.50110161e+00,
-       1.93089593e+00,  5.72153811e-01, -8.11741794e-01, -1.17641811e+00 },
-
-    {  1.75080463e-01, -7.50522832e-01, -1.03943893e+00, -1.13577509e+00,
-      -1.04197904e+00, -1.52060099e-02,  2.07048392e+00,  3.42948918e+00 },
-
-    { -1.18817020e+00,  3.66792874e-01,  1.30957830e+00,  1.68330687e+00,
-       1.25100924e+00,  9.42375752e-01,  8.26250483e-01,  4.39952741e-01 },
-
-    {  2.53322203e+00,  2.11274643e+00,  1.26288412e+00,  7.61513512e-01,
-       5.22117938e-01,  1.18680070e-01, -4.52346828e-01, -7.00352426e-01 },
-
-    {  3.99889837e+00,  4.07901751e+00,  2.82285661e+00,  1.72607213e+00,
-       6.47144377e-01, -3.31148521e-01, -8.84042571e-01, -1.12697341e+00 },
-
-    {  5.07902593e-01,  1.58838450e+00,  1.72899024e+00,  1.00692230e+00,
-       3.77121232e-01,  4.76370767e-01,  1.08754740e+00,  1.08756266e+00 },
-
-    {  3.16856825e+00,  3.25853458e+00,  2.42230591e+00,  1.79446078e+00,
-       1.52177911e+00,  1.17196707e+00,  4.89394597e-01, -6.22795716e-02 },
-
-    {  1.89414767e+00,  1.25108695e+00,  5.90451211e-01,  6.08358583e-01,
-       8.78171010e-01,  1.11912511e+00,  1.01857662e+00,  6.20453891e-01 },
-
-    {  9.48880605e-01,  2.13239439e+00,  2.72345350e+00,  2.76986077e+00,
-       2.54286973e+00,  2.02046264e+00,  8.30045859e-01, -2.75569174e-02 },
-
-    { -1.88026757e+00, -1.26431073e+00,  3.11424977e-01,  1.83670210e+00,
-       2.25634192e+00,  2.04818998e+00,  2.19526837e+00,  2.02659614e+00 },
-
-    {  2.46375746e-01,  9.55621773e-01,  1.52046777e+00,  1.97647400e+00,
-       1.94043867e+00,  2.23375847e+00,  1.98835978e+00,  1.27232673e+00 },
-
-};
-
-const float lc3_sns_hfcb[32][8] = {
-
-    {  2.32028419e-01, -1.00890271e+00, -2.14223503e+00, -2.37533814e+00,
-      -2.23041933e+00, -2.17595881e+00, -2.29065914e+00, -2.53286398e+00 },
-
-    { -1.29503937e+00, -1.79929965e+00, -1.88703148e+00, -1.80991660e+00,
-      -1.76340038e+00, -1.83418428e+00, -1.80480981e+00, -1.73679545e+00 },
-
-    {  1.39285716e-01, -2.58185126e-01, -6.50804573e-01, -1.06815732e+00,
-      -1.61928742e+00, -2.18762566e+00, -2.63757587e+00, -2.97897750e+00 },
-
-    { -3.16513102e-01, -4.77747657e-01, -5.51162076e-01, -4.84788283e-01,
-      -2.38388394e-01, -1.43024507e-01,  6.83186674e-02,  8.83061717e-02 },
-
-    {  8.79518405e-01,  2.98340096e-01, -9.15386396e-01, -2.20645975e+00,
-      -2.74142181e+00, -2.86139074e+00, -2.88841597e+00, -2.95182608e+00 },
-
-    { -2.96701922e-01, -9.75004919e-01, -1.35857500e+00, -9.83721106e-01,
-      -6.52956939e-01, -9.89986993e-01, -1.61467225e+00, -2.40712302e+00 },
-
-    {  3.40981100e-01,  2.68899789e-01,  5.63335685e-02,  4.99114047e-02,
-      -9.54130727e-02, -7.60166146e-01, -2.32758120e+00, -3.77155485e+00 },
-
-    { -1.41229759e+00, -1.48522119e+00, -1.18603580e+00, -6.25001634e-01,
-       1.53902497e-01,  5.76386498e-01,  7.95092604e-01,  5.96564632e-01 },
-
-    { -2.28839512e-01, -3.33719070e-01, -8.09321359e-01, -1.63587877e+00,
-      -1.88486397e+00, -1.64496691e+00, -1.40515778e+00, -1.46666471e+00 },
-
-    { -1.07148629e+00, -1.41767015e+00, -1.54891762e+00, -1.45296062e+00,
-      -1.03182970e+00, -6.90642640e-01, -4.28843805e-01, -4.94960215e-01 },
-
-    { -5.90988511e-01, -7.11737759e-02,  3.45719523e-01,  3.00549461e-01,
-      -1.11865218e+00, -2.44089151e+00, -2.22854732e+00, -1.89509228e+00 },
-
-    { -8.48434099e-01, -5.83226811e-01,  9.00423688e-02,  8.45025008e-01,
-       1.06572385e+00,  7.37582999e-01,  2.56590452e-01, -4.91963360e-01 },
-
-    {  1.14069146e+00,  9.64016892e-01,  3.81461206e-01, -4.82849341e-01,
-      -1.81632721e+00, -2.80279513e+00, -3.23385725e+00, -3.45908714e+00 },
-
-    { -3.76283238e-01,  4.25675462e-02,  5.16547697e-01,  2.51716882e-01,
-      -2.16179968e-01, -5.34074091e-01, -6.40786096e-01, -8.69745032e-01 },
-
-    {  6.65004121e-01,  1.09790765e+00,  1.38342667e+00,  1.34327359e+00,
-       8.22978837e-01,  2.15876799e-01, -4.04925753e-01, -1.07025606e+00 },
-
-    { -8.26265954e-01, -6.71181233e-01, -2.28495593e-01,  5.18980853e-01,
-       1.36721896e+00,  2.18023038e+00,  2.53596093e+00,  2.20121099e+00 },
-
-    {  1.41008327e+00,  7.54441908e-01, -1.30550585e+00, -1.87133711e+00,
-      -1.24008685e+00, -1.26712925e+00, -2.03670813e+00, -2.89685162e+00 },
-
-    {  3.61386818e-01, -2.19991705e-02, -5.79368834e-01, -8.79427961e-01,
-      -8.50685023e-01, -7.79397050e-01, -7.32182927e-01, -8.88348515e-01 },
-
-    {  4.37469239e-01,  3.05440420e-01, -7.38786566e-03, -4.95649855e-01,
-      -8.06651271e-01, -1.22431892e+00, -1.70157770e+00, -2.24491914e+00 },
-
-    {  6.48100319e-01,  6.82299134e-01,  2.53247464e-01,  7.35842144e-02,
-       3.14216709e-01,  2.34729881e-01,  1.44600134e-01, -6.82120179e-02 },
-
-    {  1.11919833e+00,  1.23465533e+00,  5.89170238e-01, -1.37192460e+00,
-      -2.37095707e+00, -2.00779783e+00, -1.66688540e+00, -1.92631846e+00 },
-
-    {  1.41847497e-01, -1.10660071e-01, -2.82824593e-01, -6.59813475e-03,
-       2.85929280e-01,  4.60445530e-02, -6.02596416e-01, -2.26568729e+00 },
-
-    {  5.04046955e-01,  8.26982163e-01,  1.11981236e+00,  1.17914044e+00,
-       1.07987429e+00,  6.97536239e-01, -9.12548817e-01, -3.57684747e+00 },
-
-    { -5.01076050e-01, -3.25678006e-01,  2.80798195e-02,  2.62054555e-01,
-       3.60590806e-01,  6.35623722e-01,  9.59012467e-01,  1.30745157e+00 },
-
-    {  3.74970983e+00,  1.52342612e+00, -4.57715662e-01, -7.98711008e-01,
-      -3.86819329e-01, -3.75901062e-01, -6.57836900e-01, -1.28163964e+00 },
-
-    { -1.15258991e+00, -1.10800886e+00, -5.62615117e-01, -2.20562124e-01,
-      -3.49842880e-01, -7.53432770e-01, -9.88596593e-01, -1.28790472e+00 },
-
-    {  1.02827246e+00,  1.09770519e+00,  7.68645546e-01,  2.06081978e-01,
-      -3.42805735e-01, -7.54939405e-01, -1.04196178e+00, -1.50335653e+00 },
-
-    {  1.28831972e-01,  6.89439395e-01,  1.12346905e+00,  1.30934523e+00,
-       1.35511965e+00,  1.42311381e+00,  1.15706449e+00,  4.06319438e-01 },
-
-    {  1.34033030e+00,  1.38996825e+00,  1.04467922e+00,  6.35822746e-01,
-      -2.74733756e-01, -1.54923372e+00, -2.44239710e+00, -3.02457607e+00 },
-
-    {  2.13843105e+00,  4.24711267e+00,  2.89734110e+00,  9.32730658e-01,
-      -2.92822250e-01, -8.10404297e-01, -7.88868099e-01, -9.35353149e-01 },
-
-    {  5.64830487e-01,  1.59184978e+00,  2.39771699e+00,  3.03697344e+00,
-       2.66424350e+00,  1.39304485e+00,  4.03834024e-01, -6.56270971e-01 },
-
-    { -4.22460548e-01,  3.26149625e-01,  1.39171313e+00,  2.23146615e+00,
-       2.61179442e+00,  2.66540340e+00,  2.40103554e+00,  1.75920380e+00 },
-
-};
-
-const struct lc3_sns_vq_gains lc3_sns_vq_gains[4] = {
-
-    { 2, (const float []){
-             8915.f / 4096, 12054.f / 4096 } },
-
-    { 4, (const float []){
-             6245.f / 4096, 15043.f / 4096, 17861.f / 4096, 21014.f / 4096 } },
-
-    { 4, (const float []){
-             7099.f / 4096,  9132.f / 4096, 11253.f / 4096, 14808.f / 4096 } },
-
-    { 8, (const float []){
-             4336.f / 4096,  5067.f / 4096,  5895.f / 4096,  8149.f / 4096,
-            10235.f / 4096, 12825.f / 4096, 16868.f / 4096, 19882.f / 4096 } }
-};
-
-const int32_t lc3_sns_mpvq_offsets[][11] = {
-    { 0, 1,  1,   1,    1,     1,      1,      1,      1,       1,       1 },
-    { 0, 1,  3,   5,    7,     9,     11,     13,     15,      17,      19 },
-    { 0, 1,  5,  13,   25,    41,     61,     85,    113,     145,     181 },
-    { 0, 1,  7,  25,   63,   129,    231,    377,    575,     833,    1159 },
-    { 0, 1,  9,  41,  129,   321,    681,   1289,   2241,    3649,    5641 },
-    { 0, 1, 11,  61,  231,   681,   1683,   3653,   7183,   13073  , 22363 },
-    { 0, 1, 13,  85,  377,  1289,   3653,   8989,  19825,   40081,   75517 },
-    { 0, 1, 15, 113,  575,  2241,   7183,  19825,  48639,  108545,  224143 },
-    { 0, 1, 17, 145,  833,  3649,  13073,  40081, 108545,  265729,  598417 },
-    { 0, 1, 19, 181, 1159,  5641,  22363,  75517, 224143,  598417, 1462563 },
-    { 0, 1, 21, 221, 1561,  8361,  36365, 134245, 433905, 1256465, 3317445 },
-    { 0, 1, 23, 265, 2047, 11969,  56695, 227305, 795455, 2485825, 7059735 },
-    { 0, 1, 25, 313, 2625, 16641,  85305, 369305,1392065, 4673345,14218905 },
-    { 0, 1, 27, 365, 3303, 22569, 124515, 579125,2340495, 8405905,27298155 },
-    { 0, 1, 29, 421, 4089, 29961, 177045, 880685,3800305,14546705,50250765 },
-    { 0, 1, 31, 481, 4991, 39041, 246047,1303777,5984767,24331777,89129247 },
-};
-
-
-/**
- * TNS Arithmetic Coding (cf. 3.7.5)
- * The number of bits are given at 2048th of bits
- */
-
-const struct lc3_ac_model lc3_tns_order_models[] = {
-
-    { { {    0,   3 }, {    3,   9 }, {   12,  23 }, {   35,  54 },
-        {   89, 111 }, {  200, 190 }, {  390, 268 }, {  658, 366 },
-        { 1024,   0 }, { 1024,   0 }, { 1024,   0 }, { 1024,   0 },
-        { 1024,   0 }, { 1024,   0 }, { 1024,   0 }, { 1024,   0 },
-        { 1024,   0 } } },
-
-    { { {    0,  14 }, {   14,  42 }, {   56, 100 }, {  156, 157 },
-        {  313, 181 }, {  494, 178 }, {  672, 167 }, {  839, 185 },
-        { 1024,   0 }, { 1024,   0 }, { 1024,   0 }, { 1024,   0 },
-        { 1024,   0 }, { 1024,   0 }, { 1024,   0 }, { 1024,   0 },
-        { 1024,   0 } } },
-};
-
-const uint16_t lc3_tns_order_bits[][8] = {
-    { 17234, 13988, 11216, 8694, 6566, 4977, 3961, 3040 },
-    { 12683,  9437,  6874, 5541, 5121, 5170, 5359, 5056 }
-};
-
-const struct lc3_ac_model lc3_tns_coeffs_models[] = {
-
-    { { {    0,   1 }, {    1,   5 }, {    6,  15 }, {   21,  31 },
-        {   52,  54 }, {  106,  86 }, {  192,  97 }, {  289, 120 },
-        {  409, 159 }, {  568, 152 }, {  720, 111 }, {  831, 104 },
-        {  935,  59 }, {  994,  22 }, { 1016,   6 }, { 1022,   1 },
-        { 1023,   1 } } },
-
-    { { {    0,   1 }, {    1,   1 }, {    2,   1 }, {    3,   1 },
-        {    4,  13 }, {   17,  43 }, {   60,  94 }, {  154, 139 },
-        {  293, 173 }, {  466, 160 }, {  626, 154 }, {  780, 131 },
-        {  911,  78 }, {  989,  27 }, { 1016,   6 }, { 1022,   1 },
-        { 1023,   1 } } },
-
-    { { {    0,   1 }, {    1,   1 }, {    2,   1 }, {    3,   1 },
-        {    4,   9 }, {   13,  43 }, {   56, 106 }, {  162, 199 },
-        {  361, 217 }, {  578, 210 }, {  788, 141 }, {  929,  74 },
-        { 1003,  17 }, { 1020,   1 }, { 1021,   1 }, { 1022,   1 },
-        { 1023,   1 } } },
-
-    { { {    0,   1 }, {    1,   1 }, {    2,   1 }, {    3,   1 },
-        {    4,   2 }, {    6,  11 }, {   17,  49 }, {   66, 204 },
-        {  270, 285 }, {  555, 297 }, {  852, 120 }, {  972,  39 },
-        { 1011,   9 }, { 1020,   1 }, { 1021,   1 }, { 1022,   1 },
-        { 1023,   1 } } },
-
-    { { {    0,   1 }, {    1,   1 }, {    2,   1 }, {    3,   1 },
-        {    4,   1 }, {    5,   7 }, {   12,  42 }, {   54, 241 },
-        {  295, 341 }, {  636, 314 }, {  950,  58 }, { 1008,   9 },
-        { 1017,   3 }, { 1020,   1 }, { 1021,   1 }, { 1022,   1 },
-        { 1023,   1 } } },
-
-    { { {    0,   1 }, {    1,   1 }, {    2,   1 }, {    3,   1 },
-        {    4,   1 }, {    5,   1 }, {    6,  13 }, {   19, 205 },
-        {  224, 366 }, {  590, 377 }, {  967,  47 }, { 1014,   5 },
-        { 1019,   1 }, { 1020,   1 }, { 1021,   1 }, { 1022,   1 },
-        { 1023,   1 } } },
-
-    { { {    0,   1 }, {    1,   1 }, {    2,   1 }, {    3,   1 },
-        {    4,   1 }, {    5,   1 }, {    6,  13 }, {   19, 281 },
-        {  300, 330 }, {  630, 371 }, { 1001,  17 }, { 1018,   1 },
-        { 1019,   1 }, { 1020,   1 }, { 1021,   1 }, { 1022,   1 },
-        { 1023,   1 } } },
-
-    { { {    0,   1 }, {    1,   1 }, {    2,   1 }, {    3,   1 },
-        {    4,   1 }, {    5,   1 }, {    6,   5 }, {   11, 297 },
-        {  308,   1 }, {  309, 682 }, {  991,  26 }, { 1017,   2 },
-        { 1019,   1 }, { 1020,   1 }, { 1021,   1 }, { 1022,   1 },
-        { 1023,   1 } } },
-
-};
-
-const uint16_t lc3_tns_coeffs_bits[][17] = {
-
-    { 20480, 15725, 12479, 10334,  8694,  7320,  6964,  6335,
-       5504,  5637,  6566,  6758,  8433, 11348, 15186, 20480, 20480 },
-
-    { 20480, 20480, 20480, 20480, 12902,  9368,  7057,  5901,
-       5254,  5485,  5598,  6076,  7608, 10742, 15186, 20480, 20480 },
-
-    { 20480, 20480, 20480, 20480, 13988,  9368,  6702,  4841,
-       4585,  4682,  5859,  7764, 12109, 20480, 20480, 20480, 20480 },
-
-    { 20480, 20480, 20480, 20480, 18432, 13396,  8982,  4767,
-       3779,  3658,  6335,  9656, 13988, 20480, 20480, 20480, 20480 },
-
-    { 20480, 20480, 20480, 20480, 20480, 14731,  9437,  4275,
-       3249,  3493,  8483, 13988, 17234, 20480, 20480, 20480, 20480 },
-
-    { 20480, 20480, 20480, 20480, 20480, 20480, 12902,  4753,
-       3040,  2953,  9105, 15725, 20480, 20480, 20480, 20480, 20480 },
-
-    { 20480, 20480, 20480, 20480, 20480, 20480, 12902,  3821,
-       3346,  3000, 12109, 20480, 20480, 20480, 20480, 20480, 20480 },
-
-    { 20480, 20480, 20480, 20480, 20480, 20480, 15725,  3658,
-      20480,  1201, 10854, 18432, 20480, 20480, 20480, 20480, 20480 }
-
-};
-
-
-/**
- * Long Term Postfilter Synthesis (cf. 3.7.6)
- * with - addition of a 0 for num coefficients
- *      - remove of first 0 den coefficients
- */
-
-const float *lc3_ltpf_cnum[LC3_NUM_SRATE][4] = {
-
-    [LC3_SRATE_8K] = {
-        (const float []){
-           6.02361821e-01,  4.19760926e-01, -1.88342453e-02,  0. },
-        (const float []){
-           5.99476858e-01,  4.19760926e-01, -1.59492828e-02,  0. },
-        (const float []){
-           5.96776466e-01,  4.19760926e-01, -1.32488910e-02,  0. },
-        (const float []){
-           5.94241012e-01,  4.19760926e-01, -1.07134366e-02,  0. },
-    },
-
-    [LC3_SRATE_16K] = {
-        (const float []){
-           6.02361821e-01,  4.19760926e-01, -1.88342453e-02,  0. },
-        (const float []){
-           5.99476858e-01,  4.19760926e-01, -1.59492828e-02,  0. },
-        (const float []){
-           5.96776466e-01,  4.19760926e-01, -1.32488910e-02,  0. },
-        (const float []){
-           5.94241012e-01,  4.19760926e-01, -1.07134366e-02,  0. },
-    },
-
-    [LC3_SRATE_24K] = {
-        (const float []){
-           3.98969559e-01,  5.14250861e-01,  1.00438297e-01, -1.27889396e-02,
-          -1.57228008e-03,  0.                                               },
-        (const float []){
-           3.94863491e-01,  5.12381921e-01,  1.04319493e-01, -1.09199996e-02,
-          -1.34740833e-03,  0.                                               },
-        (const float []){
-           3.90984448e-01,  5.10605352e-01,  1.07983252e-01, -9.14343107e-03,
-          -1.13212462e-03,  0.                                               },
-        (const float []){
-           3.87309389e-01,  5.08912208e-01,  1.11451738e-01, -7.45028713e-03,
-          -9.25551405e-04,  0.                                               },
-    },
-
-    [LC3_SRATE_32K] = {
-        (const float []){
-           2.98237945e-01,  4.65280920e-01,  2.10599743e-01,  3.76678038e-02,
-          -1.01569616e-02, -2.53588100e-03, -3.18294617e-04,  0.             },
-        (const float []){
-           2.94383415e-01,  4.61929400e-01,  2.12946577e-01,  4.06617500e-02,
-          -8.69327230e-03, -2.17830711e-03, -2.74288806e-04,  0.             },
-        (const float []){
-           2.90743921e-01,  4.58746191e-01,  2.15145697e-01,  4.35010477e-02,
-          -7.29549535e-03, -1.83439564e-03, -2.31692019e-04,  0.             },
-        (const float []){
-           2.87297585e-01,  4.55714889e-01,  2.17212695e-01,  4.62008888e-02,
-          -5.95746380e-03, -1.50293428e-03, -1.90385191e-04,  0.             },
-    },
-
-    [LC3_SRATE_48K] = {
-        (const float []){
-           1.98136374e-01,  3.52449490e-01,  2.51369527e-01,  1.42414624e-01,
-           5.70473102e-02,  9.29336624e-03, -7.22602537e-03, -3.17267989e-03,
-          -1.12183596e-03, -2.90295724e-04, -4.27081559e-05,  0.             },
-        (const float []){
-           1.95070943e-01,  3.48466041e-01,  2.50998846e-01,  1.44116741e-01,
-           5.92894732e-02,  1.10892383e-02, -6.19290811e-03, -2.72670551e-03,
-          -9.66712583e-04, -2.50810092e-04, -3.69993877e-05,  0.             },
-        (const float []){
-           1.92181006e-01,  3.44694556e-01,  2.50622009e-01,  1.45710245e-01,
-           6.14113213e-02,  1.27994140e-02, -5.20372109e-03, -2.29732451e-03,
-          -8.16560813e-04, -2.12385575e-04, -3.14127133e-05,  0.             },
-        (const float []){
-           1.89448531e-01,  3.41113925e-01,  2.50240688e-01,  1.47206563e-01,
-           6.34247723e-02,  1.44320343e-02, -4.25444914e-03, -1.88308147e-03,
-          -6.70961906e-04, -1.74936334e-04, -2.59386474e-05,  0.             },
-    }
-};
-
-const float *lc3_ltpf_cden[LC3_NUM_SRATE][4] = {
-
-    [LC3_SRATE_8K] = {
-        (const float []){
-           2.09880463e-01,  5.83527575e-01,  2.09880463e-01,  0.00000000e+00 },
-        (const float []){
-           1.06999186e-01,  5.50075002e-01,  3.35690625e-01,  6.69885837e-03 },
-        (const float []){
-           3.96711478e-02,  4.59220930e-01,  4.59220930e-01,  3.96711478e-02 },
-        (const float []){
-           6.69885837e-03,  3.35690625e-01,  5.50075002e-01,  1.06999186e-01 },
-    },
-
-    [LC3_SRATE_16K] = {
-        (const float []){
-           2.09880463e-01,  5.83527575e-01,  2.09880463e-01,  0.00000000e+00 },
-        (const float []){
-           1.06999186e-01,  5.50075002e-01,  3.35690625e-01,  6.69885837e-03 },
-        (const float []){
-           3.96711478e-02,  4.59220930e-01,  4.59220930e-01,  3.96711478e-02 },
-        (const float []){
-           6.69885837e-03,  3.35690625e-01,  5.50075002e-01,  1.06999186e-01 },
-    },
-
-    [LC3_SRATE_24K] = {
-        (const float []){
-           6.32223163e-02,  2.50730961e-01,  3.71390943e-01,  2.50730961e-01,
-           6.32223163e-02,  0.00000000e+00                                   },
-        (const float []){
-           3.45927217e-02,  1.98651560e-01,  3.62641173e-01,  2.98675055e-01,
-           1.01309287e-01,  4.26354371e-03                                   },
-        (const float []){
-           1.53574678e-02,  1.47434488e-01,  3.37425955e-01,  3.37425955e-01,
-           1.47434488e-01,  1.53574678e-02                                   },
-        (const float []){
-           4.26354371e-03,  1.01309287e-01,  2.98675055e-01,  3.62641173e-01,
-           1.98651560e-01,  3.45927217e-02                                   },
-    },
-
-    [LC3_SRATE_32K] = {
-        (const float []){
-           2.90040188e-02,  1.12985742e-01,  2.21202403e-01,  2.72390947e-01,
-           2.21202403e-01,  1.12985742e-01,  2.90040188e-02,  0.00000000e+00 },
-        (const float []){
-           1.70315342e-02,  8.72250379e-02,  1.96140776e-01,  2.68923798e-01,
-           2.42499910e-01,  1.40577336e-01,  4.47487717e-02,  3.12703024e-03 },
-        (const float []){
-           8.56367375e-03,  6.42622294e-02,  1.68767671e-01,  2.58744594e-01,
-           2.58744594e-01,  1.68767671e-01,  6.42622294e-02,  8.56367375e-03 },
-        (const float []){
-           3.12703024e-03,  4.47487717e-02,  1.40577336e-01,  2.42499910e-01,
-           2.68923798e-01,  1.96140776e-01,  8.72250379e-02,  1.70315342e-02 },
-    },
-
-    [LC3_SRATE_48K] = {
-        (const float []){
-           1.08235939e-02,  3.60896922e-02,  7.67640147e-02,  1.24153058e-01,
-           1.62759644e-01,  1.77677142e-01,  1.62759644e-01,  1.24153058e-01,
-           7.67640147e-02,  3.60896922e-02,  1.08235939e-02,  0.00000000e+00 },
-        (const float []){
-           7.04140493e-03,  2.81970232e-02,  6.54704494e-02,  1.12464799e-01,
-           1.54841896e-01,  1.76712238e-01,  1.69150721e-01,  1.35290158e-01,
-           8.85142501e-02,  4.49935385e-02,  1.55761371e-02,  2.03972196e-03 },
-        (const float []){
-           4.14699847e-03,  2.13575731e-02,  5.48273558e-02,  1.00497144e-01,
-           1.45606034e-01,  1.73843984e-01,  1.73843984e-01,  1.45606034e-01,
-           1.00497144e-01,  5.48273558e-02,  2.13575731e-02,  4.14699847e-03 },
-        (const float []){
-           2.03972196e-03,  1.55761371e-02,  4.49935385e-02,  8.85142501e-02,
-           1.35290158e-01,  1.69150721e-01,  1.76712238e-01,  1.54841896e-01,
-           1.12464799e-01,  6.54704494e-02,  2.81970232e-02,  7.04140493e-03 },
-    }
-};
-
-
-/**
- * Spectral Data Arithmetic Coding (cf. 3.7.7)
- * The number of bits are given at 2048th of bits
- *
- * The dimensions of the lookup table are set as following :
- *   1: Rate selection
- *   2: Half spectrum selection (1st half / 2nd half)
- *   3: State of the arithmetic coder
- *   4: Number of msb bits (significant - 2), limited to 3
- *
- * table[r][h][s][k] = table(normative)[s + h*256 + r*512 + k*1024]
- */
-
-const uint8_t lc3_spectrum_lookup[2][2][256][4] = {
-
-    { { {  1,13, 0, 0 }, { 39,13, 0, 0 }, {  7,13, 0, 0 }, { 25,13, 0, 0 },
-        { 22,13, 0, 0 }, { 22,13, 0, 0 }, { 28,13, 0, 0 }, { 22,13, 0, 0 },
-        { 22,60, 0, 0 }, { 22,60, 0, 0 }, { 22,60, 0, 0 }, { 28,60, 0, 0 },
-        { 28,60, 0, 0 }, { 28,60,13, 0 }, { 34,60,13, 0 }, { 31,16,13, 0 },
-        { 31,16,13, 0 }, { 40, 0, 0, 0 }, { 43, 0, 0, 0 }, { 46, 0, 0, 0 },
-        { 49, 0, 0, 0 }, { 52, 0, 0, 0 }, { 14, 0, 0, 0 }, { 17, 0, 0, 0 },
-        { 36, 0, 0, 0 }, { 36, 0, 0, 0 }, { 36, 0, 0, 0 }, { 38, 0, 0, 0 },
-        {  0, 0, 0, 0 }, { 57, 0, 0, 0 }, { 38,13, 0, 0 }, { 22,60, 0, 0 },
-        {  0, 0, 0, 0 }, {  8, 0, 0, 0 }, {  9, 0, 0, 0 }, { 11, 0, 0, 0 },
-        { 47, 0, 0, 0 }, { 14, 0, 0, 0 }, { 14, 0, 0, 0 }, { 17, 0, 0, 0 },
-        { 36, 0, 0, 0 }, { 36, 0, 0, 0 }, { 36, 0, 0, 0 }, { 38, 0, 0, 0 },
-        { 59, 0, 0, 0 }, { 59, 0, 0, 0 }, { 38,13, 0, 0 }, { 22,60, 0, 0 },
-        { 22,60, 0, 0 }, { 26, 0, 0, 0 }, { 46, 0, 0, 0 }, { 29, 0, 0, 0 },
-        { 30, 0, 0, 0 }, { 32, 0, 0, 0 }, { 33, 0, 0, 0 }, { 35, 0, 0, 0 },
-        { 36, 0, 0, 0 }, { 36, 0, 0, 0 }, { 36, 0, 0, 0 }, { 38, 0, 0, 0 },
-        {  0,13, 0, 0 }, { 59,13, 0, 0 }, { 23,13, 0, 0 }, { 22,60, 0, 0 },
-        { 46,60, 0, 0 }, { 46, 0, 0, 0 }, { 45, 0, 0, 0 }, { 47, 0, 0, 0 },
-        { 48, 0, 0, 0 }, { 50, 0, 0, 0 }, { 50, 0, 0, 0 }, { 18, 0, 0, 0 },
-        { 54, 0, 0, 0 }, { 54, 0, 0, 0 }, { 54, 0, 0, 0 }, { 38, 0, 0, 0 },
-        { 59,13, 0, 0 }, { 59,13, 0, 0 }, { 59,13, 0, 0 }, { 22,60, 0, 0 },
-        {  0,60, 0, 0 }, { 62, 0, 0, 0 }, { 63, 0, 0, 0 }, {  3, 0, 0, 0 },
-        { 33, 0, 0, 0 }, {  2, 0, 0, 0 }, {  2, 0, 0, 0 }, { 61, 0, 0, 0 },
-        { 20, 0, 0, 0 }, { 20, 0, 0, 0 }, { 20,13, 0, 0 }, { 21,13, 0, 0 },
-        { 59,13, 0, 0 }, { 59,13, 0, 0 }, { 39,13, 0, 0 }, { 28,60, 0, 0 },
-        { 28,60, 0, 0 }, { 63, 0, 0, 0 }, { 63, 0, 0, 0 }, {  3, 0, 0, 0 },
-        { 33, 0, 0, 0 }, {  2, 0, 0, 0 }, {  2, 0, 0, 0 }, { 61, 0, 0, 0 },
-        { 38, 0, 0, 0 }, { 38, 0, 0, 0 }, { 38,13, 0, 0 }, { 21,13, 0, 0 },
-        { 59,13, 0, 0 }, { 59,13, 0, 0 }, { 39,13, 0, 0 }, { 28,60, 0, 0 },
-        { 28,60, 0, 0 }, {  6, 0, 0, 0 }, {  6, 0, 0, 0 }, {  6, 0, 0, 0 },
-        {  2, 0, 0, 0 }, { 18, 0, 0, 0 }, { 61, 0, 0, 0 }, { 20, 0, 0, 0 },
-        { 21, 0, 0, 0 }, { 21, 0, 0, 0 }, { 21,13, 0, 0 }, { 59,13, 0, 0 },
-        { 39,13, 0, 0 }, { 39,13, 0, 0 }, {  7,13, 0, 0 }, { 34,60,13, 0 },
-        { 34,60,13, 0 }, { 34,60,13, 0 }, { 34,60,13, 0 }, { 34,60,13, 0 },
-        { 34,60,13, 0 }, { 34,60,13, 0 }, { 34,60,13, 0 }, { 34,60,13, 0 },
-        { 34,60,13, 0 }, { 34,60,13, 0 }, { 34,60,13, 0 }, { 34,60,13, 0 },
-        { 34,60,13, 0 }, { 34,60,13, 0 }, { 34,60,13, 0 }, { 34,60,13, 0 },
-        { 34,60,13, 0 }, { 51, 0, 0, 0 }, { 51, 0, 0, 0 }, { 51, 0, 0, 0 },
-        { 53, 0, 0, 0 }, { 54, 0, 0, 0 }, { 20, 0, 0, 0 }, { 38, 0, 0, 0 },
-        { 38, 0, 0, 0 }, { 57, 0, 0, 0 }, { 39,13, 0, 0 }, { 39,13, 0, 0 },
-        { 39,13, 0, 0 }, {  7,13, 0, 0 }, { 24,13, 0, 0 }, { 34,60,13, 0 },
-        {  4,60, 0, 0 }, {  4,60, 0, 0 }, {  4,60, 0, 0 }, {  4,60, 0, 0 },
-        {  4,60, 0, 0 }, {  4,60, 0, 0 }, {  4,60, 0, 0 }, {  4,60, 0, 0 },
-        {  4,60, 0, 0 }, {  4,60, 0, 0 }, {  4,60, 0, 0 }, {  4,60, 0, 0 },
-        {  4,60, 0, 0 }, {  4,60, 0, 0 }, {  4,60, 0, 0 }, {  4,60, 0, 0 },
-        {  4,60, 0, 0 }, {  4, 0, 0, 0 }, {  4, 0, 0, 0 }, {  4, 0, 0, 0 },
-        {  4, 0, 0, 0 }, { 56, 0, 0, 0 }, { 38, 0, 0, 0 }, { 57, 0, 0, 0 },
-        { 57,13, 0, 0 }, { 59,13, 0, 0 }, {  7,13, 0, 0 }, {  7,13, 0, 0 },
-        {  7,13, 0, 0 }, { 42,13, 0, 0 }, { 42,13, 0, 0 }, { 34,60,13, 0 },
-        {  0,60,13, 0 }, {  0,60,13, 0 }, {  0,60,13, 0 }, {  0,60,13, 0 },
-        {  0,60,13, 0 }, {  0,60,13, 0 }, {  0,60,13, 0 }, {  0,60,13, 0 },
-        {  0,60,13, 0 }, {  0,60,13, 0 }, {  0,60,13, 0 }, {  0,60,13, 0 },
-        {  0,60,13, 0 }, {  0,60,13, 0 }, {  0,60,13, 0 }, {  0,60,13, 0 },
-        {  0,60,13, 0 }, {  5, 0, 0, 0 }, {  4, 0, 0, 0 }, {  4, 0, 0, 0 },
-        {  5, 0, 0, 0 }, { 21, 0, 0, 0 }, { 21, 0, 0, 0 }, { 59,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, {  7,13, 0, 0 }, {  7,13, 0, 0 },
-        { 25,13, 0, 0 }, { 25,13, 0, 0 }, { 25,13, 0, 0 }, { 34,60,13, 0 },
-        {  4,13, 0, 0 }, {  4,13, 0, 0 }, {  4,13, 0, 0 }, {  4,13, 0, 0 },
-        {  5,13, 0, 0 }, { 23,13, 0, 0 }, { 23,13, 0, 0 }, { 39,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, {  7,13, 0, 0 }, { 42,13, 0, 0 },
-        { 25,13, 0, 0 }, { 25,13, 0, 0 }, { 22,13, 0, 0 }, { 31,60,13, 0 },
-        { 31,60,13, 0 }, { 39,60, 0, 0 }, { 39,60, 0, 0 }, { 39,60, 0, 0 },
-        { 39,60, 0, 0 }, {  7,60, 0, 0 }, {  7,60, 0, 0 }, { 42,60, 0, 0 },
-        {  0,60, 0, 0 }, { 25,60, 0, 0 }, { 22,60, 0, 0 }, { 22,60, 0, 0 },
-        { 22,60, 0, 0 }, { 28,60, 0, 0 }, { 34,60, 0, 0 }, { 31,16,13, 0 } },
-
-      { { 55, 0,13, 0 }, { 55, 0,13, 0 }, { 55, 0,13, 0 }, { 55, 0,13, 0 },
-        { 55, 0,13, 0 }, { 55, 0,13, 0 }, { 55, 0,13, 0 }, { 55, 0,13, 0 },
-        { 55, 0,13, 0 }, { 55, 0,13, 0 }, { 55, 0,13, 0 }, { 55, 0,13, 0 },
-        { 55, 0,13, 0 }, { 55, 0,13, 0 }, { 55, 0,13, 0 }, { 55, 0,13, 0 },
-        { 55, 0,13, 0 }, { 55, 0, 0, 0 }, { 40, 0, 0, 0 }, {  8, 0, 0, 0 },
-        {  9, 0, 0, 0 }, { 49, 0, 0, 0 }, { 49, 0, 0, 0 }, { 52, 0, 0, 0 },
-        { 17, 0, 0, 0 }, { 17, 0, 0, 0 }, { 17, 0, 0, 0 }, {  4,13, 0, 0 },
-        {  0,13, 0, 0 }, { 20,13, 0, 0 }, { 17, 0, 0, 0 }, { 60,13,60,13 },
-        { 40, 0, 0,13 }, { 40, 0, 0, 0 }, {  8, 0, 0, 0 }, { 43, 0, 0, 0 },
-        { 27, 0, 0, 0 }, { 49, 0, 0, 0 }, { 49, 0, 0, 0 }, { 14, 0, 0, 0 },
-        { 17, 0, 0, 0 }, { 17, 0, 0, 0 }, { 17, 0, 0, 0 }, { 36, 0, 0, 0 },
-        { 42,13, 0, 0 }, { 42,13, 0, 0 }, { 17, 0, 0, 0 }, { 57,60,13, 0 },
-        { 57, 0,13, 0 }, { 40, 0, 0, 0 }, {  8, 0, 0, 0 }, { 26, 0, 0, 0 },
-        { 27, 0, 0, 0 }, { 49, 0, 0, 0 }, { 12, 0, 0, 0 }, { 14, 0, 0, 0 },
-        { 17, 0, 0, 0 }, { 17, 0, 0, 0 }, { 17, 0, 0, 0 }, { 36, 0, 0, 0 },
-        {  0, 0,13, 0 }, { 38, 0,13, 0 }, { 36,13, 0, 0 }, {  1,60, 0, 0 },
-        {  8,60, 0, 0 }, {  8, 0, 0, 0 }, { 43, 0, 0, 0 }, {  9, 0, 0, 0 },
-        { 11, 0, 0, 0 }, { 49, 0, 0, 0 }, { 12, 0, 0, 0 }, { 14, 0, 0, 0 },
-        { 14, 0,13, 0 }, { 33, 0,13, 0 }, { 50, 0,13, 0 }, { 50, 0, 0, 0 },
-        { 50, 0,13, 0 }, { 61, 0,13, 0 }, { 36,13, 0, 0 }, { 39,60, 0, 0 },
-        {  8,60, 0, 0 }, {  8, 0, 0, 0 }, { 43, 0, 0, 0 }, { 46, 0, 0, 0 },
-        { 49, 0, 0, 0 }, { 52, 0, 0, 0 }, { 30, 0, 0, 0 }, { 14, 0, 0, 0 },
-        { 14, 0,13, 0 }, { 33, 0,13, 0 }, { 50, 0,13, 0 }, { 50, 0,13, 0 },
-        { 50,13,13, 0 }, { 50,13, 0, 0 }, { 18,13,13, 0 }, { 25,60,13, 0 },
-        {  8,60,13,13 }, {  8, 0, 0,13 }, { 43, 0, 0,13 }, { 46, 0, 0,13 },
-        { 49, 0, 0,13 }, { 52, 0, 0, 0 }, { 30, 0, 0, 0 }, { 14, 0, 0, 0 },
-        { 14, 0, 0, 0 }, { 18, 0,60, 0 }, {  5, 0, 0,13 }, {  5, 0, 0,13 },
-        {  5, 0, 0,13 }, { 61,13, 0,13 }, { 18,13,13, 0 }, { 23,13,60, 0 },
-        { 43,13, 0,13 }, { 43, 0, 0,13 }, { 43, 0, 0,13 }, {  9, 0, 0,13 },
-        { 49, 0, 0,13 }, { 52, 0, 0, 0 }, {  3, 0, 0, 0 }, { 14, 0, 0, 0 },
-        { 14, 0, 0, 0 }, { 50, 0, 0, 0 }, { 50,13,13, 0 }, { 50,13,13, 0 },
-        { 50,13,13, 0 }, { 61, 0, 0, 0 }, { 17,13,13, 0 }, { 24,60,13, 0 },
-        { 43,60,13, 0 }, { 43,60,13, 0 }, { 43,60,13, 0 }, { 43,60,13, 0 },
-        { 43,60,13, 0 }, { 43,60,13, 0 }, { 43,60,13, 0 }, { 43,60,13, 0 },
-        { 43,60,13, 0 }, { 43,60,13, 0 }, { 43,60,13, 0 }, { 43,60,13, 0 },
-        { 43,60,13, 0 }, { 43,60,13, 0 }, { 43,60,13, 0 }, { 43,60,13, 0 },
-        { 43,60,13, 0 }, { 43, 0, 0, 0 }, { 43, 0,19, 0 }, {  9, 0, 0, 0 },
-        { 11, 0, 0, 0 }, { 52, 0, 0, 0 }, { 52, 0, 0, 0 }, { 14, 0, 0, 0 },
-        { 14, 0, 0, 0 }, { 17, 0, 0, 0 }, { 61,13, 0, 0 }, { 61,13, 0, 0 },
-        { 61,13, 0, 0 }, { 54, 0, 0, 0 }, { 17, 0,13,13 }, { 39,13,13, 0 },
-        { 45,13,13, 0 }, { 45,13,13, 0 }, { 45,13,13, 0 }, { 45,13,13, 0 },
-        { 45,13,13, 0 }, { 45,13,13, 0 }, { 45,13,13, 0 }, { 45,13,13, 0 },
-        { 45,13,13, 0 }, { 45,13,13, 0 }, { 45,13,13, 0 }, { 45,13,13, 0 },
-        { 45,13,13, 0 }, { 45,13,13, 0 }, { 45,13,13, 0 }, { 45,13,13, 0 },
-        { 45,13,13, 0 }, { 45, 0,13, 0 }, { 44, 0,13, 0 }, { 27, 0, 0, 0 },
-        { 29, 0, 0, 0 }, { 52, 0, 0, 0 }, { 48, 0, 0, 0 }, { 52, 0, 0, 0 },
-        { 52, 0, 0, 0 }, { 17, 0, 0, 0 }, { 17, 0, 0, 0 }, { 17, 0,19, 0 },
-        { 17, 0,13, 0 }, {  2, 0,13, 0 }, { 17, 0,13, 0 }, {  7,13, 0, 0 },
-        { 27, 0, 0,13 }, { 27, 0, 0,13 }, { 27, 0, 0,13 }, { 27, 0, 0,13 },
-        { 27, 0, 0,13 }, { 27, 0, 0,13 }, { 27, 0, 0,13 }, { 27, 0, 0,13 },
-        { 27, 0, 0,13 }, { 27, 0, 0,13 }, { 27, 0, 0,13 }, { 27, 0, 0,13 },
-        { 27, 0, 0,13 }, { 27, 0, 0,13 }, { 27, 0, 0,13 }, { 27, 0, 0,13 },
-        { 27, 0, 0,13 }, { 27, 0, 0,13 }, {  9, 0, 0,13 }, { 27, 0, 0,13 },
-        { 27, 0, 0,13 }, { 12, 0, 0,13 }, { 52, 0, 0,13 }, { 14, 0, 0,13 },
-        { 14, 0, 0,13 }, { 58, 0, 0,13 }, { 41, 0, 0,13 }, { 41, 0, 0,13 },
-        { 41, 0, 0,13 }, {  6, 0, 0,13 }, { 17,60, 0,13 }, { 37, 0,19,13 },
-        {  9, 0, 0,13 }, {  9,16, 0,13 }, {  9, 0, 0,13 }, { 27, 0, 0,13 },
-        { 11, 0, 0,13 }, { 49, 0, 0, 0 }, { 12, 0, 0, 0 }, { 52, 0, 0, 0 },
-        { 14, 0, 0, 0 }, { 14, 0, 0, 0 }, { 14, 0, 0, 0 }, { 50, 0, 0, 0 },
-        {  0, 0, 0,13 }, { 53, 0, 0,13 }, { 17, 0, 0,13 }, { 28, 0,13, 0 },
-        { 52, 0,13, 0 }, { 52, 0,13, 0 }, { 49, 0,13, 0 }, { 52, 0, 0, 0 },
-        { 12, 0, 0, 0 }, { 52, 0, 0, 0 }, { 30, 0, 0, 0 }, { 14, 0, 0, 0 },
-        { 14, 0, 0, 0 }, { 17, 0, 0, 0 }, {  2, 0, 0, 0 }, {  2, 0, 0, 0 },
-        {  2, 0, 0, 0 }, { 38, 0, 0, 0 }, { 38, 0, 0, 0 }, { 34, 0, 0, 0 } } },
-
-    { { { 31,16,60,13 }, { 34,16,13, 0 }, { 34,16,13, 0 }, { 31,16,13, 0 },
-        { 31,16,13, 0 }, { 31,16,13, 0 }, { 31,16,13, 0 }, { 19,16,60, 0 },
-        { 19,16,60, 0 }, { 19,16,60, 0 }, { 19,16,60, 0 }, { 19,16,60, 0 },
-        { 19,16,60, 0 }, { 19,16,60, 0 }, { 31,16,60,13 }, { 19,37,16,60 },
-        { 44, 0, 0,60 }, { 44, 0, 0, 0 }, { 62, 0, 0, 0 }, { 30, 0, 0, 0 },
-        { 32, 0, 0, 0 }, { 58, 0, 0, 0 }, { 35, 0, 0, 0 }, { 36, 0, 0, 0 },
-        { 36, 0, 0, 0 }, { 38,13, 0, 0 }, {  0,13, 0, 0 }, { 59,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, { 39,13, 0, 0 }, { 34,60,13, 0 },
-        { 34, 0,13, 0 }, { 45, 0, 0, 0 }, { 47, 0, 0, 0 }, { 48, 0, 0, 0 },
-        { 33, 0, 0, 0 }, { 35, 0, 0, 0 }, { 35, 0, 0, 0 }, { 36, 0, 0, 0 },
-        { 38,13, 0, 0 }, { 38,13, 0, 0 }, { 38,13, 0, 0 }, { 59,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, { 39,13, 0, 0 }, { 34,60,13, 0 },
-        { 34, 0,13, 0 }, { 62, 0, 0, 0 }, { 30, 0, 0, 0 }, { 15, 0, 0, 0 },
-        { 50, 0, 0, 0 }, { 53, 0, 0, 0 }, { 53, 0, 0, 0 }, { 54,13, 0, 0 },
-        { 21,13, 0, 0 }, { 21,13, 0, 0 }, { 21,13, 0, 0 }, { 59,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, {  7,13, 0, 0 }, { 34,60,13, 0 },
-        { 30, 0,13, 0 }, { 30, 0, 0, 0 }, { 48, 0, 0, 0 }, { 33, 0, 0, 0 },
-        { 58, 0, 0, 0 }, { 18, 0, 0, 0 }, { 18, 0, 0, 0 }, { 56,13, 0, 0 },
-        { 23,13, 0, 0 }, { 23,13, 0, 0 }, { 23,13, 0, 0 }, { 59,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, { 24,13, 0, 0 }, { 34,60,13, 0 },
-        { 34, 0,13, 0 }, {  6, 0, 0, 0 }, {  6, 0, 0, 0 }, { 58, 0, 0, 0 },
-        { 53, 0, 0, 0 }, { 54, 0, 0, 0 }, { 54, 0, 0, 0 }, { 21,13, 0, 0 },
-        { 59,13, 0, 0 }, { 59,13, 0, 0 }, { 59,13, 0, 0 }, { 39,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, { 42,60, 0, 0 }, { 34,16,13, 0 },
-        {  6, 0,13, 0 }, {  6, 0, 0, 0 }, { 33, 0, 0, 0 }, { 58, 0, 0, 0 },
-        { 53, 0, 0, 0 }, { 54, 0, 0, 0 }, { 61, 0, 0, 0 }, { 21,13, 0, 0 },
-        { 59,13, 0, 0 }, { 59,13, 0, 0 }, { 59,13, 0, 0 }, { 39,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, { 42,60, 0, 0 }, { 34,16,13, 0 },
-        { 34, 0,13, 0 }, { 51, 0, 0, 0 }, { 51, 0, 0, 0 }, { 53, 0, 0, 0 },
-        { 54, 0, 0, 0 }, { 56,13, 0, 0 }, { 56,13, 0, 0 }, { 57,13, 0, 0 },
-        { 39,13, 0, 0 }, { 39,13, 0, 0 }, { 39,13, 0, 0 }, {  7,13, 0, 0 },
-        { 42,13, 0, 0 }, { 42,13, 0, 0 }, { 25,60, 0, 0 }, { 31,16,13, 0 },
-        { 31, 0,13, 0 }, { 31, 0,13, 0 }, { 31, 0,13, 0 }, { 31, 0,13, 0 },
-        { 31, 0,13, 0 }, { 31, 0,13, 0 }, { 31, 0,13, 0 }, { 31, 0,13, 0 },
-        { 31, 0,13, 0 }, { 31, 0,13, 0 }, { 31, 0,13, 0 }, { 31, 0,13, 0 },
-        { 31, 0,13, 0 }, { 31, 0,13, 0 }, { 31, 0,13, 0 }, { 31, 0,13, 0 },
-        { 31, 0,13, 0 }, {  4, 0, 0, 0 }, {  4, 0, 0, 0 }, {  4, 0, 0, 0 },
-        {  5,13, 0, 0 }, { 23,13, 0, 0 }, { 23,13, 0, 0 }, { 39,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, {  7,13, 0, 0 }, { 42,13, 0, 0 },
-        { 25,13, 0, 0 }, { 25,13, 0, 0 }, { 22,60, 0, 0 }, { 31,16,60, 0 },
-        { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 },
-        { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 },
-        { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 },
-        { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 },
-        { 31,13, 0, 0 }, {  5,13, 0, 0 }, {  5,13, 0, 0 }, {  5,13, 0, 0 },
-        {  5,13, 0, 0 }, { 57,13, 0, 0 }, { 57,13, 0, 0 }, { 39,13, 0, 0 },
-        { 24,13, 0, 0 }, { 24,13, 0, 0 }, { 24,13, 0, 0 }, { 42,13, 0, 0 },
-        { 22,13, 0, 0 }, { 22,60, 0, 0 }, { 28,60,13, 0 }, { 31,16,60, 0 },
-        { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 },
-        { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 },
-        { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 },
-        { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 }, { 31,13, 0, 0 },
-        { 31,13, 0, 0 }, { 41,13, 0, 0 }, { 41,13, 0, 0 }, { 41,13, 0, 0 },
-        { 41,13, 0, 0 }, { 39,13, 0, 0 }, { 39,13, 0, 0 }, {  7,13, 0, 0 },
-        { 42,13, 0, 0 }, { 42,13, 0, 0 }, { 42,13, 0, 0 }, { 25,13, 0, 0 },
-        { 28,13, 0, 0 }, { 28,60, 0, 0 }, { 28,60,13, 0 }, { 31,16,60,13 },
-        { 31,13, 0, 0 }, { 41,13, 0, 0 }, { 41,13, 0, 0 }, { 41,13, 0, 0 },
-        { 41,13, 0, 0 }, { 39,13, 0, 0 }, { 39,13, 0, 0 }, { 24,13, 0, 0 },
-        { 25,60, 0, 0 }, { 25,60, 0, 0 }, { 25,60, 0, 0 }, { 22,60, 0, 0 },
-        { 28,60, 0, 0 }, { 28,60, 0, 0 }, { 34,60,13, 0 }, { 31,16,60,13 },
-        { 31,60,13,13 }, { 10,60,13, 0 }, { 10,60,13, 0 }, { 10,60,13, 0 },
-        { 10,60,13, 0 }, { 10,60,13, 0 }, { 10,60,13, 0 }, { 28,60,13, 0 },
-        { 34,60,13, 0 }, { 34,60,13, 0 }, { 34,16,13, 0 }, { 34,16,13, 0 },
-        { 34,16,60, 0 }, { 34,16,60, 0 }, { 31,16,60, 0 }, { 19,37,16,13 } },
-
-      { {  8, 0,16, 0 }, {  8, 0,16, 0 }, {  8, 0,16, 0 }, {  8, 0,16, 0 },
-        {  8, 0,16, 0 }, {  8, 0,16, 0 }, {  8, 0,16, 0 }, {  8, 0,16, 0 },
-        {  8, 0,16, 0 }, {  8, 0,16, 0 }, {  8, 0,16, 0 }, {  8, 0,16, 0 },
-        {  8, 0,16, 0 }, {  8, 0,16, 0 }, {  8, 0,16, 0 }, {  8, 0,16, 0 },
-        {  8, 0,16, 0 }, {  8, 0, 0, 0 }, {  9, 0, 0, 0 }, { 11, 0, 0, 0 },
-        { 47, 0, 0, 0 }, { 32, 0, 0, 0 }, { 50, 0, 0, 0 }, { 18, 0, 0, 0 },
-        { 18, 0, 0, 0 }, { 20, 0, 0, 0 }, { 21, 0, 0, 0 }, { 21, 0, 0, 0 },
-        { 21,13, 0, 0 }, { 39,13, 0, 0 }, { 59,13, 0, 0 }, { 34,16,60, 0 },
-        { 26, 0, 0, 0 }, { 26, 0, 0, 0 }, { 27, 0, 0, 0 }, { 29, 0, 0, 0 },
-        { 30, 0, 0, 0 }, { 33, 0, 0, 0 }, { 50, 0, 0, 0 }, { 18, 0, 0, 0 },
-        { 18, 0, 0, 0 }, { 20, 0, 0, 0 }, { 57, 0, 0, 0 }, { 57,13, 0, 0 },
-        { 57,13, 0, 0 }, { 59,13, 0, 0 }, { 59,13, 0, 0 }, { 34,16,60, 0 },
-        { 27, 0, 0, 0 }, { 27, 0, 0, 0 }, { 11, 0, 0, 0 }, { 12, 0, 0, 0 },
-        { 48, 0, 0, 0 }, { 50, 0, 0, 0 }, { 58, 0, 0, 0 }, { 61, 0, 0, 0 },
-        { 61, 0, 0, 0 }, { 56, 0, 0, 0 }, { 57,13, 0, 0 }, { 57,13, 0, 0 },
-        { 57,13, 0, 0 }, { 59,13, 0, 0 }, { 39,13, 0, 0 }, { 34,16,60, 0 },
-        { 45, 0, 0, 0 }, { 45, 0, 0, 0 }, { 12, 0, 0, 0 }, { 30, 0, 0, 0 },
-        { 32, 0, 0, 0 }, {  2, 0, 0, 0 }, {  2, 0, 0, 0 }, { 61, 0, 0, 0 },
-        { 38, 0, 0, 0 }, { 38, 0, 0, 0 }, { 38,13, 0, 0 }, { 57,13, 0, 0 },
-        {  0,13, 0, 0 }, { 59,13, 0, 0 }, { 39,13, 0, 0 }, { 34,16,60, 0 },
-        { 63, 0, 0, 0 }, { 63, 0, 0, 0 }, {  3, 0, 0, 0 }, { 32, 0, 0, 0 },
-        { 58, 0, 0, 0 }, { 18, 0, 0, 0 }, { 18, 0, 0, 0 }, { 20, 0, 0, 0 },
-        { 21, 0, 0, 0 }, { 21, 0, 0, 0 }, { 21,13, 0, 0 }, { 59,13, 0, 0 },
-        { 39,13, 0, 0 }, { 39,13, 0, 0 }, {  7,13,13, 0 }, { 31,16,60, 0 },
-        { 31, 0, 0, 0 }, {  3, 0, 0, 0 }, {  3, 0, 0, 0 }, { 33, 0, 0, 0 },
-        { 58, 0, 0, 0 }, { 18, 0, 0, 0 }, { 18, 0, 0, 0 }, { 20, 0, 0, 0 },
-        { 21, 0, 0, 0 }, { 21, 0, 0, 0 }, { 21,13, 0, 0 }, { 59,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, {  7,13,13, 0 }, { 31,16,60, 0 },
-        {  6, 0, 0, 0 }, {  6, 0, 0, 0 }, { 51, 0, 0, 0 }, { 51, 0, 0, 0 },
-        { 53, 0, 0, 0 }, { 54, 0, 0, 0 }, { 54, 0, 0, 0 }, { 38, 0, 0, 0 },
-        { 57,13, 0, 0 }, { 57,13, 0, 0 }, { 57,13, 0, 0 }, { 39,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, { 42,60,13, 0 }, { 31,16,60, 0 },
-        { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 },
-        { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 },
-        { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 },
-        { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 },
-        { 31, 0, 0, 0 }, { 51, 0, 0, 0 }, { 53, 0, 0, 0 }, { 53, 0, 0, 0 },
-        { 54, 0, 0, 0 }, { 56, 0, 0, 0 }, { 56, 0, 0, 0 }, { 57,13, 0, 0 },
-        { 59,13, 0, 0 }, { 59,13, 0, 0 }, { 59,13, 0, 0 }, {  7,13, 0, 0 },
-        { 24,13, 0, 0 }, { 24,13, 0, 0 }, { 25,60,13, 0 }, { 31,16,60, 0 },
-        { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 },
-        { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 },
-        { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 },
-        { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 }, { 31, 0, 0, 0 },
-        { 31, 0, 0, 0 }, {  4, 0, 0, 0 }, {  4, 0, 0, 0 }, {  4, 0, 0, 0 },
-        { 54, 0, 0, 0 }, { 21,13, 0, 0 }, { 21, 0, 0, 0 }, { 57,13, 0, 0 },
-        { 39,13, 0, 0 }, { 39,13, 0, 0 }, { 39,13, 0, 0 }, {  7,13, 0, 0 },
-        { 42,13,13, 0 }, { 42,13,13, 0 }, { 22,60,13, 0 }, { 31,16,60, 0 },
-        { 31,16, 0, 0 }, { 31,16, 0, 0 }, { 31,16, 0, 0 }, { 31,16, 0, 0 },
-        { 31,16, 0, 0 }, { 31,16, 0, 0 }, { 31,16, 0, 0 }, { 31,16, 0, 0 },
-        { 31,16, 0, 0 }, { 31,16, 0, 0 }, { 31,16, 0, 0 }, { 31,16, 0, 0 },
-        { 31,16, 0, 0 }, { 31,16, 0, 0 }, { 31,16, 0, 0 }, { 31,16, 0, 0 },
-        { 31,16, 0, 0 }, {  5, 0, 0, 0 }, {  5, 0, 0, 0 }, {  5, 0, 0, 0 },
-        {  5,13, 0, 0 }, { 23,13, 0, 0 }, { 23,13, 0, 0 }, { 59,13, 0, 0 },
-        {  7,13, 0, 0 }, {  7,13, 0, 0 }, {  7,13,13, 0 }, { 42,13,13, 0 },
-        { 22,60,13, 0 }, { 22,60,13, 0 }, { 28,60,13, 0 }, { 31,16,60, 0 },
-        { 31,13, 0, 0 }, {  4,13, 0, 0 }, {  4,13, 0, 0 }, {  4,13, 0, 0 },
-        {  5,13, 0, 0 }, { 23,13, 0, 0 }, { 23,13, 0, 0 }, { 39,13,13, 0 },
-        { 24,60,13, 0 }, { 24,60,13, 0 }, { 24,60,13, 0 }, { 25,60,13, 0 },
-        { 28,60,13, 0 }, { 28,60,13, 0 }, { 34,16,13, 0 }, { 31,16,60, 0 },
-        { 31,16,13, 0 }, { 10,16,13, 0 }, { 10,16,13, 0 }, { 10,16,13, 0 },
-        { 10,16,13, 0 }, { 10,16,60, 0 }, { 10,16,60, 0 }, { 28,16,60, 0 },
-        { 34,16,60, 0 }, { 34,16,60, 0 }, { 34,16,60, 0 }, { 31,16,60, 0 },
-        { 31,16,60, 0 }, { 31,16,60, 0 }, { 31,16,60, 0 }, { 19,37,60, 0 } } }
-};
-
-const struct lc3_ac_model lc3_spectrum_models[] = {
-
-    { { {    0,    1 }, {    1,    1 }, {    2,  175 }, {  177,   48 },
-        {  225,    1 }, {  226,    1 }, {  227,  109 }, {  336,   36 },
-        {  372,  171 }, {  543,  109 }, {  652,   47 }, {  699,   20 },
-        {  719,   49 }, {  768,   36 }, {  804,   20 }, {  824,   10 },
-        {  834,  190 } } },
-
-    { { {    0,   18 }, {   18,   26 }, {   44,   17 }, {   61,   10 },
-        {   71,   27 }, {   98,   37 }, {  135,   24 }, {  159,   16 },
-        {  175,   22 }, {  197,   32 }, {  229,   22 }, {  251,   14 },
-        {  265,   17 }, {  282,   26 }, {  308,   20 }, {  328,   13 },
-        {  341,  683 } } },
-
-    { { {    0,   71 }, {   71,   92 }, {  163,   49 }, {  212,   25 },
-        {  237,   81 }, {  318,  102 }, {  420,   61 }, {  481,   33 },
-        {  514,   42 }, {  556,   57 }, {  613,   39 }, {  652,   23 },
-        {  675,   22 }, {  697,   30 }, {  727,   22 }, {  749,   15 },
-        {  764,  260 } } },
-
-    { { {    0,  160 }, {  160,  130 }, {  290,   46 }, {  336,   18 },
-        {  354,  121 }, {  475,  123 }, {  598,   55 }, {  653,   24 },
-        {  677,   45 }, {  722,   55 }, {  777,   31 }, {  808,   15 },
-        {  823,   19 }, {  842,   24 }, {  866,   15 }, {  881,    9 },
-        {  890,  134 } } },
-
-    { { {    0,   71 }, {   71,   73 }, {  144,   33 }, {  177,   18 },
-        {  195,   71 }, {  266,   76 }, {  342,   43 }, {  385,   26 },
-        {  411,   34 }, {  445,   44 }, {  489,   30 }, {  519,   20 },
-        {  539,   20 }, {  559,   27 }, {  586,   21 }, {  607,   15 },
-        {  622,  402 } } },
-
-    { { {    0,   48 }, {   48,   60 }, {  108,   32 }, {  140,   19 },
-        {  159,   58 }, {  217,   68 }, {  285,   42 }, {  327,   27 },
-        {  354,   31 }, {  385,   42 }, {  427,   30 }, {  457,   21 },
-        {  478,   19 }, {  497,   27 }, {  524,   21 }, {  545,   16 },
-        {  561,  463 } } },
-
-    { { {    0,  138 }, {  138,  109 }, {  247,   43 }, {  290,   18 },
-        {  308,  111 }, {  419,  112 }, {  531,   53 }, {  584,   25 },
-        {  609,   46 }, {  655,   55 }, {  710,   32 }, {  742,   17 },
-        {  759,   21 }, {  780,   27 }, {  807,   18 }, {  825,   11 },
-        {  836,  188 } } },
-
-    { { {    0,   16 }, {   16,   24 }, {   40,   22 }, {   62,   17 },
-        {   79,   24 }, {  103,   36 }, {  139,   31 }, {  170,   25 },
-        {  195,   20 }, {  215,   30 }, {  245,   25 }, {  270,   20 },
-        {  290,   15 }, {  305,   22 }, {  327,   19 }, {  346,   16 },
-        {  362,  662 } } },
-
-    { { {    0,  579 }, {  579,  150 }, {  729,   12 }, {  741,    2 },
-        {  743,  154 }, {  897,   73 }, {  970,   10 }, {  980,    2 },
-        {  982,   14 }, {  996,   11 }, { 1007,    3 }, { 1010,    1 },
-        { 1011,    3 }, { 1014,    3 }, { 1017,    1 }, { 1018,    1 },
-        { 1019,    5 } } },
-
-    { { {    0,  398 }, {  398,  184 }, {  582,   25 }, {  607,    5 },
-        {  612,  176 }, {  788,  114 }, {  902,   23 }, {  925,    6 },
-        {  931,   25 }, {  956,   23 }, {  979,    8 }, {  987,    3 },
-        {  990,    6 }, {  996,    6 }, { 1002,    3 }, { 1005,    2 },
-        { 1007,   17 } } },
-
-    { { {    0,   13 }, {   13,   21 }, {   34,   18 }, {   52,   11 },
-        {   63,   20 }, {   83,   29 }, {  112,   22 }, {  134,   15 },
-        {  149,   14 }, {  163,   20 }, {  183,   16 }, {  199,   12 },
-        {  211,   10 }, {  221,   14 }, {  235,   12 }, {  247,   10 },
-        {  257,  767 } } },
-
-    { { {    0,  281 }, {  281,  183 }, {  464,   37 }, {  501,    9 },
-        {  510,  171 }, {  681,  139 }, {  820,   37 }, {  857,   10 },
-        {  867,   35 }, {  902,   36 }, {  938,   15 }, {  953,    6 },
-        {  959,    9 }, {  968,   10 }, {  978,    6 }, {  984,    3 },
-        {  987,   37 } } },
-
-    { { {    0,  198 }, {  198,  164 }, {  362,   46 }, {  408,   13 },
-        {  421,  154 }, {  575,  147 }, {  722,   51 }, {  773,   16 },
-        {  789,   43 }, {  832,   49 }, {  881,   24 }, {  905,   10 },
-        {  915,   13 }, {  928,   16 }, {  944,   10 }, {  954,    5 },
-        {  959,   65 } } },
-
-    { { {    0,    1 }, {    1,    1 }, {    2,   93 }, {   95,   44 },
-        {  139,    1 }, {  140,    1 }, {  141,   72 }, {  213,   38 },
-        {  251,   86 }, {  337,   70 }, {  407,   43 }, {  450,   25 },
-        {  475,   40 }, {  515,   36 }, {  551,   25 }, {  576,   16 },
-        {  592,  432 } } },
-
-    { { {    0,  133 }, {  133,  141 }, {  274,   64 }, {  338,   28 },
-        {  366,  117 }, {  483,  122 }, {  605,   59 }, {  664,   27 },
-        {  691,   39 }, {  730,   48 }, {  778,   29 }, {  807,   15 },
-        {  822,   15 }, {  837,   20 }, {  857,   13 }, {  870,    8 },
-        {  878,  146 } } },
-
-    { { {    0,  128 }, {  128,  125 }, {  253,   49 }, {  302,   18 },
-        {  320,  123 }, {  443,  134 }, {  577,   59 }, {  636,   23 },
-        {  659,   49 }, {  708,   59 }, {  767,   32 }, {  799,   15 },
-        {  814,   19 }, {  833,   24 }, {  857,   15 }, {  872,    9 },
-        {  881,  143 } } },
-
-    { { {    0,    1 }, {    1,    1 }, {    2,   23 }, {   25,   17 },
-        {   42,    1 }, {   43,    1 }, {   44,   23 }, {   67,   18 },
-        {   85,   20 }, {  105,   21 }, {  126,   18 }, {  144,   15 },
-        {  159,   15 }, {  174,   17 }, {  191,   14 }, {  205,   12 },
-        {  217,  807 } } },
-
-    { { {    0,   70 }, {   70,   96 }, {  166,   63 }, {  229,   38 },
-        {  267,   89 }, {  356,  112 }, {  468,   65 }, {  533,   36 },
-        {  569,   37 }, {  606,   47 }, {  653,   32 }, {  685,   20 },
-        {  705,   17 }, {  722,   23 }, {  745,   17 }, {  762,   12 },
-        {  774,  250 } } },
-
-    { { {    0,   55 }, {   55,   75 }, {  130,   45 }, {  175,   25 },
-        {  200,   68 }, {  268,   90 }, {  358,   58 }, {  416,   33 },
-        {  449,   39 }, {  488,   54 }, {  542,   39 }, {  581,   25 },
-        {  606,   22 }, {  628,   31 }, {  659,   24 }, {  683,   16 },
-        {  699,  325 } } },
-
-    { { {    0,    1 }, {    1,    2 }, {    3,    2 }, {    5,    2 },
-        {    7,    2 }, {    9,    2 }, {   11,    2 }, {   13,    2 },
-        {   15,    2 }, {   17,    2 }, {   19,    2 }, {   21,    2 },
-        {   23,    2 }, {   25,    2 }, {   27,    2 }, {   29,    2 },
-        {   31,  993 } } },
-
-    { { {    0,   34 }, {   34,   51 }, {   85,   38 }, {  123,   24 },
-        {  147,   49 }, {  196,   69 }, {  265,   52 }, {  317,   35 },
-        {  352,   34 }, {  386,   47 }, {  433,   37 }, {  470,   27 },
-        {  497,   21 }, {  518,   31 }, {  549,   25 }, {  574,   19 },
-        {  593,  431 } } },
-
-    { { {    0,   30 }, {   30,   43 }, {   73,   32 }, {  105,   22 },
-        {  127,   43 }, {  170,   59 }, {  229,   45 }, {  274,   31 },
-        {  305,   30 }, {  335,   42 }, {  377,   34 }, {  411,   25 },
-        {  436,   19 }, {  455,   28 }, {  483,   23 }, {  506,   18 },
-        {  524,  500 } } },
-
-    { { {    0,    9 }, {    9,   15 }, {   24,   14 }, {   38,   13 },
-        {   51,   14 }, {   65,   22 }, {   87,   21 }, {  108,   18 },
-        {  126,   13 }, {  139,   20 }, {  159,   18 }, {  177,   16 },
-        {  193,   11 }, {  204,   17 }, {  221,   15 }, {  236,   14 },
-        {  250,  774 } } },
-
-    { { {    0,   30 }, {   30,   44 }, {   74,   31 }, {  105,   20 },
-        {  125,   41 }, {  166,   58 }, {  224,   42 }, {  266,   28 },
-        {  294,   28 }, {  322,   39 }, {  361,   30 }, {  391,   22 },
-        {  413,   18 }, {  431,   26 }, {  457,   21 }, {  478,   16 },
-        {  494,  530 } } },
-
-    { { {    0,   15 }, {   15,   23 }, {   38,   20 }, {   58,   15 },
-        {   73,   22 }, {   95,   33 }, {  128,   28 }, {  156,   22 },
-        {  178,   18 }, {  196,   26 }, {  222,   23 }, {  245,   18 },
-        {  263,   13 }, {  276,   20 }, {  296,   18 }, {  314,   15 },
-        {  329,  695 } } },
-
-    { { {    0,   11 }, {   11,   17 }, {   28,   16 }, {   44,   13 },
-        {   57,   17 }, {   74,   26 }, {  100,   23 }, {  123,   19 },
-        {  142,   15 }, {  157,   22 }, {  179,   20 }, {  199,   17 },
-        {  216,   12 }, {  228,   18 }, {  246,   16 }, {  262,   14 },
-        {  276,  748 } } },
-
-    { { {    0,  448 }, {  448,  171 }, {  619,   20 }, {  639,    4 },
-        {  643,  178 }, {  821,  105 }, {  926,   18 }, {  944,    4 },
-        {  948,   23 }, {  971,   20 }, {  991,    7 }, {  998,    2 },
-        { 1000,    5 }, { 1005,    5 }, { 1010,    2 }, { 1012,    1 },
-        { 1013,   11 } } },
-
-    { { {    0,  332 }, {  332,  188 }, {  520,   29 }, {  549,    6 },
-        {  555,  186 }, {  741,  133 }, {  874,   29 }, {  903,    7 },
-        {  910,   30 }, {  940,   30 }, {  970,   11 }, {  981,    4 },
-        {  985,    6 }, {  991,    7 }, {  998,    4 }, { 1002,    2 },
-        { 1004,   20 } } },
-
-    { { {    0,    8 }, {    8,   13 }, {   21,   13 }, {   34,   11 },
-        {   45,   13 }, {   58,   20 }, {   78,   18 }, {   96,   16 },
-        {  112,   12 }, {  124,   17 }, {  141,   16 }, {  157,   13 },
-        {  170,   10 }, {  180,   14 }, {  194,   13 }, {  207,   12 },
-        {  219,  805 } } },
-
-    { { {    0,  239 }, {  239,  176 }, {  415,   42 }, {  457,   11 },
-        {  468,  163 }, {  631,  145 }, {  776,   44 }, {  820,   13 },
-        {  833,   39 }, {  872,   42 }, {  914,   19 }, {  933,    7 },
-        {  940,   11 }, {  951,   13 }, {  964,    7 }, {  971,    4 },
-        {  975,   49 } } },
-
-    { { {    0,  165 }, {  165,  145 }, {  310,   49 }, {  359,   16 },
-        {  375,  138 }, {  513,  139 }, {  652,   55 }, {  707,   20 },
-        {  727,   47 }, {  774,   54 }, {  828,   28 }, {  856,   12 },
-        {  868,   16 }, {  884,   20 }, {  904,   12 }, {  916,    7 },
-        {  923,  101 } } },
-
-    { { {    0,    3 }, {    3,    5 }, {    8,    5 }, {   13,    5 },
-        {   18,    5 }, {   23,    7 }, {   30,    7 }, {   37,    7 },
-        {   44,    4 }, {   48,    7 }, {   55,    7 }, {   62,    6 },
-        {   68,    4 }, {   72,    6 }, {   78,    6 }, {   84,    6 },
-        {   90,  934 } } },
-
-    { { {    0,  115 }, {  115,  122 }, {  237,   52 }, {  289,   22 },
-        {  311,  111 }, {  422,  125 }, {  547,   61 }, {  608,   27 },
-        {  635,   45 }, {  680,   57 }, {  737,   34 }, {  771,   17 },
-        {  788,   19 }, {  807,   25 }, {  832,   17 }, {  849,   10 },
-        {  859,  165 } } },
-
-    { { {    0,  107 }, {  107,  114 }, {  221,   51 }, {  272,   21 },
-        {  293,  106 }, {  399,  122 }, {  521,   61 }, {  582,   28 },
-        {  610,   46 }, {  656,   58 }, {  714,   35 }, {  749,   18 },
-        {  767,   20 }, {  787,   26 }, {  813,   18 }, {  831,   11 },
-        {  842,  182 } } },
-
-    { { {    0,    6 }, {    6,   10 }, {   16,   10 }, {   26,    9 },
-        {   35,   10 }, {   45,   15 }, {   60,   15 }, {   75,   14 },
-        {   89,    9 }, {   98,   14 }, {  112,   13 }, {  125,   12 },
-        {  137,    8 }, {  145,   12 }, {  157,   11 }, {  168,   10 },
-        {  178,  846 } } },
-
-    { { {    0,   72 }, {   72,   88 }, {  160,   50 }, {  210,   26 },
-        {  236,   84 }, {  320,  102 }, {  422,   60 }, {  482,   32 },
-        {  514,   41 }, {  555,   53 }, {  608,   36 }, {  644,   21 },
-        {  665,   20 }, {  685,   27 }, {  712,   20 }, {  732,   13 },
-        {  745,  279 } } },
-
-    { { {    0,   45 }, {   45,   63 }, {  108,   45 }, {  153,   30 },
-        {  183,   61 }, {  244,   83 }, {  327,   58 }, {  385,   36 },
-        {  421,   34 }, {  455,   47 }, {  502,   34 }, {  536,   23 },
-        {  559,   19 }, {  578,   27 }, {  605,   21 }, {  626,   15 },
-        {  641,  383 } } },
-
-    { { {    0,    1 }, {    1,    1 }, {    2,    7 }, {    9,    7 },
-        {   16,    1 }, {   17,    1 }, {   18,    8 }, {   26,    8 },
-        {   34,    6 }, {   40,    8 }, {   48,    7 }, {   55,    7 },
-        {   62,    6 }, {   68,    7 }, {   75,    7 }, {   82,    6 },
-        {   88,  936 } } },
-
-    { { {    0,   29 }, {   29,   44 }, {   73,   35 }, {  108,   24 },
-        {  132,   42 }, {  174,   62 }, {  236,   48 }, {  284,   34 },
-        {  318,   30 }, {  348,   43 }, {  391,   35 }, {  426,   26 },
-        {  452,   19 }, {  471,   29 }, {  500,   24 }, {  524,   19 },
-        {  543,  481 } } },
-
-    { { {    0,   20 }, {   20,   31 }, {   51,   25 }, {   76,   17 },
-        {   93,   30 }, {  123,   43 }, {  166,   34 }, {  200,   25 },
-        {  225,   22 }, {  247,   32 }, {  279,   26 }, {  305,   21 },
-        {  326,   16 }, {  342,   23 }, {  365,   20 }, {  385,   16 },
-        {  401,  623 } } },
-
-    { { {    0,  742 }, {  742,  103 }, {  845,    5 }, {  850,    1 },
-        {  851,  108 }, {  959,   38 }, {  997,    4 }, { 1001,    1 },
-        { 1002,    7 }, { 1009,    5 }, { 1014,    2 }, { 1016,    1 },
-        { 1017,    2 }, { 1019,    1 }, { 1020,    1 }, { 1021,    1 },
-        { 1022,    2 } } },
-
-    { { {    0,   42 }, {   42,   52 }, {   94,   27 }, {  121,   16 },
-        {  137,   49 }, {  186,   58 }, {  244,   36 }, {  280,   23 },
-        {  303,   27 }, {  330,   36 }, {  366,   26 }, {  392,   18 },
-        {  410,   17 }, {  427,   24 }, {  451,   19 }, {  470,   14 },
-        {  484,  540 } } },
-
-    { { {    0,   13 }, {   13,   20 }, {   33,   18 }, {   51,   15 },
-        {   66,   19 }, {   85,   29 }, {  114,   26 }, {  140,   21 },
-        {  161,   17 }, {  178,   25 }, {  203,   22 }, {  225,   18 },
-        {  243,   13 }, {  256,   19 }, {  275,   17 }, {  292,   15 },
-        {  307,  717 } } },
-
-    { { {    0,  501 }, {  501,  169 }, {  670,   19 }, {  689,    4 },
-        {  693,  155 }, {  848,   88 }, {  936,   16 }, {  952,    4 },
-        {  956,   19 }, {  975,   16 }, {  991,    6 }, {  997,    2 },
-        {  999,    5 }, { 1004,    4 }, { 1008,    2 }, { 1010,    1 },
-        { 1011,   13 } } },
-
-    { { {    0,  445 }, {  445,  136 }, {  581,   22 }, {  603,    6 },
-        {  609,  158 }, {  767,   98 }, {  865,   23 }, {  888,    7 },
-        {  895,   31 }, {  926,   28 }, {  954,   10 }, {  964,    4 },
-        {  968,    9 }, {  977,    9 }, {  986,    5 }, {  991,    2 },
-        {  993,   31 } } },
-
-    { { {    0,  285 }, {  285,  157 }, {  442,   37 }, {  479,   10 },
-        {  489,  161 }, {  650,  129 }, {  779,   39 }, {  818,   12 },
-        {  830,   40 }, {  870,   42 }, {  912,   18 }, {  930,    7 },
-        {  937,   12 }, {  949,   14 }, {  963,    8 }, {  971,    4 },
-        {  975,   49 } } },
-
-    { { {    0,  349 }, {  349,  179 }, {  528,   33 }, {  561,    8 },
-        {  569,  162 }, {  731,  121 }, {  852,   31 }, {  883,    9 },
-        {  892,   31 }, {  923,   30 }, {  953,   12 }, {  965,    5 },
-        {  970,    8 }, {  978,    9 }, {  987,    5 }, {  992,    2 },
-        {  994,   30 } } },
-
-    { { {    0,  199 }, {  199,  156 }, {  355,   47 }, {  402,   15 },
-        {  417,  146 }, {  563,  137 }, {  700,   50 }, {  750,   17 },
-        {  767,   44 }, {  811,   49 }, {  860,   24 }, {  884,   10 },
-        {  894,   15 }, {  909,   17 }, {  926,   10 }, {  936,    6 },
-        {  942,   82 } } },
-
-    { { {    0,  141 }, {  141,  134 }, {  275,   50 }, {  325,   18 },
-        {  343,  128 }, {  471,  135 }, {  606,   58 }, {  664,   22 },
-        {  686,   48 }, {  734,   57 }, {  791,   31 }, {  822,   14 },
-        {  836,   18 }, {  854,   23 }, {  877,   14 }, {  891,    8 },
-        {  899,  125 } } },
-
-    { { {    0,  243 }, {  243,  194 }, {  437,   56 }, {  493,   17 },
-        {  510,  139 }, {  649,  126 }, {  775,   45 }, {  820,   16 },
-        {  836,   33 }, {  869,   36 }, {  905,   18 }, {  923,    8 },
-        {  931,   10 }, {  941,   12 }, {  953,    7 }, {  960,    4 },
-        {  964,   60 } } },
-
-    { { {    0,   91 }, {   91,  106 }, {  197,   51 }, {  248,   23 },
-        {  271,   99 }, {  370,  117 }, {  487,   63 }, {  550,   30 },
-        {  580,   45 }, {  625,   59 }, {  684,   37 }, {  721,   20 },
-        {  741,   20 }, {  761,   27 }, {  788,   19 }, {  807,   12 },
-        {  819,  205 } } },
-
-    { { {    0,  107 }, {  107,   94 }, {  201,   41 }, {  242,   20 },
-        {  262,   92 }, {  354,   97 }, {  451,   52 }, {  503,   28 },
-        {  531,   42 }, {  573,   53 }, {  626,   34 }, {  660,   20 },
-        {  680,   21 }, {  701,   29 }, {  730,   21 }, {  751,   14 },
-        {  765,  259 } } },
-
-    { { {    0,  168 }, {  168,  171 }, {  339,   68 }, {  407,   25 },
-        {  432,  121 }, {  553,  123 }, {  676,   55 }, {  731,   24 },
-        {  755,   34 }, {  789,   41 }, {  830,   24 }, {  854,   12 },
-        {  866,   13 }, {  879,   16 }, {  895,   11 }, {  906,    6 },
-        {  912,  112 } } },
-
-    { { {    0,   67 }, {   67,   80 }, {  147,   44 }, {  191,   23 },
-        {  214,   76 }, {  290,   94 }, {  384,   57 }, {  441,   31 },
-        {  472,   41 }, {  513,   54 }, {  567,   37 }, {  604,   23 },
-        {  627,   21 }, {  648,   30 }, {  678,   22 }, {  700,   15 },
-        {  715,  309 } } },
-
-    { { {    0,   46 }, {   46,   63 }, {  109,   39 }, {  148,   23 },
-        {  171,   58 }, {  229,   78 }, {  307,   52 }, {  359,   32 },
-        {  391,   36 }, {  427,   49 }, {  476,   37 }, {  513,   24 },
-        {  537,   21 }, {  558,   30 }, {  588,   24 }, {  612,   17 },
-        {  629,  395 } } },
-
-    { { {    0,  848 }, {  848,   70 }, {  918,    2 }, {  920,    1 },
-        {  921,   75 }, {  996,   16 }, { 1012,    1 }, { 1013,    1 },
-        { 1014,    2 }, { 1016,    1 }, { 1017,    1 }, { 1018,    1 },
-        { 1019,    1 }, { 1020,    1 }, { 1021,    1 }, { 1022,    1 },
-        { 1023,    1 } } },
-
-    { { {    0,   36 }, {   36,   52 }, {   88,   35 }, {  123,   22 },
-        {  145,   48 }, {  193,   67 }, {  260,   48 }, {  308,   32 },
-        {  340,   32 }, {  372,   45 }, {  417,   35 }, {  452,   24 },
-        {  476,   20 }, {  496,   29 }, {  525,   23 }, {  548,   17 },
-        {  565,  459 } } },
-
-    { { {    0,   24 }, {   24,   37 }, {   61,   29 }, {   90,   20 },
-        {  110,   35 }, {  145,   51 }, {  196,   41 }, {  237,   29 },
-        {  266,   26 }, {  292,   38 }, {  330,   31 }, {  361,   24 },
-        {  385,   18 }, {  403,   27 }, {  430,   23 }, {  453,   18 },
-        {  471,  553 } } },
-
-    { { {    0,   85 }, {   85,   97 }, {  182,   48 }, {  230,   23 },
-        {  253,   91 }, {  344,  110 }, {  454,   61 }, {  515,   30 },
-        {  545,   45 }, {  590,   58 }, {  648,   37 }, {  685,   21 },
-        {  706,   21 }, {  727,   29 }, {  756,   20 }, {  776,   13 },
-        {  789,  235 } } },
-
-    { { {    0,   22 }, {   22,   33 }, {   55,   27 }, {   82,   20 },
-        {  102,   33 }, {  135,   48 }, {  183,   39 }, {  222,   30 },
-        {  252,   26 }, {  278,   37 }, {  315,   30 }, {  345,   23 },
-        {  368,   17 }, {  385,   25 }, {  410,   21 }, {  431,   17 },
-        {  448,  576 } } },
-
-    { { {    0,    1 }, {    1,    1 }, {    2,   54 }, {   56,   33 },
-        {   89,    1 }, {   90,    1 }, {   91,   49 }, {  140,   32 },
-        {  172,   49 }, {  221,   47 }, {  268,   35 }, {  303,   25 },
-        {  328,   30 }, {  358,   30 }, {  388,   24 }, {  412,   18 },
-        {  430,  594 } } },
-
-    { { {    0,   45 }, {   45,   64 }, {  109,   43 }, {  152,   25 },
-        {  177,   62 }, {  239,   81 }, {  320,   56 }, {  376,   35 },
-        {  411,   37 }, {  448,   51 }, {  499,   38 }, {  537,   26 },
-        {  563,   22 }, {  585,   31 }, {  616,   24 }, {  640,   18 },
-        {  658,  366 } } },
-
-    { { {    0,  247 }, {  247,  148 }, {  395,   38 }, {  433,   12 },
-        {  445,  154 }, {  599,  130 }, {  729,   42 }, {  771,   14 },
-        {  785,   44 }, {  829,   46 }, {  875,   21 }, {  896,    9 },
-        {  905,   15 }, {  920,   17 }, {  937,    9 }, {  946,    5 },
-        {  951,   73 } } },
-
-    { { {    0,  231 }, {  231,  136 }, {  367,   41 }, {  408,   15 },
-        {  423,  134 }, {  557,  119 }, {  676,   47 }, {  723,   19 },
-        {  742,   44 }, {  786,   49 }, {  835,   25 }, {  860,   12 },
-        {  872,   17 }, {  889,   20 }, {  909,   12 }, {  921,    7 },
-        {  928,   96 } } }
-
-};
-
-const uint16_t lc3_spectrum_bits[][17] = {
-
-    { 20480, 20480,  5220,  9042, 20480, 20480,  6619,  9892,
-       5289,  6619,  9105, 11629,  8982,  9892, 11629, 13677,  4977 },
-
-    { 11940, 10854, 12109, 13677, 10742,  9812, 11090, 12288,
-      11348, 10240, 11348, 12683, 12109, 10854, 11629, 12902,  1197 },
-
-    {  7886,  7120,  8982, 10970,  7496,  6815,  8334, 10150,
-       9437,  8535,  9656, 11216, 11348, 10431, 11348, 12479,  4051 },
-
-    {  5485,  6099,  9168, 11940,  6311,  6262,  8640, 11090,
-       9233,  8640, 10334, 12479, 11781, 11090, 12479, 13988,  6009 },
-
-    {  7886,  7804, 10150, 11940,  7886,  7685,  9368, 10854,
-      10061,  9300, 10431, 11629, 11629, 10742, 11485, 12479,  2763 },
-
-    {  9042,  8383, 10240, 11781,  8483,  8013,  9437, 10742,
-      10334,  9437, 10431, 11485, 11781, 10742, 11485, 12288,  2346 },
-
-    {  5922,  6619,  9368, 11940,  6566,  6539,  8750, 10970,
-       9168,  8640, 10240, 12109, 11485, 10742, 11940, 13396,  5009 },
-
-    { 12288, 11090, 11348, 12109, 11090,  9892, 10334, 10970,
-      11629, 10431, 10970, 11629, 12479, 11348, 11781, 12288,  1289 },
-
-    {  1685,  5676, 13138, 18432,  5598,  7804, 13677, 18432,
-      12683, 13396, 17234, 20480, 17234, 17234, 20480, 20480, 15725 },
-
-    {  2793,  5072, 10970, 15725,  5204,  6487, 11216, 15186,
-      10970, 11216, 14336, 17234, 15186, 15186, 17234, 18432, 12109 },
-
-    { 12902, 11485, 11940, 13396, 11629, 10531, 11348, 12479,
-      12683, 11629, 12288, 13138, 13677, 12683, 13138, 13677,   854 },
-
-    {  3821,  5088,  9812, 13988,  5289,  5901,  9812, 13677,
-       9976,  9892, 12479, 15186, 13988, 13677, 15186, 17234,  9812 },
-
-    {  4856,  5412,  9168, 12902,  5598,  5736,  8863, 12288,
-       9368,  8982, 11090, 13677, 12902, 12288, 13677, 15725,  8147 },
-
-    { 20480, 20480,  7088,  9300, 20480, 20480,  7844,  9733,
-       7320,  7928,  9368, 10970,  9581,  9892, 10970, 12288,  2550 },
-
-    {  6031,  5859,  8192, 10635,  6410,  6286,  8433, 10742,
-       9656,  9042, 10531, 12479, 12479, 11629, 12902, 14336,  5756 },
-
-    {  6144,  6215,  8982, 11940,  6262,  6009,  8433, 11216,
-       8982,  8433, 10240, 12479, 11781, 11090, 12479, 13988,  5817 },
-
-    { 20480, 20480, 11216, 12109, 20480, 20480, 11216, 11940,
-      11629, 11485, 11940, 12479, 12479, 12109, 12683, 13138,   704 },
-
-    {  7928,  6994,  8239,  9733,  7218,  6539,  8147,  9892,
-       9812,  9105, 10240, 11629, 12109, 11216, 12109, 13138,  4167 },
-
-    {  8640,  7724,  9233, 10970,  8013,  7185,  8483, 10150,
-       9656,  8694,  9656, 10970, 11348, 10334, 11090, 12288,  3391 },
-
-    { 20480, 18432, 18432, 18432, 18432, 18432, 18432, 18432,
-      18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432,    91 },
-
-    { 10061,  8863,  9733, 11090,  8982,  7970,  8806,  9976,
-      10061,  9105,  9812, 10742, 11485, 10334, 10970, 11781,  2557 },
-
-    { 10431,  9368, 10240, 11348,  9368,  8433,  9233, 10334,
-      10431,  9437, 10061, 10970, 11781, 10635, 11216, 11940,  2119 },
-
-    { 13988, 12479, 12683, 12902, 12683, 11348, 11485, 11940,
-      12902, 11629, 11940, 12288, 13396, 12109, 12479, 12683,   828 },
-
-    { 10431,  9300, 10334, 11629,  9508,  8483,  9437, 10635,
-      10635,  9656, 10431, 11348, 11940, 10854, 11485, 12288,  1946 },
-
-    { 12479, 11216, 11629, 12479, 11348, 10150, 10635, 11348,
-      11940, 10854, 11216, 11940, 12902, 11629, 11940, 12479,  1146 },
-
-    { 13396, 12109, 12288, 12902, 12109, 10854, 11216, 11781,
-      12479, 11348, 11629, 12109, 13138, 11940, 12288, 12683,   928 },
-
-    {  2443,  5289, 11629, 16384,  5170,  6730, 11940, 16384,
-      11216, 11629, 14731, 18432, 15725, 15725, 18432, 20480, 13396 },
-
-    {  3328,  5009, 10531, 15186,  5040,  6031, 10531, 14731,
-      10431, 10431, 13396, 16384, 15186, 14731, 16384, 18432, 11629 },
-
-    { 14336, 12902, 12902, 13396, 12902, 11629, 11940, 12288,
-      13138, 12109, 12288, 12902, 13677, 12683, 12902, 13138,   711 },
-
-    {  4300,  5204,  9437, 13396,  5430,  5776,  9300, 12902,
-       9656,  9437, 11781, 14731, 13396, 12902, 14731, 16384,  8982 },
-
-    {  5394,  5776,  8982, 12288,  5922,  5901,  8640, 11629,
-       9105,  8694, 10635, 13138, 12288, 11629, 13138, 14731,  6844 },
-
-    { 17234, 15725, 15725, 15725, 15725, 14731, 14731, 14731,
-      16384, 14731, 14731, 15186, 16384, 15186, 15186, 15186,   272 },
-
-    {  6461,  6286,  8806, 11348,  6566,  6215,  8334, 10742,
-       9233,  8535, 10061, 12109, 11781, 10970, 12109, 13677,  5394 },
-
-    {  6674,  6487,  8863, 11485,  6702,  6286,  8334, 10635,
-       9168,  8483,  9976, 11940, 11629, 10854, 11940, 13396,  5105 },
-
-    { 15186, 13677, 13677, 13988, 13677, 12479, 12479, 12683,
-      13988, 12683, 12902, 13138, 14336, 13138, 13396, 13677,   565 },
-
-    {  7844,  7252,  8922, 10854,  7389,  6815,  8383, 10240,
-       9508,  8750,  9892, 11485, 11629, 10742, 11629, 12902,  3842 },
-
-    {  9233,  8239,  9233, 10431,  8334,  7424,  8483,  9892,
-      10061,  9105, 10061, 11216, 11781, 10742, 11485, 12479,  2906 },
-
-    { 20480, 20480, 14731, 14731, 20480, 20480, 14336, 14336,
-      15186, 14336, 14731, 14731, 15186, 14731, 14731, 15186,   266 },
-
-    { 10531,  9300,  9976, 11090,  9437,  8286,  9042, 10061,
-      10431,  9368,  9976, 10854, 11781, 10531, 11090, 11781,  2233 },
-
-    { 11629, 10334, 10970, 12109, 10431,  9368, 10061, 10970,
-      11348, 10240, 10854, 11485, 12288, 11216, 11629, 12288,  1469 },
-
-    {   952,  6787, 15725, 20480,  6646,  9733, 16384, 20480,
-      14731, 15725, 18432, 20480, 18432, 20480, 20480, 20480, 18432 },
-
-    {  9437,  8806, 10742, 12288,  8982,  8483,  9892, 11216,
-      10742,  9892, 10854, 11940, 12109, 11090, 11781, 12683,  1891 },
-
-    { 12902, 11629, 11940, 12479, 11781, 10531, 10854, 11485,
-      12109, 10970, 11348, 11940, 12902, 11781, 12109, 12479,  1054 },
-
-    {  2113,  5323, 11781, 16384,  5579,  7252, 12288, 16384,
-      11781, 12288, 15186, 18432, 15725, 16384, 18432, 20480, 12902 },
-
-    {  2463,  5965, 11348, 15186,  5522,  6934, 11216, 14731,
-      10334, 10635, 13677, 16384, 13988, 13988, 15725, 18432, 10334 },
-
-    {  3779,  5541,  9812, 13677,  5467,  6122,  9656, 13138,
-       9581,  9437, 11940, 14731, 13138, 12683, 14336, 16384,  8982 },
-
-    {  3181,  5154, 10150, 14336,  5448,  6311, 10334, 13988,
-      10334, 10431, 13138, 15725, 14336, 13988, 15725, 18432, 10431 },
-
-    {  4841,  5560,  9105, 12479,  5756,  5944,  8922, 12109,
-       9300,  8982, 11090, 13677, 12479, 12109, 13677, 15186,  7460 },
-
-    {  5859,  6009,  8922, 11940,  6144,  5987,  8483, 11348,
-       9042,  8535, 10334, 12683, 11940, 11216, 12683, 14336,  6215 },
-
-    {  4250,  4916,  8587, 12109,  5901,  6191,  9233, 12288,
-      10150,  9892, 11940, 14336, 13677, 13138, 14731, 16384,  8383 },
-
-    {  7153,  6702,  8863, 11216,  6904,  6410,  8239, 10431,
-       9233,  8433,  9812, 11629, 11629, 10742, 11781, 13138,  4753 },
-
-    {  6674,  7057,  9508, 11629,  7120,  6964,  8806, 10635,
-       9437,  8750, 10061, 11629, 11485, 10531, 11485, 12683,  4062 },
-
-    {  5341,  5289,  8013, 10970,  6311,  6262,  8640, 11090,
-      10061,  9508, 11090, 13138, 12902, 12288, 13396, 15186,  6539 },
-
-    {  8057,  7533,  9300, 11216,  7685,  7057,  8535, 10334,
-       9508,  8694,  9812, 11216, 11485, 10431, 11348, 12479,  3541 },
-
-    {  9168,  8239,  9656, 11216,  8483,  7608,  8806, 10240,
-       9892,  8982,  9812, 11090, 11485, 10431, 11090, 12109,  2815 },
-
-    {   558,  7928, 18432, 20480,  7724, 12288, 20480, 20480,
-      18432, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480 },
-
-    {  9892,  8806,  9976, 11348,  9042,  8057,  9042, 10240,
-      10240,  9233,  9976, 11090, 11629, 10531, 11216, 12109,  2371 },
-
-    { 11090,  9812, 10531, 11629,  9976,  8863,  9508, 10531,
-      10854,  9733, 10334, 11090, 11940, 10742, 11216, 11940,  1821 },
-
-    {  7354,  6964,  9042, 11216,  7153,  6592,  8334, 10431,
-       9233,  8483,  9812, 11485, 11485, 10531, 11629, 12902,  4349 },
-
-    { 11348, 10150, 10742, 11629, 10150,  9042,  9656, 10431,
-      10854,  9812, 10431, 11216, 12109, 10970, 11485, 12109,  1700 },
-
-    { 20480, 20480,  8694, 10150, 20480, 20480,  8982, 10240,
-       8982,  9105,  9976, 10970, 10431, 10431, 11090, 11940,  1610 },
-
-    {  9233,  8192,  9368, 10970,  8286,  7496,  8587,  9976,
-       9812,  8863,  9733, 10854, 11348, 10334, 11090, 11940,  3040 },
-
-    {  4202,  5716,  9733, 13138,  5598,  6099,  9437, 12683,
-       9300,  9168, 11485, 13988, 12479, 12109, 13988, 15725,  7804 },
-
-    {  4400,  5965,  9508, 12479,  6009,  6360,  9105, 11781,
-       9300,  8982, 10970, 13138, 12109, 11629, 13138, 14731,  6994 }
-
-};
diff --git a/system/embdrv/lc3/src/tables.h b/system/embdrv/lc3/src/tables.h
deleted file mode 100644
index 26bd48e..0000000
--- a/system/embdrv/lc3/src/tables.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#ifndef __LC3_TABLES_H
-#define __LC3_TABLES_H
-
-#include "common.h"
-#include "bits.h"
-
-
-/**
- * MDCT Twiddles and window coefficients
- */
-
-struct lc3_fft_bf3_twiddles { int n3; const struct lc3_complex (*t)[2]; };
-struct lc3_fft_bf2_twiddles { int n2; const struct lc3_complex *t; };
-struct lc3_mdct_rot_def { int n4; const struct lc3_complex *w; };
-
-extern const struct lc3_fft_bf3_twiddles *lc3_fft_twiddles_bf3[];
-extern const struct lc3_fft_bf2_twiddles *lc3_fft_twiddles_bf2[][3];
-extern const struct lc3_mdct_rot_def *lc3_mdct_rot[LC3_NUM_DT][LC3_NUM_SRATE];
-
-extern const float *lc3_mdct_win[LC3_NUM_DT][LC3_NUM_SRATE];
-
-
-/**
- * Limits of bands
- */
-
-#define LC3_NUM_BANDS  64
-
-extern const int lc3_band_lim[LC3_NUM_DT][LC3_NUM_SRATE][LC3_NUM_BANDS+1];
-
-
-/**
- * SNS Quantization
- */
-
-extern const float lc3_sns_lfcb[32][8];
-extern const float lc3_sns_hfcb[32][8];
-
-struct lc3_sns_vq_gains {
-    int count; const float *v;
-};
-
-extern const struct lc3_sns_vq_gains lc3_sns_vq_gains[4];
-
-extern const int32_t lc3_sns_mpvq_offsets[][11];
-
-
-/**
- * TNS Arithmetic Coding
- */
-
-extern const struct lc3_ac_model lc3_tns_order_models[];
-extern const uint16_t lc3_tns_order_bits[][8];
-
-extern const struct lc3_ac_model lc3_tns_coeffs_models[];
-extern const uint16_t lc3_tns_coeffs_bits[][17];
-
-
-/**
- * Long Term Postfilter
- */
-
-extern const float *lc3_ltpf_cnum[LC3_NUM_SRATE][4];
-extern const float *lc3_ltpf_cden[LC3_NUM_SRATE][4];
-
-
-/**
- * Spectral Data Arithmetic Coding
- */
-
-extern const uint8_t lc3_spectrum_lookup[2][2][256][4];
-extern const struct lc3_ac_model lc3_spectrum_models[];
-extern const uint16_t lc3_spectrum_bits[][17];
-
-
-#endif /* __LC3_TABLES_H */
diff --git a/system/embdrv/lc3/src/tns.c b/system/embdrv/lc3/src/tns.c
deleted file mode 100644
index 0032b4f..0000000
--- a/system/embdrv/lc3/src/tns.c
+++ /dev/null
@@ -1,457 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "tns.h"
-#include "tables.h"
-
-
-/* ----------------------------------------------------------------------------
- *  Filter Coefficients
- * -------------------------------------------------------------------------- */
-
-/**
- * Resolve LPC Weighting indication according bitrate
- * dt, nbytes      Duration and size of the frame
- * return          True when LPC Weighting enabled
- */
-static bool resolve_lpc_weighting(enum lc3_dt dt, int nbytes)
-{
-    return nbytes < (dt == LC3_DT_7M5 ? 360/8 : 480/8);
-}
-
-/**
- * Return dot product of 2 vectors
- * a, b, n         The 2 vectors of size `n`
- * return          sum( a[i] * b[i] ), i = [0..n-1]
- */
-LC3_HOT static inline float dot(const float *a, const float *b, int n)
-{
-    float v = 0;
-
-    while (n--)
-        v += *(a++) * *(b++);
-
-    return v;
-}
-
-/**
- * LPC Coefficients
- * dt, bw          Duration and bandwidth of the frame
- * x               Spectral coefficients
- * gain, a         Output the prediction gains and LPC coefficients
- */
-LC3_HOT static void compute_lpc_coeffs(
-    enum lc3_dt dt, enum lc3_bandwidth bw,
-    const float *x, float *gain, float (*a)[9])
-{
-    static const int sub_7m5_nb[]   = {  9, 26,  43,  60 };
-    static const int sub_7m5_wb[]   = {  9, 46,  83, 120 };
-    static const int sub_7m5_sswb[] = {  9, 66, 123, 180 };
-    static const int sub_7m5_swb[]  = {  9, 46,  82, 120, 159, 200, 240 };
-    static const int sub_7m5_fb[]   = {  9, 56, 103, 150, 200, 250, 300 };
-
-    static const int sub_10m_nb[]   = { 12, 34,  57,  80 };
-    static const int sub_10m_wb[]   = { 12, 61, 110, 160 };
-    static const int sub_10m_sswb[] = { 12, 88, 164, 240 };
-    static const int sub_10m_swb[]  = { 12, 61, 110, 160, 213, 266, 320 };
-    static const int sub_10m_fb[]   = { 12, 74, 137, 200, 266, 333, 400 };
-
-    /* --- Normalized autocorrelation --- */
-
-    static const float lag_window[] = {
-        1.00000000e+00, 9.98028026e-01, 9.92135406e-01, 9.82391584e-01,
-        9.68910791e-01, 9.51849807e-01, 9.31404933e-01, 9.07808230e-01,
-        8.81323137e-01
-    };
-
-    const int *sub = (const int * const [LC3_NUM_DT][LC3_NUM_SRATE]){
-        { sub_7m5_nb, sub_7m5_wb, sub_7m5_sswb, sub_7m5_swb, sub_7m5_fb },
-        { sub_10m_nb, sub_10m_wb, sub_10m_sswb, sub_10m_swb, sub_10m_fb },
-    }[dt][bw];
-
-    int nfilters = 1 + (bw >= LC3_BANDWIDTH_SWB);
-
-    const float *xs, *xe = x + *sub;
-    float r[2][9];
-
-    for (int f = 0; f < nfilters; f++) {
-        float c[9][3];
-
-        for (int s = 0; s < 3; s++) {
-            xs = xe, xe = x + *(++sub);
-
-            for (int k = 0; k < 9; k++)
-                c[k][s] = dot(xs, xs + k, (xe - xs) - k);
-        }
-
-        float e0 = c[0][0], e1 = c[0][1], e2 = c[0][2];
-
-        r[f][0] = 3;
-        for (int k = 1; k < 9; k++)
-            r[f][k] = e0 == 0 || e1 == 0 || e2 == 0 ? 0 :
-                (c[k][0]/e0 + c[k][1]/e1 + c[k][2]/e2) * lag_window[k];
-    }
-
-    /* --- Levinson-Durbin recursion --- */
-
-    for (int f = 0; f < nfilters; f++) {
-        float *a0 = a[f], a1[9];
-        float err = r[f][0], rc;
-
-        gain[f] = err;
-
-        a0[0] = 1;
-        for (int k = 1; k < 9; ) {
-
-            rc = -r[f][k];
-            for (int i = 1; i < k; i++)
-                rc -= a0[i] * r[f][k-i];
-
-            rc /= err;
-            err *= 1 - rc * rc;
-
-            for (int i = 1; i < k; i++)
-                a1[i] = a0[i] + rc * a0[k-i];
-            a1[k++] = rc;
-
-            rc = -r[f][k];
-            for (int i = 1; i < k; i++)
-                rc -= a1[i] * r[f][k-i];
-
-            rc /= err;
-            err *= 1 - rc * rc;
-
-            for (int i = 1; i < k; i++)
-                a0[i] = a1[i] + rc * a1[k-i];
-            a0[k++] = rc;
-        }
-
-        gain[f] /= err;
-    }
-}
-
-/**
- * LPC Weighting
- * gain, a         Prediction gain and LPC coefficients, weighted as output
- */
-LC3_HOT static void lpc_weighting(float pred_gain, float *a)
-{
-    float gamma = 1.f - (1.f - 0.85f) * (2.f - pred_gain) / (2.f - 1.5f);
-    float g = 1.f;
-
-    for (int i = 1; i < 9; i++)
-        a[i] *= (g *= gamma);
-}
-
-/**
- * LPC reflection
- * a               LPC coefficients
- * rc              Output refelection coefficients
- */
-LC3_HOT static void lpc_reflection(const float *a, float *rc)
-{
-    float e, b[2][7], *b0, *b1;
-
-    rc[7] = a[1+7];
-    e = 1 - rc[7] * rc[7];
-
-    b1 = b[1];
-    for (int i = 0; i < 7; i++)
-        b1[i] = (a[1+i] - rc[7] * a[7-i]) / e;
-
-    for (int k = 6; k > 0; k--) {
-        b0 = b1, b1 = b[k & 1];
-
-        rc[k] = b0[k];
-        e = 1 - rc[k] * rc[k];
-
-        for (int i = 0; i < k; i++)
-            b1[i] = (b0[i] - rc[k] * b0[k-1-i]) / e;
-    }
-
-    rc[0] = b1[0];
-}
-
-/**
- * Quantization of RC coefficients
- * rc              Refelection coefficients
- * rc_order        Return order of coefficients
- * rc_i            Return quantized coefficients
- */
-static void quantize_rc(const float *rc, int *rc_order, int *rc_q)
-{
-    /* Quantization table, sin(delta * (i + 0.5)), delta = Pi / 17 */
-
-    static float q_thr[] = {
-        9.22683595e-02, 2.73662990e-01, 4.45738356e-01, 6.02634636e-01,
-        7.39008917e-01, 8.50217136e-01, 9.32472229e-01, 9.82973100e-01
-    };
-
-    *rc_order = 8;
-
-    for (int i = 0; i < 8; i++) {
-        float rc_m = fabsf(rc[i]);
-
-        rc_q[i] = 4 * (rc_m >= q_thr[4]);
-        for (int j = 0; j < 4 && rc_m >= q_thr[rc_q[i]]; j++, rc_q[i]++);
-
-        if (rc[i] < 0)
-            rc_q[i] = -rc_q[i];
-
-        *rc_order = rc_q[i] != 0 ? 8 : *rc_order - 1;
-    }
-}
-
-/**
- * Unquantization of RC coefficients
- * rc_q            Quantized coefficients
- * rc_order        Order of coefficients
- * rc              Return refelection coefficients
- */
-static void unquantize_rc(const int *rc_q, int rc_order, float rc[8])
-{
-    /* Quantization table, sin(delta * i), delta = Pi / 17 */
-
-    static float q_inv[] = {
-        0.00000000e+00, 1.83749517e-01, 3.61241664e-01, 5.26432173e-01,
-        6.73695641e-01, 7.98017215e-01, 8.95163302e-01, 9.61825645e-01,
-        9.95734176e-01
-    };
-
-    int i;
-
-    for (i = 0; i < rc_order; i++) {
-        float rc_m = q_inv[LC3_ABS(rc_q[i])];
-        rc[i] = rc_q[i] < 0 ? -rc_m : rc_m;
-    }
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Filtering
- * -------------------------------------------------------------------------- */
-
-/**
- * Forward filtering
- * dt, bw          Duration and bandwidth of the frame
- * rc_order, rc    Order of coefficients, and coefficients
- * x               Spectral coefficients, filtered as output
- */
-LC3_HOT static void forward_filtering(
-    enum lc3_dt dt, enum lc3_bandwidth bw,
-    const int rc_order[2], const float rc[2][8], float *x)
-{
-    int nfilters = 1 + (bw >= LC3_BANDWIDTH_SWB);
-    int nf = LC3_NE(dt, bw) >> (nfilters - 1);
-    int i0, ie = 3*(3 + dt);
-
-    float s[8] = { 0 };
-
-    for (int f = 0; f < nfilters; f++) {
-
-        i0 = ie;
-        ie = nf * (1 + f);
-
-        if (!rc_order[f])
-            continue;
-
-        for (int i = i0; i < ie; i++) {
-            float xi = x[i];
-            float s0, s1 = xi;
-
-            for (int k = 0; k < rc_order[f]; k++) {
-                s0 = s[k];
-                s[k] = s1;
-
-                s1  = rc[f][k] * xi + s0;
-                xi += rc[f][k] * s0;
-            }
-
-            x[i] = xi;
-        }
-    }
-}
-
-/**
- * Inverse filtering
- * dt, bw          Duration and bandwidth of the frame
- * rc_order, rc    Order of coefficients, and unquantized coefficients
- * x               Spectral coefficients, filtered as output
- */
-LC3_HOT static void inverse_filtering(
-    enum lc3_dt dt, enum lc3_bandwidth bw,
-    const int rc_order[2], const float rc[2][8], float *x)
-{
-    int nfilters = 1 + (bw >= LC3_BANDWIDTH_SWB);
-    int nf = LC3_NE(dt, bw) >> (nfilters - 1);
-    int i0, ie = 3*(3 + dt);
-
-    float s[8] = { 0 };
-
-    for (int f = 0; f < nfilters; f++) {
-
-        i0 = ie;
-        ie = nf * (1 + f);
-
-        if (!rc_order[f])
-            continue;
-
-        for (int i = i0; i < ie; i++) {
-            float xi = x[i];
-
-            xi -= s[7] * rc[f][7];
-            for (int k = 6; k >= 0; k--) {
-                xi -= s[k] * rc[f][k];
-                s[k+1] = s[k] + rc[f][k] * xi;
-            }
-            s[0] = xi;
-            x[i] = xi;
-        }
-
-        for (int k = 7; k >= rc_order[f]; k--)
-            s[k] = 0;
-    }
-}
-
-
-/* ----------------------------------------------------------------------------
- *  Interface
- * -------------------------------------------------------------------------- */
-
-/**
- * TNS analysis
- */
-void lc3_tns_analyze(enum lc3_dt dt, enum lc3_bandwidth bw,
-    bool nn_flag, int nbytes, struct lc3_tns_data *data, float *x)
-{
-    /* Processing steps :
-     * - Determine the LPC (Linear Predictive Coding) Coefficients
-     * - Check is the filtering is disabled
-     * - The coefficients are weighted on low bitrates and predicition gain
-     * - Convert to reflection coefficients and quantize
-     * - Finally filter the spectral coefficients */
-
-    float pred_gain[2], a[2][9];
-    float rc[2][8];
-
-    data->nfilters = 1 + (bw >= LC3_BANDWIDTH_SWB);
-    data->lpc_weighting = resolve_lpc_weighting(dt, nbytes);
-
-    compute_lpc_coeffs(dt, bw, x, pred_gain, a);
-
-    for (int f = 0; f < data->nfilters; f++) {
-
-        data->rc_order[f] = 0;
-        if (nn_flag || pred_gain[f] <= 1.5f)
-            continue;
-
-        if (data->lpc_weighting && pred_gain[f] < 2.f)
-            lpc_weighting(pred_gain[f], a[f]);
-
-        lpc_reflection(a[f], rc[f]);
-
-        quantize_rc(rc[f], &data->rc_order[f], data->rc[f]);
-        unquantize_rc(data->rc[f], data->rc_order[f], rc[f]);
-    }
-
-    forward_filtering(dt, bw, data->rc_order, rc, x);
-}
-
-/**
- * TNS synthesis
- */
-void lc3_tns_synthesize(enum lc3_dt dt, enum lc3_bandwidth bw,
-    const struct lc3_tns_data *data, float *x)
-{
-    float rc[2][8] = { };
-
-    for (int f = 0; f < data->nfilters; f++)
-        if (data->rc_order[f])
-            unquantize_rc(data->rc[f], data->rc_order[f], rc[f]);
-
-    inverse_filtering(dt, bw, data->rc_order, rc, x);
-}
-
-/**
- * Bit consumption of bitstream data
- */
-int lc3_tns_get_nbits(const struct lc3_tns_data *data)
-{
-    int nbits = 0;
-
-    for (int f = 0; f < data->nfilters; f++) {
-
-        int nbits_2048 = 2048;
-        int rc_order = data->rc_order[f];
-
-        nbits_2048 += rc_order > 0 ? lc3_tns_order_bits
-            [data->lpc_weighting][rc_order-1] : 0;
-
-        for (int i = 0; i < rc_order; i++)
-            nbits_2048 += lc3_tns_coeffs_bits[i][8 + data->rc[f][i]];
-
-        nbits += (nbits_2048 + (1 << 11) - 1) >> 11;
-    }
-
-    return nbits;
-}
-
-/**
- * Put bitstream data
- */
-void lc3_tns_put_data(lc3_bits_t *bits, const struct lc3_tns_data *data)
-{
-    for (int f = 0; f < data->nfilters; f++) {
-        int rc_order = data->rc_order[f];
-
-        lc3_put_bits(bits, rc_order > 0, 1);
-        if (rc_order <= 0)
-            continue;
-
-        lc3_put_symbol(bits,
-            lc3_tns_order_models + data->lpc_weighting, rc_order-1);
-
-        for (int i = 0; i < rc_order; i++)
-            lc3_put_symbol(bits,
-                lc3_tns_coeffs_models + i, 8 + data->rc[f][i]);
-    }
-}
-
-/**
- * Get bitstream data
- */
-void lc3_tns_get_data(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_bandwidth bw, int nbytes, lc3_tns_data_t *data)
-{
-    data->nfilters = 1 + (bw >= LC3_BANDWIDTH_SWB);
-    data->lpc_weighting = resolve_lpc_weighting(dt, nbytes);
-
-    for (int f = 0; f < data->nfilters; f++) {
-
-        data->rc_order[f] = lc3_get_bit(bits);
-        if (!data->rc_order[f])
-            continue;
-
-        data->rc_order[f] += lc3_get_symbol(bits,
-            lc3_tns_order_models + data->lpc_weighting);
-
-        for (int i = 0; i < data->rc_order[f]; i++)
-            data->rc[f][i] = (int)lc3_get_symbol(bits,
-                lc3_tns_coeffs_models + i) - 8;
-    }
-}
diff --git a/system/embdrv/lc3/src/tns.h b/system/embdrv/lc3/src/tns.h
deleted file mode 100644
index 534f191..0000000
--- a/system/embdrv/lc3/src/tns.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-/**
- * LC3 - Temporal Noise Shaping
- *
- * Reference : Low Complexity Communication Codec (LC3)
- *             Bluetooth Specification v1.0
- */
-
-#ifndef __LC3_TNS_H
-#define __LC3_TNS_H
-
-#include "common.h"
-#include "bits.h"
-
-
-/**
- * Bitstream data
- */
-
-typedef struct lc3_tns_data {
-    int nfilters;
-    bool lpc_weighting;
-    int rc_order[2];
-    int rc[2][8];
-} lc3_tns_data_t;
-
-
-/* ----------------------------------------------------------------------------
- *  Encoding
- * -------------------------------------------------------------------------- */
-
-/**
- * TNS analysis
- * dt, bw          Duration and bandwidth of the frame
- * nn_flag         True when high energy detected near Nyquist frequency
- * nbytes          Size in bytes of the frame
- * data            Return bitstream data
- * x               Spectral coefficients, filtered as output
- */
-void lc3_tns_analyze(enum lc3_dt dt, enum lc3_bandwidth bw,
-    bool nn_flag, int nbytes, lc3_tns_data_t *data, float *x);
-
-/**
- * Return number of bits coding the data
- * data            Bitstream data
- * return          Bit consumption
- */
-int lc3_tns_get_nbits(const lc3_tns_data_t *data);
-
-/**
- * Put bitstream data
- * bits            Bitstream context
- * data            Bitstream data
- */
-void lc3_tns_put_data(lc3_bits_t *bits, const lc3_tns_data_t *data);
-
-
-/* ----------------------------------------------------------------------------
- *  Decoding
- * -------------------------------------------------------------------------- */
-
-/**
- * Get bitstream data
- * bits            Bitstream context
- * dt, bw          Duration and bandwidth of the frame
- * nbytes          Size in bytes of the frame
- * data            Bitstream data
- */
-void lc3_tns_get_data(lc3_bits_t *bits,
-    enum lc3_dt dt, enum lc3_bandwidth bw, int nbytes, lc3_tns_data_t *data);
-
-/**
- * TNS synthesis
- * dt, bw          Duration and bandwidth of the frame
- * data            Bitstream data
- * x               Spectral coefficients, filtered as output
- */
-void lc3_tns_synthesize(enum lc3_dt dt, enum lc3_bandwidth bw,
-    const lc3_tns_data_t *data, float *x);
-
-
-#endif /* __LC3_TNS_H */
diff --git a/system/embdrv/lc3/tables/fastmath.py b/system/embdrv/lc3/tables/fastmath.py
deleted file mode 100755
index 202561ae..0000000
--- a/system/embdrv/lc3/tables/fastmath.py
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-import matplotlib.pyplot as plt
-
-
-def fast_exp2(x, p):
-
-    p = p.astype(np.float32)
-    x = x.astype(np.float32)
-
-    y = (((((p[0]*x) + p[1])*x + p[2])*x + p[3])*x + p[4])*x + 1
-
-    return np.power(y.astype(np.float32), 16)
-
-def approx_exp2():
-
-    x = np.arange(-8, 8, step=1e-3)
-
-    p = np.polyfit(x, ((2 ** (x/16)) - 1) / x, 4)
-    y = [ fast_exp2(x[i], p) for i in range(len(x)) ]
-    e = np.abs(y - 2**x) / (2 ** x)
-
-    print('{{ {:14.8e}, {:14.8e}, {:14.8e}, {:14.8e}, {:14.8e} }}'
-                .format(p[0], p[1], p[2], p[3], p[4]))
-    print('Max relative error: ', np.max(e))
-    print('Max RMS error: ', np.sqrt(np.mean(e ** 2)))
-
-    if False:
-        fig, (ax1, ax2) = plt.subplots(2)
-
-        ax1.plot(x, 2**x, label='Reference')
-        ax1.plot(x, y, label='Approximation')
-        ax1.legend()
-
-        ax2.plot(x, e, label='Relative Error')
-        ax2.legend()
-
-        plt.show()
-
-
-def fast_log2(x, p):
-
-    p = p.astype(np.float32)
-    x = x.astype(np.float32)
-
-    (x, e) = np.frexp(x)
-
-    y = ((((p[0]*x) + p[1])*x + p[2])*x + p[3])*x + p[4]
-
-    return (e ) + y.astype(np.float32)
-
-def approx_log2():
-
-    x = np.logspace(-1, 0, base=2, num=100)
-    p = np.polyfit(x, np.log2(x), 4)
-
-    x = np.logspace(-2, 5, num=10000)
-    y = [ fast_log2(x[i], p) for i in range(len(x)) ]
-    e = np.abs(y - np.log2(x))
-
-    print('{{ {:14.8e}, {:14.8e}, {:14.8e}, {:14.8e}, {:14.8e} }}'
-                .format(p[0], p[1], p[2], p[3], p[4]))
-    print('Max absolute error: ', np.max(e))
-    print('Max RMS error: ', np.sqrt(np.mean(e ** 2)))
-
-    if False:
-        fig, (ax1, ax2) = plt.subplots(2)
-
-        ax1.plot(x, np.log2(x),  label='Reference')
-        ax1.plot(x, y, label='Approximation')
-        ax1.legend()
-
-        ax2.plot(x, e, label = 'Absolute error')
-        ax2.legend()
-
-        plt.show()
-
-
-def table_db_q16():
-
-    k = 10 * np.log10(2);
-
-    for i in range(32):
-        a = k * np.log2(np.ldexp(32 + i  , -5)) - (i // 16) * (k/2);
-        b = k * np.log2(np.ldexp(32 + i+1, -5)) - (i // 16) * (k/2);
-
-        an = np.ldexp(a, 15) + 0.5
-        bn = np.ldexp(b - a, 15) + 0.5
-        print('{{ {:5d}, {:4d} }},'
-            .format(int(np.ldexp(a, 15) + 0.5),
-                    int(np.ldexp(b - a, 15) + 0.5)),
-            end = ' ' if i % 4 < 3 else '\n')
-
-
-if __name__ == '__main__':
-
-    print('\n--- Approximation of 2^n ---')
-    approx_exp2()
-
-    print('\n--- Approximation of log2(n) ---')
-    approx_log2()
-
-    print('\n--- Table of fixed Q16 dB ---')
-    table_db_q16()
-
-    print('')
diff --git a/system/embdrv/lc3/tables/mktables.py b/system/embdrv/lc3/tables/mktables.py
deleted file mode 100755
index 67d4312..0000000
--- a/system/embdrv/lc3/tables/mktables.py
+++ /dev/null
@@ -1,229 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-
-LTPF_H12K8 = np.array([
-    -2.04305583e-05, -4.46345894e-05, -7.16366399e-05, -1.00101113e-04,
-    -1.28372848e-04, -1.54543830e-04, -1.76544567e-04, -1.92256960e-04,
-    -1.99643819e-04, -1.96888686e-04, -1.82538332e-04, -1.55639427e-04,
-    -1.15860365e-04, -6.35893034e-05,  2.81006480e-19,  7.29218021e-05,
-     1.52397076e-04,  2.34920777e-04,  3.16378650e-04,  3.92211738e-04,
-     4.57623849e-04,  5.07824294e-04,  5.38295523e-04,  5.45072918e-04,
-     5.25022155e-04,  4.76098424e-04,  3.97571380e-04,  2.90200217e-04,
-     1.56344667e-04, -5.81880142e-19, -1.73252713e-04, -3.56385965e-04,
-    -5.41155231e-04, -7.18414023e-04, -8.78505232e-04, -1.01171451e-03,
-    -1.10876706e-03, -1.16134522e-03, -1.16260169e-03, -1.10764097e-03,
-    -9.93941563e-04, -8.21692190e-04, -5.94017766e-04, -3.17074654e-04,
-     9.74695082e-19,  3.45293760e-04,  7.04480871e-04,  1.06133447e-03,
-     1.39837473e-03,  1.69763080e-03,  1.94148675e-03,  2.11357591e-03,
-     2.19968245e-03,  2.18860625e-03,  2.07294546e-03,  1.84975249e-03,
-     1.52102188e-03,  1.09397426e-03,  5.81108062e-04, -1.42248266e-18,
-    -6.27153730e-04, -1.27425140e-03, -1.91223839e-03, -2.51026925e-03,
-    -3.03703830e-03, -3.46222687e-03, -3.75800672e-03, -3.90053247e-03,
-    -3.87135231e-03, -3.65866558e-03, -3.25835851e-03, -2.67475555e-03,
-    -1.92103305e-03, -1.01925433e-03,  1.86962369e-18,  1.09841545e-03,
-     2.23113197e-03,  3.34830927e-03,  4.39702277e-03,  5.32342672e-03,
-     6.07510531e-03,  6.60352025e-03,  6.86645399e-03,  6.83034270e-03,
-     6.47239234e-03,  5.78237521e-03,  4.76401273e-03,  3.43586351e-03,
-     1.83165284e-03, -2.25189837e-18, -1.99647619e-03, -4.08266886e-03,
-    -6.17308037e-03, -8.17444895e-03, -9.98882386e-03, -1.15169871e-02,
-    -1.26621006e-02, -1.33334458e-02, -1.34501120e-02, -1.29444881e-02,
-    -1.17654154e-02, -9.88086732e-03, -7.28003640e-03, -3.97473021e-03,
-     2.50961778e-18,  4.58604422e-03,  9.70324900e-03,  1.52512477e-02,
-     2.11120585e-02,  2.71533724e-02,  3.32324245e-02,  3.92003203e-02,
-     4.49066644e-02,  5.02043309e-02,  5.49542017e-02,  5.90297032e-02,
-     6.23209727e-02,  6.47385023e-02,  6.62161245e-02,  6.67132287e-02,
-     6.62161245e-02,  6.47385023e-02,  6.23209727e-02,  5.90297032e-02,
-     5.49542017e-02,  5.02043309e-02,  4.49066644e-02,  3.92003203e-02,
-     3.32324245e-02,  2.71533724e-02,  2.11120585e-02,  1.52512477e-02,
-     9.70324900e-03,  4.58604422e-03,  2.50961778e-18, -3.97473021e-03,
-    -7.28003640e-03, -9.88086732e-03, -1.17654154e-02, -1.29444881e-02,
-    -1.34501120e-02, -1.33334458e-02, -1.26621006e-02, -1.15169871e-02,
-    -9.98882386e-03, -8.17444895e-03, -6.17308037e-03, -4.08266886e-03,
-    -1.99647619e-03, -2.25189837e-18,  1.83165284e-03,  3.43586351e-03,
-     4.76401273e-03,  5.78237521e-03,  6.47239234e-03,  6.83034270e-03,
-     6.86645399e-03,  6.60352025e-03,  6.07510531e-03,  5.32342672e-03,
-     4.39702277e-03,  3.34830927e-03,  2.23113197e-03,  1.09841545e-03,
-     1.86962369e-18, -1.01925433e-03, -1.92103305e-03, -2.67475555e-03,
-    -3.25835851e-03, -3.65866558e-03, -3.87135231e-03, -3.90053247e-03,
-    -3.75800672e-03, -3.46222687e-03, -3.03703830e-03, -2.51026925e-03,
-    -1.91223839e-03, -1.27425140e-03, -6.27153730e-04, -1.42248266e-18,
-     5.81108062e-04,  1.09397426e-03,  1.52102188e-03,  1.84975249e-03,
-     2.07294546e-03,  2.18860625e-03,  2.19968245e-03,  2.11357591e-03,
-     1.94148675e-03,  1.69763080e-03,  1.39837473e-03,  1.06133447e-03,
-     7.04480871e-04,  3.45293760e-04,  9.74695082e-19, -3.17074654e-04,
-    -5.94017766e-04, -8.21692190e-04, -9.93941563e-04, -1.10764097e-03,
-    -1.16260169e-03, -1.16134522e-03, -1.10876706e-03, -1.01171451e-03,
-    -8.78505232e-04, -7.18414023e-04, -5.41155231e-04, -3.56385965e-04,
-    -1.73252713e-04, -5.81880142e-19,  1.56344667e-04,  2.90200217e-04,
-     3.97571380e-04,  4.76098424e-04,  5.25022155e-04,  5.45072918e-04,
-     5.38295523e-04,  5.07824294e-04,  4.57623849e-04,  3.92211738e-04,
-     3.16378650e-04,  2.34920777e-04,  1.52397076e-04,  7.29218021e-05,
-     2.81006480e-19, -6.35893034e-05, -1.15860365e-04, -1.55639427e-04,
-    -1.82538332e-04, -1.96888686e-04, -1.99643819e-04, -1.92256960e-04,
-    -1.76544567e-04, -1.54543830e-04, -1.28372848e-04, -1.00101113e-04,
-    -7.16366399e-05, -4.46345894e-05, -2.04305583e-05
-])
-
-LTPF_HI = np.array([
-     6.69885837e-03,  3.96711478e-02,  1.06999186e-01,  2.09880463e-01,
-     3.35690625e-01,  4.59220930e-01,  5.50075002e-01,  5.83527575e-01,
-     5.50075002e-01,  4.59220930e-01,  3.35690625e-01,  2.09880463e-01,
-     1.06999186e-01,  3.96711478e-02,  6.69885837e-03
-])
-
-def print_table(t, m=4):
-
-    for (i, v) in enumerate(t):
-        print('{:14.8e},'.format(v), end = '\n' if i%m == m-1 else ' ')
-
-    if len(t) % 4:
-        print('')
-
-
-def mdct_fft_twiddles():
-
-    for n in (10, 20, 30, 40, 60, 80, 90, 120, 160, 180, 240):
-
-        print('\n--- fft bf2 twiddles {:3d} ---'.format(n))
-
-        kv = -2 * np.pi * np.arange(n // 2) / n
-        for (i, k) in enumerate(kv):
-            print('{{ {:14.7e}, {:14.7e} }},'.format(np.cos(k), np.sin(k)),
-                  end = '\n' if i%2 == 1 else ' ')
-
-    for n in (15, 45):
-
-        print('\n--- fft bf3 twiddles {:3d} ---'.format(n))
-
-        kv = -2 * np.pi * np.arange(n) / n
-        for k in kv:
-            print(('{{ {{ {:14.7e}, {:14.7e} }},' +
-                     ' {{ {:14.7e}, {:14.7e} }} }},').format(
-                np.cos(k), np.sin(k), np.cos(2*k), np.sin(2*k)))
-
-
-def mdct_rot_twiddles():
-
-    for n in (120, 160, 240, 320, 360, 480, 640, 720, 960):
-
-        print('\n--- mdct rot twiddles {:3d} ---'.format(n))
-
-        kv = 2 * np.pi * (np.arange(n // 4) + 1/8) / n
-        for (i, k) in enumerate(kv):
-            print('{{ {:14.7e}, {:14.7e} }},'.format(np.cos(k), np.sin(k)),
-                  end = '\n' if i%2 == 1 else ' ')
-
-
-def mdct_scaling():
-
-    print('\n--- mdct scaling ---')
-    ns = np.array([ [ 60, 120, 180, 240, 360], [ 80, 160, 240, 320, 480] ])
-    print_table(np.sqrt(2 / ns[0]))
-    print_table(np.sqrt(2 / ns[1]))
-
-
-def tns_lag_window():
-
-    print('\n--- tns lag window ---')
-    print_table(np.exp(-0.5 * (0.02 * np.pi * np.arange(9)) ** 2))
-
-
-def tns_quantization_table():
-
-    print('\n--- tns quantization table ---')
-    print_table(np.sin((np.arange(8) + 0.5) * (np.pi / 17)))
-    print_table(np.sin((np.arange(8)) * (np.pi / 17)))
-
-
-def quant_iq_table():
-
-    print('\n--- quantization iq table ---')
-    print_table(10 ** (np.arange(65) / 28))
-
-
-def sns_ge_table():
-
-    g_tilt_table = [ 14, 18, 22, 26, 30 ]
-
-    for (sr, g_tilt) in enumerate(g_tilt_table):
-        print('\n--- sns ge table, sr:{} ---'.format(sr))
-        print_table(10 ** ((np.arange(64) * g_tilt) / 630))
-
-
-def inv_table():
-
-    print('\n--- inv table ---')
-    print_table(np.append(np.zeros(1), 1 / np.arange(1, 28)))
-
-def ltpf_resampler_table():
-
-    for sr in [ 8, 16, 32, 24, 48 ]:
-
-        r = 192 // sr
-        k = 64 if r & (r-1) else 192
-
-        p = (192 // k) * (k // sr)
-        q = p * (0.5 if sr == 8 else 1)
-
-        print('\n--- LTPF resampler {:d} KHz to 12.8 KHz ---'.format(sr))
-
-        h = np.rint(np.append(LTPF_H12K8, 0.) * q * 2**15).astype(int)
-        h = h.reshape((len(h) // p, p)).T
-        h = np.flip(h, axis=0)
-        print('... Gain:', np.max(np.sum(np.abs(h), axis=1)) / 32768.)
-
-        for i in range(0, len(h), 192 // k):
-            for j in range(0, len(h[i]), 10):
-                print('{:5d}, {:5d}, {:5d}, {:5d}, {:5d}, '
-                      '{:5d}, {:5d}, {:5d}, {:5d}, {:5d},'.format(
-                    h[i][j+0], h[i][j+1], h[i][j+2], h[i][j+3], h[i][j+4],
-                    h[i][j+5], h[i][j+6], h[i][j+7], h[i][j+8], h[i][j+9]))
-
-
-def ltpf_interpolate_table():
-
-    print('\n--- LTPF interpolation ---')
-
-    h = np.rint(np.append(LTPF_HI, 0.) * 2**15).astype(int)
-
-    h = h.reshape(len(h) // 4, 4).T
-    h = np.flip(h, axis=0)
-    print('... Gain:', np.max(np.sum(np.abs(h), axis=1)) / 32768.)
-
-    for i in range(len(h)):
-        print('{:5d}, {:5d}, {:5d}, {:5d}'.format(
-            h[i][0], h[i][1], h[i][2], h[i][3]))
-
-
-if __name__ == '__main__':
-
-    mdct_fft_twiddles()
-    mdct_rot_twiddles()
-    mdct_scaling()
-
-    inv_table()
-    sns_ge_table()
-    tns_lag_window()
-    tns_quantization_table()
-    quant_iq_table()
-
-    ltpf_resampler_table()
-    ltpf_interpolate_table()
-
-    print('')
diff --git a/system/embdrv/lc3/test/appendix_c.py b/system/embdrv/lc3/test/appendix_c.py
deleted file mode 100644
index 66b6e04..0000000
--- a/system/embdrv/lc3/test/appendix_c.py
+++ /dev/null
@@ -1,4083 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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.
-#
-
-#
-# Appendix C - Intermediate verification of input and output
-#
-
-import numpy as np
-
-
-NBYTES = (32e3 * np.array([ 7.5e-3, 10e-3 ]) / 8).astype(int)
-
-
-### C.3.1.1  PCM Input
-
-X_PCM_10M = np.array([
-
-    [      0,   3212,   6392,   9512,  12539,  15446,  18205,  20788,
-       23170,  25328,  27244,  28898,  30272,  31357,  32137,  32609,
-       32767,  32609,  32138,  31356,  30272,  28898,  27245,  25330,
-       23169,  20787,  18205,  15446,  12539,   9511,   6393,   3212,
-           0,  -3212,  -6393,  -9512, -12540, -15446, -18204, -20787,
-      -23170, -25329, -27245, -28898, -30273, -31356, -32137, -32610,
-      -32766, -32609, -32137, -31356, -30272, -28898, -27244, -25329,
-      -23171, -20787, -18204, -15446, -12539,  -9511,  -6393,  -3212,
-          -1,   3212,   6393,   9512,  12540,  15446,  18204,  20788,
-       23169,  25329,  27245,  28898,  30273,  31356,  32137,  32609,
-       32767,  32609,  32137,  31356,  30273,  28898,  27245,  25330,
-       23170,  20787,  18204,  15446,  12540,   9512,   6393,   3212,
-           0,  -3212,  -6393,  -9512, -12539, -15447, -18204, -20787,
-      -23170, -25330, -27244, -28898, -30272, -31356, -32137, -32609,
-      -32767, -32609, -32137, -31356, -30273, -28898, -27244, -25330,
-      -23169, -20787, -18204, -15446, -12540,  -9511,  -6393,  -3212,
-           0,   3212,   6392,   9511,  12539,  15446,  18205,  20787,
-       23169,  25329,  27245,  28898,  30273,  31356,  32137,  32609,
-       32767,  32610,  32137,  31356,  30273,  28898,  27244,  25329,
-       23170,  20787,  18204,  15446,  12540,   9511,   6392,   3211 ],
-
-    [      0,  -3211,  -6393,  -9512, -12539, -15446, -18204, -20788,
-      -23170, -25329, -27245, -28898, -30273, -31356, -32137, -32609,
-      -32767, -32609, -32138, -31356, -30273, -28898, -27245, -25329,
-      -23170, -20788, -18205, -15447, -12539,  -9512,  -6392,  -3211,
-           0,   3211,   6393,   9512,  12539,  15446,  18204,  20787,
-       23170,  25329,  27244,  28897,  30273,  31356,  32137,  32609,
-       32767,  32609,  32137,  31356,  30273,  28897,  27244,  25330,
-       23170,  20787,  18205,  15446,  12539,   9512,   6393,   3213,
-           0,  -3212,  -6393,  -9512, -12540, -15447, -18205, -20787,
-      -23169, -25329, -27245, -28897, -30273, -31356, -32138, -32609,
-      -32767, -32609, -32138, -31356, -30273, -28898, -27244, -25330,
-      -23170, -20787, -18205, -15446, -12540,  -9513,  -6392,  -3212,
-           0,   3212,   6393,   9512,  12540,  15446,  18204,  20788,
-       23170,  25329,  27245,  28898,  30272,  31356,  32138,  32609,
-       32767,  32609,  32138,  31356,  30273,  28898,  27244,  25330,
-       23170,  20787,  18204,  15446,  12540,   9512,   6392,   3211,
-           0,  -3211,  -6393,  -9512, -12539, -15446, -18205, -20786,
-      -23170, -25329, -27245, -28898, -30272, -31356, -32137, -32609,
-      -32766, -32609, -32137, -31356, -30273, -28898, -27245, -25329,
-      -23170, -20787, -18204, -15446, -12539,  -9511,  -6393,  -3212 ]
-
-])
-
-X_PCM_7M5 = np.array([
-
-    [      0,   3212,   6392,   9512,  12539,  15446,  18205,  20788,
-       23170,  25328,  27244,  28898,  30272,  31357,  32137,  32609,
-       32767,  32609,  32138,  31356,  30272,  28898,  27245,  25330,
-       23169,  20787,  18205,  15446,  12539,   9511,   6393,   3212,
-           0,  -3212,  -6393,  -9512, -12540, -15446, -18204, -20787,
-      -23170, -25329, -27245, -28898, -30273, -31356, -32137, -32610,
-      -32766, -32609, -32137, -31356, -30272, -28898, -27244, -25329,
-      -23171, -20787, -18204, -15446, -12539,  -9511,  -6393,  -3212,
-          -1,   3212,   6393,   9512,  12540,  15446,  18204,  20788,
-       23169,  25329,  27245,  28898,  30273,  31356,  32137,  32609,
-       32767,  32609,  32137,  31356,  30273,  28898,  27245,  25330,
-       23170,  20787,  18204,  15446,  12540,   9512,   6393,   3212,
-           0,  -3212,  -6393,  -9512, -12539, -15447, -18204, -20787,
-      -23170, -25330, -27244, -28898, -30272, -31356, -32137, -32609,
-      -32767, -32609, -32137, -31356, -30273, -28898, -27244, -25330 ],
-
-    [ -23169, -20787, -18204, -15446, -12540,  -9511,  -6393,  -3212,
-           0,   3212,   6392,   9511,  12539,  15446,  18205,  20787,
-       23169,  25329,  27245,  28898,  30273,  31356,  32137,  32609,
-       32767,  32610,  32137,  31356,  30273,  28898,  27244,  25329,
-       23170,  20787,  18204,  15446,  12540,   9511,   6392,   3211,
-           0,  -3211,  -6393,  -9512, -12539, -15446, -18204, -20788,
-      -23170, -25329, -27245, -28898, -30273, -31356, -32137, -32609,
-      -32767, -32609, -32138, -31356, -30273, -28898, -27245, -25329,
-      -23170, -20788, -18205, -15447, -12539,  -9512,  -6392,  -3211,
-           0,   3211,   6393,   9512,  12539,  15446,  18204,  20787,
-       23170,  25329,  27244,  28897,  30273,  31356,  32137,  32609,
-       32767,  32609,  32137,  31356,  30273,  28897,  27244,  25330,
-       23170,  20787,  18205,  15446,  12539,   9512,   6393,   3213,
-           0,  -3212,  -6393,  -9512, -12540, -15447, -18205, -20787,
-      -23169, -25329, -27245, -28897, -30273, -31356, -32138, -32609 ]
-
-])
-
-X_PCM = [ X_PCM_7M5, X_PCM_10M ]
-
-
-### C.3.1.2  MDCT
-
-X_10M = np.array([
-
-    [ -5.8990646e+02,  3.2262618e+04, -2.7619007e+04,  9.5178147e+04,
-      -1.1418053e+05, -2.2419557e+05, -2.2347007e+03, -1.9867627e+04,
-      -1.6067159e+04,  5.8154816e+02, -1.5722676e+04,  4.0158688e+03,
-      -1.0088102e+04,  1.9017417e+03, -4.1954471e+03, -9.7496049e+02,
-       4.7235950e+02, -2.7499647e+03,  2.4705648e+03, -2.7529252e+03,
-       2.2309610e+03, -1.4814949e+03,  8.0924574e+02,  1.0419403e+02,
-      -6.3151413e+02,  1.1597939e+03, -1.3202428e+03,  1.3143498e+03,
-      -1.1022736e+03,  7.2762067e+02, -3.2778511e+02, -1.1134462e+02,
-       4.5185190e+02, -7.0065111e+02,  8.1286568e+02, -7.8143265e+02,
-       6.4645208e+02, -4.1055413e+02,  1.5009894e+02,  1.1832095e+02,
-      -3.4137778e+02,  4.8690132e+02, -5.5346349e+02,  5.1921969e+02,
-      -4.1808273e+02,  2.5332159e+02, -6.4719513e+01, -1.1746306e+02,
-       2.7072573e+02, -3.6623733e+02,  4.0439184e+02, -3.7097974e+02,
-       2.8704947e+02, -1.6297022e+02,  2.0395888e+01,  1.1218314e+02,
-      -2.2577273e+02,  2.8951685e+02, -3.1083971e+02,  2.7771234e+02,
-      -2.0730346e+02,  1.0688285e+02,  6.1320766e+00, -1.0817459e+02,
-       1.9320762e+02, -2.3845257e+02,  2.4826403e+02, -2.1609003e+02,
-       1.5207388e+02, -6.9546445e+01, -2.4211219e+01,  1.0403883e+02,
-      -1.6888652e+02,  2.0050394e+02, -2.0326028e+02,  1.7179995e+02,
-      -1.1399655e+02,  4.4242025e+01,  3.4454794e+01, -1.0009362e+02,
-       1.5218958e+02, -1.7342213e+02,  1.7018985e+02, -1.3794243e+02,
-       8.6156013e+01, -2.4921223e+01, -4.2552602e+01,  9.5559562e+01,
-      -1.3740945e+02,  1.5218075e+02, -1.4462762e+02,  1.1451272e+02,
-      -6.4959967e+01,  1.0627359e+01,  4.7836856e+01, -9.2854453e+01,
-       1.2654514e+02, -1.3593370e+02,  1.2459754e+02, -9.4075815e+01,
-       4.9816314e+01,  2.1529924e-01, -5.2425581e+01,  9.0995703e+01,
-      -1.1737069e+02,  1.2263969e+02, -1.0917602e+02,  7.9712422e+01,
-      -3.5722986e+01, -9.6955535e+00,  5.6602292e+01, -8.9431609e+01,
-       1.0953959e+02, -1.1249540e+02,  9.6333120e+01, -6.6446434e+01,
-       2.3693799e+01,  1.7708430e+01, -5.8794346e+01,  8.8078076e+01,
-      -1.0478463e+02,  1.0227969e+02, -8.4957399e+01,  5.5726976e+01,
-      -1.4768315e+01, -2.3981122e+01,  6.1741642e+01, -8.6508895e+01,
-       9.9868691e+01, -9.6459597e+01,  7.6331011e+01, -4.6588689e+01,
-       6.4212746e+00,  3.0432190e+01, -6.6116496e+01,  8.6861568e+01,
-      -9.6320778e+01,  8.9835533e+01, -6.7287784e+01,  3.7477990e+01,
-       1.3842189e+00, -3.5731585e+01,  6.8605400e+01, -8.7530923e+01,
-       9.3616286e+01, -8.3728496e+01,  6.0230516e+01, -2.9414119e+01,
-      -7.6609110e+00,  4.2149725e+01, -7.1968501e+01,  8.7743888e+01,
-      -9.0802890e+01,  7.8923198e+01, -5.4973827e+01,  2.2745574e+01,
-       1.5766746e+01, -4.7673661e+01,  7.5778794e+01, -8.9162686e+01 ],
-
-    [ -6.2843560e+03, -1.4627418e+04,  4.9801516e+03, -7.0344966e+04,
-       8.5564327e+04,  2.5840606e+05, -3.5208419e+04,  5.4644134e+04,
-      -1.1212441e+04,  1.8683629e+04,  1.8049757e+03,  7.0569176e+03,
-       1.2829514e+03,  4.0930299e+03,  1.1947052e+03,  1.5425662e+03,
-       6.0325642e+02,  6.4703789e+02,  1.5715070e+02,  1.0644751e+02,
-      -7.5883978e+00, -5.2773353e+01, -6.0996565e+01, -8.3409817e+01,
-      -4.3996776e+01, -3.3763658e+01, -1.2075849e+01, -2.9293481e+00,
-       5.2907677e+00,  1.4501256e+01,  1.0866309e+01,  9.1905374e+00,
-       3.0606323e+00,  1.6027594e+00, -1.1174900e+00, -5.4280671e+00,
-      -4.5919914e+00, -4.4199737e+00, -2.0085059e+00, -4.2065884e-01,
-       6.3446132e-01,  2.7718649e+00,  3.5654183e+00,  2.6588468e+00,
-       1.3730777e+00,  1.3705866e+00,  6.3801955e-02, -1.1191414e+00,
-      -2.1495458e+00, -2.1211746e+00, -1.1188698e+00, -1.3646427e+00,
-      -1.2245570e+00,  1.5630676e-01,  1.0243080e+00,  2.0104419e+00,
-       1.8985278e+00,  1.2447591e+00,  1.7309919e+00,  9.4234385e-01,
-      -3.3619810e-01, -1.6030368e+00, -7.1471558e-01, -2.0710920e+00,
-      -2.9423847e+00, -1.7873501e+00, -8.0633559e-01, -1.6093125e-03,
-       7.9415802e-01,  8.7937487e-01,  1.7154607e+00,  1.0383457e+00,
-       2.3742697e+00,  9.7376296e-01,  1.5290469e+00,  1.1078186e-01,
-      -1.3382843e+00, -8.0183060e-01, -1.1937948e+00, -1.6144857e+00,
-      -2.4688787e+00,  2.7158214e-01, -2.8802354e-01,  1.5704268e-01,
-      -2.7983134e-01,  7.3794617e-01,  1.0364997e+00,  6.9563470e-01,
-       9.9846216e-02,  7.4018952e-01, -1.6599106e-02, -6.7106650e-01,
-       9.4477394e-02, -1.1325310e+00, -1.2183007e+00, -1.1326694e+00,
-      -7.0637699e-01, -1.1096511e+00,  1.4381563e-01,  4.6121573e-01,
-       7.9281879e-01,  1.4577665e+00,  1.9515924e+00,  1.5493961e+00,
-       7.6923395e-01,  1.0666962e-01, -5.8376568e-01, -1.7768814e-01,
-      -7.8840041e-01, -8.4273142e-01, -1.1227955e-01,  1.7007988e-01,
-      -1.2788265e+00, -8.2037634e-01,  2.9027089e-02,  1.1299878e+00,
-       1.5466537e+00,  5.7106120e-01,  1.0054291e+00,  7.7078972e-01,
-      -1.2498850e-01,  1.4337381e-01, -9.1649732e-03,  1.5189923e-01,
-      -1.8656702e+00, -1.3065376e+00, -1.0474639e+00, -6.1982978e-01,
-      -4.0826276e-01, -2.5749212e-01,  7.5196564e-01,  5.9204803e-01,
-       3.8971675e-01,  4.7356386e-01,  9.6497659e-01,  1.0654369e+00,
-       1.0179577e-01, -8.7312829e-01, -1.0535862e+00, -5.5302243e-01,
-      -1.4681184e+00, -9.4220508e-01, -7.2255455e-01, -5.3132915e-01,
-      -1.4926868e-01,  3.6245889e-01,  1.6544183e-01,  7.2654545e-01,
-      -7.6464228e-02,  1.9158155e-02,  8.5964508e-01,  5.0113123e-02,
-      -3.5523428e-02, -1.0719814e+00, -1.0268355e+00, -3.9656991e-01,
-      -3.7302065e-01, -7.6860159e-01, -3.9431418e-01, -8.4906570e-02 ]
-
-])
-
-X_7M5 = np.array([
-
-    [ -2.7808220e+04,  3.1171944e+04, -1.0484449e+05, -1.6109747e+05,
-       7.1020534e+03,  6.1529776e+04, -4.3352596e+03,  1.6872730e+03,
-       8.3021669e+03, -8.0904023e+03,  2.8986710e+03,  1.6300470e+03,
-      -3.9442485e+03,  3.3369609e+03, -7.5314157e+02, -1.5665608e+03,
-       2.3627282e+03, -1.5968140e+03, -2.9454372e+01,  1.3170795e+03,
-      -1.5221502e+03,  7.5501127e+02,  3.4796318e+02, -1.0725998e+03,
-       9.9368163e+02, -2.9346750e+02, -4.7457374e+02,  8.5309969e+02,
-      -6.3326698e+02,  2.2874568e+01,  5.0703180e+02, -6.5956792e+02,
-       3.7615149e+02,  1.3645752e+02, -4.8970087e+02,  4.9257049e+02,
-      -1.8802536e+02, -2.2788339e+02,  4.4305145e+02, -3.4924777e+02,
-       5.0697164e+01,  2.7264213e+02, -3.8309643e+02,  2.2685761e+02,
-       4.8724694e+01, -2.8629820e+02,  3.1493179e+02, -1.2493286e+02,
-      -1.1855917e+02,  2.7825778e+02, -2.4421869e+02,  3.9752437e+01,
-       1.6382158e+02, -2.5263703e+02,  1.7444462e+02,  2.6833647e+01,
-      -1.8770774e+02,  2.1765469e+02, -1.1027623e+02, -7.8965164e+01,
-       1.9573957e+02, -1.7599945e+02,  4.9679803e+01,  1.1484006e+02,
-      -1.8898112e+02,  1.3092016e+02,  7.2222470e-01, -1.3789235e+02,
-       1.7321190e+02, -8.5147249e+01, -4.3896934e+01,  1.4839226e+02,
-      -1.4915199e+02,  4.1079567e+01,  7.7806126e+01, -1.4826509e+02,
-       1.1954125e+02, -9.5155767e-01, -1.0279192e+02,  1.3980505e+02,
-      -8.5889140e+01, -3.4465766e+01,  1.1923625e+02, -1.2267683e+02,
-       5.1023135e+01,  6.4082513e+01, -1.2613323e+02,  9.9909559e+01,
-      -1.6509945e+01, -8.6980952e+01,  1.2384580e+02, -7.3451876e+01,
-      -1.5354773e+01,  1.0245575e+02, -1.1445423e+02,  4.5501562e+01,
-       4.3867941e+01, -1.1121715e+02,  1.0028146e+02, -1.5168901e+01,
-      -6.9443433e+01,  1.1170827e+02, -7.9410276e+01, -1.4798645e+01,
-       8.7563510e+01, -1.0510000e+02,  5.5384253e+01,  4.1531846e+01,
-      -1.0069765e+02,  9.2428209e+01, -2.7848167e+01, -6.4462037e+01,
-       1.0628096e+02, -7.4978815e+01,  1.6316106e-01,  8.3599643e+01,
-      -1.0561445e+02,  5.2787841e+01,  2.7101412e+01, -9.7922081e+01 ],
-
-     [ 3.8121925e+03, -2.2031854e+04,  6.2134301e+04, -2.5584480e+05,
-      -4.0289726e+04, -1.1591648e+04,  9.9946415e+03, -5.6931570e+03,
-      -1.4027155e+03, -8.4816729e+01, -1.6692931e+02,  3.7467791e+01,
-       1.5876357e+02,  1.0909061e+02, -1.2446699e+01, -7.8016012e+01,
-      -5.9665224e+01,  1.7150538e+01,  4.1435247e+01,  1.4446691e+01,
-      -3.8364171e+00, -9.0141422e+00, -1.1905776e+01, -1.1758083e+01,
-       2.4695821e+00,  1.6421675e+01,  1.7958775e+01, -1.0112627e+00,
-      -1.7276148e+01, -1.4579197e+01, -3.0796621e-01,  3.4903800e+00,
-       8.9247274e+00,  1.0542251e+01,  5.1085251e+00, -6.3561781e+00,
-      -1.5196238e+01, -8.5814612e+00,  5.9801475e+00,  8.8788786e+00,
-       6.6693886e+00,  4.0520010e+00, -2.5648927e+00, -7.3376816e+00,
-      -9.4020988e+00, -3.7886737e+00,  9.8324354e+00,  1.0369412e+01,
-       2.7236309e+00, -1.1806094e+00, -5.4027659e+00, -7.1662503e+00,
-      -5.2723002e+00,  2.2261048e+00,  1.0161892e+01,  7.6651861e+00,
-      -2.2518007e+00, -5.1245529e+00, -5.3667831e+00, -4.6532034e+00,
-      -9.0748070e-01,  2.8558608e+00,  8.7396007e+00,  5.8069435e+00,
-      -4.9824625e+00, -6.3131200e+00, -4.2366242e+00, -1.2878986e+00,
-       1.9593460e+00,  4.4854081e+00,  6.1847341e+00,  1.9350643e+00,
-      -6.7952522e+00, -7.6836416e+00, -1.1341833e+00,  8.9242434e-01,
-       4.4208241e+00,  4.7103946e+00,  3.3250393e+00, -5.6553445e-01,
-      -6.6710206e+00, -5.9125999e+00,  8.2386239e-01,  4.8587135e+00,
-       4.1530321e+00,  3.6100791e+00,  3.4896727e-01, -3.5268730e+00,
-      -6.6259985e+00, -4.6374540e+00,  4.0614812e+00,  4.8600515e+00,
-       3.4816753e+00,  7.5221655e-01, -1.5176717e+00, -3.5752094e+00,
-      -5.3492120e+00, -1.8813288e+00,  4.5633890e+00,  5.8660550e+00,
-       2.6127035e+00, -1.4095531e+00, -3.2585158e+00, -4.0492745e+00,
-      -3.0513819e+00,  1.0864826e+00,  6.1166169e+00,  5.3791204e+00,
-      -2.6627677e-01, -3.3401114e+00, -4.3742918e+00, -3.7773803e+00,
-      -3.2386710e-01,  2.3283535e+00,  6.5557289e+00,  4.4470718e+00,
-      -2.5793855e+00, -4.6626375e+00, -3.9104033e+00, -1.1065239e+00 ]
-
-])
-
-X = [ X_7M5, X_10M ]
-
-
-### C.3.1.3  12.8 kHz resampler
-
-X_TILDE_12K8D_10M = np.array([
-
-    [  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00, -2.7136560e+00, -2.6569981e+00, -8.2098309e+00,
-       2.5435023e+00, -1.2692479e+01,  3.2383771e+00, -4.3038498e+01,
-       1.6475668e+02,  3.9002834e+03,  7.6897871e+03,  1.1168030e+04,
-       1.4388464e+04,  1.7228860e+04,  1.9693442e+04,  2.1734211e+04,
-       2.3341435e+04,  2.4458687e+04,  2.5091505e+04,  2.5237788e+04,
-       2.4898746e+04,  2.4056493e+04,  2.2750447e+04,  2.1002836e+04,
-       1.8837538e+04,  1.6271528e+04,  1.3371876e+04,  1.0177959e+04,
-       6.7345474e+03,  3.0859120e+03, -6.8926189e+02, -4.5387403e+03,
-      -8.4127977e+03, -1.2248756e+04, -1.5972309e+04, -1.9535642e+04,
-      -2.2893718e+04, -2.5977602e+04, -2.8736936e+04, -3.1140674e+04,
-      -3.3157374e+04, -3.4729421e+04, -3.5844044e+04, -3.6489767e+04,
-      -3.6657691e+04, -3.6317413e+04, -3.5493490e+04, -3.4198956e+04,
-      -3.2451848e+04, -3.0250170e+04, -2.7655246e+04, -2.4700058e+04,
-      -2.1422013e+04, -1.7854871e+04, -1.4073383e+04, -1.0130011e+04,
-      -6.0696279e+03, -1.9523115e+03,  2.1432690e+03,  6.1682081e+03,
-       1.0075311e+04,  1.3793150e+04,  1.7261182e+04,  2.0439154e+04,
-       2.3290746e+04,  2.5752435e+04,  2.7792879e+04,  2.9395551e+04,
-       3.0539419e+04,  3.1182642e+04,  3.1334658e+04,  3.1000121e+04,
-       3.0185153e+04,  2.8876238e+04,  2.7121382e+04,  2.4947537e+04,
-       2.2382237e+04,  1.9447889e+04,  1.6216333e+04,  1.2730773e+04,
-       9.0366635e+03,  5.1844000e+03,  1.2542494e+03, -2.7040631e+03,
-      -6.6402694e+03, -1.0487968e+04, -1.4176374e+04, -1.7659977e+04,
-      -2.0896015e+04, -2.3817756e+04, -2.6379477e+04, -2.8555427e+04,
-      -3.0316787e+04, -3.1610147e+04, -3.2431837e+04, -3.2774372e+04,
-      -3.2634728e+04, -3.1986767e+04, -3.0864057e+04, -2.9283835e+04,
-      -2.7267471e+04, -2.4822384e+04, -2.2011114e+04, -1.8874753e+04,
-      -1.5449273e+04                                                 ],
-
-    [ -1.5449273e+04, -1.1776342e+04, -7.9350741e+03, -3.9753420e+03,
-       5.6212790e+01,  4.0948091e+03,  8.0660721e+03,  1.1920829e+04,
-       1.5610547e+04,  1.9067628e+04,  2.2236206e+04,  2.5079838e+04,
-       2.7562773e+04,  2.9625190e+04,  3.1247708e+04,  3.2411870e+04,
-       3.3105731e+04,  3.3290869e+04,  3.2986189e+04,  3.2200486e+04,
-       3.0944512e+04,  2.9210715e+04,  2.7055339e+04,  2.4507368e+04,
-       2.1598895e+04,  1.8359789e+04,  1.4864140e+04,  1.1157303e+04,
-       7.2876208e+03,  3.3081055e+03, -7.0156289e+02, -4.6891569e+03,
-      -8.6058356e+03, -1.2384088e+04, -1.5958973e+04, -1.9284607e+04,
-      -2.2323146e+04, -2.5008095e+04, -2.7303120e+04, -2.9181339e+04,
-      -3.0623681e+04, -3.1579731e+04, -3.2052556e+04, -3.2039965e+04,
-      -3.1544863e+04, -3.0546815e+04, -2.9086911e+04, -2.7186800e+04,
-      -2.4871192e+04, -2.2155336e+04, -1.9106913e+04, -1.5768099e+04,
-      -1.2180718e+04, -8.3886907e+03, -4.4727275e+03, -4.8397983e+02,
-       3.5287168e+03,  7.5007937e+03,  1.1356312e+04,  1.5052293e+04,
-       1.8540697e+04,  2.1752202e+04,  2.4638955e+04,  2.7166972e+04,
-       2.9306556e+04,  3.0998943e+04,  3.2231603e+04,  3.2995051e+04,
-       3.3278595e+04,  3.3050597e+04,  3.2338337e+04,  3.1153755e+04,
-       2.9512841e+04,  2.7417674e+04,  2.4925451e+04,  2.2071406e+04,
-       1.8894430e+04,  1.5423055e+04,  1.1739349e+04,  7.8892265e+03,
-       3.9209702e+03, -1.0578292e+02, -4.1130091e+03, -8.0523247e+03,
-      -1.1874762e+04, -1.5509335e+04, -1.8896353e+04, -2.1995901e+04,
-      -2.4770068e+04, -2.7154380e+04, -2.9121926e+04, -3.0652080e+04,
-      -3.1724909e+04, -3.2299883e+04, -3.2385523e+04, -3.1985436e+04,
-      -3.1105919e+04, -2.9735628e+04, -2.7920239e+04, -2.5686848e+04,
-      -2.3064137e+04, -2.0073280e+04, -1.6788431e+04, -1.3249779e+04,
-      -9.5054623e+03, -5.6021829e+03, -1.6246392e+03,  2.3816986e+03,
-       6.3633660e+03,  1.0254738e+04,  1.3986117e+04,  1.7511038e+04,
-       2.0787917e+04,  2.3746769e+04,  2.6347708e+04,  2.8559387e+04,
-       3.0354770e+04,  3.1683451e+04,  3.2536757e+04,  3.2910967e+04,
-       3.2802210e+04,  3.2182725e+04,  3.1087832e+04,  2.9535420e+04,
-       2.7544339e+04,  2.5123821e+04,  2.2336463e+04,  1.9222835e+04,
-       1.5819986e+04                                                 ]
-
-])
-
-X_TILDE_12K8D_7M5 = np.array([
-
-     [ 0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00, -2.7136560e+00, -2.6569981e+00, -8.2098309e+00,
-       2.5435023e+00, -1.2692479e+01,  3.2383771e+00, -4.3038498e+01,
-       1.6475668e+02,  3.9002834e+03,  7.6897871e+03,  1.1168030e+04,
-       1.4388464e+04,  1.7228860e+04,  1.9693442e+04,  2.1734211e+04,
-       2.3341435e+04,  2.4458687e+04,  2.5091505e+04,  2.5237788e+04,
-       2.4898746e+04,  2.4056493e+04,  2.2750447e+04,  2.1002836e+04,
-       1.8837538e+04,  1.6271528e+04,  1.3371876e+04,  1.0177959e+04,
-       6.7345474e+03,  3.0859120e+03, -6.8926189e+02, -4.5387403e+03,
-      -8.4127977e+03, -1.2248756e+04, -1.5972309e+04, -1.9535642e+04,
-      -2.2893718e+04, -2.5977602e+04, -2.8736936e+04, -3.1140674e+04,
-      -3.3157374e+04, -3.4729421e+04, -3.5844044e+04, -3.6489767e+04,
-      -3.6657691e+04, -3.6317413e+04, -3.5493490e+04, -3.4198956e+04,
-      -3.2451848e+04, -3.0250170e+04, -2.7655246e+04, -2.4700058e+04,
-      -2.1422013e+04                                                 ],
-
-    [ -2.1422013e+04, -1.7854871e+04, -1.4073383e+04, -1.0130011e+04,
-      -6.0696279e+03, -1.9523115e+03,  2.1432690e+03,  6.1682081e+03,
-       1.0075311e+04,  1.3793150e+04,  1.7261182e+04,  2.0439154e+04,
-       2.3290746e+04,  2.5752435e+04,  2.7792879e+04,  2.9395551e+04,
-       3.0539419e+04,  3.1182642e+04,  3.1334658e+04,  3.1000121e+04,
-       3.0185153e+04,  2.8876238e+04,  2.7121382e+04,  2.4947537e+04,
-       2.2382237e+04,  1.9447889e+04,  1.6216333e+04,  1.2730773e+04,
-       9.0366635e+03,  5.1844000e+03,  1.2542494e+03, -2.7040631e+03,
-      -6.6402694e+03, -1.0487968e+04, -1.4176374e+04, -1.7659977e+04,
-      -2.0896015e+04, -2.3817756e+04, -2.6379477e+04, -2.8555427e+04,
-      -3.0316787e+04, -3.1610147e+04, -3.2431837e+04, -3.2774372e+04,
-      -3.2634728e+04, -3.1986767e+04, -3.0864057e+04, -2.9283835e+04,
-      -2.7267471e+04, -2.4822384e+04, -2.2011114e+04, -1.8874753e+04,
-      -1.5449273e+04, -1.1776342e+04, -7.9350741e+03, -3.9753420e+03,
-       5.6212790e+01,  4.0948091e+03,  8.0660721e+03,  1.1920829e+04,
-       1.5610547e+04,  1.9067628e+04,  2.2236206e+04,  2.5079838e+04,
-       2.7562773e+04,  2.9625190e+04,  3.1247708e+04,  3.2411870e+04,
-       3.3105731e+04,  3.3290869e+04,  3.2986189e+04,  3.2200486e+04,
-       3.0944512e+04,  2.9210715e+04,  2.7055339e+04,  2.4507368e+04,
-       2.1598895e+04,  1.8359789e+04,  1.4864140e+04,  1.1157303e+04,
-       7.2876208e+03,  3.3081055e+03, -7.0156289e+02, -4.6891569e+03,
-      -8.6058356e+03, -1.2384088e+04, -1.5958973e+04, -1.9284607e+04,
-      -2.2323146e+04, -2.5008095e+04, -2.7303120e+04, -2.9181339e+04,
-      -3.0623681e+04, -3.1579731e+04, -3.2052556e+04, -3.2039965e+04,
-      -3.1544863e+04                                                 ]
-
-])
-
-X_TILDE_12K8D = [ X_TILDE_12K8D_7M5, X_TILDE_12K8D_10M ]
-
-
-### C.3.1.4  Pitch analysis
-
-T_CURR_10M = np.array([ 25, 26 ])
-T_CURR_7M5 = np.array([ 22, 25 ])
-T_CURR = [ T_CURR_7M5, T_CURR_10M ]
-
-NORMCORR_10M = np.array([ 0.677220, 0.992748 ])
-NORMCORR_7M5 = np.array([ 0.473429, 0.952099 ])
-NORMCORR = [ NORMCORR_7M5, NORMCORR_10M ]
-
-T1_10M = np.array([ 25, 26 ])
-T1_7M5 = np.array([ 22, 25 ])
-T1 = [ T1_7M5, T1_10M ]
-
-T2_10M = np.array([ 21, 26 ])
-T2_7M5 = np.array([ 21, 25 ])
-T2 = [ T2_7M5, T2_10M ]
-
-NORMCORR1_10M = np.array([ 0.677220, 0.992748 ])
-NORMCORR1_7M5 = np.array([ 0.473429, 0.952099 ])
-NORMCORR1 = [ NORMCORR1_7M5, NORMCORR1_10M ]
-
-NORMCORR2_10M = np.array([ 0.276293, 0.992748 ])
-NORMCORR2_7M5 = np.array([ 0.000000, 0.952099 ])
-NORMCORR2 = [ NORMCORR2_7M5, NORMCORR2_10M ]
-
-
-### C.3.1.5  LTPF encoder
-
-PITCH_PRESENT_10M = np.array([ 1, 1 ])
-PITCH_PRESENT_7M5 = np.array([ 0, 1 ])
-PITCH_PRESENT = [ PITCH_PRESENT_7M5, PITCH_PRESENT_10M ]
-
-PITCH_INDEX_10M = np.array([ 76, 76 ])
-PITCH_INDEX_7M5 = np.array([  0, 72 ])
-PITCH_INDEX = [ PITCH_INDEX_7M5, PITCH_INDEX_10M ]
-
-LTPF_ACTIVE_10M = np.array([ 0, 0 ])
-LTPF_ACTIVE_7M5 = np.array([ 0, 0 ])
-LTPF_ACTIVE = [ LTPF_ACTIVE_7M5, LTPF_ACTIVE_10M ]
-
-NC_LTPF_10M = np.array([ 0.690317, 0.998707 ])
-NC_LTPF_7M5 = np.array([ 0.000000, 0.963121 ])
-NC_LTPF = [ NC_LTPF_7M5, NC_LTPF_10M ]
-
-
-### C.3.1.6  Per-band energy
-
-E_B_10M = np.array([
-
-    [  3.4798963e+05,  1.0408765e+09,  7.6280953e+08,  9.0588797e+09,
-       1.3037195e+10,  5.0263652e+10,  4.9938874e+06,  3.9472260e+08,
-       2.5815358e+08,  3.3819826e+05,  2.4720253e+08,  1.6127203e+07,
-       1.0176981e+08,  3.6166214e+06,  1.7601776e+07,  9.5054796e+05,
-       2.2312350e+05,  7.5623058e+06,  6.1036902e+06,  7.5785971e+06,
-       4.9771870e+06,  2.1948272e+06,  6.5487867e+05,  1.0856397e+04,
-       3.9881009e+05,  1.3451218e+06,  1.7430411e+06,  1.7275154e+06,
-       8.7221944e+05,  5.9920352e+04,  3.4754106e+05,  6.3569381e+05,
-       2.9322749e+05,  1.8264770e+04,  1.7680584e+05,  2.8795546e+05,
-       1.1948250e+05,  8.9930924e+03,  1.0371110e+05,  1.5057936e+05,
-       3.6457561e+04,  4.9126130e+04,  7.2240065e+04,  7.7210962e+03,
-       5.1941280e+04,  2.4886024e+04,  1.3310972e+04,  3.7010599e+04,
-       6.5396090e+03,  2.5307401e+04,  4.7465699e+03,  1.9017662e+04,
-       3.8107631e+03,  1.2269623e+04,  7.9690160e+03,  4.5694858e+03,
-       9.2694695e+03,  4.6138651e+03,  4.2316029e+03,  5.8000267e+03,
-       5.7863671e+03,  4.4037279e+03,  3.9216878e+03,  3.7117332e+03 ],
-
-    [  3.9493130e+07,  2.1396136e+08,  2.4801910e+07,  4.9484143e+09,
-       7.3212541e+09,  6.6773691e+10,  1.2396327e+09,  2.9859813e+09,
-       1.2571883e+08,  3.4907798e+08,  3.2579374e+06,  4.9800086e+07,
-       1.6459643e+06,  1.6752894e+07,  1.4273206e+06,  2.3795106e+06,
-       3.6391831e+05,  4.1865804e+05,  2.4696343e+04,  1.1331072e+04,
-       5.7583781e+01,  2.7850268e+03,  3.7205810e+03,  6.9571975e+03,
-       1.9357163e+03,  1.1399846e+03,  1.4582614e+02,  8.5810802e+00,
-       1.1913932e+02,  1.0127132e+02,  5.9681539e+00,  1.5356348e+01,
-       2.0311276e+01,  2.1055250e+00,  4.0428882e+00,  9.8908370e+00,
-       1.8819250e+00,  6.2827408e-01,  4.5599643e+00,  1.5570596e+00,
-       8.5772617e-01,  3.0652366e+00,  1.3324581e+00,  2.4566558e+00,
-       4.1674750e+00,  4.6799657e-01,  3.2193746e+00,  1.0994905e+00,
-       1.6164118e+00,  1.5691847e+00,  5.4527735e-01,  2.5211389e-01,
-       1.0146872e+00,  5.1845169e-01,  1.7875047e+00,  3.4334672e-01,
-       7.2301137e-01,  8.6777500e-01,  1.0547766e+00,  2.8083700e-01,
-       6.9555959e-01,  6.1528702e-01,  1.8631657e-01,  4.6476980e-01 ]
-
-])
-
-E_B_7M5 = np.array([
-
-    [  7.7329708e+08,  9.7169006e+08,  1.0992368e+10,  2.5952396e+10,
-       5.0439162e+07,  3.7859133e+09,  1.8794475e+07,  2.8468901e+06,
-       6.8925976e+07,  6.5454609e+07,  8.4022938e+06,  2.6570531e+06,
-       1.5557097e+07,  1.1135308e+07,  5.6722222e+05,  2.4541127e+06,
-       5.5824847e+06,  2.5498151e+06,  8.6756001e+02,  1.7346984e+06,
-       2.3169412e+06,  5.7004201e+05,  1.2107837e+05,  1.1504704e+06,
-       9.8740319e+05,  8.6123175e+04,  2.2522023e+05,  7.2777908e+05,
-       4.0102706e+05,  5.2324588e+02,  2.5708125e+05,  4.3502984e+05,
-       1.4148995e+05,  1.8620654e+04,  2.4121631e+05,  4.3642188e+04,
-       1.5913430e+05,  3.8451966e+04,  9.9113627e+04,  4.2170377e+04,
-       5.7395127e+04,  4.5741834e+04,  3.0611513e+04,  4.5331490e+04,
-       1.5575486e+04,  4.1303881e+04,  9.1981721e+03,  3.4644893e+04,
-       1.7123395e+04,  1.2051636e+04,  1.3059786e+04,  1.5318037e+04,
-       1.4108814e+04,  1.0037512e+04,  7.5940392e+03,  7.2531779e+03,
-       8.4324439e+03,  7.8664771e+03,  7.3659535e+03,  6.8968988e+03,
-       6.3095928e+03,  5.8688253e+03,  4.3696732e+03,  6.2506180e+03 ],
-
-    [  1.4532812e+07,  4.8540259e+08,  3.8606713e+09,  6.5456561e+10,
-       1.6232620e+09,  1.3436631e+08,  9.9892859e+07,  3.2412037e+07,
-       1.9676109e+06,  7.1938775e+03,  2.7865394e+04,  1.4038353e+03,
-       2.5205872e+04,  1.1900762e+04,  1.5492031e+02,  6.0864981e+03,
-       3.5599390e+03,  2.9414095e+02,  1.7168797e+03,  2.0870689e+02,
-       1.4718096e+01,  8.1254760e+01,  1.4174751e+02,  1.3825251e+02,
-       6.0988360e+00,  2.6967143e+02,  3.2251759e+02,  1.0226523e+00,
-       2.9846530e+02,  2.1255299e+02,  9.4843183e-02,  1.2182752e+01,
-       7.9650760e+01,  1.1113906e+02,  3.3249014e+01,  1.5228356e+02,
-       5.7298325e+01,  3.0449728e+01,  3.0210123e+01,  5.1376755e+01,
-       1.0210074e+02,  4.4060019e+00,  4.0272511e+01,  1.6376346e+01,
-       8.1009561e+01,  1.5665824e+01,  2.5227331e+01,  4.4897310e+00,
-       4.4975382e+01,  1.9821051e+01,  2.0736286e+01,  3.6319425e+01,
-       7.2088263e+00,  1.1187844e+01,  2.6713368e+01,  1.7962481e+01,
-       1.9492612e+01,  1.3200905e+01,  1.1809729e+01,  1.6012045e+01,
-       9.3764812e+00,  1.9393796e+01,  1.6381346e+01,  1.2937103e+01 ]
-
-])
-
-E_B = [ E_B_7M5, E_B_10M ]
-
-
-### C.3.1.7  Bandwidth detector
-
-P_BW_10M = np.array([ 1, 1 ])
-P_BW_7M5 = np.array([ 1, 1 ])
-P_BW = [ P_BW_7M5, P_BW_10M ]
-
-
-### C.3.1.8  SNS gains
-
-SCF_10M = np.array([
-
-    [  3.5009846e+00,  4.4942639e+00,  2.3071956e+00,  1.2022551e+00,
-       6.2362294e-01,  9.4722039e-03, -3.2828840e-02, -3.3840570e-01,
-      -7.2946152e-01, -1.0018093e+00, -1.2127892e+00, -1.4678244e+00,
-      -1.6640459e+00, -1.8392946e+00, -1.9307510e+00, -1.9205837e+00 ],
-
-    [  3.7432369e+00,  5.6927098e+00,  3.2662471e+00,  1.4474935e+00,
-      -4.4505556e-01, -1.2458756e+00, -1.2458756e+00, -1.2458756e+00,
-      -1.2458756e+00, -1.2458756e+00, -1.2458756e+00, -1.2458756e+00,
-      -1.2458756e+00, -1.2458756e+00, -1.2458756e+00, -1.2458756e+00 ],
-
-    [ -1.5649514e+00, -1.1656014e+00, -1.5624815e+00, -1.1411195e+00,
-      -7.4976482e-01, -2.4654068e-01,  1.3534391e-01, -1.1293867e-01,
-      -1.6932960e-01,  5.7623565e-02,  6.7159547e-01,  9.3859612e-01,
-       1.0824257e+00,  1.2057632e+00,  1.4053510e+00,  1.2160286e+00 ]
-
-])
-
-SCF_7M5 = np.array([
-
-    [  4.4048340e+00,  3.5389298e+00,  1.5267043e+00,  7.9358598e-01,
-       3.0615231e-01, -3.2867352e-02, -2.7847443e-01, -4.6371063e-01,
-      -6.8557046e-01, -8.7944953e-01, -1.0976367e+00, -1.2286102e+00,
-      -1.3879732e+00, -1.5027094e+00, -1.5071962e+00, -1.5060084e+00 ],
-
-    [  5.4182466e+00,  4.3372862e+00,  3.1487482e-01, -7.7464674e-01,
-      -7.7464674e-01, -7.7464674e-01, -7.7464674e-01, -7.7464674e-01,
-      -7.7464674e-01, -7.7464674e-01, -7.7464674e-01, -7.7464674e-01,
-      -7.7464674e-01, -7.7464674e-01, -7.7464674e-01, -7.7464674e-01 ]
-
-])
-
-SCF = [ SCF_7M5, SCF_10M ]
-
-
-### C.3.1.9  SNS quantization: stage 2
-
-T2ROT_10M = np.array([
-
-    [ -3.8060310e-01,  2.8077898e-01, -6.7415911e-01, -4.4050504e-01,
-      -5.0680535e-02,  1.2946234e+00, -3.1905543e-02, -8.3983883e-01,
-      -3.0573474e-02, -1.7180800e-01, -3.1795511e-01, -5.6801435e-01,
-      -3.4459445e-01, -5.3266246e-02, -6.2922325e-02, -2.0856957e-01 ],
-
-    [ -6.9862836e-01,  7.4316023e-01,  1.6797292e-01,  1.7318569e+00,
-       2.0540381e-01, -3.3073095e-01, -5.2816094e-01, -1.0380535e+00,
-      -6.4017558e-01, -3.8876809e-01, -3.3239735e-01, -4.8437565e-01,
-      -4.2394514e-01, -3.6228481e-01, -2.3217161e-01, -2.2684893e-01 ],
-
-    [ -1.0349648e+00, -8.7888573e-01,  1.9107834e-01, -9.0835649e-01,
-       7.6273219e-01,  5.4107875e-01,  7.5161773e-01, -7.8846551e-02,
-       1.4299991e-01, -1.5545871e-01, -6.6321266e-02, -7.2264622e-02,
-      -1.3018946e-01, -2.4094909e-01, -1.6785267e-01,  7.1244633e-02 ]
-
-])
-
-T2ROT_7M5 = np.array([
-
-    [  5.5508969e-01,  3.8471081e-01,  4.0948426e-02, -7.5029612e-01,
-      -9.7881975e-01,  4.9702346e-01, -8.2168015e-02, -4.3546804e-01,
-       1.8914981e-01, -1.9792621e-01, -2.4794744e-01, -2.9000112e-01,
-      -2.2928306e-01, -8.1890752e-02, -1.0731157e-01, -2.2349961e-01 ],
-
-    [ -1.9137509e-01, -1.3751444e-01,  4.4707625e-01,  4.0374158e-01,
-       1.6628366e+00,  3.1663673e-01,  5.0786462e-01,  5.0214496e-01,
-      -2.8554914e-01, -6.2625497e-01, -4.7743904e-01, -5.5803079e-01,
-      -4.8903072e-01, -4.6108945e-01, -2.3248007e-01, -1.2652277e-01 ],
-
-])
-
-T2ROT = [ T2ROT_7M5, T2ROT_10M ]
-
-SNS_Y0_10M = np.array([
-    [ -1, 1,-2,-1, 0, 3, 0,-2, 0, 0, 0,-1, 0, 0, 0, 0 ],
-    [ -1, 1, 0, 3, 0, 0,-1,-2,-1,-1, 0,-1, 0, 0, 0, 0 ],
-    [ -2,-2, 0,-2, 2, 1, 1, 0, 0, 0, 0, 0, 0,-1, 0, 0 ]
-])
-
-SNS_Y1_10M = np.array([
-    [ -1, 1,-2,-1, 0, 3, 0,-2, 0, 0 ],
-    [ -1, 1, 0, 3, 0, 0,-1,-2,-1,-1 ],
-    [ -2,-2, 0,-2, 2, 1, 1, 0, 0, 0 ]
-])
-
-SNS_Y2_10M = np.array([
-    [ -1, 0,-1,-1, 0, 2, 0,-1, 0, 0, 0,-1,-1, 0, 0, 0 ],
-    [ -1, 1, 0, 2, 0, 0,-1,-1,-1, 0, 0,-1, 0, 0, 0, 0 ],
-    [ -2,-1, 0,-2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
-])
-
-SNS_Y3_10M = np.array([
-    [  0, 0,-1,-1, 0, 2, 0,-1, 0, 0, 0,-1, 0, 0, 0, 0 ],
-    [ -1, 1, 0, 2, 0, 0, 0,-1,-1, 0, 0, 0, 0, 0, 0, 0 ],
-    [ -1,-1, 0,-1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
-])
-
-SNS_Y0_7M5 = np.array([
-    [  1, 1, 0,-2,-3, 1, 0,-1, 0,-1, 0,-1, 0, 0, 0, 0 ],
-    [  0, 0, 1, 1, 3, 1, 1, 1,-1,-1, 0,-1, 0, 0, 0, 0 ]
-])
-
-SNS_Y1_7M5 = np.array([
-    [  1, 1, 0,-2,-3, 1, 0,-1, 0,-1 ],
-    [  0, 0, 1, 1, 3, 1, 1, 1,-1,-1 ]
-])
-
-SNS_Y2_7M5 = np.array([
-    [  1, 1, 0,-1,-2, 1, 0,-1, 0, 0, 0,-1, 0, 0, 0, 0 ],
-    [  0, 0, 0, 0, 2, 0, 1, 1, 0,-1,-1,-1,-1, 0, 0, 0 ]
-])
-
-SNS_Y3_7M5 = np.array([
-    [  1, 0, 0,-1,-2, 1, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0 ],
-    [  0, 0, 0, 0, 2, 0, 1, 1, 0,-1, 0,-1, 0, 0, 0, 0 ]
-])
-
-SNS_Y0 = [ SNS_Y0_7M5, SNS_Y0_10M ]
-SNS_Y1 = [ SNS_Y1_7M5, SNS_Y1_10M ]
-SNS_Y2 = [ SNS_Y2_7M5, SNS_Y2_10M ]
-SNS_Y3 = [ SNS_Y3_7M5, SNS_Y3_10M ]
-
-SNS_XQ0_10M = np.array([
-
-    [ -2.1821789e-01,  2.1821789e-01, -4.3643578e-01, -2.1821789e-01,
-      -0.0000000e+00,  6.5465367e-01, -0.0000000e+00, -4.3643578e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -2.1821789e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ],
-
-    [ -2.2941573e-01,  2.2941573e-01,  0.0000000e+00,  6.8824720e-01,
-       0.0000000e+00, -0.0000000e+00, -2.2941573e-01, -4.5883147e-01,
-      -2.2941573e-01, -2.2941573e-01, -0.0000000e+00, -2.2941573e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ],
-
-    [ -4.5883147e-01, -4.5883147e-01,  0.0000000e+00, -4.5883147e-01,
-       4.5883147e-01,  2.2941573e-01,  2.2941573e-01, -0.0000000e+00,
-       0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -2.2941573e-01, -0.0000000e+00,  0.0000000e+00 ]
-
-])
-
-SNS_XQ1_10M = np.array([
-
-    [ -2.2360680e-01,  2.2360680e-01, -4.4721360e-01, -2.2360680e-01,
-      -0.0000000e+00,  6.7082039e-01, -0.0000000e+00, -4.4721360e-01,
-      -0.0000000e+00, -0.0000000e+00 ],
-
-    [ -2.3570226e-01,  2.3570226e-01,  0.0000000e+00,  7.0710678e-01,
-       0.0000000e+00, -0.0000000e+00, -2.3570226e-01, -4.7140452e-01,
-      -2.3570226e-01, -2.3570226e-01 ],
-
-    [ -4.7140452e-01, -4.7140452e-01, 0.0000000e+00, -4.7140452e-01,
-       4.7140452e-01,  2.3570226e-01, 2.3570226e-01, -0.0000000e+00,
-       0.0000000e+00, -0.0000000e+00 ],
-
-])
-
-SNS_XQ2_10M = np.array([
-
-    [ -3.1622777e-01,  0.0000000e+00, -3.1622777e-01, -3.1622777e-01,
-      -0.0000000e+00,  6.3245553e-01, -0.0000000e+00, -3.1622777e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -3.1622777e-01,
-      -3.1622777e-01, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ],
-
-    [ -3.1622777e-01,  3.1622777e-01,  0.0000000e+00,  6.3245553e-01,
-       0.0000000e+00, -0.0000000e+00, -3.1622777e-01, -3.1622777e-01,
-      -3.1622777e-01, -0.0000000e+00, -0.0000000e+00, -3.1622777e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ],
-
-    [ -5.7735027e-01, -2.8867513e-01,  0.0000000e+00, -5.7735027e-01,
-       2.8867513e-01,  2.8867513e-01,  2.8867513e-01, -0.0000000e+00,
-       0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,  0.0000000e+00 ]
-
-])
-
-SNS_XQ3_10M = np.array([
-
-    [ -0.0000000e+00,  0.0000000e+00, -3.5355339e-01, -3.5355339e-01,
-      -0.0000000e+00,  7.0710678e-01, -0.0000000e+00, -3.5355339e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -3.5355339e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ],
-
-    [ -3.5355339e-01,  3.5355339e-01,  0.0000000e+00,  7.0710678e-01,
-       0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -3.5355339e-01,
-      -3.5355339e-01, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ],
-
-    [ -4.0824829e-01, -4.0824829e-01,  0.0000000e+00, -4.0824829e-01,
-       4.0824829e-01,  4.0824829e-01,  4.0824829e-01, -0.0000000e+00,
-       0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,  0.0000000e+00 ]
-
-])
-
-SNS_XQ0_7M5 = np.array([
-
-    [  2.2941573e-01,  2.2941573e-01,  0.0000000e+00, -4.5883147e-01,
-      -6.8824720e-01,  2.2941573e-01, -0.0000000e+00, -2.2941573e-01,
-       0.0000000e+00, -2.2941573e-01, -0.0000000e+00, -2.2941573e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ],
-
-    [ -0.0000000e+00, -0.0000000e+00,  2.4253563e-01,  2.4253563e-01,
-       7.2760688e-01,  2.4253563e-01,  2.4253563e-01,  2.4253563e-01,
-      -2.4253563e-01, -2.4253563e-01, -0.0000000e+00, -2.4253563e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ]
-
-])
-
-SNS_XQ1_7M5 = np.array([
-
-    [  2.3570226e-01,  2.3570226e-01,  0.0000000e+00, -4.7140452e-01,
-      -7.0710678e-01,  2.3570226e-01, -0.0000000e+00, -2.3570226e-01,
-       0.0000000e+00, -2.3570226e-01 ],
-
-    [ -0.0000000e+00, -0.0000000e+00,  2.5000000e-01,  2.5000000e-01,
-       7.5000000e-01,  2.5000000e-01,  2.5000000e-01,  2.5000000e-01,
-      -2.5000000e-01, -2.5000000e-01 ]
-
-])
-
-SNS_XQ2_7M5 = np.array([
-
-    [  3.1622777e-01,  3.1622777e-01,  0.0000000e+00, -3.1622777e-01,
-      -6.3245553e-01,  3.1622777e-01, -0.0000000e+00, -3.1622777e-01,
-       0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -3.1622777e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ],
-
-    [ -0.0000000e+00, -0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       6.3245553e-01,  0.0000000e+00,  3.1622777e-01,  3.1622777e-01,
-      -0.0000000e+00, -3.1622777e-01, -3.1622777e-01, -3.1622777e-01,
-      -3.1622777e-01, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ]
-
-])
-
-SNS_XQ3_7M5 = np.array([
-
-    [  3.5355339e-01,  0.0000000e+00,  0.0000000e+00, -3.5355339e-01,
-      -7.0710678e-01,  3.5355339e-01, -0.0000000e+00, -3.5355339e-01,
-       0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ],
-
-    [ -0.0000000e+00, -0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       7.0710678e-01,  0.0000000e+00,  3.5355339e-01,  3.5355339e-01,
-      -0.0000000e+00, -3.5355339e-01, -0.0000000e+00, -3.5355339e-01,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00 ]
-
-])
-
-SNS_XQ0 = [ SNS_XQ0_7M5, SNS_XQ0_10M ]
-SNS_XQ1 = [ SNS_XQ1_7M5, SNS_XQ1_10M ]
-SNS_XQ2 = [ SNS_XQ2_7M5, SNS_XQ2_10M ]
-SNS_XQ3 = [ SNS_XQ3_7M5, SNS_XQ3_10M ]
-
-
-### C.3.1.10/12  SNS quantization gains
-
-IND_LF_10M = np.array([ 25, 25,  4 ])
-IND_LF_7M5 = np.array([ 17, 17 ])
-IND_LF = [ IND_LF_7M5, IND_LF_10M ]
-
-IND_HF_10M = np.array([  8,  9, 27 ])
-IND_HF_7M5 = np.array([  8, 25 ])
-IND_HF = [ IND_HF_7M5, IND_HF_10M ]
-
-SUBMODE_MSB_10M = np.array([ 0, 0, 1 ])
-SUBMODE_MSB_7M5 = np.array([ 0, 0 ])
-SUBMODE_MSB = [ SUBMODE_MSB_7M5, SUBMODE_MSB_10M ]
-
-SUBMODE_LSB_10M = np.array([ 0, 0, 1 ])
-SUBMODE_LSB_7M5 = np.array([ 1, 0 ])
-SUBMODE_LSB = [ SUBMODE_LSB_7M5, SUBMODE_LSB_10M ]
-
-G_IND_10M = np.array([ 0, 0, 3 ])
-G_IND_7M5 = np.array([ 0, 0 ])
-G_IND = [ G_IND_7M5, G_IND_10M ]
-
-LS_IND_A_10M = np.array([ 1, 1, 1 ])
-LS_IND_A_7M5 = np.array([ 0, 0 ])
-LS_IND_A = [ LS_IND_A_7M5, LS_IND_A_10M ]
-
-LS_IND_B_10M = np.array([ True, True, None ])
-LS_IND_B_7M5 = np.array([ None, 1 ])
-LS_IND_B = [ LS_IND_B_7M5, LS_IND_B_10M ]
-
-IDX_A_10M = np.array([  865837, 1023911, 61886 ])
-IDX_A_7M5 = np.array([ 1025681, 2213651 ])
-IDX_A = [ IDX_A_7M5, IDX_A_10M ]
-
-IDX_B_10M = np.array([ 1, 1, None ])
-IDX_B_7M5 = np.array([ None, 1 ])
-IDX_B = [ IDX_B_7M5, IDX_B_10M ]
-
-SCF_Q_10M = np.array([
-
-    [  3.6627046e+00,  4.1100042e+00,  2.4746060e+00,  1.1870402e+00,
-       5.2877727e-01,  7.5258069e-02,  2.3608016e-01, -2.5249380e-01,
-      -6.8201580e-01, -1.1328318e+00, -1.0800359e+00, -1.6239492e+00,
-      -1.7143487e+00, -1.9073626e+00, -2.2321087e+00, -2.0267285e+00 ],
-
-    [  3.8168009e+00,  5.1162010e+00,  3.4807329e+00,  1.1997530e+00,
-      -2.2081921e-01, -1.0181629e+00, -1.0657728e+00, -1.1230690e+00,
-      -1.2811443e+00, -1.3048218e+00, -1.2081961e+00, -1.2804827e+00,
-      -8.0867787e-01, -1.0022261e+00, -1.3971454e+00, -1.1057621e+00 ],
-
-    [ -1.3038278e+00, -1.3183731e+00, -1.4433352e+00, -1.3025832e+00,
-      -7.2407159e-01, -7.0739510e-02,  3.0891592e-01,  1.1110561e-01,
-      -2.3826205e-01,  2.2790355e-01,  7.5958402e-01,  1.0128069e+00,
-       1.0886346e+00,  1.3319042e+00,  1.4367016e+00,  1.0146522e+00 ]
-
-])
-
-
-SCF_Q_7M5 = np.array([
-
-    [  4.3811806e+00,  3.2082316e+00,  1.7782399e+00,  6.4980749e-01,
-       2.0221356e-01,  7.0332445e-02, -2.8668104e-01, -8.5700304e-01,
-      -6.1964726e-01, -8.0165679e-01, -1.0516986e+00, -1.3477232e+00,
-      -1.4003932e+00, -1.5566567e+00, -1.6059594e+00, -1.5454836e+00 ],
-
-    [  5.8316083e+00,  3.9835316e+00,  8.6468396e-01, -9.5443316e-01,
-      -8.1173658e-01, -6.2522498e-01, -6.4450858e-01, -7.4038105e-01,
-      -1.0412349e+00, -6.5113903e-01, -5.8671926e-01, -9.6639127e-01,
-      -5.7817825e-01, -5.4629017e-01, -9.2263684e-01, -8.4544942e-01 ],
-
-])
-
-SCF_Q = [ SCF_Q_7M5, SCF_Q_10M ]
-
-
-### C.3.1.11  SNS interpolation
-
-G_SNS_10M = np.array([
-
-    [  7.8961620e-02,  7.8961620e-02,  7.5959959e-02,  7.0294618e-02,
-       6.5051816e-02,  6.0200039e-02,  6.6727283e-02,  8.8588922e-02,
-       1.1761302e-01,  1.5614618e-01,  2.0114945e-01,  2.5143043e-01,
-       3.1428006e-01,  3.9284010e-01,  4.6498069e-01,  5.2116367e-01,
-       5.8413517e-01,  6.5471542e-01,  7.2092077e-01,  7.7986290e-01,
-       8.4362411e-01,  9.1259840e-01,  9.3603811e-01,  9.1031240e-01,
-       8.8529374e-01,  8.6096267e-01,  8.8576235e-01,  9.6402006e-01,
-       1.0491919e+00,  1.1418887e+00,  1.2364330e+00,  1.3319727e+00,
-       1.4348949e+00,  1.5457699e+00,  1.6682873e+00,  1.8038405e+00,
-       1.9504078e+00,  2.1088841e+00,  2.1828793e+00,  2.1629996e+00,
-       2.1433009e+00,  2.1237816e+00,  2.2161032e+00,  2.4351378e+00,
-       2.6758212e+00,  2.9402932e+00,  3.1064119e+00,  3.1554571e+00,
-       3.2052766e+00,  3.2558828e+00,  3.3368236e+00,  3.4503169e+00,
-       3.5676705e+00,  3.6890154e+00,  3.8582746e+00,  4.0816214e+00,
-       4.3178972e+00,  4.5678504e+00,  4.6153375e+00,  4.4539677e+00,
-       4.2982400e+00,  4.1479572e+00,  4.0029288e+00,  3.8629712e+00 ],
-
-    [  7.0962424e-02,  7.0962424e-02,  6.3406480e-02,  5.0622552e-02,
-       4.0416103e-02,  3.2267464e-02,  3.3220843e-02,  4.4105412e-02,
-       5.8556233e-02,  7.7741760e-02,  1.0915042e-01,  1.6206389e-01,
-       2.4062852e-01,  3.5727938e-01,  4.9237129e-01,  6.2979823e-01,
-       8.0558272e-01,  1.0304308e+00,  1.2487522e+00,  1.4337794e+00,
-       1.6462221e+00,  1.8901424e+00,  2.0337102e+00,  2.0505581e+00,
-       2.0675455e+00,  2.0846737e+00,  2.1037086e+00,  2.1246996e+00,
-       2.1459001e+00,  2.1673122e+00,  2.2081350e+00,  2.2694571e+00,
-       2.3324822e+00,  2.3972575e+00,  2.4353075e+00,  2.4453201e+00,
-       2.4553739e+00,  2.4654690e+00,  2.4499352e+00,  2.4092551e+00,
-       2.3692505e+00,  2.3299102e+00,  2.3250020e+00,  2.3543088e+00,
-       2.3839851e+00,  2.4140354e+00,  2.3319019e+00,  2.1488367e+00,
-       1.9801431e+00,  1.8246926e+00,  1.7812270e+00,  1.8419813e+00,
-       1.9048079e+00,  1.9697773e+00,  2.0728144e+00,  2.2196325e+00,
-       2.3768496e+00,  2.5452025e+00,  2.5681376e+00,  2.4416843e+00,
-       2.3214575e+00,  2.2071505e+00,  2.0984720e+00,  1.9951447e+00 ]
-
-])
-
-G_SNS_7M5 = np.array([
-
-    [  4.7988064e-02,  4.7988064e-02,  5.3121439e-02,  6.5094311e-02,
-       7.9765711e-02,  9.7743851e-02,  1.2247145e-01,  1.5691066e-01,
-       2.0103424e-01,  2.5756547e-01,  3.2148295e-01,  3.9091375e-01,
-       4.7533955e-01,  5.7799882e-01,  6.6256861e-01,  7.1600434e-01,
-       7.7374963e-01,  8.3615204e-01,  8.7920505e-01,  8.9952910e-01,
-       9.2032297e-01,  9.4159751e-01,  9.8233993e-01,  1.0450324e+00,
-       1.1117258e+00,  1.1826756e+00,  1.2816224e+00,  1.4147546e+00,
-       1.5617162e+00,  1.7239439e+00,  1.7744028e+00,  1.7029011e+00,
-       1.6342806e+00,  1.5684252e+00,  1.5609220e+00,  1.6109378e+00,
-       1.6625563e+00,  1.7158288e+00,  1.7812772e+00,  1.8601545e+00,
-       1.9425246e+00,  2.0285422e+00,  2.1268255e+00,  2.2387723e+00,
-       2.3566116e+00,  2.4806533e+00,  2.5567427e+00,  2.5801849e+00,
-       2.6038421e+00,  2.6277162e+00,  2.6757181e+00,  2.7491622e+00,
-       2.8246222e+00,  2.9021534e+00,  2.9543066e+00,  2.9796549e+00,
-       3.0052206e+00,  3.0310057e+00,  3.0280729e+00,  2.9965054e+00,
-       2.9652670e+00,  2.9343543e+00,  2.9037639e+00,  2.8734924e+00 ],
-
-    [  1.7559453e-02,  1.7559453e-02,  2.0608757e-02,  2.8387910e-02,
-       3.9103448e-02,  5.3863762e-02,  8.2831558e-02,  1.4220423e-01,
-       2.4413451e-01,  4.1912721e-01,  6.4291757e-01,  8.8116553e-01,
-       1.2077018e+00,  1.6552436e+00,  1.9140070e+00,  1.8672589e+00,
-       1.8216525e+00,  1.7771601e+00,  1.7271850e+00,  1.6722548e+00,
-       1.6190716e+00,  1.5675798e+00,  1.5450306e+00,  1.5502021e+00,
-       1.5553909e+00,  1.5605971e+00,  1.5762459e+00,  1.6026515e+00,
-       1.6294995e+00,  1.6567972e+00,  1.7147376e+00,  1.8065050e+00,
-       1.9031836e+00,  2.0050361e+00,  1.9895925e+00,  1.8595441e+00,
-       1.7379963e+00,  1.6243933e+00,  1.5616667e+00,  1.5443306e+00,
-       1.5271869e+00,  1.5102336e+00,  1.5520535e+00,  1.6576004e+00,
-       1.7703250e+00,  1.8907155e+00,  1.8893168e+00,  1.7663992e+00,
-       1.6514784e+00,  1.5440344e+00,  1.4888436e+00,  1.4806393e+00,
-       1.4724802e+00,  1.4643660e+00,  1.5087288e+00,  1.6104012e+00,
-       1.7189253e+00,  1.8347627e+00,  1.8829418e+00,  1.8579241e+00,
-       1.8332388e+00,  1.8088814e+00,  1.7848477e+00,  1.7611333e+00 ],
-
-])
-
-G_SNS = [ G_SNS_7M5, G_SNS_10M ]
-
-
-### C.3.1.13  Spectral shaping
-
-X_S_10M = np.array([
-
-    [ -4.6579970e+01,  2.5475086e+03, -2.0979386e+03,  6.6905115e+03,
-      -7.4276511e+03, -1.3496582e+04, -1.4911551e+02, -1.7600517e+03,
-      -1.8897070e+03,  9.0806525e+01, -3.1626076e+03,  1.0097116e+03,
-      -3.1704894e+03,  7.4708040e+02, -1.9508019e+03, -5.0811399e+02,
-       2.7592179e+02, -1.8004443e+03,  1.7810815e+03, -2.1469042e+03,
-       1.8820925e+03, -1.3520099e+03,  7.5748485e+02,  9.4849121e+01,
-      -5.5907550e+02,  9.9853924e+02, -1.1694214e+03,  1.2670596e+03,
-      -1.1564965e+03,  7.6341371e+02, -3.7429412e+02, -1.2714316e+02,
-       5.5868459e+02, -8.6630814e+02,  1.0827149e+03, -1.0408470e+03,
-       9.2759078e+02, -5.8910202e+02,  2.3201842e+02,  1.8289697e+02,
-      -5.6951623e+02,  8.1229132e+02, -9.9835988e+02,  9.3658953e+02,
-      -8.1543182e+02,  4.9408042e+02, -1.3648595e+02, -2.4771597e+02,
-       5.9096160e+02, -7.9945187e+02,  8.7469937e+02, -8.0242901e+02,
-       6.1523337e+02, -3.4929423e+02,  4.3714524e+01,  2.3825249e+02,
-      -4.7949197e+02,  6.1487056e+02, -6.8885288e+02,  6.1543922e+02,
-      -4.5940587e+02,  2.6027447e+02,  1.4932451e+01, -2.6342003e+02,
-       5.1698905e+02, -6.3805644e+02,  6.6431016e+02, -6.3536804e+02,
-       4.4714179e+02, -2.0448694e+02, -7.5210020e+01,  3.2318745e+02,
-      -5.2463109e+02,  6.3268159e+02, -6.4137909e+02,  5.4210738e+02,
-      -3.6539049e+02,  1.4180793e+02,  1.1043715e+02, -3.2082774e+02,
-       4.9551143e+02, -5.6464214e+02,  5.5411819e+02, -4.4912438e+02,
-       2.8748742e+02, -8.3157725e+01, -1.4199053e+02,  3.1886540e+02,
-      -4.7410616e+02,  5.2507181e+02, -4.9901112e+02,  3.9510519e+02,
-      -2.3175576e+02,  3.7914915e+01,  1.7066614e+02, -3.3127409e+02,
-       4.6682698e+02, -5.0146152e+02,  4.5964223e+02, -3.4704713e+02,
-       1.8377315e+02,  8.3068359e-01, -2.0227229e+02,  3.5108641e+02,
-      -4.5284836e+02,  4.7317762e+02, -4.4561519e+02,  3.2535592e+02,
-      -1.4580770e+02, -3.9573578e+01,  2.3102912e+02, -3.8615649e+02,
-       4.7298066e+02, -4.8574358e+02,  4.1595651e+02, -2.8690887e+02,
-       1.0822973e+02,  8.0889458e+01, -2.6856378e+02,  4.0232748e+02,
-      -4.7864051e+02,  4.7205528e+02, -3.9210707e+02,  2.5719880e+02,
-      -6.8160760e+01, -1.1068097e+02,  2.8495852e+02, -3.8530783e+02,
-       4.4481193e+02, -4.2962793e+02,  3.3997586e+02, -2.0750452e+02,
-       2.8600150e+01,  1.3080486e+02, -2.8418457e+02,  3.7335187e+02,
-      -4.1400983e+02,  3.8613469e+02, -2.8921905e+02,  1.5545710e+02,
-       5.7416806e+00, -1.4821308e+02,  2.8457226e+02, -3.6307452e+02,
-       3.8831635e+02, -3.4730222e+02,  2.4109847e+02, -1.1774263e+02,
-      -3.0666081e+01,  1.6872235e+02, -2.8808479e+02,  3.5123254e+02,
-      -3.6347751e+02,  3.0487804e+02, -2.1236231e+02,  8.7865496e+01,
-       6.0906484e+01, -1.8416198e+02,  2.9273130e+02, -3.4443289e+02 ],
-
-    [ -4.4595314e+02, -1.0379970e+03,  3.1577388e+02, -3.5610417e+03,
-       3.4581766e+03,  8.3381081e+03, -1.1696533e+03,  2.4101021e+03,
-      -6.5655829e+02,  1.4524982e+03,  1.9701386e+02,  1.1436715e+03,
-       3.0871470e+02,  1.4623552e+03,  5.8823857e+02,  9.7150548e+02,
-       4.8597295e+02,  6.6672780e+02,  1.9624228e+02,  1.5262224e+02,
-      -1.2492188e+01, -9.9749152e+01, -1.2404934e+02, -1.7103668e+02,
-      -9.0965339e+01, -7.0386211e+01, -2.5404068e+01, -6.2239848e+00,
-       1.1353459e+01,  3.1118246e+01,  2.3550684e+01,  1.9918764e+01,
-       6.7582893e+00,  3.5391093e+00, -2.5360957e+00, -1.2318765e+01,
-      -1.0710738e+01, -1.0309510e+01, -4.8149059e+00, -1.0084275e+00,
-       1.5451084e+00,  6.7503435e+00,  8.7185892e+00,  6.5017316e+00,
-       3.3714192e+00,  3.3653026e+00,  1.5730174e-01, -2.7592084e+00,
-      -5.2662479e+00, -5.1967404e+00, -2.6956428e+00, -3.2877724e+00,
-      -2.9012824e+00,  3.7032988e-01,  2.4268422e+00,  4.6841491e+00,
-       4.4233993e+00,  2.9001769e+00,  4.0245597e+00,  2.1909513e+00,
-      -7.8166125e-01, -3.7740437e+00, -1.6826612e+00, -4.8759902e+00,
-      -7.0146012e+00, -4.2610160e+00, -1.9222920e+00, -3.8849374e-03,
-       1.9171256e+00,  2.1228421e+00,  4.0002861e+00,  2.4213202e+00,
-       5.5365640e+00,  2.0924576e+00,  3.2856722e+00,  2.3805212e-01,
-      -2.6499944e+00, -1.5877393e+00, -2.3638844e+00, -3.1969127e+00,
-      -4.5049447e+00,  4.9555393e-01, -5.2555443e-01,  2.8655462e-01,
-      -4.9844313e-01,  1.3144496e+00,  1.8462413e+00,  1.2390833e+00,
-       1.8391487e-01,  1.3634153e+00, -3.0575242e-02, -1.2360920e+00,
-       1.7996128e-01, -2.1572539e+00, -2.3206289e+00, -2.1575175e+00,
-      -1.3914054e+00, -2.1857655e+00,  2.8328476e-01,  9.0849227e-01,
-       1.5616765e+00,  3.0216795e+00,  4.0452889e+00,  3.2116107e+00,
-       1.5944792e+00,  2.2110634e-01, -1.2957452e+00, -3.9440236e-01,
-      -1.7499591e+00, -1.8705540e+00, -2.4921933e-01,  4.0425431e-01,
-      -3.0395784e+00, -1.9499112e+00,  6.8993026e-02,  2.6858112e+00,
-       3.9365468e+00,  1.4534664e+00,  2.5590207e+00,  1.9618160e+00,
-      -3.1812104e-01,  3.6820368e-01, -2.3536912e-02,  3.9009812e-01,
-      -4.7912977e+00, -3.3553682e+00, -2.6900314e+00, -1.5134286e+00,
-      -9.9684877e-01, -6.2871446e-01,  1.8360627e+00,  1.4455944e+00,
-       9.5156525e-01,  1.0993584e+00,  2.2401521e+00,  2.4733664e+00,
-       2.3631456e-01, -2.0269302e+00, -2.4458555e+00, -1.2206038e+00,
-      -3.2403583e+00, -2.0795885e+00, -1.5947867e+00, -1.1727234e+00,
-      -3.2945845e-01,  8.0000134e-01,  3.4717506e-01,  1.5246353e+00,
-      -1.6045804e-01,  4.0202852e-02,  1.8039411e+00,  1.0516099e-01,
-      -7.4544919e-02, -2.1387580e+00, -2.0486855e+00, -7.9121437e-01,
-      -7.4423018e-01, -1.5334714e+00, -7.8671386e-01, -1.6940090e-01 ]
-
-])
-
-X_S_7M5 = np.array([
-
-    [ -1.3344626e+03,  1.4958812e+03, -5.5694903e+03, -1.0486529e+04,
-       5.6650034e+02,  6.0141573e+03, -5.3094551e+02,  2.6475111e+02,
-       1.6690199e+03, -2.0838083e+03,  9.3187330e+02,  6.3720777e+02,
-      -1.8748573e+03,  1.9287595e+03, -4.9900796e+02, -1.1216643e+03,
-       1.8281601e+03, -1.3351793e+03, -2.5896432e+01,  1.1847513e+03,
-      -1.4008698e+03,  7.1091673e+02,  3.4181812e+02, -1.1209015e+03,
-       1.1047015e+03, -3.4707684e+02, -6.0822434e+02,  1.2069267e+03,
-      -9.8898328e+02,  3.9434472e+01,  8.9967867e+02, -1.1231789e+03,
-       6.1473708e+02,  2.1402341e+02, -7.6438486e+02,  7.6886411e+02,
-      -3.0289717e+02, -3.6710597e+02,  7.3659799e+02, -5.8064409e+02,
-       8.6987653e+01,  4.6780721e+02, -6.8240093e+02,  4.0409628e+02,
-       9.0635459e+01, -5.3255888e+02,  6.1176277e+02, -2.4268516e+02,
-      -2.4050228e+02,  5.6445766e+02, -5.1941054e+02,  8.4546495e+01,
-       3.6675922e+02, -5.6559679e+02,  4.1109821e+02,  6.3236483e+01,
-      -4.6563784e+02,  5.3992584e+02, -2.8194795e+02, -2.0189361e+02,
-       5.0504429e+02, -4.5411112e+02,  1.2935836e+02,  2.9902539e+02,
-      -4.9207698e+02,  3.4402101e+02,  1.8978015e+00, -3.6234196e+02,
-       4.6346622e+02, -2.2783004e+02, -1.1745582e+02,  4.0795439e+02,
-      -4.1004303e+02,  1.1293439e+02,  2.1977291e+02, -4.1879288e+02,
-       3.3765885e+02, -2.7615664e+00, -2.9831792e+02,  4.0573572e+02,
-      -2.5374285e+02, -1.0182244e+02,  3.5226045e+02, -3.6553462e+02,
-       1.5203133e+02,  1.9094377e+02, -3.7905818e+02,  3.0025026e+02,
-      -4.9616026e+01, -2.6139695e+02,  3.7537731e+02, -2.2263305e+02,
-      -4.6540403e+01,  3.1054395e+02, -3.4657574e+02,  1.3778205e+02,
-       1.3283532e+02, -3.3677365e+02,  3.0049394e+02, -4.5453695e+01,
-      -2.0808762e+02,  3.3473443e+02, -2.3547267e+02, -4.3881934e+01,
-       2.5964919e+02, -3.1164958e+02,  1.6251702e+02,  1.2186915e+02,
-      -2.9548260e+02,  2.7121711e+02, -8.0864501e+01, -1.8718254e+02,
-       3.0861481e+02, -2.1772078e+02,  4.7378118e-01,  2.4022293e+02,
-      -3.0348233e+02,  1.5168546e+02,  7.7875700e+01, -2.8137835e+02 ],
-
-    [  6.6940015e+01, -3.8686730e+02,  1.2805107e+03, -7.2628992e+03,
-      -1.5754672e+03, -6.2436979e+02,  8.2787173e+02, -8.0959099e+02,
-      -3.4245127e+02, -3.5548999e+01, -1.0732178e+02,  3.3015325e+01,
-       1.9173905e+02,  1.8057154e+02, -2.3823068e+01, -1.4567609e+02,
-      -1.0868931e+02,  3.0479251e+01,  7.1566338e+01,  2.4158550e+01,
-      -6.2114342e+00, -1.4130388e+01, -1.8394789e+01, -1.8227404e+01,
-       3.8411657e+00,  2.5627619e+01,  2.8307446e+01, -1.6207017e+00,
-      -2.8151474e+01, -2.4154773e+01, -5.2808122e-01,  6.3053889e+00,
-       1.6985395e+01,  2.1137594e+01,  1.0163883e+01, -1.2646204e+01,
-      -2.8258074e+01, -1.5957606e+01,  1.0393474e+01,  1.5431458e+01,
-       1.0833710e+01,  6.5820435e+00, -4.0055074e+00, -1.1459013e+01,
-      -1.4519949e+01, -5.8509646e+00,  1.5015967e+01,  1.5836030e+01,
-       4.1133189e+00, -1.7829961e+00, -8.3853815e+00, -1.1122404e+01,
-      -8.7393669e+00,  3.6899921e+00,  1.7989851e+01,  1.3569871e+01,
-      -4.2575144e+00, -9.6890713e+00, -1.0139553e+01, -8.7913755e+00,
-      -1.6029731e+00,  5.0445901e+00,  1.4433262e+01,  9.5900419e+00,
-      -8.2284294e+00, -9.7476743e+00, -6.5414935e+00, -1.9885597e+00,
-       2.9171599e+00,  6.6780713e+00,  9.2081019e+00,  2.8651322e+00,
-      -1.0061317e+01, -1.1376702e+01, -1.6700624e+00,  1.3140771e+00,
-       6.5095758e+00,  6.8977417e+00,  4.8690746e+00, -8.2814942e-01,
-      -1.0064761e+01, -8.9205098e+00,  1.2429849e+00,  7.8244782e+00,
-       6.6880480e+00,  5.8136759e+00,  5.9984867e-01, -6.0624312e+00,
-      -1.1389596e+01, -7.9714369e+00,  7.4518543e+00,  8.9170413e+00,
-       6.3880481e+00,  1.3801389e+00, -2.8576875e+00, -6.7319113e+00,
-      -1.0072255e+01, -3.5424326e+00,  8.4784304e+00,  1.0898685e+01,
-       4.8542048e+00, -2.6188427e+00, -5.9736376e+00, -7.4232870e+00,
-      -5.5939115e+00,  1.9917820e+00,  1.1064235e+01,  9.7301910e+00,
-      -4.8166311e-01, -6.0418655e+00, -7.8074447e+00, -6.7420485e+00,
-      -5.7805346e-01,  4.1557564e+00,  1.1700978e+01,  7.8318863e+00,
-      -4.5426418e+00, -8.2115262e+00, -6.8867416e+00, -1.9487360e+00 ]
-
-])
-
-X_S = [ X_S_7M5, X_S_10M ]
-
-
-### C.3.1.14  TNS coder
-
-X_F_10M = np.array([
-
-    [ -4.6579970e+01,  2.5475086e+03, -2.0979386e+03,  6.6905115e+03,
-      -7.4276511e+03, -1.3496582e+04, -1.4911551e+02, -1.7600517e+03,
-      -1.8897070e+03,  9.0806525e+01, -3.1626076e+03,  1.0097116e+03,
-      -3.1704894e+03, -1.4629727e+03,  1.6177531e+02, -2.8070849e+02,
-       1.6836280e+02, -4.8794939e+02,  4.8735463e+01, -3.2812648e+02,
-       2.0567762e+02, -2.7612607e+02,  1.7089464e+02, -1.4903447e+02,
-       1.7634897e+02, -1.7636863e+02, -1.4114282e+01,  1.5326904e+02,
-      -2.0051680e+02,  7.5885344e+01, -1.2030117e+02,  8.6222257e+01,
-       3.4247381e+01, -3.1564007e+01,  1.3038568e+02, -1.1547149e+02,
-       1.5722828e+02, -1.1575179e+02,  8.3918034e+01, -4.2997589e+01,
-      -4.9151429e+01,  5.7178185e+01, -1.4462317e+02,  1.2240116e+02,
-      -1.4658879e+02,  1.0329710e+02, -4.8119513e+01,  1.5046477e+01,
-       4.7974194e+01, -6.8870459e+01,  7.5850716e+01, -1.0491255e+02,
-       1.0512982e+02, -9.6951573e+01,  6.8788954e+01, -3.9997908e+01,
-      -8.7506165e+00,  3.5520535e+01, -9.4932383e+01,  8.5065288e+01,
-      -6.7462450e+01,  9.2204890e+01, -2.2005176e+01, -2.2102766e+01,
-       7.7845918e+01, -7.8223738e+01,  7.3211489e+01, -1.4466156e+02,
-       7.8730279e+01, -2.1569349e+01,  2.0934594e+01,  1.0330210e+01,
-      -5.6199662e+01,  7.4347857e+01, -8.7431560e+01,  8.2441742e+01,
-      -7.4246878e+01,  5.8052214e+01, -1.2858317e+01, -8.5491490e+00,
-       4.9073587e+01, -6.1606297e+01,  7.0756502e+01, -7.3226896e+01,
-       6.7149964e+01, -4.3873945e+01, -3.2798184e-01,  1.2618393e+01,
-      -6.0263717e+01,  6.9581326e+01, -6.2258677e+01,  7.0807773e+01,
-      -5.6473108e+01,  2.9499750e+01,  6.4843952e+00, -2.1637431e+01,
-       6.5900289e+01, -6.9209851e+01,  5.7574118e+01, -6.3239986e+01,
-       5.0201888e+01, -2.5161732e+01, -1.8407647e+01,  3.3057952e+01,
-      -5.1394596e+01,  6.6274453e+01, -8.9093497e+01,  6.1636186e+01,
-      -1.8970991e+01,  1.8110690e+01,  1.4664315e+01, -5.7379639e+01,
-       5.9659304e+01, -6.7425126e+01,  6.6740221e+01, -5.6441736e+01,
-       3.3370764e+01, -8.8727172e+00, -2.8768291e+01,  4.7072609e+01,
-      -6.4504039e+01,  7.0679425e+01, -6.4413968e+01,  5.7895311e+01,
-      -2.2822099e+01,  8.2462832e+00,  2.1908355e+01, -3.0461871e+01,
-       5.6741507e+01, -7.3393219e+01,  5.3252893e+01, -4.6826469e+01,
-       2.0122779e+01, -5.9699302e+00, -2.3230042e+01,  3.8784526e+01,
-      -5.3705628e+01,  6.2675409e+01, -4.5504385e+01,  3.5709340e+01,
-      -1.9387569e+01,  5.2553786e+00,  3.0873151e+01, -4.6681999e+01,
-       5.1345152e+01, -5.3802726e+01,  3.5105259e+01, -3.7404776e+01,
-       2.0074043e+01,  1.0637688e+01, -3.4769882e+01,  4.1709646e+01,
-      -5.1828676e+01,  4.2299294e+01, -4.3335588e+01,  3.5793638e+01,
-       1.1265725e+00, -1.0395933e+01,  3.3544261e+01, -4.9299157e+01 ],
-
-    [ -4.4595314e+02, -1.0379970e+03,  3.1577388e+02, -3.5610417e+03,
-       3.4581766e+03,  8.3381081e+03, -1.1696533e+03,  2.4101021e+03,
-      -6.5655829e+02,  1.4524982e+03,  1.9701386e+02,  1.1436715e+03,
-       3.0871470e+02,  1.3134384e+03, -2.1221169e+02,  2.4285486e+02,
-      -1.3136018e+02,  2.0492262e+02, -5.7778142e+01,  2.0474887e+02,
-       7.0763142e+01,  9.9953062e+01,  8.3698665e+01,  6.2817480e+01,
-       7.7348745e+01,  4.8618716e+01,  1.9547983e+01, -7.2171464e+00,
-      -1.9159653e+01, -1.4038329e+01, -1.8628426e+01, -1.6163696e+01,
-      -1.4520857e+01, -4.8554891e+00, -5.7820395e-01, -3.7918076e+00,
-       2.3294407e+00,  2.9495405e+00,  4.7407475e+00,  4.4409321e+00,
-       1.5250457e+00,  2.7760466e+00,  1.9722269e+00, -2.1015209e+00,
-      -3.2710967e+00, -2.8401413e-03, -1.3044069e+00, -1.6457901e+00,
-      -1.6669278e+00, -2.3124530e-01,  2.3073051e+00,  8.9693576e-02,
-      -9.1429477e-01,  1.6390903e+00,  1.5921541e+00,  2.0781925e+00,
-       5.6774613e-01, -1.4873030e+00,  9.0090862e-01, -1.7392411e-01,
-      -2.0619629e+00, -2.6804981e+00,  1.5808344e+00, -1.9739969e+00,
-      -3.2934549e+00,  8.3984861e-01,  1.6902572e+00,  1.1574818e+00,
-       1.5309696e+00, -4.5293583e-01,  6.7334155e-01, -1.0828159e+00,
-       2.8647595e+00, -1.0355011e+00,  1.2478700e+00, -1.0934505e+00,
-      -2.6607804e+00,  6.5988107e-01,  5.0203258e-01, -9.3129652e-01,
-      -1.6993602e+00,  3.3832184e+00, -1.1600967e-01, -2.6664726e-01,
-      -1.2900776e+00,  4.8121635e-01,  5.7458524e-01, -1.3748852e-02,
-      -1.0422755e+00,  9.6404835e-01, -6.5515813e-01, -1.2033320e+00,
-       1.2628304e+00, -1.5881764e+00, -1.2024205e+00, -1.5188743e-01,
-       2.1530142e-01, -1.1518121e+00,  1.5159517e+00,  7.6772612e-01,
-       3.4785413e-01,  1.4223757e+00,  1.6791664e+00,  1.6342604e-03,
-      -9.7881678e-01, -1.0907888e+00, -1.1879306e+00,  1.1798907e+00,
-      -9.0898948e-02, -1.9025707e-01,  1.4674577e+00,  9.8643104e-01,
-      -3.5040854e+00, -8.8416089e-01,  1.4586672e+00,  2.8440488e+00,
-       2.5053395e+00, -1.5126041e+00, -3.8048891e-02,  5.8273982e-02,
-      -1.6839084e+00,  8.3919618e-01,  8.4288521e-01,  8.2299662e-01,
-      -4.3286655e+00, -8.2387031e-01,  3.8407497e-01,  8.0932325e-01,
-       4.2209188e-01, -1.6088979e-01,  1.1786569e+00, -1.5473109e-01,
-      -9.7397503e-01, -1.5447399e-01,  1.2455806e+00,  1.1521613e+00,
-      -1.1394046e+00, -2.4879158e+00, -1.1733733e+00,  1.0462318e+00,
-      -1.2964334e+00,  2.4531392e-01,  1.9106251e-01, -4.3741350e-01,
-       5.7814855e-02,  7.3157372e-01, -7.7503310e-01,  5.5390581e-01,
-      -1.3940056e+00, -5.7191178e-01,  1.8789088e+00, -5.6670606e-01,
-      -4.8435902e-01, -1.8372728e+00, -9.7604008e-01,  1.0244293e+00,
-       5.7039949e-01, -1.0034018e+00, -8.3178864e-02,  7.5770740e-02 ]
-
-])
-
-X_F_7M5 = np.array([
-
-    [ -1.3344626e+03,  1.4958812e+03, -5.5694903e+03, -1.0486529e+04,
-       5.6650034e+02,  6.0141573e+03, -5.3094551e+02,  2.6475111e+02,
-       1.6690199e+03, -2.0838083e+03, -5.7181477e+02,  3.0979708e+02,
-      -2.1393724e+02,  1.2521746e+02,  3.2684730e+02, -1.7994574e+02,
-      -2.0020361e+02,  8.6111917e+00, -2.4832990e+02,  1.7213677e+02,
-       1.4131656e+00, -8.5287997e+01,  1.7851510e+02, -2.3922723e+02,
-       9.9313025e+01,  8.9492487e+01, -2.1678225e+02,  2.8445423e+02,
-      -1.0586008e+02, -1.7011189e+02,  2.0924470e+02, -4.3658588e+01,
-      -5.3305585e+01,  9.5065494e+01, -2.4959448e+01, -1.9882483e+01,
-       6.3950343e+01, -1.8254423e+02,  1.4871147e+02, -2.9924201e+01,
-      -3.8358507e+01,  1.3662271e+02, -9.1506608e+01, -3.6536786e+01,
-       6.1379812e+01, -1.2146687e+02,  7.5261109e+01,  6.3261448e+01,
-      -7.4977224e+01,  9.6981001e+01, -5.2501206e+01, -8.8542526e+01,
-       8.7236325e+01, -7.1242762e+01,  3.4918529e+01,  1.0382986e+02,
-      -9.4414359e+01,  4.3111460e+01, -6.8550987e+00, -1.1139641e+02,
-       6.8058914e+01, -6.1872729e+00, -2.3074735e+01,  1.0246797e+02,
-      -5.7063670e+01, -1.0528237e+01,  3.4668643e+01, -9.7928146e+01,
-       5.4841513e+01,  3.8537271e+01, -4.1668071e+01,  9.3225595e+01,
-      -3.6482817e+01, -5.6719701e+01,  4.9322737e+01, -7.0376756e+01,
-       2.4449679e+01,  7.1939467e+01, -5.4394295e+01,  5.0564463e+01,
-      -1.2276169e+01, -8.5637045e+01,  5.2333982e+01, -2.5308579e+01,
-      -5.5784300e+00,  8.7058263e+01, -5.1715921e+01, -7.2178424e-01,
-       1.4946171e+01, -8.5511725e+01,  4.3784053e+01,  1.9685956e+01,
-      -2.5023328e+01,  8.0537198e+01, -3.0868111e+01, -3.4548956e+01,
-       3.4272902e+01, -7.1390131e+01,  1.8631772e+01,  5.2295899e+01,
-      -3.9080597e+01,  5.2223213e+01, -9.1791474e+00, -6.4321817e+01,
-       3.7530035e+01, -3.6279190e+01,  1.3252369e+00,  7.2448665e+01,
-      -3.9490622e+01,  1.4609831e+01,  9.3290814e+00, -7.2230799e+01,
-       3.7265287e+01,  3.1414748e+00, -1.8859243e+01,  7.1138748e+01,
-      -3.1848909e+01, -2.1851571e+01,  2.6573367e+01, -6.9966757e+01 ],
-
-    [  6.6940015e+01, -3.8686730e+02,  1.2805107e+03, -7.2628992e+03,
-      -1.5754672e+03, -6.2436979e+02,  8.2787173e+02, -8.0959099e+02,
-      -3.4245127e+02, -3.5548999e+01, -7.4832843e+01,  9.1419785e+01,
-       5.1342258e+01,  5.3971888e+01, -2.7664071e+01,  4.6162382e+01,
-      -1.0556418e+01,  6.7202580e+01, -1.7808740e+01, -6.8463927e+00,
-      -1.7273295e+01,  1.1657273e+00, -3.0473621e+00,  7.6704533e+00,
-       3.2019440e+00, -4.9821490e+00,  9.7767521e+00, -5.6595863e+00,
-      -2.2488613e+00, -2.3116498e+00,  3.6060424e+00, -1.0509700e+01,
-       1.0880662e+01,  1.3223251e+00,  3.8299992e+00, -3.2105583e+00,
-      -1.3455890e+00, -1.3800967e+00,  3.9392807e+00, -5.9971359e+00,
-       3.0005141e+00,  3.1693816e+00, -2.4580934e+00,  7.4517245e-01,
-      -2.5522465e+00, -1.7665735e+00,  6.2439566e+00, -2.5566772e+00,
-       1.0487489e+00,  4.9010207e+00, -2.4551119e+00, -2.3895763e+00,
-      -1.5405507e+00,  1.9495530e+00,  3.6880545e+00,  5.9918442e-01,
-      -2.4705668e+00,  4.8115098e+00, -4.1881444e+00, -3.6102608e+00,
-      -5.5087057e-01, -2.6206962e+00,  3.9639713e+00, -7.7640910e-01,
-      -3.9510665e+00,  4.4026144e+00, -3.2255765e+00, -1.5538782e+00,
-      -3.1853054e-01,  1.6928250e+00,  8.0901945e-01,  9.8517710e-01,
-      -3.9968911e+00,  5.2957448e-01,  7.5228614e-01, -5.5755012e+00,
-       4.1839400e+00, -8.6149818e-01,  2.4716293e+00, -3.9843760e-01,
-      -1.6905671e+00, -8.5829267e-01,  1.9447429e+00, -3.4134235e-01,
-       7.0851852e-02,  5.1275041e+00, -1.0122600e+00,  1.4475050e-01,
-      -3.3223224e+00, -1.0374444e+00,  3.7779787e+00, -4.5459512e+00,
-       4.0726493e+00, -2.5433866e-01,  2.0525115e+00, -3.0465923e+00,
-      -1.7020097e+00, -1.0852672e+00,  2.3054607e+00, -5.4698297e-01,
-       1.0938816e+00,  9.5295631e-01,  1.8110373e-01, -2.4274422e+00,
-      -9.0034207e-01,  3.4598368e-01,  3.0697391e+00, -3.3366935e-03,
-      -3.4637690e-02,  2.0742294e+00, -2.0643727e+00, -2.3004709e+00,
-       3.4207323e-01, -1.8133178e+00,  5.0366054e+00, -8.8976033e-01,
-      -7.4204267e-01,  1.6508374e+00, -1.2431019e+00, -1.1825352e+00 ]
-
-])
-
-X_F = [ X_F_7M5, X_F_10M ]
-
-RC_ORDER_10M = np.array([ [ 6, 0 ], [ 6, 0 ] ])
-RC_ORDER_7M5 = np.array([ [ 8, 0 ], [ 8, 0 ] ])
-RC_ORDER = [ RC_ORDER_7M5, RC_ORDER_10M ]
-
-RC_I_1_10M = np.array([ [ 13,  9,  4,  9,  8,  9,  8,  8 ],
-                        [  4,  7,  9,  9,  9,  9,  8,  8 ] ])
-
-RC_I_1_7M5 = np.array([ [ 12, 13,  6,  9,  7,  9,  7,  9 ],
-                        [  5, 13,  8, 10,  9,  9,  7,  9 ] ])
-
-RC_I_1 = [ RC_I_1_7M5, RC_I_1_10M ]
-
-RC_I_2_10M = np.array([ [  8,  8,  8,  8,  8,  8,  8,  8 ],
-                        [  8,  8,  8,  8,  8,  8,  8,  8 ] ])
-
-RC_I_2_7M5 = np.array([ [  8,  8,  8,  8,  8,  8,  8,  8 ],
-                        [  8,  8,  8,  8,  8,  8,  8,  8 ] ])
-
-RC_I_2 = [ RC_I_2_7M5, RC_I_2_10M ]
-
-RC_Q_1_10M = np.array([
-
-    [  7.9801723e-01,  1.8374952e-01, -6.7369564e-01,  1.8374952e-01,
-       0.0000000e+00,  1.8374952e-01,  0.0000000e+00,  0.0000000e+00 ],
-
-    [ -6.7369564e-01, -1.8374952e-01,  1.8374952e-01,  1.8374952e-01,
-       1.8374952e-01,  1.8374952e-01,  0.0000000e+00,  0.0000000e+00 ]
-
-])
-
-RC_Q_1_7M5 = np.array([
-
-    [  6.7369564e-01,  7.9801723e-01, -3.6124167e-01,  1.8374952e-01,
-      -1.8374952e-01,  1.8374952e-01, -1.8374952e-01,  1.8374952e-01 ],
-
-    [ -5.2643216e-01,  7.9801723e-01,  0.0000000e+00,  3.6124167e-01,
-       1.8374952e-01,  1.8374952e-01, -1.8374952e-01,  1.8374952e-01 ]
-
-])
-
-RC_Q_1 = [ RC_Q_1_7M5, RC_Q_1_10M ]
-
-RC_Q_2_10M = np.array([
-
-    [  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00 ],
-
-    [  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00 ]
-
-])
-
-RC_Q_2_7M5 = np.array([
-
-    [  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00 ],
-
-    [  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00 ]
-
-])
-
-RC_Q_2 = [ RC_Q_2_7M5, RC_Q_2_10M ]
-
-NUM_TNS_FILTERS_10M = [ 1, 1 ]
-NUM_TNS_FILTERS_7M5 = [ 1, 1 ]
-NUM_TNS_FILTERS = [ NUM_TNS_FILTERS_7M5, NUM_TNS_FILTERS_10M ]
-
-TNS_LEV_A_10M = np.array([
-
-    [  1.0000000e+00,  7.2228594e-01, -5.9139666e-01, -5.8613895e-01,
-       1.1856746e-01,  2.9269254e-02,  8.0280074e-02,  3.8446982e-02,
-       4.3781506e-02 ],
-
-    [  1.0000000e+00, -5.6089086e-01, -2.3332924e-01,  1.3258672e-01,
-      -7.2233128e-02,  1.1753190e-01,  1.9802609e-01, -6.1275417e-02,
-       2.2455104e-02 ]
-
-])
-
-TNS_LEV_A_7M5 = np.array([
-
-    [  1.0000000e+00,  5.2890099e-01,  5.4205760e-01, -4.4859180e-01,
-       3.5954391e-01, -1.9179061e-01,  1.8237136e-01, -1.1968822e-01,
-       1.6649226e-01 ],
-
-    [  1.0000000e+00, -9.0587790e-01,  9.8849245e-01, -1.4294859e-01,
-       2.5680120e-01, -5.8559598e-03,  3.0914531e-01, -2.2365546e-01,
-       1.0115038e-01 ]
-
-])
-
-TNS_LEV_A = [ TNS_LEV_A_7M5, TNS_LEV_A_10M ]
-
-TNS_LEV_E_10M = [ 0.412635, 1.396833 ]
-TNS_LEV_E_7M5 = [ 0.397854, 0.665554 ]
-TNS_LEV_E = [ TNS_LEV_E_7M5, TNS_LEV_E_10M ]
-
-TNS_LEV_RC_10M = np.array([
-
-    [  8.3775274e-01,  1.7149527e-01, -7.0757376e-01,  1.9028627e-01,
-      -1.4202392e-02,  1.0144450e-01,  6.8373224e-03,  4.3781506e-02 ],
-
-    [ -6.7692801e-01, -9.9895702e-02,  2.2652538e-01,  1.0193040e-01,
-       2.0702019e-01,  1.7652182e-01, -4.8705113e-02,  2.2455104e-02 ]
-
-])
-
-TNS_LEV_RC_7M5 = np.array([
-
-    [  6.0637394e-01,  8.3208082e-01, -4.0823140e-01,  2.1979501e-01,
-      -1.5450397e-01,  2.2566273e-01, -2.1366897e-01,  1.6649226e-01 ],
-
-    [ -5.4911119e-01,  7.7810682e-01, -2.2286927e-03,  3.4649135e-01,
-       2.2484419e-01,  9.3957220e-02, -1.3339034e-01,  1.0115038e-01 ]
-
-])
-
-TNS_LEV_RC = [ TNS_LEV_RC_7M5, TNS_LEV_RC_10M ]
-
-NBITS_TNS_10M = np.array([ 24, 18 ])
-NBITS_TNS_7M5 = np.array([ 24, 24 ])
-NBITS_TNS = [ NBITS_TNS_7M5, NBITS_TNS_10M ]
-
-
-### C.3.1.15 Global gain estimation
-
-GG_OFF_10M = np.array([ -131, -131 ])
-GG_OFF_7M5 = np.array([ -127, -127 ])
-GG_OFF = [ GG_OFF_7M5, GG_OFF_10M ]
-
-GG_IND_10M = np.array([  191,  166 ])
-GG_IND_7M5 = np.array([  189,  162 ])
-GG_IND = [ GG_IND_7M5, GG_IND_10M ]
-
-GG_MIN_10M = np.array([  121,  115 ])
-GG_MIN_7M5 = np.array([  114,  109 ])
-GG_MIN = [ GG_MIN_7M5, GG_MIN_10M ]
-
-GG_10M = np.array([ 138.949549, 17.782794 ])
-GG_7M5 = np.array([ 163.789371, 17.782794 ])
-GG = [ GG_7M5, GG_10M ]
-
-NBITS_OFFSET_10M = np.array([ 0.000000, -1.200000 ])
-NBITS_OFFSET_7M5 = np.array([ 0.000000, -1.600000 ])
-NBITS_OFFSET = [ NBITS_OFFSET_7M5, NBITS_OFFSET_10M ]
-
-
-### C.3.1.16 Quantization
-
-X_Q_10M = np.array([
-
-    [    0,   18,  -15,   48,  -53,  -97,   -1,  -13,  -13,    1,
-       -23,    7,  -23,  -10,    1,   -2,    1,   -3,    0,   -2,
-         1,   -2,    1,   -1,    1,   -1,    0,    1,   -1,    0,
-        -1,    0,    0,    0,    1,   -1,    1,   -1,    0,    0,
-         0,    0,   -1,    1,   -1,    1,    0,    0,    0,    0,
-         0,   -1,    1,   -1,    0,    0,    0,    0,   -1,    0,
-         0,    1,    0,    0,    0,    0,    0,   -1,    0,    0,
-         0,    0,    0,    0,   -1,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,   -1,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0 ],
-
-    [  -25,  -58,   18, -200,  194,  469,  -66,  135,  -37,   82,
-        11,   64,   17,   74,  -12,   14,   -7,   11,   -3,   11,
-         4,    5,    5,    3,    4,    3,    1,    0,   -1,   -1,
-        -1,   -1,   -1,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0 ]
-
-])
-
-X_Q_7M5 = np.array([
-
-    [   -8,    9,  -34,  -64,    3,   37,   -3,    1,   10,  -13,
-        -3,    2,   -1,    1,    2,   -1,   -1,    0,   -1,    1,
-         0,    0,    1,   -1,    0,    0,   -1,    2,   -1,   -1,
-         1,    0,    0,    0,    0,    0,    0,   -1,    1,    0,
-         0,    1,    0,    0,    0,   -1,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    1,    0,    0,    0,   -1,
-         0,    0,    0,    1,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0 ],
-
-    [    4,  -22,   72, -408,  -88,  -35,   46,  -45,  -19,   -2,
-        -4,    5,    3,    3,   -1,    2,    0,    4,   -1,    0,
-        -1,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0 ]
-
-])
-
-X_Q = [ X_Q_7M5, X_Q_10M ]
-
-LASTNZ_10M = np.array([ 108,  34 ])
-LASTNZ_7M5 = np.array([  64,  22 ])
-LASTNZ = [ LASTNZ_7M5, LASTNZ_10M ]
-
-NBITS_EST_10M = np.array([ 231, 250 ])
-NBITS_EST_7M5 = np.array([ 164, 140 ])
-NBITS_EST = [ NBITS_EST_7M5, NBITS_EST_10M ]
-
-LSB_MODE_10M = np.array([ 0, 0 ])
-LSB_MODE_7M5 = np.array([ 0, 0 ])
-LSB_MODE = [ LSB_MODE_7M5, LSB_MODE_10M ]
-
-NBITS_SPEC_10M = np.array([ 225, 231 ])
-NBITS_SPEC_7M5 = np.array([ 156, 146 ])
-NBITS_SPEC = [ NBITS_SPEC_7M5, NBITS_SPEC_10M ]
-
-
-### C.3.1.17 Global Gain adjustement
-
-GG_IND_ADJ_10M = np.array([ 192, 168 ])
-GG_IND_ADJ_7M5 = np.array([ 190, 162 ])
-GG_IND_ADJ = [ GG_IND_ADJ_7M5, GG_IND_ADJ_10M ]
-
-GG_ADJ_10M = np.array([ 1.508591e+02, 2.096180e+01 ])
-GG_ADJ_7M5 = np.array([ 1.778279e+02, 1.778279e+01 ])
-GG_ADJ = [ GG_ADJ_7M5, GG_ADJ_10M ]
-
-
-### C.3.1.18 Requantization
-
-X_Q_REQ_10M = np.array([
-
-    [    0,   17,  -14,   44,  -49,  -89,   -1,  -12,  -12,    0,
-       -21,    7,  -21,  -10,    1,   -2,    1,   -3,    0,   -2,
-         1,   -2,    1,   -1,    1,   -1,    0,    1,   -1,    0,
-        -1,    0,    0,    0,    1,   -1,    1,   -1,    0,    0,
-         0,    0,   -1,    1,   -1,    1,    0,    0,    0,    0,
-         0,   -1,    1,   -1,    0,    0,    0,    0,   -1,    0,
-         0,    0,    0,    0,    0,    0,    0,   -1,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0 ],
-
-    [  -21,  -49,   15, -170,  165,  398,  -56,  115,  -31,   69,
-         9,   54,   15,   63,  -10,   11,   -6,   10,   -3,   10,
-         3,    5,    4,    3,    4,    2,    1,    0,   -1,   -1,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0 ]
-
-])
-
-X_Q_REQ_7M5 = np.array([
-
-    [   -7,    8,  -31,  -59,    3,   34,   -3,    1,    9,  -12,
-        -3,    2,   -1,    1,    2,   -1,   -1,    0,   -1,    1,
-         0,    0,    1,   -1,    0,    0,   -1,    1,    0,   -1,
-         1,    0,    0,    0,    0,    0,    0,   -1,    1,    0,
-         0,    1,    0,    0,    0,   -1,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,   -1,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-         0,    0,    0,    0,    0,    0,    0,    0,    0,    0 ],
-
-])
-
-X_Q_REQ = [ X_Q_REQ_7M5, X_Q_REQ_10M ]
-
-LASTNZ_REQ_10M = np.array([ 68, 30 ])
-LASTNZ_REQ_7M5 = np.array([ 60, None ])
-LASTNZ_REQ = [ LASTNZ_REQ_7M5, LASTNZ_REQ_10M ]
-
-NBITS_EST_REQ_10M = np.array([ 206, 237 ])
-NBITS_EST_REQ_7M5 = np.array([ 147, None ])
-NBITS_EST_REQ = [ NBITS_EST_REQ_7M5, NBITS_EST_REQ_10M ]
-
-NBITS_TRUNC_REQ_10M = np.array([ 206, 229 ])
-NBITS_TRUNC_REQ_7M5 = np.array([ 147, None ])
-NBITS_TRUNC_REQ = [ NBITS_TRUNC_REQ_7M5, NBITS_TRUNC_REQ_10M ]
-
-LSB_MODE_REQ_10M = np.array([ 0, 0 ])
-LSB_MODE_REQ_7M5 = np.array([ 0, None ])
-LSB_MODE_REQ = [ LSB_MODE_REQ_7M5, LSB_MODE_REQ_10M ]
-
-
-### C.3.1.19 Residual Coding
-
-RES_BITS_10M = np.array([
-    [ 0, 1 ,1 ,0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0,
-      0, 1, 1, 1, 1, 1, 0 ],
-    [ 0, 0, 1, 1, 0, 0 ]
-], dtype=object)
-
-RES_BITS_7M5 = np.array([
-    [ 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0 ],
-    [ 0, 1, 1, 0, 0, 0, 1, 0, 0, 1 ]
-], dtype=object)
-
-RES_BITS = [ RES_BITS_7M5, RES_BITS_10M ]
-
-
-### C.3.1.20 Noise factor
-
-F_NF_10M = np.array([ 3, 7 ])
-F_NF_7M5 = np.array([ 4, 6 ])
-F_NF = [ F_NF_7M5, F_NF_10M ]
-
-
-### C.3.1.21 Side information encoding
-
-BYTES_SIDE_10M = [
-
-    bytearray([
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x19, 0x30, 0xc3, 0x8d, 0xf1, 0x88, 0xcf, 0x80, 0x43 ]),
-
-    bytearray([
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x39, 0x30, 0xc5, 0xf7, 0x6b, 0x89, 0xcf, 0x50, 0x1d ])
-
-]
-
-BYTES_SIDE_7M5 = [
-
-    bytearray([
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x04, 0x07, 0xd3, 0x48, 0x84, 0x45, 0xbe, 0x3b ]),
-
-    bytearray([
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x18, 0x90, 0x6c, 0x0f, 0x6b, 0x8c, 0xc7, 0xa2, 0x15 ])
-
-]
-
-BYTES_SIDE = [ BYTES_SIDE_7M5, BYTES_SIDE_10M ]
-
-
-### C.3.1.22 Arithmetic encoding
-
-BYTES_AC_10M = [
-
-    bytearray([
-        0xa7, 0x1c, 0x2a, 0x35, 0xaf, 0x69, 0x0e, 0xf9, 0xbe, 0xf1,
-        0xcb, 0x2e, 0x87, 0x5f, 0x05, 0x13, 0xd7, 0x8e, 0x3c, 0xf2,
-        0xcd, 0xd5, 0xad, 0x56, 0xb6, 0x5e, 0xe1, 0x8f, 0xc1, 0xbe,
-        0x80, 0x99, 0x30, 0xc3, 0x8d, 0xf1, 0x88, 0xcf, 0x80, 0x43 ]),
-
-    bytearray([
-        0x7e, 0x55, 0xff, 0x85, 0x35, 0x86, 0xf7, 0x01, 0xf8, 0x8c,
-        0xaa, 0x1a, 0x1e, 0x42, 0x43, 0xd6, 0x26, 0x02, 0x37, 0x5c,
-        0x78, 0xff, 0x1a, 0x55, 0xdd, 0xd2, 0x81, 0x2e, 0x68, 0xdd,
-        0xc4, 0xf9, 0x30, 0xc5, 0xf7, 0x6b, 0x89, 0xcf, 0x50, 0x1d ])
-
-]
-
-BYTES_AC_7M5 = [
-
-    bytearray([
-        0xfe, 0x86, 0xdb, 0xa2, 0x90, 0x78, 0x54, 0xb1, 0x5d, 0x1b,
-        0x1f, 0x3b, 0x24, 0x62, 0xaf, 0xb5, 0x95, 0x9c, 0xb0, 0xa0,
-        0x6f, 0xbe, 0xac, 0x07, 0xd3, 0x48, 0x84, 0x45, 0xbe, 0x3b ]),
-
-    bytearray([
-        0xda, 0x52, 0x63, 0x57, 0xf8, 0x66, 0x37, 0xcf, 0x85, 0xe1,
-        0xd4, 0x32, 0x46, 0xc2, 0x36, 0x6d, 0xed, 0xa9, 0x52, 0x58,
-        0x17, 0x18, 0x90, 0x6c, 0x0f, 0x6b, 0x8c, 0xc7, 0xa2, 0x15 ])
-
-]
-
-BYTES_AC = [ BYTES_AC_7M5, BYTES_AC_10M ]
-
-
-### C.3.1.23 Attack detector intermediate data
-
-NBYTES_ATT = (88e3 * np.array([ 7.5e-3, 10e-3 ]) / 8).astype(int)
-
-X_PCM_ATT_10M = np.array([
-
-    [      0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,  27852,  29491,  27852,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0 ],
-
-    [      0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0 ]
-
-])
-
-X_PCM_ATT_7M5 = np.array([
-
-    [      0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,  27852,  29491,  27852,      0,
-           0,      0,      0,      0,      0,      0,      0,      0 ],
-
-    [      0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0,
-           0,      0,      0,      0,      0,      0,      0,      0 ],
-
-])
-
-X_PCM_ATT = [ X_PCM_ATT_7M5, X_PCM_ATT_10M ]
-
-F_ATT_7M5 = np.array([ 1, 1 ])
-F_ATT_10M = np.array([ 1, 1 ])
-F_ATT = [ F_ATT_7M5, F_ATT_10M ]
-
-
-### C.4.1.1-4  Spectrum Decoding
-
-X_HAT_Q_10M = np.array([
-
-    [    0.0000,   16.8125,  -13.8125,   44.3125,  -49.3125,  -89.3125,
-        -0.8125,  -11.8125,  -12.3125,    0.0000,  -20.8125,    6.8125,
-       -21.3125,   -9.8125,    1.3125,   -1.8125,    1.3125,   -3.3125,
-         0.0000,   -2.3125,    1.3125,   -1.8125,    1.3125,   -0.8125,
-         1.0000,   -1.0000,    0.0000,    1.0000,   -1.0000,    0.0000,
-        -1.0000,    0.0000,    0.0000,    0.0000,    1.0000,   -1.0000,
-         1.0000,   -1.0000,    0.0000,    0.0000,    0.0000,    0.0000,
-        -1.0000,    1.0000,   -1.0000,    1.0000,    0.0000,    0.0000,
-         0.0000,    0.0000,    0.0000,   -1.0000,    1.0000,   -1.0000,
-         0.0000,    0.0000,    0.0000,    0.0000,   -1.0000,    0.0000,
-         0.0000,    0.0000,   -0.3125,   -0.3125,    0.0000,    0.0000,
-         0.0000,   -1.0000,    0.0000,    0.0000,    0.0000,    0.3125,
-        -0.3125,   -0.3125,   -0.3125,   -0.3125,    0.3125,   -0.3125,
-        -0.3125,    0.3125,    0.3125,    0.3125,    0.3125,    0.3125,
-        -0.3125,    0.3125,    0.3125,    0.3125,    0.3125,    0.3125,
-         0.3125,    0.3125,    0.3125,   -0.3125,   -0.3125,   -0.3125,
-        -0.3125,   -0.3125,   -0.3125,   -0.3125,   -0.3125,   -0.3125,
-         0.3125,   -0.3125,   -0.3125,    0.3125,   -0.3125,   -0.3125,
-        -0.3125,   -0.3125,   -0.3125,   -0.3125,    0.3125,    0.3125,
-        -0.3125,    0.3125,    0.3125,    0.3125,   -0.3125,    0.3125,
-         0.3125,   -0.3125,    0.3125,   -0.3125,    0.3125,    0.3125,
-        -0.3125,    0.3125,    0.3125,    0.3125,   -0.3125,   -0.3125,
-         0.3125,   -0.3125,    0.3125,    0.3125,   -0.3125,   -0.3125,
-        -0.3125,   -0.3125,    0.3125,    0.3125,    0.3125,   -0.3125,
-        -0.3125,    0.3125,    0.3125,    0.3125,   -0.3125,   -0.3125,
-         0.3125,    0.3125,   -0.3125,   -0.3125,   -0.3125,   -0.3125,
-        -0.3125,    0.3125,   -0.3125,   -0.3125 ],
-
-    [  -21.3125,  -49.3125,   15.3125, -170.0000,  165.0000,  398.0000,
-       -56.0000,  115.0000,  -31.0000,   69.0000,    9.0000,   54.0000,
-        15.0000,   63.0000,  -10.0000,   11.0000,   -6.0000,   10.0000,
-        -3.0000,   10.0000,    3.0000,    5.0000,    4.0000,    3.0000,
-         4.0000,    2.0000,    1.0000,    0.0000,   -1.0000,   -1.0000,
-         0.0000,    0.0000,    0.0000,    0.0625,    0.0625,   -0.0625,
-        -0.0625,   -0.0625,    0.0625,    0.0625,   -0.0625,    0.0625,
-        -0.0625,    0.0625,   -0.0625,   -0.0625,    0.0625,   -0.0625,
-        -0.0625,   -0.0625,    0.0625,    0.0625,    0.0625,   -0.0625,
-        -0.0625,   -0.0625,   -0.0625,    0.0625,   -0.0625,    0.0625,
-        -0.0625,    0.0625,    0.0625,    0.0625,    0.0625,   -0.0625,
-         0.0625,    0.0625,    0.0625,   -0.0625,   -0.0625,   -0.0625,
-        -0.0625,    0.0625,   -0.0625,    0.0625,    0.0625,   -0.0625,
-         0.0625,    0.0625,    0.0625,   -0.0625,    0.0625,   -0.0625,
-        -0.0625,    0.0625,   -0.0625,   -0.0625,   -0.0625,   -0.0625,
-        -0.0625,    0.0625,   -0.0625,    0.0625,   -0.0625,   -0.0625,
-        -0.0625,   -0.0625,    0.0625,    0.0625,   -0.0625,    0.0625,
-        -0.0625,   -0.0625,    0.0625,   -0.0625,   -0.0625,   -0.0625,
-         0.0625,    0.0625,   -0.0625,   -0.0625,    0.0625,   -0.0625,
-        -0.0625,    0.0625,    0.0625,    0.0625,    0.0625,    0.0625,
-        -0.0625,   -0.0625,   -0.0625,    0.0625,   -0.0625,    0.0625,
-         0.0625,    0.0625,   -0.0625,    0.0625,   -0.0625,   -0.0625,
-        -0.0625,    0.0625,   -0.0625,   -0.0625,   -0.0625,    0.0625,
-        -0.0625,   -0.0625,   -0.0625,    0.0625,    0.0625,    0.0625,
-        -0.0625,   -0.0625,    0.0625,   -0.0625,    0.0625,    0.0625,
-         0.0625,    0.0625,    0.0625,   -0.0625,    0.0625,    0.0625,
-         0.0625,   -0.0625,    0.0625,    0.0625 ]
-
-])
-
-X_HAT_Q_7M5 = np.array([
-
-    [   -7.3125,    8.3125,  -31.3125,  -58.8125,    3.3125,   33.8125,
-        -2.8125,    1.3125,    9.3125,  -11.8125,   -3.0000,    2.0000,
-        -1.0000,    1.0000,    2.0000,   -1.0000,   -1.0000,    0.0000,
-        -1.0000,    1.0000,    0.0000,    0.0000,    1.0000,   -1.0000,
-         0.0000,    0.0000,   -1.0000,    1.0000,    0.0000,   -1.0000,
-         1.0000,    0.0000,    0.0000,    0.2500,   -0.2500,    0.0000,
-         0.0000,   -1.0000,    1.0000,    0.0000,    0.0000,    1.0000,
-         0.0000,    0.0000,    0.0000,   -1.0000,    0.0000,    0.0000,
-        -0.2500,    0.2500,    0.2500,    0.2500,   -0.2500,    0.2500,
-         0.2500,    0.2500,   -0.2500,    0.0000,    0.0000,   -1.0000,
-         0.0000,    0.0000,    0.2500,    0.2500,   -0.2500,   -0.2500,
-         0.2500,    0.2500,    0.2500,   -0.2500,   -0.2500,    0.2500,
-        -0.2500,    0.2500,   -0.2500,    0.2500,   -0.2500,   -0.2500,
-        -0.2500,   -0.2500,   -0.2500,   -0.2500,    0.2500,   -0.2500,
-         0.2500,    0.2500,   -0.2500,   -0.2500,    0.2500,   -0.2500,
-         0.2500,    0.2500,   -0.2500,   -0.2500,    0.2500,   -0.2500,
-         0.2500,   -0.2500,    0.2500,    0.2500,    0.2500,    0.2500,
-         0.2500,   -0.2500,   -0.2500,   -0.2500,    0.2500,    0.2500,
-         0.2500,    0.2500,    0.2500,   -0.2500,   -0.2500,   -0.2500,
-         0.2500,    0.2500,    0.2500,   -0.2500,    0.2500,    0.2500 ],
-
-    [    3.8125,  -21.8125,   72.3125, -408.3125,  -88.3125,  -35.3125,
-        46.3125,  -45.0000,  -19.0000,   -2.0000,   -4.0000,    5.0000,
-         3.0000,    3.0000,   -1.0000,    2.0000,    0.0000,    4.0000,
-        -1.0000,    0.0000,   -1.0000,    0.0000,    0.0000,   -0.1250,
-        -0.1250,    0.1250,   -0.1250,   -0.1250,    0.1250,   -0.1250,
-        -0.1250,   -0.1250,   -0.1250,   -0.1250,    0.1250,    0.1250,
-        -0.1250,   -0.1250,    0.1250,   -0.1250,    0.1250,   -0.1250,
-        -0.1250,    0.1250,    0.1250,   -0.1250,   -0.1250,    0.1250,
-        -0.1250,   -0.1250,   -0.1250,    0.1250,   -0.1250,    0.1250,
-        -0.1250,   -0.1250,    0.1250,   -0.1250,    0.1250,   -0.1250,
-        -0.1250,    0.1250,   -0.1250,   -0.1250,   -0.1250,   -0.1250,
-        -0.1250,   -0.1250,   -0.1250,    0.1250,   -0.1250,    0.1250,
-         0.1250,    0.1250,   -0.1250,   -0.1250,   -0.1250,   -0.1250,
-         0.1250,    0.1250,    0.1250,    0.1250,    0.1250,    0.1250,
-        -0.1250,   -0.1250,   -0.1250,    0.1250,    0.1250,   -0.1250,
-        -0.1250,   -0.1250,   -0.1250,   -0.1250,   -0.1250,    0.1250,
-         0.1250,   -0.1250,    0.1250,    0.1250,    0.1250,   -0.1250,
-        -0.1250,   -0.1250,   -0.1250,    0.1250,   -0.1250,    0.1250,
-        -0.1250,   -0.1250,   -0.1250,   -0.1250,    0.1250,    0.1250,
-        -0.1250,    0.1250,    0.1250,    0.1250,   -0.1250,   -0.1250 ]
-
-])
-
-X_HAT_Q = [ X_HAT_Q_7M5, X_HAT_Q_10M ]
-
-### C.4.1.6  TNS
-
-X_HAT_TNS_10M = np.array([
-
-    [  0.0000000e+00,  2.5363181e+03, -2.0837409e+03,  6.6849426e+03,
-      -7.4392379e+03, -1.3473601e+04, -1.2257300e+02, -1.7820228e+03,
-      -1.8574523e+03,  0.0000000e+00, -3.1397544e+03,  1.0277274e+03,
-      -3.2151839e+03,  7.6090367e+02, -1.9466503e+03, -5.2445445e+02,
-       3.3140414e+02, -1.8519794e+03,  1.7920998e+03, -2.1685321e+03,
-       1.8597265e+03, -1.3374728e+03,  7.4531009e+02,  1.3315563e+02,
-      -6.0572955e+02,  1.0738661e+03, -1.2042433e+03,  1.2934874e+03,
-      -1.0948115e+03,  6.2875792e+02, -2.6176578e+02, -3.3286816e+02,
-       6.3874781e+02, -9.2546646e+02,  1.0536075e+03, -1.0082162e+03,
-       8.4609116e+02, -5.3643862e+02,  8.6016577e+01,  3.1548104e+02,
-      -6.4537299e+02,  7.8414243e+02, -9.2031551e+02,  8.4727748e+02,
-      -7.1936859e+02,  4.6589807e+02, -6.4881797e+01, -2.6457448e+02,
-       5.6215195e+02, -6.6813432e+02,  6.6226333e+02, -6.5474860e+02,
-       5.2409793e+02, -4.0142502e+02,  6.3505532e+01,  1.7235901e+02,
-      -4.1924611e+02,  5.0550392e+02, -6.5734435e+02,  5.0345309e+02,
-      -3.6608788e+02,  8.0016434e+01,  9.3237184e+01, -3.5017721e+02,
-       4.2795992e+02, -4.6886670e+02,  3.7402812e+02, -3.7888490e+02,
-       1.5291801e+02, -1.5802320e+01, -1.9719846e+02,  3.4187888e+02,
-      -4.2748515e+02,  3.5181826e+02, -3.0466662e+02,  7.6284275e+01,
-       8.7663093e+01, -2.9659233e+02,  3.1030268e+02, -2.9594060e+02,
-       2.6439080e+02, -9.3629481e+01,  5.7399177e+01,  1.6375927e+02,
-      -2.3208956e+02,  3.5517121e+02, -2.5708880e+02,  2.5690950e+02,
-      -5.2915469e+01,  2.3237996e+01,  1.8246977e+02, -1.5537990e+02,
-       2.8029628e+02, -2.5001493e+02,  1.6287623e+02, -1.2710806e+02,
-      -6.9041960e+01,  5.1949460e+01, -2.3006446e+02,  1.3249613e+02,
-      -2.3061667e+02,  6.5507752e+01, -6.6442896e+00, -1.4373816e+02,
-       1.3523410e+02, -1.2379774e+02,  5.2635973e+01, -6.1214557e+01,
-      -4.6571211e+01,  6.8132465e+00, -1.2664460e+02,  3.6959535e+01,
-      -3.5846511e+01,  2.8934234e+01, -4.3948154e+01,  8.1935863e+01,
-       7.4660900e+00,  5.1207805e+01, -2.1748068e+01,  8.5958555e+01,
-       4.9435547e+00, -4.1272410e+01,  1.2552229e+02, -1.6625839e+02,
-       1.9307762e+02, -1.0613596e+02,  1.4547859e+01,  1.0890433e+02,
-      -1.0539493e+02,  1.9916467e+02, -1.9460994e+02,  1.3162563e+02,
-      -2.6679242e+01, -1.0641065e+02,  1.9984677e+02, -1.8499813e+02,
-       1.3746427e+02, -1.2384807e+02, -4.8113073e+00, -1.1154394e+01,
-      -4.8901484e+01,  9.9602073e+01, -6.2727442e+01,  4.0646873e+01,
-      -3.8839607e+01,  5.5618701e+01,  1.5609019e+01,  2.6413984e+01,
-      -1.3661998e+01, -2.1935131e+01,  7.0604596e+01, -3.6013874e+01,
-      -5.2534512e+00, -1.9095831e+01, -5.9462211e+01, -2.0474437e+01,
-      -8.2457334e+01,  6.6458037e+01, -1.3905842e+02,  4.4992015e+01 ],
-
-    [ -4.4674836e+02, -1.0336788e+03,  3.2097756e+02, -3.5635060e+03,
-       3.4586970e+03,  8.3427964e+03, -1.1738608e+03,  2.4106070e+03,
-      -6.4981580e+02,  1.4463642e+03,  1.8865620e+02,  1.1319372e+03,
-       3.1442700e+02,  1.4722656e+03,  5.9737149e+02,  9.6658866e+02,
-       4.9169825e+02,  6.7177464e+02,  1.9327293e+02,  1.5499589e+02,
-      -2.1600722e+01, -9.8228391e+01, -1.2744379e+02, -1.7269979e+02,
-      -8.5793205e+01, -7.4701778e+01, -2.2833266e+01,  8.7668786e-01,
-       1.4493209e+01,  2.7797597e+01,  4.0794920e+01,  4.3722928e+01,
-       3.6950947e+01,  2.9794074e+01,  2.0218141e+01,  7.2233914e+00,
-      -4.3603335e+00, -1.3552659e+01, -1.6604838e+01, -1.8237845e+01,
-      -1.9241801e+01, -1.3923796e+01, -1.1242668e+01, -3.6760056e+00,
-      -1.1829705e+00,  2.5681718e+00,  7.2841081e+00,  6.6584846e+00,
-       6.5203815e+00,  4.4803237e+00,  5.1830531e+00,  3.8023813e+00,
-       2.5801707e+00, -8.9194181e-01, -2.7289508e+00, -4.3183797e+00,
-      -5.5686696e+00, -3.5658387e+00, -5.0013288e+00, -1.5969716e+00,
-      -2.5434186e+00,  1.0495670e+00,  2.5178803e+00,  4.0287891e+00,
-       5.1309495e+00,  2.8569829e+00,  4.5137803e+00,  3.7678816e+00,
-       3.5274736e+00,  2.1055313e-01, -1.4896329e+00, -3.0388583e+00,
-      -4.4849681e+00, -2.7741285e+00, -4.6088456e+00, -1.5252078e+00,
-      -1.4816216e-01, -7.4985193e-01,  2.1073955e+00,  3.0534784e+00,
-       4.4307001e+00,  2.0418555e+00,  3.6512371e+00,  8.8259920e-01,
-      -5.5756792e-01,  2.4731249e-01, -2.4585823e+00, -3.1307473e+00,
-      -4.3178686e+00, -4.4328370e+00, -4.5859669e+00, -1.9461803e+00,
-      -2.7535202e+00,  5.2435412e-01, -5.7680820e-01, -1.1178729e-01,
-      -4.7067976e-01, -9.7145273e-01,  1.1713253e+00,  1.5417023e+00,
-      -6.2164141e-02,  1.8196547e+00, -3.3253562e-01, -8.7085257e-01,
-       4.0545492e-01, -1.6971448e+00, -2.1315433e+00, -3.1522747e+00,
-      -7.0840626e-01,  1.9820684e-01, -1.2521487e+00, -1.2659519e+00,
-       9.9900232e-01, -5.5880815e-01, -1.1085204e+00,  6.9059170e-01,
-       1.6309021e+00,  2.4853740e+00,  2.8911996e+00,  3.6269550e+00,
-       1.3909661e+00,  9.7770819e-02, -1.4853328e+00, -1.9863830e-01,
-      -2.7510985e+00, -8.4492066e-01, -1.7063127e-01,  1.1338053e+00,
-      -4.4636302e-01,  1.7476214e+00, -3.9440033e-02, -6.3944530e-01,
-      -1.7198507e+00,  7.4867347e-02, -1.8649133e+00, -2.4597157e+00,
-      -2.9678215e+00, -5.7798031e-01, -2.1073509e+00, -2.2439980e+00,
-      -2.4007588e+00,  2.2681540e-01,  1.3649713e+00,  2.4256778e+00,
-       9.1907814e-01,  4.7699713e-01,  2.1704836e+00, -3.5175645e-01,
-       1.3125148e+00,  1.2589840e+00,  2.0656111e+00,  2.3990940e+00,
-       2.6834440e+00,  6.0589675e-01,  1.9858819e+00,  1.9373749e+00,
-       2.1901227e+00, -3.8495770e-01,  1.1720109e+00,  1.3860966e+00 ]
-
-])
-
-X_HAT_TNS_7M5 = np.array([
-
-    [ -1.3003668e+03,  1.4781948e+03, -5.5682374e+03, -1.0458506e+04,
-       5.8905505e+02,  6.0128073e+03, -5.0014108e+02,  2.3339917e+02,
-       1.6560227e+03, -2.1005926e+03,  9.8231590e+02,  6.5472037e+02,
-      -1.8816616e+03,  1.9995050e+03, -5.2483056e+02, -1.1296152e+03,
-       1.8917056e+03, -1.4074840e+03,  7.3771850e+01,  1.1607281e+03,
-      -1.4654795e+03,  9.0328961e+02,  1.8790564e+02, -1.0278688e+03,
-       1.0629204e+03, -5.3634594e+02, -3.2451913e+02,  9.0207993e+02,
-      -8.0267777e+02,  1.2277267e+02,  5.5484886e+02, -6.8584739e+02,
-       4.1901310e+02,  7.2779950e+01, -4.3460765e+02,  4.5133152e+02,
-      -2.1578787e+02, -2.4628376e+02,  5.3756691e+02, -3.8146878e+02,
-       3.4097661e+01,  4.5169871e+02, -5.0575362e+02,  2.7278971e+02,
-       9.4493294e+01, -5.2727129e+02,  4.9704432e+02, -2.0290693e+02,
-      -2.1943755e+02,  4.6316564e+02, -3.5988557e+02,  1.5354987e+02,
-       8.8971222e+01, -2.1177185e+02,  2.9338708e+02, -1.6015893e+02,
-      -3.0076244e+01,  1.4282354e+02, -1.8203323e+02, -3.8237583e+01,
-       8.0989223e+01, -6.7911301e+01,  4.5876843e+01,  5.8160967e+01,
-      -1.2140603e+02,  3.0523591e+01,  1.0177828e+02, -7.1769925e+01,
-       6.9697281e+01, -4.4342614e+01, -7.7392269e+01,  1.5965917e+02,
-      -1.5467801e+02,  8.0965471e+01,  5.9745822e+00, -7.8890418e+01,
-       9.3802490e+01, -1.3176806e+02,  3.1336676e+01,  5.8640239e-01,
-      -1.0941547e+02,  8.4322101e+01, -1.0231229e+01, -7.6312076e+01,
-       1.2996536e+02, -2.7610121e+01, -8.9190623e+01,  7.8060953e+01,
-       6.1715955e+00, -9.5954760e+01,  1.3405798e+02, -1.2502304e+01,
-      -1.3552601e+02,  1.1952233e+02, -6.4806966e+00, -1.2142849e+02,
-       1.8115437e+02, -1.4257871e+02,  3.1225251e+01,  1.6033227e+02,
-      -1.6511810e+02,  1.5240738e+02,  1.2638046e+01, -1.6469393e+02,
-       1.4205406e+02, -1.1641621e+02,  4.7933541e+01,  9.6516025e+01,
-      -1.1306054e+02,  1.3942714e+02, -2.5241905e+01, -9.3530513e+01,
-       8.8433097e+01, -1.0959238e+02,  8.7282861e+01,  3.9977748e+01,
-      -7.0411977e+01,  4.1780370e+01,  1.4511943e+01,  3.1125577e+01 ],
-
-    [  6.7796903e+01, -3.8788720e+02,  1.2859183e+03, -7.2609371e+03,
-      -1.5704430e+03, -6.2795492e+02,  8.2356565e+02, -8.0022573e+02,
-      -3.3787309e+02, -3.5565588e+01, -1.0363528e+02,  3.3897199e+01,
-       1.9043187e+02,  1.7876914e+02, -1.5669449e+01, -1.4678018e+02,
-      -1.0987901e+02,  3.8138527e+01,  7.5635735e+01,  2.8493842e+01,
-      -9.6336587e+00, -2.2372156e+01, -1.9441869e+01, -2.6295536e+01,
-      -6.7279947e+00,  3.2810187e+01,  3.3795132e+01,  2.0377831e+00,
-      -2.2847912e+01, -2.2110999e+01, -7.4749335e+00,  8.1844245e-01,
-       6.9094406e+00,  1.1427591e+01,  1.0664575e+01,  2.5808397e+00,
-      -1.1296056e+01, -1.5517110e+01, -3.3427801e+00,  6.2108447e+00,
-       1.0440121e+01,  4.7195530e+00, -4.6830353e+00, -4.3321706e+00,
-      -4.2308894e-01, -6.6286888e-01, -3.0541198e+00,  1.0803641e+00,
-       3.4706447e+00, -1.0277278e+00, -5.8300676e+00, -1.2004259e+00,
-       2.9511818e+00,  4.4258015e+00, -1.2798751e-01, -5.6192448e+00,
-      -1.1434112e+00,  2.9285732e-01,  2.3558315e+00,  8.5236355e-01,
-      -3.6033829e+00,  3.6155080e-01, -1.9369648e-02, -3.0335453e+00,
-      -3.9032015e+00, -3.0556779e+00,  3.9409059e-01,  3.5833234e-01,
-      -1.1380321e+00,  3.0590734e+00,  2.1256269e+00,  1.1948865e+00,
-       1.8418728e+00,  1.9339640e+00, -1.8934727e+00, -7.8211470e+00,
-      -7.2315006e+00, -1.6085474e+00,  7.0887341e+00,  1.1289772e+01,
-       7.3233713e+00,  5.9236776e-01, -4.6290981e+00, -4.8012930e+00,
-      -5.3731448e+00, -4.9951949e+00, -1.0331793e+00,  6.8575568e+00,
-       1.1172332e+01,  2.1481470e+00, -9.4111289e+00, -1.2048567e+01,
-      -6.2321457e+00,  1.2834480e+00,  4.2595916e+00,  8.2235361e+00,
-       9.4292179e+00, -8.2653408e-01, -7.7277654e+00, -5.0381286e+00,
-       2.5809009e+00,  2.9671834e+00, -3.1758077e+00, -4.8014638e+00,
-      -2.4972384e+00,  3.7424503e+00,  3.1651108e+00,  2.1258770e+00,
-      -3.9826224e-01, -5.5310115e+00, -6.7195904e+00, -4.6113744e+00,
-       4.7359889e+00,  1.1737180e+01,  4.3792994e+00, -2.4150647e+00,
-      -3.2444202e+00, -3.3837188e-01, -2.0142283e+00, -6.2230109e+00 ]
-
-])
-
-X_HAT_TNS = [ X_HAT_TNS_7M5, X_HAT_TNS_10M ]
-
-X_HAT_F_48K_10M = np.array([
-
-       9.4517170e+02,  6.1123091e+02,  2.6864344e+03, -9.2131878e+02,
-       3.9506388e+03, -1.6369062e+03,  2.4082498e+04, -3.0114303e+02,
-      -7.0038117e+03, -5.0717257e+03, -4.3561383e+03, -4.8809024e+03,
-      -2.4479052e+03, -6.2613898e+01,  2.0513506e+03, -1.4311748e+02,
-       1.4311748e+02, -9.0641071e+02,  9.5411654e+01, -1.2880573e+03,
-       5.2476410e+02, -1.1926457e+03,  4.7705827e+01, -9.5411654e+01,
-       3.8164662e+02,  9.5411654e+01,  2.3852913e+02,  9.5411654e+01,
-       9.5411654e+01, -9.5411654e+01, -1.9082331e+02, -1.9082331e+02,
-       0.0000000e+00,  0.0000000e+00, -4.7705827e+02,  5.7246992e+02,
-       2.8623496e+02,  3.3394079e+02, -4.7705827e+01, -9.5411654e+01,
-      -9.5411654e+01, -9.5411654e+01, -9.5411654e+01,  0.0000000e+00,
-       0.0000000e+00,  2.3852913e+02,  4.7705827e+01,  8.1099906e+02,
-      -5.2476410e+02,  0.0000000e+00, -9.5411654e+01,  9.5411654e+01,
-      -1.9082331e+02, -4.7705827e+01, -9.5411654e+01,  1.4311748e+02,
-       4.7705827e+01,  9.5411654e+01,  0.0000000e+00,  0.0000000e+00,
-      -2.3852913e+02, -9.5411654e+01, -2.8623496e+02, -3.3394079e+02,
-      -1.4311748e+02, -4.7705827e+01,  4.7705827e+01,  4.7705827e+01,
-       1.9082331e+02,  2.3852913e+02,  9.5411654e+01,  0.0000000e+00,
-      -9.5411654e+01, -1.9082331e+02, -1.9082331e+02, -9.5411654e+01,
-      -1.4311748e+02,  4.7705827e+01,  4.7705827e+01,  2.8623496e+02,
-       1.4311748e+02,  2.3852913e+02,  1.9082331e+02,  1.4311748e+02,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00, -4.7705827e+01,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00, -4.7705827e+01,
-       4.7705827e+01, -2.3852913e+02,  0.0000000e+00, -9.5411654e+01,
-      -2.8623496e+02, -2.3852913e+02, -4.7705827e+01, -4.7705827e+01,
-       0.0000000e+00,  0.0000000e+00, -4.7705827e+01,  4.7705827e+01,
-      -4.7705827e+01, -9.5411654e+01, -4.7705827e+01, -4.7705827e+01,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  4.7705827e+01,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00, -4.7705827e+01, -4.7705827e+01,  0.0000000e+00,
-       0.0000000e+00,  4.7705827e+01,  0.0000000e+00, -4.7705827e+01,
-       0.0000000e+00, -4.7705827e+01,  1.4311748e+02, -9.5411654e+01,
-      -4.7705827e+01,  4.7705827e+01, -4.7705827e+01, -4.7705827e+01,
-       4.7705827e+01,  4.7705827e+01,  0.0000000e+00,  9.5411654e+01,
-      -1.9082331e+02,  9.5411654e+01,  1.9082331e+02, -9.5411654e+01,
-      -9.5411654e+01,  1.9082331e+02, -1.9082331e+02,  9.5411654e+01,
-       4.7705827e+01,  0.0000000e+00, -4.7705827e+01,  0.0000000e+00,
-       0.0000000e+00,  4.7705827e+01,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  5.9632284e+00,  5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00, -5.9632284e+00, -5.9632284e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  4.7705827e+01,
-      -4.7705827e+01, -4.7705827e+01,  0.0000000e+00,  0.0000000e+00,
-      -4.7705827e+01,  0.0000000e+00,  4.7705827e+01,  0.0000000e+00,
-       0.0000000e+00, -4.7705827e+01,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  4.7705827e+01,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  4.7705827e+01,  0.0000000e+00, -4.7705827e+01,
-      -4.7705827e+01, -4.7705827e+01, -4.7705827e+01,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  4.7705827e+01,  4.7705827e+01,
-       4.7705827e+01,  0.0000000e+00,  0.0000000e+00, -4.7705827e+01,
-      -9.5411654e+01, -9.5411654e+01, -9.5411654e+01,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  9.5411654e+01,
-       4.7705827e+01,  4.7705827e+01,  4.7705827e+01,  4.7705827e+01,
-       0.0000000e+00,  0.0000000e+00, -4.7705827e+01, -4.7705827e+01,
-      -4.7705827e+01,  0.0000000e+00, -4.7705827e+01, -4.7705827e+01,
-      -4.7705827e+01,  0.0000000e+00, -4.7705827e+01,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  4.7705827e+01,  4.7705827e+01,  0.0000000e+00,
-       0.0000000e+00,  4.7705827e+01, -4.7705827e+01,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00, -4.7705827e+01,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00, -5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00,  5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-      -5.9632284e+00, -5.9632284e+00,  5.9632284e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  4.7705827e+01,  0.0000000e+00,
-       4.7705827e+01,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-      -5.9632284e+00, -5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-      -5.9632284e+00, -5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00, -5.9632284e+00, -5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00,  5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00, -5.9632284e+00, -5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00,  5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00,  5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-      -5.9632284e+00,  5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       4.7705827e+01, -4.7705827e+01,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00, -5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-      -5.9632284e+00, -5.9632284e+00, -5.9632284e+00, -5.9632284e+00,
-      -5.9632284e+00, -5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-      -5.9632284e+00, -5.9632284e+00,  5.9632284e+00,  5.9632284e+00,
-      -5.9632284e+00, -5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-      -5.9632284e+00, -5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00, -5.9632284e+00, -5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00,  5.9632284e+00, -5.9632284e+00,
-      -5.9632284e+00, -5.9632284e+00, -5.9632284e+00, -5.9632284e+00,
-      -5.9632284e+00, -5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00, -5.9632284e+00,  5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00, -5.9632284e+00, -5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00, -5.9632284e+00,  5.9632284e+00,
-       5.9632284e+00,  5.9632284e+00,  5.9632284e+00, -5.9632284e+00
-
-])
-
-RC_ORDER_48K_10M = np.array([ 4, 5 ])
-
-RC_I_1_48K_10M = np.array([ 3, 10, 10, 9, 8, 8, 8, 8 ])
-RC_I_2_48K_10M = np.array([ 4,  9, 11, 9, 9, 8, 8, 8 ])
-
-X_HAT_TNS_48K_10M = np.array([
-
-       9.4517170e+02,  6.1123091e+02,  2.6864344e+03, -9.2131878e+02,
-       3.9506388e+03, -1.6369062e+03,  2.4082498e+04, -3.0114303e+02,
-      -7.0038117e+03, -5.0717257e+03, -4.3561383e+03, -4.8809024e+03,
-      -2.4479052e+03, -2.2398317e+03, -3.1132766e+01,  2.0090579e+02,
-       1.1862042e+03,  5.7338427e+02,  6.1759350e+02, -9.7469088e+02,
-      -6.4374897e+02, -2.0211697e+03, -1.7062857e+03, -1.3890132e+03,
-      -4.2328832e+02,  3.5577372e+02,  1.1106915e+03,  1.4302110e+03,
-       1.4201992e+03,  9.4898618e+02,  2.3607533e+02, -4.7224392e+02,
-      -8.4841824e+02, -9.9022181e+02, -1.3448133e+03, -4.1592231e+02,
-       2.0637545e+02,  9.3371650e+02,  1.1146864e+03,  9.6858809e+02,
-       5.9597157e+02,  9.1928376e+01, -3.7626331e+02, -6.1986247e+02,
-      -6.9177579e+02, -3.4667628e+02, -1.0196915e+02,  9.4981560e+02,
-       5.0772033e+02,  5.6925480e+02,  2.7206846e+02,  8.9632931e+01,
-      -3.0001737e+02, -4.6633999e+02, -5.8788470e+02, -3.5775041e+02,
-      -1.5049111e+02,  1.4316978e+02,  2.9621193e+02,  3.6240991e+02,
-       9.5814668e+01, -7.8107250e+01, -4.7386669e+02, -8.4266712e+02,
-      -9.1319744e+02, -7.8870946e+02, -4.4400310e+02, -5.1962032e+01,
-       4.4241817e+02,  8.5744581e+02,  9.6559775e+02,  8.1789237e+02,
-       4.2722231e+02, -1.1744557e+02, -6.0875733e+02, -8.7077276e+02,
-      -9.9676704e+02, -7.3639748e+02, -3.7055666e+02,  2.7449836e+02,
-       6.9342929e+02,  1.0695021e+03,  1.1847878e+03,  1.0572091e+03,
-       6.5809175e+02,  2.0789887e+02, -2.0474320e+02, -5.3854975e+02,
-      -6.4606559e+02, -5.9469535e+02, -4.1519106e+02, -2.2005318e+02,
-       6.5763023e+01, -1.8180468e+00,  1.1794585e+02,  3.7652871e+01,
-      -2.6013985e+02, -4.9007184e+02, -5.2184675e+02, -4.8856445e+02,
-      -3.1503130e+02, -1.1131138e+02,  2.8240046e+01,  2.1696333e+02,
-       2.2485577e+02,  1.2779759e+02,  2.8796892e+01, -9.8980705e+01,
-      -1.5201103e+02, -1.6768213e+02, -1.4166855e+02, -3.8081735e+01,
-       1.9957705e+01,  7.3452728e+01,  9.9166879e+01,  9.4204641e+01,
-       7.0145854e+01, -1.3744063e+01, -9.3049197e+01, -1.1359718e+02,
-      -1.1480749e+02, -3.8801493e+01, -5.6439787e-01, -7.4563180e+00,
-       2.1645294e+01, -2.1494684e+01,  1.2628610e+02,  1.3469076e+01,
-      -3.1053646e+01,  1.0922488e+00, -7.3585334e+01, -1.0982479e+02,
-      -4.7186648e+01,  1.5142158e+01,  4.5632808e+01,  1.6549594e+02,
-      -3.6083949e+01,  5.8172372e+01,  2.0212810e+02,  6.2799792e+01,
-      -3.6264662e+01,  1.1267918e+02, -1.4073978e+02, -3.0415441e+01,
-       1.2090131e+00,  5.3715012e+00, -1.1377286e+01, -4.5565675e+00,
-      -5.6917226e+00,  4.3600122e+01,  4.1505158e+01,  4.0418175e+01,
-       3.0433405e+01,  1.8807364e+01,  8.6851800e+00,  1.3063317e+00,
-      -1.6374357e+00, -5.1287186e-01, -8.3181817e+00, -1.3316624e+01,
-      -1.1754958e+01, -9.3082910e+00, -4.7125436e+00,  4.7799716e+01,
-      -1.4779469e+00, -4.4671371e+01, -4.7792557e+01, -5.2664965e+01,
-      -8.7747532e+01, -6.2908560e+01,  7.0728829e+00,  2.9933713e+01,
-       5.4684947e+01,  1.2283279e+01,  6.0871825e+00, -9.7832692e+00,
-      -2.0805101e+01,  2.5453410e+01,  2.2568456e+01,  2.6671505e+01,
-       2.3653407e+01,  6.0861792e+01,  4.5907030e+01, -1.3920574e+01,
-      -7.4036162e+01, -1.3377313e+02, -1.7526999e+02, -1.4452509e+02,
-      -9.6576994e+01, -3.4117396e+01,  7.2828977e+01,  1.5570493e+02,
-       2.1295914e+02,  1.8790695e+02,  1.3270346e+02,  9.1184381e+00,
-      -1.5641394e+02, -2.9335242e+02, -3.8817395e+02, -3.2871789e+02,
-      -2.0655338e+02, -4.3550786e+01,  1.0968391e+02,  3.0824064e+02,
-       3.3663279e+02,  3.0642586e+02,  1.8519446e+02,  5.7390329e+01,
-      -1.1630607e+02, -2.0276872e+02, -2.7699576e+02, -2.4543885e+02,
-      -1.7669630e+02, -1.9362678e+01,  4.7199101e+01,  1.0516333e+02,
-       8.6666990e+01,  8.8657037e+01, -1.5558419e+01, -3.6914314e+01,
-      -8.0595009e+01, -7.0566026e+01, -5.9248668e+01, -1.4894165e+01,
-       1.6364229e+01,  9.4426516e+01,  1.2736629e+02,  9.8422987e+01,
-       5.1256905e+01,  4.3261239e+01, -7.3161150e+01, -8.1108665e+01,
-      -9.8148881e+01, -5.9938566e+01, -2.8972996e+01, -2.3262373e+01,
-       2.3557895e+01,  4.1063112e+01,  4.9456507e+01,  2.9572229e+01,
-       2.1373324e+01, -1.2116045e+01, -1.7920284e+01, -2.5397799e+01,
-      -1.5011495e+01, -1.6604903e+01,  5.7220095e+00,  4.2124559e+00,
-       9.1295553e+00,  2.0972476e+00,  1.0219314e+01,  1.7408319e+00,
-       9.3716408e-01, -4.4755970e+00,  4.3615172e+01,  2.1619284e+01,
-       7.0197660e+01,  2.9541340e+01,  2.1495384e+01, -1.5543427e+01,
-      -2.9701692e+01, -4.8601263e+01, -4.1417636e+01, -2.0218456e+01,
-      -4.1919983e+00,  1.0811220e+01,  1.8282903e+01,  2.9153799e+01,
-       2.6637698e+01,  2.0822319e+01, -2.0049692e+00, -1.7515145e+01,
-      -1.8945085e+01, -2.6096971e+01, -1.0443081e+01,  2.8259408e+00,
-       1.9054250e+01,  2.6408940e+01,  2.9532072e+01,  1.1442436e+01,
-       7.5316255e+00, -3.0305702e+00, -5.1747435e+00, -1.8407036e+01,
-      -7.2025275e+00, -1.3142410e+00, -8.9104503e-01, -1.9580507e+00,
-       9.0242278e+00,  5.0649445e-01,  9.0989185e+00,  8.4374358e+00,
-       1.2180369e+01,  9.8043365e+00,  1.0390873e+01, -4.5184465e+00,
-      -3.0104815e-01, -1.3617490e+01, -2.8571680e+00,  4.7226441e-02,
-       1.0968668e+01,  1.4263590e+01,  1.9054635e+01,  4.5694855e+00,
-      -5.1714614e+00, -5.6951038e+00, -1.5868058e+01, -6.3038212e+00,
-       6.0252467e-01,  5.9026641e+00,  7.8745777e+00,  8.8744978e+00,
-       5.3462027e+01, -1.8654382e+01, -4.3353244e+00, -2.6929767e+01,
-      -1.5311914e+01, -2.7217677e+01,  3.5149041e-01, -3.3211226e+00,
-       7.7038597e+00,  2.4167585e+00,  3.1307056e+00, -6.1700798e+00,
-      -9.6229135e+00, -1.5399820e+01, -1.5266153e+01, -2.7143843e+00,
-       8.4318705e+00,  1.8387062e+01,  1.1142869e+01,  1.6194808e+01,
-      -9.1085598e-01, -9.8865640e+00, -9.6606173e+00, -4.4966142e+00,
-      -1.0006828e+01, -8.3653492e+00,  3.2044245e+00, -2.9444546e-01,
-       1.0908085e+01,  1.3460563e+01,  1.7296403e+01,  2.3331346e+00,
-      -6.4643323e+00, -1.8141900e+01, -2.2182809e+01, -1.1540102e+01,
-       1.1288138e+00,  2.4890144e+00,  1.6559739e+01,  8.5186198e+00,
-       1.5415016e+01, -1.5380758e+00, -8.2713929e+00, -8.0722274e+00,
-      -2.5013191e+00, -8.9116220e+00, -7.8813678e+00, -9.3233771e+00,
-       3.7714591e+00, -1.8991746e+00, -8.2806999e-01,  5.9195021e+00,
-       1.1264459e+01,  1.3340521e+01,  1.4254734e+01,  7.3923870e-01,
-       2.7992830e+00, -1.2191542e+01, -4.1559084e+00, -1.4491802e+01,
-      -1.1202358e+01, -1.3530333e+01, -8.4296049e+00, -7.8650114e+00,
-      -4.0242598e+00, -3.9190447e+00, -3.2052868e+00,  6.8414475e+00,
-       1.2298364e+01,  4.5966329e+00, -2.2198027e+00,  1.4780361e+00,
-       2.6041179e+00, -5.9986780e+00,  1.8848742e+00,  5.1985761e+00,
-       1.0933162e+01,  1.2576045e+01,  2.6295117e+00, -6.3792422e+00,
-      -3.2379866e+00, -1.0344676e+00, -7.5143096e+00,  2.5669904e+00,
-       7.6423118e+00,  1.4123706e+01,  1.5442459e+01,  4.3051286e+00
-
-])
-
-### C.4.1.7  Spectral shaping
-
-X_HAT_SNS_10M = np.array([
-
-    [  0.0000000e+00,  3.2120898e+04, -2.7432096e+04,  9.5098925e+04,
-      -1.1435865e+05, -2.2381382e+05, -1.8369247e+03, -2.0115639e+04,
-      -1.5792914e+04,  0.0000000e+00, -1.5609063e+04,  4.0875220e+03,
-      -1.0230315e+04,  1.9369297e+03, -4.1865185e+03, -1.0063143e+03,
-       5.6734153e+02, -2.8286784e+03,  2.4858485e+03, -2.7806581e+03,
-       2.2044492e+03, -1.4655656e+03,  7.9623905e+02,  1.4627465e+02,
-      -6.8421307e+02,  1.2472853e+03, -1.3595558e+03,  1.3417640e+03,
-      -1.0434807e+03,  5.9927829e+02, -2.2923931e+02, -2.9150665e+02,
-       5.1660528e+02, -7.4849706e+02,  7.9101280e+02, -7.5693454e+02,
-       5.8965376e+02, -3.7385221e+02,  5.5646432e+01,  2.0409315e+02,
-      -3.8684762e+02,  4.7002840e+02, -5.1019782e+02,  4.6970753e+02,
-      -3.6882983e+02,  2.3887213e+02, -3.0765937e+01, -1.2545710e+02,
-       2.5752773e+02, -3.0607937e+02,  3.0617821e+02, -3.0270399e+02,
-       2.4452840e+02, -1.8729289e+02,  2.9629779e+01,  8.1156654e+01,
-      -1.9740547e+02,  2.3802067e+02, -2.9662172e+02,  2.2717944e+02,
-      -1.6519442e+02,  3.2859099e+01,  3.8288258e+01, -1.4380181e+02,
-       1.5993592e+02, -1.7522348e+02,  1.3978068e+02, -1.2885956e+02,
-       5.2007743e+01, -5.3744028e+00, -6.3481105e+01,  1.1005588e+02,
-      -1.3761380e+02,  1.1149518e+02, -9.6552293e+01,  2.4175349e+01,
-       2.7349618e+01, -9.2532521e+01,  9.6809951e+01, -9.2329191e+01,
-       8.1204030e+01, -2.8757018e+01,  1.7629374e+01,  5.0296426e+01,
-      -6.9554039e+01,  1.0643991e+02, -7.7045965e+01,  7.6992232e+01,
-      -1.5336408e+01,  6.7350324e+00,  5.2884931e+01, -4.5033515e+01,
-       7.8565630e+01, -7.0077921e+01,  4.5653385e+01, -3.5627746e+01,
-      -1.8715552e+01,  1.4082202e+01, -6.2364732e+01,  3.5916392e+01,
-      -6.2514422e+01,  1.6978510e+01, -1.7220883e+00, -3.7254517e+01,
-       3.5050409e+01, -3.2086295e+01,  1.2895849e+01, -1.4997608e+01,
-      -1.1409978e+01,  1.6692500e+00, -3.1028013e+01,  8.5596145e+00,
-      -8.3018444e+00,  6.7010013e+00, -1.0178138e+01,  1.8975872e+01,
-       1.6344865e+00,  1.1210482e+01, -4.7611165e+00,  1.8818163e+01,
-       1.0822497e+00, -8.9424468e+00,  2.7196773e+01, -3.6023020e+01,
-       4.1833912e+01, -2.2996359e+01,  3.1520683e+00,  2.4451081e+01,
-      -2.3663155e+01,  4.4716235e+01, -4.3693613e+01,  2.9552445e+01,
-      -5.9899945e+00, -2.4756795e+01,  4.6495022e+01, -4.3040437e+01,
-       3.1981524e+01, -2.8813669e+01, -1.1193668e+00, -2.6891294e+00,
-      -1.1789293e+01,  2.4012319e+01, -1.5122490e+01,  9.7992507e+00,
-      -9.3635505e+00,  1.3408697e+01,  3.8993996e+00,  6.5986643e+00,
-      -3.4130004e+00, -5.4797703e+00,  1.7638234e+01, -8.9968811e+00,
-      -1.3124018e+00, -4.9433015e+00, -1.5392870e+01, -5.3001786e+00,
-      -2.1345573e+01,  1.7203866e+01, -3.5997789e+01,  1.1646997e+01 ],
-
-    [ -6.2955623e+03, -1.4566565e+04,  5.0622202e+03, -7.0393645e+04,
-       8.5577202e+04,  2.5855135e+05, -3.5335070e+04,  5.4655582e+04,
-      -1.1097295e+04,  1.8604727e+04,  1.7284056e+03,  6.9845121e+03,
-       1.3066905e+03,  4.1207686e+03,  1.2132541e+03,  1.5347593e+03,
-       6.1036346e+02,  6.5193569e+02,  1.5477285e+02,  1.0810302e+02,
-      -1.3121390e+01, -5.1968778e+01, -6.2665661e+01, -8.4220873e+01,
-      -4.1495195e+01, -3.5833798e+01, -1.0853816e+01,  4.1261732e-01,
-       6.7539065e+00,  1.2953817e+01,  1.8822817e+01,  2.0173803e+01,
-       1.6734007e+01,  1.3492868e+01,  8.9088009e+00,  3.1828720e+00,
-      -1.8693963e+00, -5.8104021e+00, -6.9265975e+00, -7.6077959e+00,
-      -7.9011791e+00, -5.7174692e+00, -4.5976263e+00, -1.5032819e+00,
-      -4.8178832e-01,  1.0459392e+00,  2.9544513e+00,  2.7006970e+00,
-       2.6614506e+00,  1.8287519e+00,  2.1513094e+00,  1.5782394e+00,
-       1.0890240e+00, -3.7646581e-01, -1.1518203e+00, -1.8534533e+00,
-      -2.3900791e+00, -1.5304619e+00, -2.1511073e+00, -6.8686891e-01,
-      -1.0939425e+00,  4.4580685e-01,  1.0694775e+00,  1.7112407e+00,
-       2.1522574e+00,  1.1984063e+00,  1.8933761e+00,  1.5608228e+00,
-       1.4612352e+00,  8.7220397e-02, -6.3880600e-01, -1.3031673e+00,
-      -1.9233091e+00, -1.2909908e+00, -2.1448096e+00, -7.0978302e-01,
-      -7.4823965e-02, -3.7868573e-01,  1.0642643e+00,  1.5420494e+00,
-       2.4281898e+00,  1.1190134e+00,  2.0010149e+00,  4.8369747e-01,
-      -3.1302463e-01,  1.3884389e-01, -1.3802746e+00, -1.7576352e+00,
-      -2.3441435e+00, -2.4065591e+00, -2.4896924e+00, -1.0565690e+00,
-      -1.4455632e+00,  2.7527927e-01, -3.0281700e-01, -5.8686912e-02,
-      -2.3895075e-01, -4.9317896e-01,  5.9464861e-01,  7.8267846e-01,
-      -3.1558969e-02,  8.7786664e-01, -1.6042710e-01, -4.2013050e-01,
-       1.9560599e-01, -8.1876351e-01, -9.6031363e-01, -1.4201787e+00,
-      -3.1915476e-01,  8.9297143e-02, -5.6412436e-01, -5.3261760e-01,
-       4.2030523e-01, -2.3510454e-01, -4.6638224e-01,  2.9054917e-01,
-       6.4077498e-01,  9.7649360e-01,  1.1359409e+00,  1.4250163e+00,
-       5.4650505e-01,  3.8070710e-02, -5.7836964e-01, -7.7347222e-02,
-      -1.0712427e+00, -3.2900133e-01, -6.6441638e-02,  4.6435375e-01,
-      -1.8280947e-01,  7.1574422e-01, -1.6152798e-02, -2.6188697e-01,
-      -7.0437063e-01,  3.2250148e-02, -8.0333729e-01, -1.0595566e+00,
-      -1.2784303e+00, -2.4897303e-01, -9.0777061e-01, -1.0166946e+00,
-      -1.0877187e+00,  1.0276390e-01,  6.1843142e-01,  1.0990088e+00,
-       4.1640936e-01,  2.1611445e-01,  1.0343162e+00, -1.6762504e-01,
-       6.2546212e-01,  5.9995270e-01,  9.8434057e-01,  1.1432576e+00,
-       1.2787609e+00,  3.0368561e-01,  9.9535729e-01,  9.7104477e-01,
-       1.0977262e+00, -1.9294725e-01,  5.8743153e-01,  6.9473485e-01 ]
-])
-
-X_HAT_SNS_7M5 = np.array([
-
-    [ -2.7097714e+04,  3.0803384e+04, -1.0482091e+05, -1.6066697e+05,
-       7.3848155e+03,  6.1515964e+04, -4.0837362e+03,  1.4874654e+03,
-       8.2375155e+03, -8.1555674e+03,  3.0555770e+03,  1.6748461e+03,
-      -3.9585632e+03,  3.4593583e+03, -7.9211504e+02, -1.5776652e+03,
-       2.4448549e+03, -1.6832871e+03,  8.3907446e+01,  1.2903730e+03,
-      -1.5923535e+03,  9.5931605e+02,  1.9128372e+02, -9.8357603e+02,
-       9.5609940e+02, -4.5350218e+02, -2.5320962e+02,  6.3762292e+02,
-      -5.1397160e+02,  7.1216163e+01,  3.1269611e+02, -4.0275234e+02,
-       2.5638994e+02,  4.6403201e+01, -2.7843009e+02,  2.8914418e+02,
-      -1.3395170e+02, -1.5288222e+02,  3.2333756e+02, -2.2944713e+02,
-       1.9872415e+01,  2.6325395e+02, -2.8392752e+02,  1.5314277e+02,
-       5.0798626e+01, -2.8345564e+02,  2.5587542e+02, -1.0445526e+02,
-      -1.0817500e+02,  2.2832437e+02, -1.6921255e+02,  7.2196740e+01,
-       3.9741076e+01, -9.4592848e+01,  1.2449531e+02, -6.7961530e+01,
-      -1.2124324e+01,  5.7574971e+01, -7.1197322e+01, -1.4955585e+01,
-       3.1388922e+01, -2.6320323e+01,  1.7618904e+01,  2.2336595e+01,
-      -4.6625728e+01,  1.1616015e+01,  3.8732599e+01, -2.7312663e+01,
-       2.6048066e+01, -1.6572229e+01, -2.8923924e+01,  5.8075573e+01,
-      -5.6263689e+01,  2.9450962e+01,  2.1151792e+00, -2.7929547e+01,
-       3.3208863e+01, -4.5403546e+01,  1.0797732e+01,  2.0205768e-01,
-      -3.7035922e+01,  2.8542095e+01, -3.4631575e+00, -2.5611045e+01,
-       4.3617589e+01, -9.2662146e+00, -2.9678561e+01,  2.5975116e+01,
-       2.0536248e+00, -3.1929357e+01,  4.4228879e+01, -4.1248038e+00,
-      -4.4713214e+01,  3.9433226e+01, -2.1402050e+00, -4.0100915e+01,
-       5.9824970e+01, -4.7085628e+01,  1.0420556e+01,  5.3506419e+01,
-      -5.5103555e+01,  5.0861706e+01,  4.2620261e+00, -5.5541010e+01,
-       4.7905991e+01, -3.9259942e+01,  1.6335294e+01,  3.2891742e+01,
-      -3.8529956e+01,  4.7515440e+01, -8.6928230e+00, -3.2210096e+01,
-       3.0454645e+01, -3.7741492e+01,  3.0058525e+01,  1.3912599e+01,
-      -2.4503972e+01,  1.4539927e+01,  5.0502807e+00,  1.0831968e+01 ],
-
-    [  3.8609917e+03, -2.2089936e+04,  6.2396694e+04, -2.5577568e+05,
-      -4.0161241e+04, -1.1658208e+04,  9.9426556e+03, -5.6272992e+03,
-      -1.3839628e+03, -8.4856310e+01, -1.6119528e+02,  3.8468595e+01,
-       1.5768120e+02,  1.0800171e+02, -8.1867250e+00, -7.8607304e+01,
-      -6.0318312e+01,  2.1460378e+01,  4.3791334e+01,  1.7039175e+01,
-      -5.9501127e+00, -1.4271781e+01, -1.2583485e+01, -1.6962650e+01,
-      -4.3255972e+00,  2.1024124e+01,  2.1440266e+01,  1.2715073e+00,
-      -1.4021429e+01, -1.3345628e+01, -4.3592289e+00,  4.5305297e-01,
-       3.6304646e+00,  5.6994442e+00,  5.3601809e+00,  1.2971700e+00,
-      -6.0746373e+00, -8.3445774e+00, -1.9233529e+00,  3.5735662e+00,
-       6.4270894e+00,  2.9054250e+00, -2.9987419e+00, -2.7740687e+00,
-      -2.7396267e-01, -4.2922732e-01, -1.9998336e+00,  7.0742100e-01,
-       2.2980847e+00, -6.8050918e-01, -3.7563574e+00, -7.7344365e-01,
-       1.7803940e+00,  2.6700051e+00, -7.2296053e-02, -3.1741317e+00,
-      -6.0475055e-01,  1.5489233e-01,  1.2469224e+00,  4.5114909e-01,
-      -2.0399596e+00,  2.0468239e-01, -1.1728671e-02, -1.8368664e+00,
-      -2.3634589e+00, -1.9790219e+00,  2.5523434e-01,  2.3207537e-01,
-      -7.6437315e-01,  2.0546640e+00,  1.4277033e+00,  8.0700716e-01,
-       1.2439713e+00,  1.3061682e+00, -1.2859071e+00, -5.3115466e+00,
-      -4.9111022e+00, -1.0984600e+00,  4.8408213e+00,  7.7096653e+00,
-       4.8540011e+00,  3.9262706e-01, -3.0682108e+00, -2.9814266e+00,
-      -3.3365255e+00, -3.1018325e+00, -6.0106119e-01,  3.9894443e+00,
-       6.4996032e+00,  1.2497035e+00, -5.1293438e+00, -6.5668257e+00,
-      -3.3967039e+00,  6.9951716e-01,  2.2622003e+00,  4.3673873e+00,
-       5.0077054e+00, -4.3895891e-01, -4.1593548e+00, -2.7116978e+00,
-       1.3891315e+00,  1.5970423e+00, -1.7323481e+00, -2.6191154e+00,
-      -1.3622003e+00,  2.0414418e+00,  1.7497613e+00,  1.1752440e+00,
-      -2.2017045e-01, -3.0576971e+00, -3.7647976e+00, -2.5836234e+00,
-       2.6534415e+00,  6.5760121e+00,  2.4535983e+00, -1.3713128e+00,
-      -1.8422343e+00, -1.9213303e-01, -1.1437114e+00, -3.5335263e+00 ],
-])
-
-X_HAT_SNS = [ X_HAT_SNS_7M5, X_HAT_SNS_10M ]
-
-### C.4.1.8  MDCT
-
-T_HAT_MDCT_10M = np.array([
-
-    [  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-       6.1525095e-01,  1.7117620e+00, -1.6200436e-01, -2.1107548e+00,
-       9.1636911e-01,  8.7583398e+00,  1.4291828e+01,  1.4615904e+01,
-       1.9312730e+01,  2.2178311e+01,  2.1178760e+01,  1.9939021e+01,
-       3.2745193e+01,  3.1385007e+01,  4.5642888e+01,  3.8185668e+01,
-       4.3452271e+01,  3.0174130e+01,  2.7510416e+01,  3.9048290e+00,
-      -4.1911157e+00, -3.4032526e+01, -4.3089457e+01, -7.1817465e+01,
-      -7.3319439e+01, -1.0231340e+02, -8.2645833e+01, -1.0207070e+02,
-      -7.4253115e+01, -9.5269932e+01, -1.2210097e+02, -1.3216707e+02,
-      -1.2665681e+02, -1.6033791e+02, -1.3067613e+02, -1.8796611e+01,
-       6.2097263e+01,  7.2290617e+00, -1.2550979e+02, -8.9649115e+01,
-       7.6135408e+02,  2.7072170e+03,  6.1558256e+03,  9.6522574e+03,
-       1.2566221e+04,  1.5421574e+04,  1.8329565e+04,  2.1102710e+04,
-       2.3323039e+04,  2.5451924e+04,  2.7365468e+04,  2.8891223e+04,
-       3.0341813e+04,  3.1380038e+04,  3.2134454e+04,  3.2606958e+04,
-       3.2683895e+04,  3.2518324e+04,  3.2036626e+04,  3.1278795e+04,
-       3.0136453e+04,  2.8694957e+04,  2.7009676e+04,  2.5075921e+04,
-       2.2940269e+04,  2.0564299e+04,  1.7956441e+04,  1.5202722e+04,
-       1.2352834e+04,  9.3639445e+03,  6.2714619e+03,  3.1112267e+03,
-      -1.0075267e+02, -3.3115389e+03, -6.4954073e+03, -9.6110111e+03,
-      -1.2594144e+04, -1.5477538e+04, -1.8214406e+04, -2.0797170e+04,
-      -2.3195615e+04, -2.5355124e+04, -2.7302557e+04, -2.8919494e+04,
-      -3.0273227e+04, -3.1357707e+04, -3.2152224e+04, -3.2625545e+04,
-      -3.2802109e+04, -3.2640407e+04, -3.2172861e+04, -3.1393517e+04,
-      -3.0316009e+04, -2.8922986e+04, -2.7290775e+04, -2.5351571e+04,
-      -2.3183937e+04, -2.0795771e+04, -1.8212609e+04, -1.5453610e+04,
-      -1.2543978e+04, -9.4923186e+03, -6.3663932e+03, -3.1740539e+03,
-       4.2407582e+01,  3.2479359e+03,  6.4369448e+03,  9.5564107e+03,
-       1.2589389e+04,  1.5494437e+04,  1.8238168e+04,  2.0814647e+04,
-       2.3183440e+04,  2.5340307e+04,  2.7236643e+04,  2.8879098e+04,
-       3.0219987e+04,  3.1304367e+04,  3.2073455e+04,  3.2541098e+04,
-       3.2660415e+04,  3.2490788e+04,  3.2004309e+04,  3.1215306e+04,
-       3.0131477e+04,  2.8751611e+04,  2.7114762e+04,  2.5198099e+04,
-       2.3044166e+04,  2.0655799e+04,  1.8068458e+04,  1.5312555e+04,
-       1.2400688e+04,  9.3834439e+03,  6.2670869e+03,  3.1043555e+03,
-      -9.0364008e+01, -3.2949280e+03, -6.4558716e+03, -9.5736656e+03,
-      -1.2593429e+04, -1.5487321e+04, -1.8231003e+04, -2.0808229e+04,
-      -2.3191386e+04, -2.5327915e+04, -2.7226934e+04, -2.8860268e+04,
-      -3.0235350e+04, -3.1313467e+04, -3.2092397e+04, -3.2585580e+04,
-      -3.2777259e+04, -3.2606534e+04, -3.2146234e+04, -3.1383325e+04,
-      -3.0295384e+04, -2.8926934e+04, -2.7297985e+04, -2.5343669e+04,
-      -2.3152305e+04, -2.0670493e+04, -1.7934304e+04, -1.4904360e+04,
-      -1.1690361e+04, -8.2034060e+03, -4.4966301e+03, -5.9716414e+02,
-       3.3525737e+03,  7.3668269e+03,  1.1354417e+04,  1.5112467e+04,
-       1.8623854e+04,  2.1896001e+04,  2.4687380e+04,  2.6938007e+04,
-       2.8626837e+04,  2.9696113e+04,  3.0136274e+04,  2.9938040e+04,
-       2.9086081e+04,  2.7633076e+04,  2.5690739e+04,  2.3353541e+04,
-       2.0706676e+04,  1.7862611e+04,  1.4893850e+04,  1.1890794e+04,
-       9.0400724e+03,  6.3496693e+03,  3.8416289e+03,  1.6255098e+03,
-      -2.6829587e+02, -1.8648283e+03, -3.1265144e+03, -4.0739027e+03,
-      -4.7217057e+03, -5.1308259e+03, -5.2994797e+03, -5.2717085e+03,
-      -5.0702632e+03, -4.7404796e+03, -4.2926332e+03, -3.7668674e+03,
-      -3.1863326e+03, -2.5717341e+03, -1.9487052e+03, -1.3380710e+03,
-      -7.5480786e+02, -2.1731911e+02,  2.6258546e+02,  6.7531993e+02,
-       1.0134680e+03,  1.2732878e+03,  1.4518662e+03,  1.5497281e+03,
-       1.5671087e+03,  1.5098872e+03,  1.3843562e+03,  1.1969411e+03,
-       9.5570061e+02,  6.6987624e+02,  3.5217663e+02,  9.8694010e+00,
-      -3.4394935e+02, -6.9967997e+02, -1.0489605e+03, -1.3797643e+03,
-      -1.6859141e+03, -1.9573045e+03, -2.1892388e+03, -2.3761776e+03,
-      -2.5135134e+03, -2.6015039e+03, -2.6377063e+03, -2.6272971e+03,
-      -2.5708325e+03, -2.4734547e+03, -2.3405452e+03, -2.1774990e+03,
-      -1.9928710e+03, -1.7900465e+03, -1.5787667e+03, -1.3647693e+03,
-      -1.1565972e+03, -9.5713834e+02, -7.7246019e+02, -6.0536929e+02,
-      -4.5916520e+02, -3.3470350e+02, -2.3251725e+02, -1.5151486e+02,
-      -9.0224117e+01, -4.6447782e+01, -1.7377196e+01, -1.6206249e-01,
-       8.2694380e+00,  1.0620902e+01,  9.2650945e+00,  5.7978679e+00 ],
-
-    [ -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -5.8307275e+01, -1.5961611e+02, -3.2822433e+02, -5.8684193e+02,
-      -9.5063064e+02, -1.4274461e+03, -2.0154696e+03, -2.7009630e+03,
-      -3.4569464e+03, -4.2430564e+03, -5.0068652e+03, -5.6873576e+03,
-      -6.2169269e+03, -6.5268185e+03, -6.5539697e+03, -6.2419962e+03,
-      -5.5512117e+03, -4.4569448e+03, -2.9581517e+03, -1.0742654e+03,
-       1.1530263e+03,  3.6594640e+03,  6.3646353e+03,  9.1749853e+03,
-       1.1985116e+04,  1.4691422e+04,  1.7187008e+04,  1.9378907e+04,
-       2.1179777e+04,  2.2522740e+04,  2.3359818e+04,  2.3657684e+04,
-       2.3405693e+04,  2.2611255e+04,  2.1297773e+04,  1.9499385e+04,
-       1.7264572e+04,  1.4646235e+04,  1.1703370e+04,  8.4960382e+03,
-       5.0883571e+03,  1.5400010e+03, -2.0852434e+03, -5.7317898e+03,
-      -9.3443119e+03, -1.2867162e+04, -1.6250847e+04, -1.9448892e+04,
-      -2.2417993e+04, -2.5119550e+04, -2.7519523e+04, -2.9589531e+04,
-      -3.1306279e+04, -3.2653256e+04, -3.3615026e+04, -3.4186391e+04,
-      -3.4364519e+04, -3.4152077e+04, -3.3554064e+04, -3.2586709e+04,
-      -3.1263292e+04, -2.9603089e+04, -2.7630063e+04, -2.5371579e+04,
-      -2.2853607e+04, -2.0109903e+04, -1.7174483e+04, -1.4079475e+04,
-      -1.0861936e+04, -7.5574424e+03, -4.2020683e+03, -8.2997756e+02,
-       2.5249878e+03,  5.8295399e+03,  9.0523976e+03,  1.2164914e+04,
-       1.5141205e+04,  1.7954597e+04,  2.0583186e+04,  2.3005366e+04,
-       2.5205897e+04,  2.7165327e+04,  2.8871803e+04,  3.0312108e+04,
-       3.1479927e+04,  3.2363955e+04,  3.2960351e+04,  3.3265900e+04,
-       3.3277052e+04,  3.2993786e+04,  3.2419707e+04,  3.1554084e+04,
-       3.0408074e+04,  2.8986068e+04,  2.7298583e+04,  2.5360460e+04,
-       2.3186540e+04,  2.0794689e+04,  1.8206602e+04,  1.5444741e+04,
-       1.2535402e+04,  9.5045150e+03,  6.3825434e+03,  3.1961404e+03,
-      -1.9030604e+01, -3.2350097e+03, -6.4178539e+03, -9.5376996e+03,
-      -1.2564818e+04, -1.5468879e+04, -1.8222757e+04, -2.0799617e+04,
-      -2.3177492e+04, -2.5329860e+04, -2.7239548e+04, -2.8887388e+04,
-      -3.0257022e+04, -3.1336972e+04, -3.2115659e+04, -3.2585521e+04,
-      -3.2741691e+04, -3.2583544e+04, -3.2112334e+04, -3.1332786e+04,
-      -3.0253039e+04, -2.8881031e+04, -2.7231757e+04, -2.5321143e+04,
-      -2.3168095e+04, -2.0790131e+04, -1.8212051e+04, -1.5458479e+04,
-      -1.2554090e+04, -9.5278709e+03, -6.4088050e+03, -3.2282766e+03,
-      -1.5127187e+01,  3.1974595e+03,  6.3806758e+03,  9.5013370e+03,
-       1.2533377e+04,  1.5443257e+04,  1.8203988e+04,  2.0793445e+04,
-       2.3180292e+04,  2.5344634e+04,  2.7265053e+04,  2.8919963e+04,
-       3.0294320e+04,  3.1376795e+04,  3.2154178e+04,  3.2622959e+04,
-       3.2776568e+04,  3.2617286e+04,  3.2145842e+04,  3.1360660e+04,
-       3.0278348e+04,  2.8901877e+04,  2.7241369e+04,  2.5301056e+04,
-       2.3077170e+04,  2.0602593e+04,  1.7860920e+04,  1.4845935e+04,
-       1.1575770e+04,  8.0714330e+03,  4.3590833e+03,  4.8619425e+02,
-      -3.4812252e+03, -7.4811451e+03, -1.1420224e+04, -1.5218969e+04,
-      -1.8767216e+04, -2.1982348e+04, -2.4766842e+04, -2.7038512e+04,
-      -2.8729372e+04, -2.9797530e+04, -3.0215913e+04, -2.9983529e+04,
-      -2.9130275e+04, -2.7706100e+04, -2.5778477e+04, -2.3437188e+04,
-      -2.0783932e+04, -1.7920105e+04, -1.4952584e+04, -1.1982327e+04,
-      -9.1037807e+03, -6.3864700e+03, -3.9012432e+03, -1.6878870e+03,
-       2.1843895e+02,  1.8077853e+03,  3.0762163e+03,  4.0339692e+03,
-       4.7031968e+03,  5.1098317e+03,  5.2820716e+03,  5.2546005e+03,
-       5.0617380e+03,  4.7306477e+03,  4.2889148e+03,  3.7647491e+03,
-       3.1840313e+03,  2.5717028e+03,  1.9493478e+03,  1.3380429e+03,
-       7.5567371e+02,  2.1773747e+02, -2.6311651e+02, -6.7663706e+02,
-      -1.0155643e+03, -1.2750705e+03, -1.4528246e+03, -1.5489867e+03,
-      -1.5659952e+03, -1.5076498e+03, -1.3804175e+03, -1.1912334e+03,
-      -9.4848033e+02, -6.6207374e+02, -3.4175876e+02,  1.6521653e+00,
-       3.5767927e+02,  7.1550189e+02,  1.0651058e+03,  1.3968326e+03,
-       1.7019804e+03,  1.9728594e+03,  2.2034762e+03,  2.3889564e+03,
-       2.5257870e+03,  2.6127289e+03,  2.6495795e+03,  2.6378966e+03,
-       2.5805079e+03,  2.4818035e+03,  2.3472272e+03,  2.1829178e+03,
-       1.9955916e+03,  1.7924019e+03,  1.5804111e+03,  1.3664419e+03,
-       1.1569292e+03,  9.5724044e+02,  7.7214172e+02,  6.0521399e+02,
-       4.5883364e+02,  3.3442067e+02,  2.3213371e+02,  1.5121914e+02,
-       9.0047461e+01,  4.6310025e+01,  1.7308037e+01,  7.2726310e-02,
-      -8.3269806e+00, -1.0647845e+01, -9.2769990e+00, -5.7939041e+00 ],
-
-])
-
-T_HAT_MDCT_7M5 = np.array([
-
-    [ -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-       9.0382948e-01,  2.8563300e+00,  2.0863167e+00,  3.2605273e+00,
-       1.8231017e+00, -2.6473031e+00, -7.7420704e+00, -1.6971743e+01,
-      -4.4169569e+00,  4.7473387e+00,  7.9882732e+00,  2.1090757e+00,
-       6.9477046e+00,  7.6294361e+00,  4.5069158e+00,  1.1288109e+00,
-       5.5301798e-01, -1.2320805e+00,  1.2696965e+01,  1.7998129e+01,
-       1.9997378e+01,  2.3310802e+01,  3.4116671e+01,  3.1619222e+01,
-       2.3643252e+01,  2.2595989e+01,  2.4150879e+01,  1.7561939e+01,
-       2.4167995e+01,  2.1868269e+01,  1.2021561e+01,  1.0810360e+01,
-      -1.1321816e+01, -1.3811836e+01, -2.7571991e+01, -3.3459505e+01,
-      -2.6720233e+01, -4.0425004e+01, -4.1666697e+01, -4.8106995e+01,
-      -7.1121739e+01, -8.5018856e+01, -6.4519501e+01, -6.1651047e+01,
-      -6.2001672e+01, -4.9054098e+01,  5.3605147e+00, -2.7222279e+00,
-      -6.3200946e+00, -2.8873822e+01, -5.6314175e+01, -5.9551902e+01,
-      -2.1183627e+01, -9.5007617e+01, -6.7674879e+01,  7.6546124e+01,
-       3.6355638e+02,  2.0908440e+02,  9.2290767e+01, -8.4453487e+01,
-      -2.0810832e+02, -1.9235273e+02, -4.0634578e+02, -2.2011977e+02,
-       6.2920459e+02,  3.1481663e+03,  6.2343351e+03,  9.4022080e+03,
-       1.2520451e+04,  1.5313131e+04,  1.8128985e+04,  2.0762454e+04,
-       2.3084787e+04,  2.5275848e+04,  2.7095495e+04,  2.8665301e+04,
-       3.0094623e+04,  3.1202047e+04,  3.2006678e+04,  3.2461623e+04,
-       3.2568832e+04,  3.2408327e+04,  3.1961953e+04,  3.1146555e+04,
-       3.0073949e+04,  2.8725124e+04,  2.7099832e+04,  2.5196695e+04,
-       2.3022972e+04,  2.0643354e+04,  1.8079103e+04,  1.5352852e+04,
-       1.2476728e+04,  9.4135962e+03,  6.2948219e+03,  3.1010477e+03,
-      -9.0897787e+01, -3.3383673e+03, -6.5093586e+03, -9.6214110e+03,
-      -1.2638625e+04, -1.5564780e+04, -1.8289238e+04, -2.0877731e+04,
-      -2.3274493e+04, -2.5456613e+04, -2.7372293e+04, -2.9018289e+04,
-      -3.0399516e+04, -3.1474248e+04, -3.2213279e+04, -3.2686770e+04,
-      -3.2836146e+04, -3.2655386e+04, -3.2161995e+04, -3.1363572e+04,
-      -3.0296725e+04, -2.8893704e+04, -2.7226660e+04, -2.5303018e+04,
-      -2.3185989e+04, -2.0860640e+04, -1.8388180e+04, -1.5768019e+04,
-      -1.3017648e+04, -1.0256225e+04, -7.4288937e+03, -4.6404303e+03,
-      -1.8479563e+03,  8.9695589e+02,  3.6050165e+03,  6.1774761e+03,
-       8.5802409e+03,  1.0843314e+04,  1.2957357e+04,  1.4915752e+04,
-       1.6688121e+04,  1.8234848e+04,  1.9622853e+04,  2.0843143e+04,
-       2.1874370e+04,  2.2730604e+04,  2.3433492e+04,  2.3972978e+04,
-       2.4361621e+04,  2.4632443e+04,  2.4789213e+04,  2.4834368e+04,
-       2.4817473e+04,  2.4691904e+04,  2.4506825e+04,  2.4260930e+04,
-       2.3952544e+04,  2.3583514e+04,  2.3158707e+04,  2.2682736e+04,
-       2.2114814e+04,  2.1501602e+04,  2.0804765e+04,  2.0028682e+04,
-       1.9176325e+04,  1.8229068e+04,  1.7186098e+04,  1.6064305e+04,
-       1.4857345e+04,  1.3566101e+04,  1.2216378e+04,  1.0824025e+04,
-       9.3566686e+03,  7.8525218e+03,  6.3408636e+03,  4.8352443e+03,
-       3.3500951e+03,  1.8786022e+03,  4.4842764e+02, -8.8483949e+02,
-      -2.1241927e+03, -3.2448273e+03, -4.2656467e+03, -5.1440714e+03,
-      -5.9062863e+03, -6.5125637e+03, -6.9672797e+03, -7.2820785e+03,
-      -7.4483481e+03, -7.4911171e+03, -7.4042834e+03, -7.2043747e+03,
-      -6.8934126e+03, -6.5065747e+03, -6.0545237e+03, -5.5534407e+03,
-      -5.0174587e+03, -4.4641711e+03, -3.9151968e+03, -3.3732391e+03,
-      -2.8528068e+03, -2.3664418e+03, -1.9195477e+03, -1.5167365e+03,
-      -1.1638365e+03, -8.6204669e+02, -6.1212532e+02, -4.0852043e+02,
-      -2.5108888e+02, -1.3435049e+02, -5.3189240e+01, -1.0851528e+00,
-       2.6728439e+01,  3.7348684e+01,  3.5932093e+01,  2.7568411e+01 ],
-
-    [ -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00, -0.0000000e+00, -0.0000000e+00, -0.0000000e+00,
-      -0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       0.0000000e+00,  0.0000000e+00,  0.0000000e+00,  0.0000000e+00,
-       7.4140637e+01,  1.4259396e+02,  2.4292388e+02,  3.8197729e+02,
-       5.6596483e+02,  8.0010169e+02,  1.0881546e+03,  1.4325879e+03,
-       1.8337245e+03,  2.2902305e+03,  2.7978462e+03,  3.3513844e+03,
-       3.9420820e+03,  4.5597592e+03,  5.1914056e+03,  5.8224872e+03,
-       6.4359174e+03,  7.0133507e+03,  7.5344894e+03,  7.9783768e+03,
-       8.3235628e+03,  8.5499202e+03,  8.6355016e+03,  8.5623082e+03,
-       8.3125769e+03,  7.8732452e+03,  7.2322659e+03,  6.3830254e+03,
-       5.3220070e+03,  4.0508083e+03,  2.5743934e+03,  9.0382380e+02,
-      -9.4658515e+02, -2.9577774e+03, -5.1073476e+03, -7.3680800e+03,
-      -9.7116793e+03, -1.2105745e+04, -1.4518157e+04, -1.6913624e+04,
-      -1.9259368e+04, -2.1519698e+04, -2.3664419e+04, -2.5661182e+04,
-      -2.7484674e+04, -2.9109128e+04, -3.0513566e+04, -3.1680767e+04,
-      -3.2599500e+04, -3.3259046e+04, -3.3656364e+04, -3.3789359e+04,
-      -3.3661417e+04, -3.3275865e+04, -3.2647991e+04, -3.1778553e+04,
-      -3.0687860e+04, -2.9386993e+04, -2.7893292e+04, -2.6217793e+04,
-      -2.4380305e+04, -2.2396921e+04, -2.0285631e+04, -1.8056304e+04,
-      -1.5731396e+04, -1.3310749e+04, -1.0817930e+04, -8.2632546e+03,
-      -5.6584950e+03, -3.0169039e+03, -3.5049466e+02,  2.3300294e+03,
-       5.0072558e+03,  7.6692538e+03,  1.0296341e+04,  1.2873294e+04,
-       1.5379430e+04,  1.7799709e+04,  2.0110518e+04,  2.2292425e+04,
-       2.4321243e+04,  2.6179470e+04,  2.7838772e+04,  2.9288536e+04,
-       3.0502052e+04,  3.1467674e+04,  3.2161346e+04,  3.2580900e+04,
-       3.2712443e+04,  3.2548152e+04,  3.2079638e+04,  3.1315042e+04,
-       3.0239307e+04,  2.8881574e+04,  2.7232557e+04,  2.5325302e+04,
-       2.3165560e+04,  2.0776212e+04,  1.8202819e+04,  1.5452512e+04,
-       1.2552870e+04,  9.5323059e+03,  6.4141989e+03,  3.2353808e+03,
-       3.2008805e+01, -3.1778835e+03, -6.3568255e+03, -9.4682388e+03,
-      -1.2490842e+04, -1.5403926e+04, -1.8154186e+04, -2.0745346e+04,
-      -2.3121161e+04, -2.5285375e+04, -2.7200767e+04, -2.8855100e+04,
-      -3.0236188e+04, -3.1303814e+04, -3.2106304e+04, -3.2610143e+04,
-      -3.2801911e+04, -3.2722047e+04, -3.2333816e+04, -3.1622380e+04,
-      -3.0637858e+04, -2.9319050e+04, -2.7722782e+04, -2.5854268e+04,
-      -2.3707550e+04, -2.1309710e+04, -1.8683478e+04, -1.5843106e+04,
-      -1.2810610e+04, -9.6245078e+03, -6.3128385e+03, -2.9018457e+03,
-       5.6021860e+02,  4.0355321e+03,  7.4888401e+03,  1.0869459e+04,
-       1.4146120e+04,  1.7266962e+04,  2.0195488e+04,  2.2894666e+04,
-       2.5327458e+04,  2.7459548e+04,  2.9268259e+04,  3.0729712e+04,
-       3.1824168e+04,  3.2542974e+04,  3.2879994e+04,  3.2838377e+04,
-       3.2420961e+04,  3.1641217e+04,  3.0522280e+04,  2.9086732e+04,
-       2.7364573e+04,  2.5386626e+04,  2.3192561e+04,  2.0822736e+04,
-       1.8313768e+04,  1.5710203e+04,  1.3055162e+04,  1.0388760e+04,
-       7.7479346e+03,  5.1773492e+03,  2.7035918e+03,  3.6336147e+02,
-      -1.8203312e+03, -3.8257571e+03, -5.6281400e+03, -7.2191945e+03,
-      -8.5918440e+03, -9.7361057e+03, -1.0653660e+04, -1.1351662e+04,
-      -1.1834991e+04, -1.2108888e+04, -1.2194030e+04, -1.2106898e+04,
-      -1.1844914e+04, -1.1451706e+04, -1.0928890e+04, -1.0302174e+04,
-      -9.5993172e+03, -8.8336978e+03, -8.0218966e+03, -7.1899794e+03,
-      -6.3420744e+03, -5.5028868e+03, -4.6880751e+03, -3.9103856e+03,
-      -3.1844358e+03, -2.5158381e+03, -1.9161507e+03, -1.3860285e+03,
-      -9.3082869e+02, -5.4957243e+02, -2.3962727e+02,  2.0859282e+00,
-       1.8035744e+02,  3.0232747e+02,  3.7488265e+02,  4.0574857e+02,
-       4.0326247e+02,  3.7569870e+02,  3.3102137e+02,  2.7655429e+02,
-       2.1828293e+02,  1.6157727e+02,  1.1024218e+02,  6.6816369e+01 ]
-
-])
-
-T_HAT_MDCT = [ T_HAT_MDCT_7M5, T_HAT_MDCT_10M ]
-
-### C.4.1.9  LTPF
-
-X_HAT_LTPF_10M = np.array([
-
-    [  6.1525095e-01,  1.7117620e+00, -1.6200436e-01, -2.1107548e+00,
-       9.1636911e-01,  8.7583398e+00,  1.4291828e+01,  1.4615904e+01,
-       1.9312730e+01,  2.2178311e+01,  2.1178760e+01,  1.9939021e+01,
-       3.2745193e+01,  3.1385007e+01,  4.5642888e+01,  3.8185668e+01,
-       4.3452271e+01,  3.0174130e+01,  2.7510416e+01,  3.9048290e+00,
-      -4.1911157e+00, -3.4032526e+01, -4.3089457e+01, -7.1817465e+01,
-      -7.3319439e+01, -1.0231340e+02, -8.2645833e+01, -1.0207070e+02,
-      -7.4253115e+01, -9.5269932e+01, -1.2210097e+02, -1.3216707e+02,
-      -1.2665681e+02, -1.6033791e+02, -1.3067613e+02, -1.8796611e+01,
-       6.2097263e+01,  7.2290617e+00, -1.2550979e+02, -8.9649115e+01,
-       7.6135408e+02,  2.7072170e+03,  6.1558256e+03,  9.6522574e+03,
-       1.2566221e+04,  1.5421574e+04,  1.8329565e+04,  2.1102710e+04,
-       2.3323039e+04,  2.5451924e+04,  2.7365468e+04,  2.8891223e+04,
-       3.0341813e+04,  3.1380038e+04,  3.2134454e+04,  3.2606958e+04,
-       3.2683895e+04,  3.2518324e+04,  3.2036626e+04,  3.1278795e+04,
-       3.0136453e+04,  2.8694957e+04,  2.7009676e+04,  2.5075921e+04,
-       2.2940269e+04,  2.0564299e+04,  1.7956441e+04,  1.5202722e+04,
-       1.2352834e+04,  9.3639445e+03,  6.2714619e+03,  3.1112267e+03,
-      -1.0075267e+02, -3.3115389e+03, -6.4954073e+03, -9.6110111e+03,
-      -1.2594144e+04, -1.5477538e+04, -1.8214406e+04, -2.0797170e+04,
-      -2.3195615e+04, -2.5355124e+04, -2.7302557e+04, -2.8919494e+04,
-      -3.0273227e+04, -3.1357707e+04, -3.2152224e+04, -3.2625545e+04,
-      -3.2802109e+04, -3.2640407e+04, -3.2172861e+04, -3.1393517e+04,
-      -3.0316009e+04, -2.8922986e+04, -2.7290775e+04, -2.5351571e+04,
-      -2.3183937e+04, -2.0795771e+04, -1.8212609e+04, -1.5453610e+04,
-      -1.2543978e+04, -9.4923186e+03, -6.3663932e+03, -3.1740539e+03,
-       4.2407582e+01,  3.2479359e+03,  6.4369448e+03,  9.5564107e+03,
-       1.2589389e+04,  1.5494437e+04,  1.8238168e+04,  2.0814647e+04,
-       2.3183440e+04,  2.5340307e+04,  2.7236643e+04,  2.8879098e+04,
-       3.0219987e+04,  3.1304367e+04,  3.2073455e+04,  3.2541098e+04,
-       3.2660415e+04,  3.2490788e+04,  3.2004309e+04,  3.1215306e+04,
-       3.0131477e+04,  2.8751611e+04,  2.7114762e+04,  2.5198099e+04,
-       2.3044166e+04,  2.0655799e+04,  1.8068458e+04,  1.5312555e+04,
-       1.2400688e+04,  9.3834439e+03,  6.2670869e+03,  3.1043555e+03,
-      -9.0364008e+01, -3.2949280e+03, -6.4558716e+03, -9.5736656e+03,
-      -1.2593429e+04, -1.5487321e+04, -1.8231003e+04, -2.0808229e+04,
-      -2.3191386e+04, -2.5327915e+04, -2.7226934e+04, -2.8860268e+04,
-      -3.0235350e+04, -3.1313467e+04, -3.2092397e+04, -3.2585580e+04,
-      -3.2777259e+04, -3.2606534e+04, -3.2146234e+04, -3.1383325e+04,
-      -3.0295384e+04, -2.8926934e+04, -2.7297985e+04, -2.5343669e+04 ],
-
-    [ -2.3210612e+04, -2.0830109e+04, -1.8262528e+04, -1.5491202e+04,
-      -1.2640992e+04, -9.6308521e+03, -6.5120997e+03, -3.2981272e+03,
-      -1.0437273e+02,  3.1237705e+03,  6.3475523e+03,  9.4251090e+03,
-       1.2406927e+04,  1.5369182e+04,  1.8133410e+04,  2.0696011e+04,
-       2.3075625e+04,  2.5239168e+04,  2.7178122e+04,  2.8863775e+04,
-       3.0239107e+04,  3.1292540e+04,  3.2055374e+04,  3.2528526e+04,
-       3.2691792e+04,  3.2554032e+04,  3.2080858e+04,  3.1269701e+04,
-       3.0219849e+04,  2.8872409e+04,  2.7201446e+04,  2.5283194e+04,
-       2.3137397e+04,  2.0746427e+04,  1.8171258e+04,  1.5425482e+04,
-       1.2542866e+04,  9.5154094e+03,  6.4038902e+03,  3.2243297e+03,
-       1.8093877e+01, -3.2004786e+03, -6.3778766e+03, -9.4986572e+03,
-      -1.2530644e+04, -1.5438896e+04, -1.8199553e+04, -2.0786963e+04,
-      -2.3172801e+04, -2.5336869e+04, -2.7256938e+04, -2.8914212e+04,
-      -3.0292811e+04, -3.1379968e+04, -3.2163160e+04, -3.2636663e+04,
-      -3.2797410e+04, -3.2642190e+04, -3.2169708e+04, -3.1389768e+04,
-      -3.0307591e+04, -2.8933213e+04, -2.7277887e+04, -2.5361709e+04,
-      -2.3197556e+04, -2.0809583e+04, -1.8223444e+04, -1.5459240e+04,
-      -1.2547850e+04, -9.5147469e+03, -6.3913071e+03, -3.2061552e+03,
-       1.1474435e+01,  3.2280361e+03,  6.4146913e+03,  9.5376172e+03,
-       1.2570372e+04,  1.5481142e+04,  1.8242641e+04,  2.0827867e+04,
-       2.3213026e+04,  2.5375280e+04,  2.7293037e+04,  2.8947339e+04,
-       3.0323330e+04,  3.1406817e+04,  3.2187891e+04,  3.2660531e+04,
-       3.2817886e+04,  3.2659083e+04,  3.2187190e+04,  3.1402569e+04,
-       3.0317849e+04,  2.8939620e+04,  2.7281206e+04,  2.5360297e+04,
-       2.3194809e+04,  2.0805310e+04,  1.8215867e+04,  1.5450539e+04,
-       1.2535402e+04,  9.5045150e+03,  6.3825434e+03,  3.1961404e+03,
-      -1.9030604e+01, -3.2350097e+03, -6.4178539e+03, -9.5376996e+03,
-      -1.2564818e+04, -1.5468879e+04, -1.8222757e+04, -2.0799617e+04,
-      -2.3177492e+04, -2.5329860e+04, -2.7239548e+04, -2.8887388e+04,
-      -3.0257022e+04, -3.1336972e+04, -3.2115659e+04, -3.2585521e+04,
-      -3.2741691e+04, -3.2583544e+04, -3.2112334e+04, -3.1332786e+04,
-      -3.0253039e+04, -2.8881031e+04, -2.7231757e+04, -2.5321143e+04,
-      -2.3168095e+04, -2.0790131e+04, -1.8212051e+04, -1.5458479e+04,
-      -1.2554090e+04, -9.5278709e+03, -6.4088050e+03, -3.2282766e+03,
-      -1.5127187e+01,  3.1974595e+03,  6.3806758e+03,  9.5013370e+03,
-       1.2533377e+04,  1.5443257e+04,  1.8203988e+04,  2.0793445e+04,
-       2.3180292e+04,  2.5344634e+04,  2.7265053e+04,  2.8919963e+04,
-       3.0294320e+04,  3.1376795e+04,  3.2154178e+04,  3.2622959e+04,
-       3.2776568e+04,  3.2617286e+04,  3.2145842e+04,  3.1360660e+04,
-       3.0278348e+04,  2.8901877e+04,  2.7241369e+04,  2.5301056e+04 ],
-
-])
-
-X_HAT_LTPF_7M5 = np.array([
-
-    [  9.0382948e-01,  2.8563300e+00,  2.0863167e+00,  3.2605273e+00,
-       1.8231017e+00, -2.6473031e+00, -7.7420704e+00, -1.6971743e+01,
-      -4.4169569e+00,  4.7473387e+00,  7.9882732e+00,  2.1090757e+00,
-       6.9477046e+00,  7.6294361e+00,  4.5069158e+00,  1.1288109e+00,
-       5.5301798e-01, -1.2320805e+00,  1.2696965e+01,  1.7998129e+01,
-       1.9997378e+01,  2.3310802e+01,  3.4116671e+01,  3.1619222e+01,
-       2.3643252e+01,  2.2595989e+01,  2.4150879e+01,  1.7561939e+01,
-       2.4167995e+01,  2.1868269e+01,  1.2021561e+01,  1.0810360e+01,
-      -1.1321816e+01, -1.3811836e+01, -2.7571991e+01, -3.3459505e+01,
-      -2.6720233e+01, -4.0425004e+01, -4.1666697e+01, -4.8106995e+01,
-      -7.1121739e+01, -8.5018856e+01, -6.4519501e+01, -6.1651047e+01,
-      -6.2001672e+01, -4.9054098e+01,  5.3605147e+00, -2.7222279e+00,
-      -6.3200946e+00, -2.8873822e+01, -5.6314175e+01, -5.9551902e+01,
-      -2.1183627e+01, -9.5007617e+01, -6.7674879e+01,  7.6546124e+01,
-       3.6355638e+02,  2.0908440e+02,  9.2290767e+01, -8.4453487e+01,
-      -2.0810832e+02, -1.9235273e+02, -4.0634578e+02, -2.2011977e+02,
-       6.2920459e+02,  3.1481663e+03,  6.2343351e+03,  9.4022080e+03,
-       1.2520451e+04,  1.5313131e+04,  1.8128985e+04,  2.0762454e+04,
-       2.3084787e+04,  2.5275848e+04,  2.7095495e+04,  2.8665301e+04,
-       3.0094623e+04,  3.1202047e+04,  3.2006678e+04,  3.2461623e+04,
-       3.2568832e+04,  3.2408327e+04,  3.1961953e+04,  3.1146555e+04,
-       3.0073949e+04,  2.8725124e+04,  2.7099832e+04,  2.5196695e+04,
-       2.3022972e+04,  2.0643354e+04,  1.8079103e+04,  1.5352852e+04,
-       1.2476728e+04,  9.4135962e+03,  6.2948219e+03,  3.1010477e+03,
-      -9.0897787e+01, -3.3383673e+03, -6.5093586e+03, -9.6214110e+03,
-      -1.2638625e+04, -1.5564780e+04, -1.8289238e+04, -2.0877731e+04,
-      -2.3274493e+04, -2.5456613e+04, -2.7372293e+04, -2.9018289e+04,
-      -3.0399516e+04, -3.1474248e+04, -3.2213279e+04, -3.2686770e+04,
-      -3.2836146e+04, -3.2655386e+04, -3.2161995e+04, -3.1363572e+04,
-      -3.0296725e+04, -2.8893704e+04, -2.7226660e+04, -2.5303018e+04 ],
-
-    [ -2.3111848e+04, -2.0718046e+04, -1.8145256e+04, -1.5386042e+04,
-      -1.2451683e+04, -9.4561229e+03, -6.3407390e+03, -3.2078423e+03,
-      -1.4231827e+01,  3.1871864e+03,  6.4028626e+03,  9.5288605e+03,
-       1.2522323e+04,  1.5403074e+04,  1.8148763e+04,  2.0738239e+04,
-       2.3124038e+04,  2.5248199e+04,  2.7157343e+04,  2.8821520e+04,
-       3.0197933e+04,  3.1280524e+04,  3.2068994e+04,  3.2535286e+04,
-       3.2674198e+04,  3.2505688e+04,  3.2021479e+04,  3.1217393e+04,
-       3.0139480e+04,  2.8742712e+04,  2.7081219e+04,  2.5164753e+04,
-       2.3005958e+04,  2.0625737e+04,  1.8051360e+04,  1.5314656e+04,
-       1.2403135e+04,  9.3958576e+03,  6.2866076e+03,  3.1150574e+03,
-      -8.3043055e+01, -3.2906309e+03, -6.4783209e+03, -9.5968770e+03,
-      -1.2627329e+04, -1.5543027e+04, -1.8297188e+04, -2.0856742e+04,
-      -2.3242831e+04, -2.5406524e+04, -2.7315500e+04, -2.8954115e+04,
-      -3.0311322e+04, -3.1397263e+04, -3.2199564e+04, -3.2663393e+04,
-      -3.2812053e+04, -3.2631820e+04, -3.2158938e+04, -3.1361864e+04,
-      -3.0286591e+04, -2.8909485e+04, -2.7252911e+04, -2.5338382e+04,
-      -2.3179744e+04, -2.0801866e+04, -1.8222214e+04, -1.5467629e+04,
-      -1.2551908e+04, -9.5234786e+03, -6.4050183e+03, -3.2234113e+03,
-      -1.0202956e+01,  3.2050827e+03,  6.3811438e+03,  9.5000550e+03,
-       1.2526624e+04,  1.5433267e+04,  1.8190970e+04,  2.0775689e+04,
-       2.3157406e+04,  2.5317424e+04,  2.7226647e+04,  2.8880016e+04,
-       3.0250964e+04,  3.1333324e+04,  3.2108157e+04,  3.2579815e+04,
-       3.2739171e+04,  3.2585501e+04,  3.2115570e+04,  3.1342610e+04,
-       3.0239307e+04,  2.8881574e+04,  2.7232557e+04,  2.5325302e+04,
-       2.3165560e+04,  2.0776212e+04,  1.8202819e+04,  1.5452512e+04,
-       1.2552870e+04,  9.5323059e+03,  6.4141989e+03,  3.2353808e+03,
-       3.2008805e+01, -3.1778835e+03, -6.3568255e+03, -9.4682388e+03,
-      -1.2490842e+04, -1.5403926e+04, -1.8154186e+04, -2.0745346e+04,
-      -2.3121161e+04, -2.5285375e+04, -2.7200767e+04, -2.8855100e+04,
-      -3.0236188e+04, -3.1303814e+04, -3.2106304e+04, -3.2610143e+04 ]
-
-])
-
-X_HAT_LTPF = [ X_HAT_LTPF_7M5, X_HAT_LTPF_10M ]
-
-LTPF_C2_NBITS = 320
-
-LTPF_C2_ACTIVE = np.array([ False, True ])
-LTPF_C2_PITCH_INDEX = np.array([ 60, 56 ])
-
-LTPF_C2_C_N = np.array([
-    [  0.           ,  0.           ,  0.            ],
-    [  2.0480302e-01,  1.4271871e-01, -6.4036434e-03 ],
-])
-
-LTPF_C2_C_D = np.array([
-    [  0.           ,  0.           ,  0.           ,  0.            ],
-    [  1.5868459e-02,  1.8368837e-01,  1.8368837e-01,  1.5868459e-02 ],
-])
-
-LTPF_C2_X = np.array([
-
-    [  2.1004800e+03,  2.0660438e+03,  2.0070810e+03,  1.9335150e+03,
-       1.8437505e+03,  1.7474564e+03,  1.6433626e+03,  1.5341100e+03,
-       1.4257583e+03,  1.3211557e+03,  1.2062000e+03,  1.0819606e+03,
-       9.5194047e+02,  8.1893657e+02,  6.7789896e+02,  5.3732954e+02,
-       4.0506079e+02,  2.7179124e+02,  1.3998655e+02,  1.9709326e+00,
-      -1.3683087e+02, -2.7783589e+02, -4.1717230e+02, -5.5239764e+02,
-      -6.7544678e+02, -7.8374115e+02, -8.8533384e+02, -9.8169275e+02,
-      -1.0740844e+03, -1.1619601e+03, -1.2468187e+03, -1.3276334e+03,
-      -1.3976625e+03, -1.4581676e+03, -1.5080701e+03, -1.5565703e+03,
-      -1.6009886e+03, -1.6442955e+03, -1.6843665e+03, -1.7151734e+03,
-      -1.7459758e+03, -1.7579559e+03, -1.7383847e+03, -1.6558090e+03,
-      -1.4937678e+03, -1.2402230e+03, -9.0680789e+02, -5.2126546e+02,
-      -1.0907639e+02,  2.9396419e+02,  6.6844988e+02,  9.9743497e+02,
-       1.2728461e+03,  1.5005211e+03,  1.6749621e+03,  1.8070876e+03,
-       1.9030819e+03,  1.9644916e+03,  1.9851832e+03,  1.9771133e+03,
-       1.9382403e+03,  1.8819654e+03,  1.8027215e+03,  1.7116504e+03,
-       1.6036716e+03,  1.4931560e+03,  1.3786289e+03,  1.2583587e+03,
-       1.1369652e+03,  1.0121811e+03,  8.8292297e+02,  7.5232872e+02,
-       6.2279997e+02,  4.8668604e+02,  3.4937590e+02,  2.1277380e+02,
-       7.7335654e+01, -5.2960798e+01, -1.8097039e+02, -3.0070120e+02,
-      -4.1472118e+02, -5.2643886e+02, -6.3396297e+02, -7.3691168e+02,
-      -8.3480847e+02, -9.2600159e+02, -1.0147685e+03, -1.0954793e+03,
-      -1.1736112e+03, -1.2509220e+03, -1.3222525e+03, -1.3883874e+03,
-      -1.4496560e+03, -1.5064726e+03, -1.5576614e+03, -1.6028926e+03,
-      -1.6434987e+03, -1.6803495e+03, -1.7069786e+03, -1.7018749e+03,
-      -1.6517403e+03, -1.5416168e+03, -1.3607723e+03, -1.1195564e+03,
-      -8.2569063e+02, -4.9652560e+02, -1.5319529e+02,  1.8519693e+02,
-       5.0070752e+02,  7.8614834e+02,  1.0314193e+03,  1.2402508e+03,
-       1.4166554e+03,  1.5555207e+03,  1.6600606e+03,  1.7324994e+03,
-       1.7753655e+03,  1.7931195e+03,  1.7839794e+03,  1.7543157e+03,
-       1.7049335e+03,  1.6342871e+03,  1.5549646e+03,  1.4629383e+03,
-       1.3581758e+03,  1.2457595e+03,  1.1299878e+03,  1.0119864e+03,
-       8.9090477e+02,  7.6719677e+02,  6.4285660e+02,  5.1408928e+02,
-       3.8508372e+02,  2.5534679e+02,  1.2497756e+02, -3.9966107e+00,
-      -1.2599628e+02, -2.4595825e+02, -3.6034285e+02, -4.7095724e+02,
-      -5.7730810e+02, -6.8592514e+02, -7.8960041e+02, -8.9182726e+02,
-      -9.8741569e+02, -1.0733871e+03, -1.1555560e+03, -1.2285264e+03,
-      -1.2950383e+03, -1.3587508e+03, -1.4190449e+03, -1.4731166e+03,
-      -1.5244161e+03, -1.5700435e+03, -1.6143711e+03, -1.6442522e+03,
-      -1.6504480e+03, -1.6200592e+03, -1.5385467e+03, -1.4018632e+03 ],
-
-    [ -1.2097642e+03, -9.7471997e+02, -7.0651192e+02, -4.1925510e+02,
-      -1.2278127e+02,  1.6308666e+02,  4.3389868e+02,  6.8416589e+02,
-       9.0871878e+02,  1.1028177e+03,  1.2670447e+03,  1.4015901e+03,
-       1.5108776e+03,  1.5894427e+03,  1.6428125e+03,  1.6684610e+03,
-       1.6687068e+03,  1.6467684e+03,  1.6069401e+03,  1.5504379e+03,
-       1.4802355e+03,  1.3985477e+03,  1.3037872e+03,  1.1957600e+03,
-       1.0853732e+03,  9.7233331e+02,  8.4944729e+02,  7.2549848e+02,
-       5.9937450e+02,  4.8205790e+02,  3.5945231e+02,  2.3181221e+02,
-       1.0289577e+02, -2.2787734e+01, -1.4227469e+02, -2.5954609e+02,
-      -3.7614640e+02, -4.8391910e+02, -5.8341913e+02, -6.8123214e+02,
-      -7.7747871e+02, -8.6932601e+02, -9.5415487e+02, -1.0385138e+03,
-      -1.1184902e+03, -1.1867290e+03, -1.2499404e+03, -1.3128700e+03,
-      -1.3730047e+03, -1.4324194e+03, -1.4841659e+03, -1.5297608e+03,
-      -1.5601321e+03, -1.5603639e+03, -1.5252096e+03, -1.4567924e+03,
-      -1.3535284e+03, -1.2050129e+03, -1.0232675e+03, -8.1556031e+02,
-      -5.8338838e+02, -3.4263538e+02, -9.1284066e+01,  1.4792883e+02,
-       3.7724977e+02,  5.9063752e+02,  7.8210956e+02,  9.5337152e+02,
-       1.1069618e+03,  1.2346417e+03,  1.3304957e+03,  1.4081378e+03,
-       1.4660754e+03,  1.5064930e+03,  1.5231194e+03,  1.5173564e+03,
-       1.4891356e+03,  1.4460229e+03,  1.3890302e+03,  1.3171848e+03,
-       1.2329448e+03,  1.1400293e+03,  1.0376506e+03,  9.3324803e+02,
-       8.2426407e+02,  7.1443513e+02,  6.0252527e+02,  4.9088960e+02,
-       3.7646014e+02,  2.6184052e+02,  1.4340035e+02,  3.0422051e+01,
-      -8.5724331e+01, -1.9809318e+02, -3.0918985e+02, -4.1384220e+02,
-      -5.0992503e+02, -6.0275760e+02, -6.9829113e+02, -7.9525188e+02,
-      -8.9453019e+02, -9.7802721e+02, -1.0526954e+03, -1.1251736e+03,
-      -1.1955435e+03, -1.2637584e+03, -1.3263901e+03, -1.3794649e+03,
-      -1.4229086e+03, -1.4542303e+03, -1.4659972e+03, -1.4469638e+03,
-      -1.3956114e+03, -1.3075606e+03, -1.1938217e+03, -1.0487411e+03,
-      -8.7590742e+02, -6.8650630e+02, -4.8348960e+02, -2.7915529e+02,
-      -7.3951415e+01,  1.2639053e+02,  3.2037647e+02,  5.0816397e+02,
-       6.7805869e+02,  8.2207151e+02,  9.5456310e+02,  1.0705573e+03,
-       1.1629335e+03,  1.2429314e+03,  1.3063791e+03,  1.3382875e+03,
-       1.3364949e+03,  1.3197603e+03,  1.3011283e+03,  1.2904738e+03,
-       1.2841453e+03,  1.2735627e+03,  1.2483109e+03,  1.1982190e+03,
-       1.1236072e+03,  1.0353182e+03,  9.4811986e+02,  8.4791470e+02,
-       7.4977805e+02,  6.0946439e+02,  4.1686370e+02,  1.5206161e+02,
-      -7.9210822e+01, -2.2784261e+02, -3.2684361e+02, -3.9056312e+02,
-      -4.6716161e+02, -5.4206330e+02, -6.2230545e+02, -7.0009951e+02,
-      -7.7171489e+02, -8.3103588e+02, -8.8858361e+02, -9.5353054e+02 ]
-
-])
-
-LTPF_C2_PREV = np.array([
-
-    [  3.6075890e+02,  2.5558573e+02,  1.5676072e+02,  6.2869832e+01,
-      -2.7464215e+01, -1.0807999e+02, -1.8538536e+02, -2.5213341e+02,
-      -3.1737297e+02, -3.7675903e+02, -4.3291912e+02, -4.8396843e+02,
-      -5.3257774e+02, -5.7812590e+02, -6.2166743e+02, -6.6533460e+02,
-      -7.0685062e+02, -7.4579194e+02, -7.7729679e+02, -8.0489134e+02,
-      -8.3252315e+02, -8.5569655e+02, -8.7998412e+02, -9.0742496e+02,
-      -9.3370927e+02, -9.5471476e+02, -9.7276449e+02, -9.9514771e+02,
-      -1.0136386e+03, -1.0163600e+03, -9.9100449e+02, -9.3570452e+02,
-      -8.4068970e+02, -7.0878785e+02, -5.4236585e+02, -3.4883259e+02,
-      -1.3544369e+02,  7.6143761e+01,  2.8313432e+02,  4.7371481e+02,
-       6.3854542e+02,  7.8591376e+02,  9.1647781e+02,  1.0326049e+03,
-       1.1226394e+03,  1.1855486e+03,  1.2110267e+03,  1.2072759e+03,
-       1.1810221e+03,  1.1433488e+03,  1.0943565e+03,  1.0414966e+03,
-       9.8463390e+02,  9.1048007e+02,  8.1752062e+02,  7.2333732e+02,
-       6.4850080e+02,  5.9874218e+02,  5.6539980e+02,  5.5634736e+02,
-       5.3800637e+02,  4.9467989e+02,  4.1631880e+02,  2.8517505e+02,
-       1.1305724e+02, -8.8498535e+01, -2.9818901e+02, -4.9635689e+02,
-      -6.7273562e+02, -8.1632731e+02, -9.2538304e+02, -1.0146932e+03,
-      -1.0962177e+03, -1.1660919e+03, -1.2306080e+03, -1.2853503e+03,
-      -1.3346346e+03, -1.3883886e+03, -1.4496624e+03, -1.5155564e+03,
-      -1.5793268e+03, -1.6396210e+03, -1.6937295e+03, -1.7314870e+03,
-      -1.7610564e+03, -1.7775059e+03, -1.7806815e+03, -1.7684558e+03,
-      -1.6949960e+03, -1.4776266e+03, -1.0700242e+03, -5.1121101e+02,
-       1.0329967e+02,  6.7165782e+02,  1.1381176e+03,  1.4974226e+03,
-       1.7689436e+03,  1.9620604e+03,  2.0875907e+03,  2.1420343e+03,
-       2.1555856e+03,  2.1497779e+03,  2.1345923e+03,  2.1141460e+03,
-       2.0831857e+03,  2.0328927e+03,  1.9628672e+03,  1.8762276e+03,
-       1.7788061e+03,  1.6631901e+03,  1.5303905e+03,  1.3855681e+03,
-       1.2298992e+03,  1.0770934e+03,  9.1998634e+02,  7.6869655e+02,
-       6.1555385e+02,  4.7278828e+02,  3.3511742e+02,  2.0334643e+02,
-       8.1069660e+01, -4.1916116e+01, -1.7411361e+02, -3.0622256e+02,
-      -4.3179154e+02, -5.5570746e+02, -6.8069811e+02, -7.9809071e+02,
-      -9.0053455e+02, -9.9388321e+02, -1.0834733e+03, -1.1654917e+03,
-      -1.2466250e+03, -1.3184789e+03, -1.3814537e+03, -1.4386054e+03,
-      -1.5024822e+03, -1.5617271e+03, -1.6148868e+03, -1.6639722e+03,
-      -1.7108728e+03, -1.7519590e+03, -1.7805217e+03, -1.8036393e+03,
-      -1.8080682e+03, -1.7690236e+03, -1.6458189e+03, -1.4003994e+03,
-      -1.0280788e+03, -5.6864419e+02, -7.5505097e+01,  3.9987715e+02,
-       8.1589313e+02,  1.1654034e+03,  1.4482132e+03,  1.6762780e+03,
-       1.8458377e+03,  1.9698779e+03,  2.0528472e+03,  2.0941040e+03 ],
-
-    [  2.1004800e+03,  2.0660438e+03,  2.0070810e+03,  1.9335150e+03,
-       1.8437505e+03,  1.7474564e+03,  1.6433626e+03,  1.5341100e+03,
-       1.4257583e+03,  1.3211557e+03,  1.2062000e+03,  1.0819606e+03,
-       9.5194047e+02,  8.1893657e+02,  6.7789896e+02,  5.3732954e+02,
-       4.0506079e+02,  2.7179124e+02,  1.3998655e+02,  1.9709326e+00,
-      -1.3683087e+02, -2.7783589e+02, -4.1717230e+02, -5.5239764e+02,
-      -6.7544678e+02, -7.8374115e+02, -8.8533384e+02, -9.8169275e+02,
-      -1.0740844e+03, -1.1619601e+03, -1.2468187e+03, -1.3276334e+03,
-      -1.3976625e+03, -1.4581676e+03, -1.5080701e+03, -1.5565703e+03,
-      -1.6009886e+03, -1.6442955e+03, -1.6843665e+03, -1.7151734e+03,
-      -1.7459758e+03, -1.7579559e+03, -1.7383847e+03, -1.6558090e+03,
-      -1.4937678e+03, -1.2402230e+03, -9.0680789e+02, -5.2126546e+02,
-      -1.0907639e+02,  2.9396419e+02,  6.6844988e+02,  9.9743497e+02,
-       1.2728461e+03,  1.5005211e+03,  1.6749621e+03,  1.8070876e+03,
-       1.9030819e+03,  1.9644916e+03,  1.9851832e+03,  1.9771133e+03,
-       1.9382403e+03,  1.8819654e+03,  1.8027215e+03,  1.7116504e+03,
-       1.6036716e+03,  1.4931560e+03,  1.3786289e+03,  1.2583587e+03,
-       1.1369652e+03,  1.0121811e+03,  8.8292297e+02,  7.5232872e+02,
-       6.2279997e+02,  4.8668604e+02,  3.4937590e+02,  2.1277380e+02,
-       7.7335654e+01, -5.2960798e+01, -1.8097039e+02, -3.0070120e+02,
-      -4.1472118e+02, -5.2643886e+02, -6.3396297e+02, -7.3691168e+02,
-      -8.3480847e+02, -9.2600159e+02, -1.0147685e+03, -1.0954793e+03,
-      -1.1736112e+03, -1.2509220e+03, -1.3222525e+03, -1.3883874e+03,
-      -1.4496560e+03, -1.5064726e+03, -1.5576614e+03, -1.6028926e+03,
-      -1.6434987e+03, -1.6803495e+03, -1.7069786e+03, -1.7018749e+03,
-      -1.6517403e+03, -1.5416168e+03, -1.3607723e+03, -1.1195564e+03,
-      -8.2569063e+02, -4.9652560e+02, -1.5319529e+02,  1.8519693e+02,
-       5.0070752e+02,  7.8614834e+02,  1.0314193e+03,  1.2402508e+03,
-       1.4166554e+03,  1.5555207e+03,  1.6600606e+03,  1.7324994e+03,
-       1.7753655e+03,  1.7931195e+03,  1.7839794e+03,  1.7543157e+03,
-       1.7049335e+03,  1.6342871e+03,  1.5549646e+03,  1.4629383e+03,
-       1.3581758e+03,  1.2457595e+03,  1.1299878e+03,  1.0119864e+03,
-       8.9090477e+02,  7.6719677e+02,  6.4285660e+02,  5.1408928e+02,
-       3.8508372e+02,  2.5534679e+02,  1.2497756e+02, -3.9966107e+00,
-      -1.2599628e+02, -2.4595825e+02, -3.6034285e+02, -4.7095724e+02,
-      -5.7730810e+02, -6.8592514e+02, -7.8960041e+02, -8.9182726e+02,
-      -9.8741569e+02, -1.0733871e+03, -1.1555560e+03, -1.2285264e+03,
-      -1.2950383e+03, -1.3587508e+03, -1.4190449e+03, -1.4731166e+03,
-      -1.5244161e+03, -1.5700435e+03, -1.6143711e+03, -1.6442522e+03,
-      -1.6504480e+03, -1.6200592e+03, -1.5385467e+03, -1.4018632e+03 ],
-
-])
-
-LTPF_C2_TRANS = np.array([
-
-      -1.2097642e+03, -9.7530715e+02, -7.0586256e+02, -4.1543418e+02,
-      -1.1414107e+02,  1.7777097e+02,  4.5531279e+02,  7.1225199e+02,
-       9.4297106e+02,  1.1428504e+03,  1.3122721e+03,  1.4510401e+03,
-       1.5632810e+03,  1.6437088e+03,  1.6979967e+03,  1.7236213e+03,
-       1.7231450e+03,  1.6998644e+03,  1.6575399e+03,  1.5978279e+03,
-       1.5240838e+03,  1.4378667e+03,  1.3380626e+03,  1.2256564e+03,
-       1.1110323e+03,  9.9312189e+02,  8.6572627e+02,  7.3745656e+02,
-       6.0633674e+02,  4.8191123e+02,  3.5135472e+02,  2.1648189e+02,
-       8.0831679e+01, -5.1111363e+01, -1.7717212e+02, -3.0113312e+02,
-      -4.2348388e+02, -5.3759466e+02, -6.4602675e+02, -7.5403092e+02,
-      -8.6017221e+02, -9.5944484e+02, -1.0506850e+03, -1.1398086e+03,
-      -1.2228263e+03, -1.2941518e+03, -1.3612258e+03, -1.4273749e+03,
-      -1.4894501e+03, -1.5495534e+03, -1.6019734e+03, -1.6490374e+03,
-      -1.6814663e+03, -1.6844562e+03, -1.6513997e+03, -1.5793876e+03,
-      -1.4632011e+03, -1.2939942e+03, -1.0852074e+03, -8.4480887e+02,
-      -5.7732264e+02, -3.0008156e+02, -1.5103259e+01,  2.5424761e+02,
-       5.1054364e+02,  7.4618547e+02,  9.5531914e+02,  1.1392454e+03,
-       1.2997276e+03,  1.4305069e+03,  1.5279454e+03,  1.6037570e+03,
-       1.6553093e+03,  1.6847892e+03,  1.6879843e+03,  1.6682815e+03,
-       1.6264868e+03,  1.5696403e+03,  1.4983779e+03,  1.4119806e+03,
-       1.3127144e+03,  1.2052332e+03,  1.0900241e+03,  9.7210566e+02,
-       8.4877207e+02,  7.2467907e+02,  5.9961756e+02,  4.7505541e+02,
-       3.4639404e+02,  2.1700781e+02,  8.5155762e+01, -4.0026523e+01,
-      -1.6698145e+02, -2.8953979e+02, -4.0971310e+02, -5.2233024e+02,
-      -6.2776248e+02, -7.3117198e+02, -8.3540014e+02, -9.3755661e+02,
-      -1.0392995e+03, -1.1263481e+03, -1.2052728e+03, -1.2805748e+03,
-      -1.3532553e+03, -1.4234920e+03, -1.4883360e+03, -1.5444401e+03,
-      -1.5915550e+03, -1.6260829e+03, -1.6379761e+03, -1.6152110e+03,
-      -1.5560714e+03, -1.4554812e+03, -1.3203947e+03, -1.1456045e+03,
-      -9.3870522e+02, -7.1082654e+02, -4.6691560e+02, -2.2039906e+02,
-       2.5206392e+01,  2.6135641e+02,  4.8629103e+02,  6.9790232e+02,
-       8.8592993e+02,  1.0460616e+03,  1.1899193e+03,  1.3098413e+03,
-       1.4023098e+03,  1.4788921e+03,  1.5346583e+03,  1.5579963e+03,
-       1.5491955e+03,  1.5242006e+03,  1.4921986e+03,  1.4607118e+03,
-       1.4256432e+03,  1.3811223e+03,  1.3213357e+03,  1.2406869e+03,
-       1.1418577e+03,  1.0338657e+03,  9.2734345e+02,  8.0983956e+02,
-       6.9568058e+02,  5.4691592e+02,  3.6168815e+02,  1.2569299e+02,
-      -7.2937241e+01, -2.1016935e+02, -3.1888920e+02, -4.0476988e+02,
-      -5.0359952e+02, -5.9623817e+02, -6.9164993e+02, -7.8397385e+02,
-      -8.7149927e+02, -9.4943765e+02, -1.0247441e+03, -1.1018532e+03,
-
-])
-
-LTPF_C3_NBITS = 320
-
-LTPF_C3_ACTIVE = np.array([ True, False ])
-LTPF_C3_PITCH_INDEX = np.array([ 56, 0 ])
-
-LTPF_C3_C_N = np.array([
-    [  2.0480302e-01,  1.4271871e-01, -6.4036434e-03 ],
-    [  0.           ,  0.           ,  0.            ],
-])
-
-LTPF_C3_C_D = np.array([
-    [  1.5868459e-02,  1.8368837e-01,  1.8368837e-01,  1.5868459e-02 ],
-    [  0.           ,  0.           ,  0.           ,  0.            ],
-])
-
-LTPF_C3_X = np.array([
-
-    [ -3.4872147e+02, -3.2869651e+02, -3.0368708e+02, -2.7206929e+02,
-      -2.3135299e+02, -1.8746149e+02, -1.4271272e+02, -9.8455678e+01,
-      -4.8137684e+01,  2.0589588e-01,  4.5670854e+01,  8.5887062e+01,
-       1.2485726e+02,  1.6126194e+02,  1.9191268e+02,  2.2387152e+02,
-       2.4650629e+02,  2.6665533e+02,  2.8210563e+02,  2.9385531e+02,
-       3.0042345e+02,  2.9898320e+02,  2.9792092e+02,  2.9201085e+02,
-       2.8341711e+02,  2.7006923e+02,  2.5925000e+02,  2.4396424e+02,
-       2.2871379e+02,  2.1604489e+02,  2.0086440e+02,  1.8158468e+02,
-       1.6604772e+02,  1.4881816e+02,  1.3450578e+02,  1.1691585e+02,
-       1.0011405e+02,  8.1066153e+01,  5.7608036e+01,  3.3232973e+01,
-       8.7252186e+00, -1.9268859e+01, -5.0019465e+01, -7.8718920e+01,
-      -1.1264618e+02, -1.4558774e+02, -1.7907091e+02, -2.1353821e+02,
-      -2.4860083e+02, -2.7968268e+02, -3.0845126e+02, -3.3166214e+02,
-      -3.4854139e+02, -3.6210272e+02, -3.6311551e+02, -3.6538831e+02,
-      -3.5698782e+02, -3.4320560e+02, -3.2228293e+02, -2.9431070e+02,
-      -2.6126653e+02, -2.2161989e+02, -1.7770590e+02, -1.3258934e+02,
-      -8.8768668e+01, -4.1250510e+01,  4.8812815e+00,  4.8960277e+01,
-       9.1265856e+01,  1.2722198e+02,  1.5854814e+02,  1.8841450e+02,
-       2.1263776e+02,  2.3530944e+02,  2.5135346e+02,  2.6817644e+02,
-       2.7730158e+02,  2.7972049e+02,  2.7893036e+02,  2.7638406e+02,
-       2.6804826e+02,  2.6051672e+02,  2.4861891e+02,  2.3713159e+02,
-       2.2260424e+02,  2.0911457e+02,  1.9338336e+02,  1.8071811e+02,
-       1.6387353e+02,  1.5139501e+02,  1.3594276e+02,  1.2442513e+02,
-       1.1022492e+02,  9.7036956e+01,  7.4818560e+01,  5.7999575e+01,
-       3.1360863e+01,  8.7303630e+00, -1.6051537e+01, -4.5294499e+01,
-      -7.3824309e+01, -1.0455773e+02, -1.3439723e+02, -1.6704153e+02,
-      -1.9839956e+02, -2.3387826e+02, -2.6412575e+02, -2.9170423e+02,
-      -3.1531207e+02, -3.3274995e+02, -3.4693113e+02, -3.5369132e+02,
-      -3.5301724e+02, -3.4943527e+02, -3.3608402e+02, -3.1755446e+02,
-      -2.9205556e+02, -2.6071672e+02, -2.2432542e+02, -1.8738248e+02,
-      -1.4392604e+02, -1.0106545e+02, -5.5577429e+01, -9.8667562e+00,
-       3.7353443e+01,  7.5303068e+01,  1.1105079e+02,  1.4264083e+02,
-       1.7184174e+02,  1.9822361e+02,  2.2081903e+02,  2.4084291e+02,
-       2.5643437e+02,  2.6758684e+02,  2.7445589e+02,  2.7569331e+02,
-       2.7159104e+02,  2.6648908e+02,  2.5914969e+02,  2.4863883e+02,
-       2.3470167e+02,  2.2613959e+02,  2.1126267e+02,  2.0090440e+02,
-       1.8404815e+02,  1.6736836e+02,  1.5327263e+02,  1.3857671e+02,
-       1.2564592e+02,  1.1183479e+02,  9.8674586e+01,  7.9462699e+01,
-       6.1566145e+01,  3.4757509e+01,  9.1597341e+00, -1.6506764e+01,
-      -4.4310986e+01, -7.7716465e+01, -1.1113407e+02, -1.4150980e+02 ],
-
-    [ -1.6775203e+02, -2.0410277e+02, -2.4176001e+02, -2.7386652e+02,
-      -2.9629852e+02, -3.0908748e+02, -3.0195831e+02, -2.8578850e+02,
-      -2.6686979e+02, -2.6140403e+02, -2.6601879e+02, -2.7595293e+02,
-      -2.8053255e+02, -2.9088609e+02, -2.8340082e+02, -2.8587804e+02,
-      -2.5164112e+02, -2.1308294e+02, -1.5229837e+02, -8.7968057e+01,
-      -3.9965246e+01,  1.9682469e+01,  8.3439201e+01,  1.3071313e+02,
-       2.0278676e+02,  2.2530998e+02,  2.4917273e+02,  2.2837462e+02,
-       2.0851337e+02,  1.8379204e+02,  1.0597931e+02,  5.0251896e+01,
-       5.3816342e+01,  1.9418724e+02,  3.6073746e+02,  4.9001894e+02,
-       5.6206313e+02,  5.4551465e+02,  5.0418915e+02,  3.9875911e+02,
-       2.9036149e+02,  2.1569487e+02,  1.2216776e+02,  5.6342202e+01,
-       1.9881173e+01, -1.0527142e+01, -2.8641229e+01, -6.5396380e+01,
-      -2.2087227e+01, -1.9320385e+00,  9.9837180e+00, -1.2901176e+01,
-      -4.8912099e+01, -1.0229260e+02, -1.3236180e+02, -1.1357973e+02,
-      -1.6394336e+02, -2.0430337e+02, -2.3625105e+02, -2.7025726e+02,
-      -2.1302135e+02, -2.2632729e+02, -2.0952571e+02, -2.1427507e+02,
-      -2.5180218e+02, -2.9681097e+02, -3.2929367e+02, -3.2993365e+02,
-      -3.3630273e+02, -3.0716107e+02, -2.2776881e+02, -2.2007043e+02,
-      -1.7036950e+02, -1.6356561e+02, -1.0711820e+02,  3.8723772e+01,
-       9.2665406e+01,  1.2719129e+02,  2.3419328e+02,  2.4827798e+02,
-       2.2061102e+02,  1.4474493e+02,  1.0064081e+02,  9.5343023e+01,
-       5.5501524e+01,  3.6218435e+00, -6.9277352e+01, -1.2309115e+01,
-      -2.9683737e+01, -1.1059060e+01,  1.2650129e+02,  2.8907139e+02,
-       5.9646998e+02,  7.2219690e+02,  6.4274592e+02,  2.5572095e+02,
-       8.9009716e+00, -1.6887796e+02, -5.4165018e+02, -8.8289542e+02,
-      -9.0816906e+02,  2.8400805e+02,  1.3941683e+03,  1.4288425e+03,
-       1.0428507e+03,  6.0910516e+02,  4.9505488e+02,  5.1165385e+02,
-      -4.9324168e+01, -5.0538446e+02, -6.1278936e+02, -6.0237215e+02,
-      -1.0190721e+03, -1.3790932e+03, -1.3541381e+03,  7.7933615e+01,
-       1.3253623e+03,  1.3970218e+03,  1.1115023e+03,  7.7044387e+02,
-       5.1675398e+02,  2.7357534e+02, -1.1882263e+02, -3.6893899e+02,
-      -5.9907798e+02, -6.8538645e+02, -1.0290340e+03, -1.2093927e+03,
-      -1.3183905e+03, -6.4097534e+02,  7.8577340e+02,  1.3858651e+03,
-       1.2575136e+03,  1.0978140e+03,  7.7239776e+02,  3.5047643e+02,
-       1.3677282e+02, -2.9932975e+01, -3.3626785e+02, -6.8938845e+02,
-      -8.0039496e+02, -8.8267000e+02, -8.9787173e+02, -8.6951038e+02,
-       4.6805939e+02,  1.7552178e+03,  1.5546574e+03,  1.1148179e+03,
-       7.4606055e+02,  5.7001670e+02,  3.6944970e+02,  9.7838518e+01,
-      -1.1941272e+02, -4.1377871e+02, -7.6382784e+02, -8.4434380e+02,
-      -7.9151095e+02, -8.6493987e+02, -2.8343467e+02,  1.2284377e+03 ],
-
-])
-
-LTPF_C3_PREV = np.array([
-
-    [  6.4066155e+01,  9.8702625e+01,  1.3788395e+02,  1.7357908e+02,
-       2.0965843e+02,  2.3764314e+02,  2.6644923e+02,  2.9043075e+02,
-       3.1194822e+02,  3.2482869e+02,  3.3339160e+02,  3.4071606e+02,
-       3.4077768e+02,  3.3516446e+02,  3.2705459e+02,  3.1766371e+02,
-       3.0450863e+02,  2.8884502e+02,  2.7626398e+02,  2.5687540e+02,
-       2.3783751e+02,  2.1914457e+02,  1.9792827e+02,  1.7491941e+02,
-       1.5135385e+02,  1.2210867e+02,  9.3873907e+01,  6.1133113e+01,
-       2.5655472e+01, -9.0068272e+00, -4.6864087e+01, -8.5961652e+01,
-      -1.2549724e+02, -1.6396142e+02, -2.0212479e+02, -2.3817055e+02,
-      -2.7665292e+02, -3.0851152e+02, -3.4115267e+02, -3.6978674e+02,
-      -3.9095963e+02, -4.0758998e+02, -4.1787548e+02, -4.2041787e+02,
-      -4.1483578e+02, -4.0572811e+02, -3.8202738e+02, -3.5555284e+02,
-      -3.2171184e+02, -2.8408934e+02, -2.3936543e+02, -1.8996836e+02,
-      -1.4140617e+02, -9.0899975e+01, -4.2131433e+01,  5.1447212e+00,
-       5.1259588e+01,  9.2032590e+01,  1.3108193e+02,  1.6957969e+02,
-       2.0599062e+02,  2.3735997e+02,  2.6672423e+02,  2.9065678e+02,
-       3.1149055e+02,  3.2543573e+02,  3.3604446e+02,  3.4053351e+02,
-       3.4068048e+02,  3.3756977e+02,  3.3026320e+02,  3.2068706e+02,
-       3.0912932e+02,  2.9328335e+02,  2.7625928e+02,  2.6245063e+02,
-       2.4670232e+02,  2.2666582e+02,  2.0533576e+02,  1.8873190e+02,
-       1.6715637e+02,  1.4420637e+02,  1.1629663e+02,  8.8561616e+01,
-       5.8962789e+01,  2.5166894e+01, -1.1130553e+01, -4.5737395e+01,
-      -8.2162714e+01, -1.2346999e+02, -1.6150200e+02, -2.0028107e+02,
-      -2.3669696e+02, -2.7393564e+02, -3.0977094e+02, -3.4398480e+02,
-      -3.6896668e+02, -3.9241987e+02, -4.0955117e+02, -4.2052014e+02,
-      -4.2287410e+02, -4.1573504e+02, -4.0403021e+02, -3.8178373e+02,
-      -3.5516185e+02, -3.2075205e+02, -2.8058887e+02, -2.3403076e+02,
-      -1.8587624e+02, -1.3565255e+02, -8.4981034e+01, -3.4327765e+01,
-       1.6122604e+01,  6.2803153e+01,  1.0703665e+02,  1.4333420e+02,
-       1.8149169e+02,  2.1834576e+02,  2.4733100e+02,  2.7545243e+02,
-       2.9756989e+02,  3.1498586e+02,  3.2836159e+02,  3.3505102e+02,
-       3.3656005e+02,  3.3923920e+02,  3.3129967e+02,  3.2145289e+02,
-       3.1074707e+02,  2.9901767e+02,  2.8311559e+02,  2.6682498e+02,
-       2.4940742e+02,  2.3261497e+02,  2.1196627e+02,  1.9333406e+02,
-       1.7509963e+02,  1.5279050e+02,  1.3109055e+02,  1.0449469e+02,
-       7.8409595e+01,  4.9363873e+01,  1.7554459e+01, -1.5539732e+01,
-      -4.8805768e+01, -8.5198692e+01, -1.2189071e+02, -1.5886754e+02,
-      -1.9634205e+02, -2.3335312e+02, -2.7073502e+02, -3.0557489e+02,
-      -3.3663951e+02, -3.6449227e+02, -3.8546603e+02, -4.0164063e+02,
-      -4.1170176e+02, -4.1157567e+02, -4.0619410e+02, -3.9613725e+02 ],
-
-    [ -3.7147766e+02, -3.4548841e+02, -3.1333857e+02, -2.7463625e+02,
-      -2.2842732e+02, -1.8027124e+02, -1.3148070e+02, -8.3137241e+01,
-      -3.0054878e+01,  1.9883532e+01,  6.6714609e+01,  1.0807815e+02,
-       1.4822202e+02,  1.8586131e+02,  2.1800420e+02,  2.5060047e+02,
-       2.7401990e+02,  2.9513896e+02,  3.1106912e+02,  3.2286594e+02,
-       3.2918272e+02,  3.2833211e+02,  3.2739842e+02,  3.2088124e+02,
-       3.1147526e+02,  2.9799117e+02,  2.8657122e+02,  2.7043218e+02,
-       2.5431668e+02,  2.3996550e+02,  2.2300791e+02,  2.0270053e+02,
-       1.8568534e+02,  1.6689279e+02,  1.5012389e+02,  1.2962033e+02,
-       1.0942755e+02,  8.6477433e+01,  5.9720239e+01,  3.1778371e+01,
-       2.9431398e+00, -2.9247908e+01, -6.3765298e+01, -9.6917096e+01,
-      -1.3470119e+02, -1.7108334e+02, -2.0808704e+02, -2.4574024e+02,
-      -2.8326377e+02, -3.1633765e+02, -3.4671102e+02, -3.7097994e+02,
-      -3.8869922e+02, -4.0237282e+02, -4.0334704e+02, -4.0408504e+02,
-      -3.9385794e+02, -3.7723701e+02, -3.5253919e+02, -3.2150862e+02,
-      -2.8492979e+02, -2.4106826e+02, -1.9287257e+02, -1.4370111e+02,
-      -9.5563935e+01, -4.3545337e+01,  7.0713976e+00,  5.5117442e+01,
-       1.0039302e+02,  1.3954207e+02,  1.7504613e+02,  2.0852764e+02,
-       2.3658875e+02,  2.6248596e+02,  2.8111481e+02,  2.9971212e+02,
-       3.1019868e+02,  3.1449414e+02,  3.1475549e+02,  3.1252800e+02,
-       3.0472635e+02,  2.9675698e+02,  2.8382811e+02,  2.7136329e+02,
-       2.5584249e+02,  2.4078158e+02,  2.2398280e+02,  2.0974116e+02,
-       1.9071035e+02,  1.7568738e+02,  1.5790704e+02,  1.4374608e+02,
-       1.2653380e+02,  1.0989392e+02,  8.5342354e+01,  6.5160001e+01,
-       3.5365747e+01,  9.6994716e+00, -1.9148759e+01, -5.2247954e+01,
-      -8.4460390e+01, -1.1917021e+02, -1.5343168e+02, -1.9000568e+02,
-      -2.2535999e+02, -2.6422733e+02, -2.9746477e+02, -3.2794048e+02,
-      -3.5381425e+02, -3.7289983e+02, -3.8800620e+02, -3.9445315e+02,
-      -3.9341234e+02, -3.8871213e+02, -3.7329028e+02, -3.5219370e+02,
-      -3.2339521e+02, -2.8848818e+02, -2.4782613e+02, -2.0517168e+02,
-      -1.5630958e+02, -1.0872773e+02, -5.8413582e+01, -7.9029246e+00,
-       4.3081069e+01,  8.5378547e+01,  1.2552492e+02,  1.6074903e+02,
-       1.9342830e+02,  2.2272087e+02,  2.4784891e+02,  2.6963531e+02,
-       2.8673587e+02,  2.9921346e+02,  3.0619842e+02,  3.0724158e+02,
-       3.0343005e+02,  2.9800183e+02,  2.8972999e+02,  2.7822622e+02,
-       2.6355926e+02,  2.5308771e+02,  2.3630848e+02,  2.2381546e+02,
-       2.0555172e+02,  1.8801065e+02,  1.7231093e+02,  1.5597740e+02,
-       1.4132322e+02,  1.2578292e+02,  1.1037371e+02,  8.8726457e+01,
-       6.8198855e+01,  3.9334854e+01,  1.1725578e+01, -1.6214439e+01,
-      -4.7122569e+01, -8.2888237e+01, -1.1823402e+02, -1.5159407e+02 ],
-
-])
-
-LTPF_C3_TRANS = np.array([
-
-      -1.8246409e+02, -2.2173994e+02, -2.6085374e+02, -2.9483717e+02,
-      -3.2011835e+02, -3.3711584e+02, -3.3663215e+02, -3.2868626e+02,
-      -3.1677331e+02, -3.1352548e+02, -3.1534653e+02, -3.1872408e+02,
-      -3.1513281e+02, -3.1569554e+02, -2.9910316e+02, -2.9198409e+02,
-      -2.5148901e+02, -2.0982533e+02, -1.4838034e+02, -8.5301198e+01,
-      -3.6694674e+01,  2.3482066e+01,  8.5828448e+01,  1.3216759e+02,
-       2.0172964e+02,  2.2393590e+02,  2.4953564e+02,  2.3256572e+02,
-       2.1734484e+02,  1.9615122e+02,  1.2352168e+02,  7.1940230e+01,
-       7.4762310e+01,  2.0724785e+02,  3.6354237e+02,  4.8584290e+02,
-       5.5511323e+02,  5.3946449e+02,  5.0046673e+02,  3.9744659e+02,
-       2.9036149e+02,  2.1569487e+02,  1.2216776e+02,  5.6342202e+01,
-       1.9881173e+01, -1.0527142e+01, -2.8641229e+01, -6.5396380e+01,
-      -2.2087227e+01, -1.9320385e+00,  9.9837180e+00, -1.2901176e+01,
-      -4.8912099e+01, -1.0229260e+02, -1.3236180e+02, -1.1357973e+02,
-      -1.6394336e+02, -2.0430337e+02, -2.3625105e+02, -2.7025726e+02,
-      -2.1302135e+02, -2.2632729e+02, -2.0952571e+02, -2.1427507e+02,
-      -2.5180218e+02, -2.9681097e+02, -3.2929367e+02, -3.2993365e+02,
-      -3.3630273e+02, -3.0716107e+02, -2.2776881e+02, -2.2007043e+02,
-      -1.7036950e+02, -1.6356561e+02, -1.0711820e+02,  3.8723772e+01,
-       9.2665406e+01,  1.2719129e+02,  2.3419328e+02,  2.4827798e+02,
-       2.2061102e+02,  1.4474493e+02,  1.0064081e+02,  9.5343023e+01,
-       5.5501524e+01,  3.6218435e+00, -6.9277352e+01, -1.2309115e+01,
-      -2.9683737e+01, -1.1059060e+01,  1.2650129e+02,  2.8907139e+02,
-       5.9646998e+02,  7.2219690e+02,  6.4274592e+02,  2.5572095e+02,
-       8.9009716e+00, -1.6887796e+02, -5.4165018e+02, -8.8289542e+02,
-      -9.0816906e+02,  2.8400805e+02,  1.3941683e+03,  1.4288425e+03,
-       1.0428507e+03,  6.0910516e+02,  4.9505488e+02,  5.1165385e+02,
-      -4.9324168e+01, -5.0538446e+02, -6.1278936e+02, -6.0237215e+02,
-      -1.0190721e+03, -1.3790932e+03, -1.3541381e+03,  7.7933615e+01,
-       1.3253623e+03,  1.3970218e+03,  1.1115023e+03,  7.7044387e+02,
-       5.1675398e+02,  2.7357534e+02, -1.1882263e+02, -3.6893899e+02,
-      -5.9907798e+02, -6.8538645e+02, -1.0290340e+03, -1.2093927e+03,
-      -1.3183905e+03, -6.4097534e+02,  7.8577340e+02,  1.3858651e+03,
-       1.2575136e+03,  1.0978140e+03,  7.7239776e+02,  3.5047643e+02,
-       1.3677282e+02, -2.9932975e+01, -3.3626785e+02, -6.8938845e+02,
-      -8.0039496e+02, -8.8267000e+02, -8.9787173e+02, -8.6951038e+02,
-       4.6805939e+02,  1.7552178e+03,  1.5546574e+03,  1.1148179e+03,
-       7.4606055e+02,  5.7001670e+02,  3.6944970e+02,  9.7838518e+01,
-      -1.1941272e+02, -4.1377871e+02, -7.6382784e+02, -8.4434380e+02,
-      -7.9151095e+02, -8.6493987e+02, -2.8343467e+02,  1.2284377e+03,
-
-])
-
-LTPF_C4_NBITS = 320
-
-LTPF_C4_ACTIVE = np.array([ True, True ])
-LTPF_C4_PITCH_INDEX = np.array([ 56, 56 ])
-
-LTPF_C4_C_N = np.array([
-    [  2.0480302e-01,  1.4271871e-01, -6.4036434e-03 ],
-    [  2.0480302e-01,  1.4271871e-01, -6.4036434e-03 ],
-])
-
-LTPF_C4_C_D = np.array([
-    [  1.5868459e-02,  1.8368837e-01,  1.8368837e-01,  1.5868459e-02 ],
-    [  1.5868459e-02,  1.8368837e-01,  1.8368837e-01,  1.5868459e-02 ],
-])
-
-LTPF_C4_X = np.array([
-
-    [ -1.3212378e+02,  4.5530263e+00,  1.4615982e+02,  2.9071151e+02,
-       4.2471908e+02,  5.4825306e+02,  6.6032559e+02,  7.5740827e+02,
-       8.3891172e+02,  9.0995714e+02,  9.6747077e+02,  1.0121710e+03,
-       1.0274871e+03,  1.0037721e+03,  9.3793010e+02,  8.2480912e+02,
-       6.6687812e+02,  5.0610704e+02,  3.7535508e+02,  2.8417912e+02,
-       2.3507950e+02,  2.2431573e+02,  2.3503665e+02,  2.5100781e+02,
-       2.5591188e+02,  2.4573096e+02,  2.3732458e+02,  2.2379033e+02,
-       2.0393082e+02,  1.7940329e+02,  1.5349002e+02,  1.2421970e+02,
-       8.2607174e+01,  3.3890085e+01, -1.4919514e+01, -6.7050562e+01,
-      -1.2022717e+02, -1.7802094e+02, -2.3045847e+02, -2.7788664e+02,
-      -3.3683640e+02, -4.1806874e+02, -5.2071432e+02, -6.3745995e+02,
-      -7.6831195e+02, -8.9933303e+02, -1.0058396e+03, -1.0704297e+03,
-      -1.0883680e+03, -1.0715794e+03, -1.0235340e+03, -9.4781945e+02,
-      -8.5584778e+02, -7.4716794e+02, -6.2920874e+02, -5.0223965e+02,
-      -3.6316475e+02, -2.2074077e+02, -7.5442666e+01,  6.7658245e+01,
-       2.0420074e+02,  3.3396592e+02,  4.4656898e+02,  5.4596637e+02,
-       6.3808859e+02,  7.2787698e+02,  8.1472882e+02,  8.9279612e+02,
-       9.5178350e+02,  9.7288475e+02,  9.4735790e+02,  8.8244167e+02,
-       7.9508887e+02,  7.0282080e+02,  6.0598321e+02,  5.1640859e+02,
-       4.3937674e+02,  3.7245496e+02,  3.2160255e+02,  2.8003421e+02,
-       2.5119199e+02,  2.3495839e+02,  2.2200688e+02,  2.0973737e+02,
-       1.9073208e+02,  1.6793312e+02,  1.4320783e+02,  1.1276855e+02,
-       8.0839691e+01,  4.8429863e+01,  1.1662609e+01, -3.3424450e+01,
-      -7.9726479e+01, -1.2393767e+02, -1.7110449e+02, -2.1662821e+02,
-      -2.6310613e+02, -3.1774127e+02, -3.9885548e+02, -5.1583270e+02,
-      -6.4243905e+02, -7.6184139e+02, -8.6566023e+02, -9.4822201e+02,
-      -1.0112142e+03, -1.0437627e+03, -1.0457937e+03, -1.0207768e+03,
-      -9.6819885e+02, -8.9378058e+02, -7.9861844e+02, -6.8734848e+02,
-      -5.6329990e+02, -4.2840844e+02, -2.8589443e+02, -1.4101586e+02,
-      -5.6273423e-01,  1.3949815e+02,  2.7417238e+02,  4.0010557e+02,
-       5.2193759e+02,  6.3439172e+02,  7.3810702e+02,  8.2658657e+02,
-       8.9885978e+02,  9.5846967e+02,  9.8349123e+02,  9.6474177e+02,
-       9.0987618e+02,  8.3178192e+02,  7.4721512e+02,  6.6076640e+02,
-       5.7958128e+02,  5.0626404e+02,  4.3829495e+02,  3.7955641e+02,
-       3.3400402e+02,  3.0012060e+02,  2.6635949e+02,  2.3180026e+02,
-       1.9980913e+02,  1.7106449e+02,  1.4708346e+02,  1.1651799e+02,
-       8.3457665e+01,  5.6291779e+01,  3.0698836e+01,  5.4712667e+00,
-      -2.9709210e+01, -7.0496428e+01, -1.0899206e+02, -1.5337912e+02,
-      -1.9940203e+02, -2.4464990e+02, -2.9496636e+02, -3.6218204e+02,
-      -4.5477003e+02, -5.6091604e+02, -6.6582484e+02, -7.5807547e+02 ],
-
-    [ -8.3474175e+02, -8.9039341e+02, -9.2355843e+02, -9.3729800e+02,
-      -9.2464041e+02, -8.9038579e+02, -8.3678014e+02, -7.6008712e+02,
-      -6.6935538e+02, -5.6422713e+02, -4.4941540e+02, -3.2822721e+02,
-      -1.9802065e+02, -6.6766936e+01,  6.3833225e+01,  1.9485046e+02,
-       3.2069998e+02,  4.3761734e+02,  5.4460736e+02,  6.4521909e+02,
-       7.4169295e+02,  8.2759672e+02,  8.9638366e+02,  9.3673025e+02,
-       9.4669839e+02,  9.2302657e+02,  8.8064813e+02,  8.2833056e+02,
-       7.6070809e+02,  6.9391450e+02,  6.2171679e+02,  5.4789771e+02,
-       4.8160318e+02,  4.2010754e+02,  3.6391872e+02,  3.1081191e+02,
-       2.6834464e+02,  2.2822565e+02,  1.8578653e+02,  1.5071546e+02,
-       1.1735576e+02,  8.3222831e+01,  4.9624826e+01,  1.0744867e+01,
-      -2.5764012e+01, -6.4812135e+01, -1.0358083e+02, -1.4353059e+02,
-      -1.8297458e+02, -2.2359073e+02, -2.6775258e+02, -3.1656152e+02,
-      -3.7871430e+02, -4.4751491e+02, -5.2485941e+02, -6.0299909e+02,
-      -6.8515536e+02, -7.6401821e+02, -8.2770748e+02, -8.7387481e+02,
-      -8.9915932e+02, -9.0643654e+02, -8.8914063e+02, -8.5144521e+02,
-      -7.9103639e+02, -7.1441804e+02, -6.2692990e+02, -5.2729939e+02,
-      -4.2275301e+02, -3.0657474e+02, -1.8688744e+02, -6.1633756e+01,
-       5.8686296e+01,  1.8250335e+02,  3.0377201e+02,  4.1978552e+02,
-       5.3054960e+02,  6.2835118e+02,  7.2035468e+02,  7.9934358e+02,
-       8.5642737e+02,  8.9765907e+02,  9.2069014e+02,  9.1957966e+02,
-       8.9145864e+02,  8.3848660e+02,  7.7429387e+02,  7.0401776e+02,
-       6.3210907e+02,  5.6076246e+02,  4.9172735e+02,  4.2473574e+02,
-       3.6317680e+02,  3.0715534e+02,  2.5275264e+02,  2.0664538e+02,
-       1.6042746e+02,  1.1820978e+02,  7.3335304e+01,  3.4381568e+01,
-      -5.8542117e+00, -4.4553111e+01, -8.3479478e+01, -1.2048090e+02,
-      -1.6116063e+02, -2.0192831e+02, -2.4373220e+02, -2.8256043e+02,
-      -3.2919802e+02, -3.7322793e+02, -4.2010268e+02, -4.7218497e+02,
-      -5.3461522e+02, -6.1386549e+02, -6.9152470e+02, -7.6738453e+02,
-      -8.2527123e+02, -8.6158674e+02, -8.7895938e+02, -8.7597504e+02,
-      -8.5108741e+02, -8.0480220e+02, -7.4250154e+02, -6.5876002e+02,
-      -5.6356029e+02, -4.5524527e+02, -3.4038894e+02, -2.2360514e+02,
-      -1.0268552e+02,  2.5676655e+01,  1.5166378e+02,  2.7540954e+02,
-       3.9465179e+02,  5.0881759e+02,  6.1514014e+02,  7.1241132e+02,
-       7.9504576e+02,  8.7166197e+02,  9.3226594e+02,  9.7438372e+02,
-       9.8472937e+02,  9.6485548e+02,  9.1860685e+02,  8.5256605e+02,
-       7.8008367e+02,  7.0149395e+02,  6.2054247e+02,  5.4108956e+02,
-       4.6123090e+02,  3.8599064e+02,  3.1521582e+02,  2.4589274e+02,
-       1.8211975e+02,  1.3163470e+02,  8.4985461e+01,  3.6873046e+01,
-      -6.0437124e+00, -4.8770494e+01, -8.4128492e+01, -1.2196451e+02 ],
-
-])
-
-LTPF_C4_PREV = np.array([
-
-    [  8.5827694e+02,  8.3064489e+02,  8.0499625e+02,  7.8056066e+02,
-       7.6744901e+02,  7.5611316e+02,  7.1123694e+02,  6.4234457e+02,
-       5.7975881e+02,  5.3276775e+02,  5.0354201e+02,  4.6493297e+02,
-       4.2651511e+02,  3.8147439e+02,  3.2876260e+02,  2.7004720e+02,
-       2.0149312e+02,  1.2434150e+02,  4.6683685e+01, -2.4488830e+01,
-      -9.4758661e+01, -1.6910076e+02, -2.4454068e+02, -3.2272819e+02,
-      -4.1153117e+02, -5.2406430e+02, -6.6312995e+02, -7.9501146e+02,
-      -9.1517707e+02, -1.0195879e+03, -1.1003671e+03, -1.1506760e+03,
-      -1.1718109e+03, -1.1569278e+03, -1.1251298e+03, -1.0925962e+03,
-      -1.0572755e+03, -1.0074938e+03, -9.3394117e+02, -8.3773785e+02,
-      -7.2203590e+02, -5.9088086e+02, -4.5612949e+02, -3.2040519e+02,
-      -1.7104088e+02, -2.9232508e+01,  1.0254422e+02,  2.3645451e+02,
-       3.6931210e+02,  4.8954097e+02,  5.9869449e+02,  7.0325516e+02,
-       7.9700038e+02,  8.6886012e+02,  9.1083211e+02,  9.2243956e+02,
-       9.0940850e+02,  8.7320143e+02,  8.2061884e+02,  7.7544913e+02,
-       7.3767280e+02,  7.0507470e+02,  6.8093384e+02,  6.6014067e+02,
-       6.3137283e+02,  6.0264808e+02,  5.6924398e+02,  5.4487108e+02,
-       5.1295321e+02,  4.8260718e+02,  4.4645113e+02,  4.0584256e+02,
-       3.5619699e+02,  2.8935939e+02,  2.1760106e+02,  1.4449684e+02,
-       7.4653166e+01, -1.4735621e+00, -8.3781827e+01, -1.5582846e+02,
-      -2.3783590e+02, -3.2355910e+02, -4.0747488e+02, -5.0371066e+02,
-      -6.0048961e+02, -7.0726549e+02, -8.2713764e+02, -9.4294256e+02,
-      -1.0401191e+03, -1.1242695e+03, -1.1843296e+03, -1.1977911e+03,
-      -1.1759957e+03, -1.1382443e+03, -1.0869538e+03, -1.0150254e+03,
-      -9.2445459e+02, -8.2418678e+02, -7.1231843e+02, -5.8566805e+02,
-      -4.4749524e+02, -3.2163393e+02, -1.9483077e+02, -6.6146712e+01,
-       7.2606186e+01,  2.1709089e+02,  3.6141116e+02,  4.9774327e+02,
-       6.2102585e+02,  7.3301494e+02,  8.2884523e+02,  9.1772806e+02,
-       9.8287592e+02,  1.0258870e+03,  1.0350466e+03,  1.0124387e+03,
-       9.5526828e+02,  8.4870459e+02,  6.9698370e+02,  5.5384197e+02,
-       4.7329566e+02,  4.4187783e+02,  4.2164793e+02,  4.0723567e+02,
-       4.0287348e+02,  3.9294225e+02,  3.8536364e+02,  3.6737694e+02,
-       3.4038387e+02,  3.2024898e+02,  2.9202545e+02,  2.5623304e+02,
-       2.0059931e+02,  1.4494932e+02,  8.7316562e+01,  2.0208470e+01,
-      -5.1106062e+01, -1.0972390e+02, -1.7160643e+02, -2.3635682e+02,
-      -3.0057986e+02, -3.6841959e+02, -4.5812030e+02, -5.5409463e+02,
-      -6.5667832e+02, -7.6531178e+02, -9.0843842e+02, -1.0761546e+03,
-      -1.2069858e+03, -1.2737779e+03, -1.2929641e+03, -1.2624172e+03,
-      -1.2008239e+03, -1.1122508e+03, -9.9807662e+02, -8.7056340e+02,
-      -7.3753241e+02, -6.0499387e+02, -4.6029956e+02, -3.2128673e+02 ],
-
-    [ -1.8675929e+02, -4.2406693e+01,  1.0644289e+02,  2.5996083e+02,
-       4.0591306e+02,  5.4343259e+02,  6.6906247e+02,  7.7899810e+02,
-       8.7298704e+02,  9.5506440e+02,  1.0209952e+03,  1.0688758e+03,
-       1.0833988e+03,  1.0572928e+03,  9.8779757e+02,  8.6895392e+02,
-       7.0635769e+02,  5.4680902e+02,  4.2601966e+02,  3.4987333e+02,
-       3.1179292e+02,  3.0331866e+02,  3.0951274e+02,  3.1789860e+02,
-       3.1563910e+02,  3.0013119e+02,  2.8624618e+02,  2.6714149e+02,
-       2.4150830e+02,  2.0837106e+02,  1.7059521e+02,  1.2855863e+02,
-       7.5094806e+01,  1.5176971e+01, -4.3358321e+01, -1.0315083e+02,
-      -1.6398964e+02, -2.2910287e+02, -2.9037721e+02, -3.5326931e+02,
-      -4.3186627e+02, -5.2797253e+02, -6.4097239e+02, -7.7021355e+02,
-      -9.1916034e+02, -1.0635912e+03, -1.1696907e+03, -1.2240756e+03,
-      -1.2277996e+03, -1.1942767e+03, -1.1287017e+03, -1.0350001e+03,
-      -9.2445100e+02, -7.9889645e+02, -6.6691580e+02, -5.2684803e+02,
-      -3.7726996e+02, -2.2821747e+02, -7.6317887e+01,  7.6079467e+01,
-       2.2531300e+02,  3.6948450e+02,  4.9788068e+02,  6.1407961e+02,
-       7.2081446e+02,  8.2045742e+02,  9.1242924e+02,  9.9212633e+02,
-       1.0508862e+03,  1.0718419e+03,  1.0464386e+03,  9.7937941e+02,
-       8.8142794e+02,  7.6477603e+02,  6.3740319e+02,  5.2412010e+02,
-       4.3571759e+02,  3.6988854e+02,  3.2884698e+02,  3.0173096e+02,
-       2.8691049e+02,  2.7882926e+02,  2.6731147e+02,  2.5356111e+02,
-       2.3340364e+02,  2.0894382e+02,  1.8071643e+02,  1.4579425e+02,
-       1.0855560e+02,  6.8052759e+01,  2.0813765e+01, -3.3555159e+01,
-      -8.7826734e+01, -1.4078458e+02, -1.9733530e+02, -2.5226649e+02,
-      -3.0811893e+02, -3.7371017e+02, -4.6559419e+02, -5.8909591e+02,
-      -7.2198295e+02, -8.5479624e+02, -9.7874703e+02, -1.0795937e+03,
-      -1.1503967e+03, -1.1796433e+03, -1.1713652e+03, -1.1317595e+03,
-      -1.0619895e+03, -9.6960460e+02, -8.5726543e+02, -7.3060563e+02,
-      -5.9289544e+02, -4.4495754e+02, -2.9059997e+02, -1.3479531e+02,
-       1.7757467e+01,  1.7012020e+02,  3.1647370e+02,  4.5257127e+02,
-       5.8125188e+02,  6.9866277e+02,  8.0708027e+02,  9.0151273e+02,
-       9.8106784e+02,  1.0459462e+03,  1.0733030e+03,  1.0542555e+03,
-       9.9525660e+02,  9.0830183e+02,  8.0934326e+02,  7.0399220e+02,
-       6.0377076e+02,  5.1638905e+02,  4.4150465e+02,  3.8256291e+02,
-       3.4048809e+02,  3.1118639e+02,  2.8406392e+02,  2.5713040e+02,
-       2.3131764e+02,  2.0600503e+02,  1.8197113e+02,  1.5035197e+02,
-       1.1569230e+02,  8.4266914e+01,  5.1996878e+01,  1.7843797e+01,
-      -2.6863170e+01, -7.5983644e+01, -1.2245636e+02, -1.7436828e+02,
-      -2.2708180e+02, -2.7906654e+02, -3.3755256e+02, -4.1588336e+02,
-      -5.2295173e+02, -6.4523634e+02, -7.6683613e+02, -8.7677439e+02 ],
-
-])
-
-LTPF_C4_TRANS = np.array([
-
-      -9.6976885e+02, -1.0376330e+03, -1.0764010e+03, -1.0872965e+03,
-      -1.0660439e+03, -1.0190108e+03, -9.4899438e+02, -8.5471701e+02,
-      -7.4561708e+02, -6.2178553e+02, -4.8798080e+02, -3.4713910e+02,
-      -1.9841833e+02, -5.0343781e+01,  9.6414723e+01,  2.4225179e+02,
-       3.8080735e+02,  5.0947979e+02,  6.2776176e+02,  7.3826963e+02,
-       8.4169803e+02,  9.3159353e+02,  1.0031138e+03,  1.0438329e+03,
-       1.0480614e+03,  1.0127914e+03,  9.5385188e+02,  8.8138762e+02,
-       7.9431025e+02,  7.0979394e+02,  6.2416163e+02,  5.4300509e+02,
-       4.7372434e+02,  4.1360311e+02,  3.6279724e+02,  3.1673275e+02,
-       2.7937838e+02,  2.4267574e+02,  2.0418215e+02,  1.7211796e+02,
-       1.3914947e+02,  1.0348883e+02,  6.8276707e+01,  2.9143910e+01,
-      -7.9633499e+00, -4.9829988e+01, -9.3834050e+01, -1.3941156e+02,
-      -1.8501204e+02, -2.3271716e+02, -2.8327412e+02, -3.3841588e+02,
-      -4.0881202e+02, -4.9188847e+02, -5.8927352e+02, -6.8905431e+02,
-      -7.8967502e+02, -8.8144454e+02, -9.5326854e+02, -1.0026317e+03,
-      -1.0264190e+03, -1.0268593e+03, -9.9874748e+02, -9.4795333e+02,
-      -8.7252905e+02, -7.7954835e+02, -6.7411996e+02, -5.5559866e+02,
-      -4.3134824e+02, -2.9558412e+02, -1.5723885e+02, -1.5143571e+01,
-       1.2171974e+02,  2.6041913e+02,  3.9322892e+02,  5.1828299e+02,
-       6.3629063e+02,  7.4159801e+02,  8.3990996e+02,  9.2215495e+02,
-       9.7897443e+02,  1.0129522e+03,  1.0197564e+03,  9.9746243e+02,
-       9.4932784e+02,  8.7962855e+02,  8.0193420e+02,  7.2094920e+02,
-       6.4030858e+02,  5.6344020e+02,  4.9240203e+02,  4.2631986e+02,
-       3.6714651e+02,  3.1424334e+02,  2.6363836e+02,  2.1946532e+02,
-       1.7487874e+02,  1.3448581e+02,  9.0857113e+01,  5.1852661e+01,
-       1.0318345e+01, -3.0189483e+01, -7.1704052e+01, -1.1290529e+02,
-      -1.5806523e+02, -2.0311837e+02, -2.4946754e+02, -2.9428673e+02,
-      -3.4738868e+02, -4.0117413e+02, -4.6307505e+02, -5.3393190e+02,
-      -6.1557536e+02, -7.0983150e+02, -7.9873811e+02, -8.8091592e+02,
-      -9.4071232e+02, -9.7639817e+02, -9.9018818e+02, -9.8005670e+02,
-      -9.4511932e+02, -8.8673987e+02, -8.1023192e+02, -7.1271293e+02,
-      -6.0397885e+02, -4.8247591e+02, -3.5415373e+02, -2.2240894e+02,
-      -8.6345911e+01,  5.4855371e+01,  1.9247458e+02,  3.2774740e+02,
-       4.5715456e+02,  5.8013934e+02,  6.9372155e+02,  7.9720836e+02,
-       8.8545637e+02,  9.6285451e+02,  1.0187327e+03,  1.0521050e+03,
-       1.0516389e+03,  1.0206935e+03,  9.6358799e+02,  8.8842240e+02,
-       8.0838051e+02,  7.2366647e+02,  6.3870123e+02,  5.5707357e+02,
-       4.7704505e+02,  4.0312802e+02,  3.3463611e+02,  2.6847649e+02,
-       2.0824825e+02,  1.5901226e+02,  1.1171069e+02,  6.3026014e+01,
-       1.8987746e+01, -2.5300684e+01, -6.3942070e+01, -1.0563596e+02,
-
-])
-
-LTPF_C5_NBITS = 320
-
-LTPF_C5_ACTIVE = np.array([ True, True ])
-LTPF_C5_PITCH_INDEX = np.array([ 56, 52 ])
-
-LTPF_C5_C_N = np.array([
-    [  2.0480302e-01,  1.4271871e-01, -6.4036434e-03 ],
-    [  2.0480302e-01,  1.4271871e-01, -6.4036434e-03 ],
-])
-
-LTPF_C5_C_D = np.array([
-    [  1.5868459e-02,  1.8368837e-01,  1.8368837e-01,  1.5868459e-02 ],
-    [  4.2799674e-02,  2.2003000e-01,  1.3427625e-01,  2.6795433e-03 ],
-])
-
-LTPF_C5_X = np.array([
-
-    [ -1.6060766e+02, -1.9568387e+02, -2.3100280e+02, -2.6630342e+02,
-      -3.0336764e+02, -3.3854889e+02, -3.7043669e+02, -4.1163573e+02,
-      -4.5567039e+02, -5.1558927e+02, -5.7814485e+02, -6.4080974e+02,
-      -6.9798700e+02, -7.4456362e+02, -7.7654511e+02, -7.9319453e+02,
-      -7.9402247e+02, -7.7652860e+02, -7.4572416e+02, -6.9989705e+02,
-      -6.3841065e+02, -5.6041492e+02, -4.6954673e+02, -3.7010557e+02,
-      -2.6541886e+02, -1.5525665e+02, -4.1036675e+01,  7.7898304e+01,
-       1.9450287e+02,  3.0691788e+02,  4.1560715e+02,  5.1731671e+02,
-       6.1122047e+02,  6.9593032e+02,  7.7071271e+02,  8.3246711e+02,
-       8.7294256e+02,  8.9535004e+02,  8.9058119e+02,  8.6785095e+02,
-       8.2896133e+02,  7.8301121e+02,  7.2447207e+02,  6.6383224e+02,
-       5.9764845e+02,  5.3040514e+02,  4.6518541e+02,  3.9659186e+02,
-       3.2746766e+02,  2.6753027e+02,  2.0739840e+02,  1.4677357e+02,
-       9.5287069e+01,  4.7197825e+01, -1.1700140e+00, -4.9161682e+01,
-      -9.6726950e+01, -1.4038011e+02, -1.8662421e+02, -2.2897297e+02,
-      -2.6864070e+02, -3.1060300e+02, -3.4908047e+02, -3.8903634e+02,
-      -4.3354889e+02, -4.7982553e+02, -5.3003184e+02, -5.8409985e+02,
-      -6.3676286e+02, -6.8405252e+02, -7.2580553e+02, -7.5743871e+02,
-      -7.7706979e+02, -7.8082023e+02, -7.7270174e+02, -7.4653280e+02,
-      -7.1067906e+02, -6.5596456e+02, -5.8829707e+02, -5.0656026e+02,
-      -4.1202861e+02, -3.1175500e+02, -2.0302114e+02, -9.4710595e+01,
-       2.0375247e+01,  1.3270062e+02,  2.4243959e+02,  3.5297406e+02,
-       4.5553582e+02,  5.5590475e+02,  6.4792544e+02,  7.2690018e+02,
-       7.9536436e+02,  8.5008231e+02,  8.8455109e+02,  9.0442411e+02,
-       8.9923508e+02,  8.7803657e+02,  8.3884819e+02,  7.8786430e+02,
-       7.2427475e+02,  6.5372622e+02,  5.8223046e+02,  5.0499317e+02,
-       4.2757252e+02,  3.4979799e+02,  2.7384122e+02,  2.0202195e+02,
-       1.3245127e+02,  6.9157310e+01,  1.0836031e+01, -5.0190037e+01,
-      -1.0459364e+02, -1.5805356e+02, -2.0733214e+02, -2.4766726e+02,
-      -2.9312137e+02, -3.3816241e+02, -3.7627128e+02, -4.1399424e+02,
-      -4.4836893e+02, -4.8327539e+02, -5.1189667e+02, -5.4560547e+02,
-      -5.8121896e+02, -6.1491595e+02, -6.4841741e+02, -6.7347617e+02,
-      -6.9479054e+02, -7.0418229e+02, -7.0192552e+02, -6.8320863e+02,
-      -6.5899253e+02, -6.2202504e+02, -5.7186005e+02, -5.1046639e+02,
-      -4.4158751e+02, -3.6145704e+02, -2.7098565e+02, -1.7538139e+02,
-      -7.6006538e+01,  2.3722024e+01,  1.2888233e+02,  2.3123425e+02,
-       3.3082160e+02,  4.2819177e+02,  5.1739280e+02,  6.0102219e+02,
-       6.7529473e+02,  7.3951297e+02,  8.0008252e+02,  8.4203459e+02,
-       8.6655983e+02,  8.6963556e+02,  8.5619100e+02,  8.2481263e+02,
-       7.8075652e+02,  7.2216895e+02,  6.6035959e+02,  5.8959592e+02 ],
-
-    [  5.1338681e+02,  4.3458985e+02,  3.6021867e+02,  2.8040781e+02,
-       2.0424899e+02,  1.3450352e+02,  6.6513343e+01,  5.6705127e+00,
-      -5.2746410e+01, -1.1038385e+02, -1.6030998e+02, -2.0583244e+02,
-      -2.4887172e+02, -2.9195508e+02, -3.2720109e+02, -3.6021736e+02,
-      -3.9187844e+02, -4.2450350e+02, -4.5121148e+02, -4.8051913e+02,
-      -5.0679429e+02, -5.3473222e+02, -5.6274008e+02, -5.8904566e+02,
-      -6.1354818e+02, -6.2864496e+02, -6.3771702e+02, -6.3780508e+02,
-      -6.2690341e+02, -6.0523722e+02, -5.7468865e+02, -5.3607922e+02,
-      -4.8568411e+02, -4.2700735e+02, -3.5998795e+02, -2.8215398e+02,
-      -2.0120100e+02, -1.1187382e+02, -2.5873841e+01,  6.7900858e+01,
-       1.6266005e+02,  2.5147024e+02,  3.4066102e+02,  4.2711439e+02,
-       5.0521368e+02,  5.8027967e+02,  6.4926123e+02,  7.0606425e+02,
-       7.5197499e+02,  7.7936955e+02,  7.9241574e+02,  7.9273830e+02,
-       7.7226162e+02,  7.4318941e+02,  6.9912979e+02,  6.4987144e+02,
-       5.8935287e+02,  5.2867427e+02,  4.5818050e+02,  3.9091836e+02,
-       3.1175882e+02,  2.4316163e+02,  1.6901936e+02,  1.0131712e+02,
-       4.0884705e+01, -1.8284360e+01, -7.2463773e+01, -1.2615956e+02,
-      -1.7449474e+02, -2.1740936e+02, -2.5946712e+02, -2.9956841e+02,
-      -3.3765147e+02, -3.6878225e+02, -4.0090001e+02, -4.3495156e+02,
-      -4.6061972e+02, -4.8952939e+02, -5.1712969e+02, -5.4061916e+02,
-      -5.6432757e+02, -5.8583512e+02, -5.9997738e+02, -6.0321240e+02,
-      -6.0215834e+02, -5.9411694e+02, -5.7827835e+02, -5.5352370e+02,
-      -5.2218352e+02, -4.8118496e+02, -4.3153207e+02, -3.7290541e+02,
-      -3.0763879e+02, -2.3045577e+02, -1.5030005e+02, -6.8343251e+01,
-       1.9827505e+01,  1.0879534e+02,  2.0178224e+02,  2.8957061e+02,
-       3.7779733e+02,  4.6428381e+02,  5.4088918e+02,  6.1377535e+02,
-       6.8124334e+02,  7.3734864e+02,  7.7681316e+02,  8.0293170e+02,
-       8.0890279e+02,  8.0620381e+02,  7.8580399e+02,  7.5204050e+02,
-       7.1105755e+02,  6.5621411e+02,  5.9523050e+02,  5.2862471e+02,
-       4.5293754e+02,  3.7743682e+02,  3.0188947e+02,  2.2243974e+02,
-       1.4611001e+02,  6.9741588e+01,  3.0745399e+00, -6.6469812e+01,
-      -1.2845539e+02, -1.8845561e+02, -2.4293629e+02, -2.9314084e+02,
-      -3.3894702e+02, -3.8275365e+02, -4.2041853e+02, -4.5718992e+02,
-      -4.8545538e+02, -5.1088490e+02, -5.3523663e+02, -5.5122371e+02,
-      -5.7210009e+02, -5.8455365e+02, -5.9877467e+02, -6.0919241e+02,
-      -6.1143839e+02, -6.0342076e+02, -5.8730343e+02, -5.6903506e+02,
-      -5.3921882e+02, -5.0539402e+02, -4.6156875e+02, -4.1089541e+02,
-      -3.4967700e+02, -2.8252157e+02, -2.1092501e+02, -1.3135475e+02,
-      -4.8131277e+01,  3.4353741e+01,  1.2201908e+02,  2.0824054e+02,
-       2.9606295e+02,  3.8043837e+02,  4.5867136e+02,  5.3182302e+02 ],
-
-])
-
-LTPF_C5_PREV = np.array([
-
-    [ -9.6976885e+02, -1.0376330e+03, -1.0764010e+03, -1.0872965e+03,
-      -1.0660439e+03, -1.0190108e+03, -9.4899438e+02, -8.5471701e+02,
-      -7.4561708e+02, -6.2178553e+02, -4.8798080e+02, -3.4713910e+02,
-      -1.9841833e+02, -5.0343781e+01,  9.6414723e+01,  2.4225179e+02,
-       3.8080735e+02,  5.0947979e+02,  6.2776176e+02,  7.3826963e+02,
-       8.4169803e+02,  9.3159353e+02,  1.0031138e+03,  1.0438329e+03,
-       1.0480614e+03,  1.0127914e+03,  9.5385188e+02,  8.8138762e+02,
-       7.9431025e+02,  7.0979394e+02,  6.2416163e+02,  5.4300509e+02,
-       4.7372434e+02,  4.1360311e+02,  3.6279724e+02,  3.1673275e+02,
-       2.7937838e+02,  2.4267574e+02,  2.0418215e+02,  1.7211796e+02,
-       1.3914947e+02,  1.0348883e+02,  6.8276707e+01,  2.9143910e+01,
-      -7.9633499e+00, -4.9829988e+01, -9.3834050e+01, -1.3941156e+02,
-      -1.8501204e+02, -2.3271716e+02, -2.8327412e+02, -3.3841588e+02,
-      -4.0881202e+02, -4.9188847e+02, -5.8927352e+02, -6.8905431e+02,
-      -7.8967502e+02, -8.8144454e+02, -9.5326854e+02, -1.0026317e+03,
-      -1.0264190e+03, -1.0268593e+03, -9.9874748e+02, -9.4795333e+02,
-      -8.7252905e+02, -7.7954835e+02, -6.7411996e+02, -5.5559866e+02,
-      -4.3134824e+02, -2.9558412e+02, -1.5723885e+02, -1.5143571e+01,
-       1.2171974e+02,  2.6041913e+02,  3.9322892e+02,  5.1828299e+02,
-       6.3629063e+02,  7.4159801e+02,  8.3990996e+02,  9.2215495e+02,
-       9.7897443e+02,  1.0129522e+03,  1.0197564e+03,  9.9746243e+02,
-       9.4932784e+02,  8.7962855e+02,  8.0193420e+02,  7.2094920e+02,
-       6.4030858e+02,  5.6344020e+02,  4.9240203e+02,  4.2631986e+02,
-       3.6714651e+02,  3.1424334e+02,  2.6363836e+02,  2.1946532e+02,
-       1.7487874e+02,  1.3448581e+02,  9.0857113e+01,  5.1852661e+01,
-       1.0318345e+01, -3.0189483e+01, -7.1704052e+01, -1.1290529e+02,
-      -1.5806523e+02, -2.0311837e+02, -2.4946754e+02, -2.9428673e+02,
-      -3.4738868e+02, -4.0117413e+02, -4.6307505e+02, -5.3393190e+02,
-      -6.1557536e+02, -7.0983150e+02, -7.9873811e+02, -8.8091592e+02,
-      -9.4071232e+02, -9.7639817e+02, -9.9018818e+02, -9.8005670e+02,
-      -9.4511932e+02, -8.8673987e+02, -8.1023192e+02, -7.1271293e+02,
-      -6.0397885e+02, -4.8247591e+02, -3.5415373e+02, -2.2240894e+02,
-      -8.6345911e+01,  5.4855371e+01,  1.9247458e+02,  3.2774740e+02,
-       4.5715456e+02,  5.8013934e+02,  6.9372155e+02,  7.9720836e+02,
-       8.8545637e+02,  9.6285451e+02,  1.0187327e+03,  1.0521050e+03,
-       1.0516389e+03,  1.0206935e+03,  9.6358799e+02,  8.8842240e+02,
-       8.0838051e+02,  7.2366647e+02,  6.3870123e+02,  5.5707357e+02,
-       4.7704505e+02,  4.0312802e+02,  3.3463611e+02,  2.6847649e+02,
-       2.0824825e+02,  1.5901226e+02,  1.1171069e+02,  6.3026014e+01,
-       1.8987746e+01, -2.5300684e+01, -6.3942070e+01, -1.0563596e+02 ],
-
-    [ -1.4774474e+02, -1.8760166e+02, -2.2888898e+02, -2.7036124e+02,
-      -3.1332685e+02, -3.5581512e+02, -3.9771599e+02, -4.4936892e+02,
-      -5.0524457e+02, -5.7736164e+02, -6.5368200e+02, -7.3121324e+02,
-      -8.0200835e+02, -8.5934306e+02, -8.9755526e+02, -9.1640499e+02,
-      -9.1557588e+02, -8.9266707e+02, -8.5215743e+02, -7.9312106e+02,
-      -7.1595184e+02, -6.2138325e+02, -5.1398723e+02, -3.9767506e+02,
-      -2.7618028e+02, -1.4941349e+02, -1.8433316e+01,  1.1606930e+02,
-       2.4697130e+02,  3.7323736e+02,  4.9468012e+02,  6.0791740e+02,
-       7.1199453e+02,  8.0485505e+02,  8.8578958e+02,  9.5117733e+02,
-       9.9264372e+02,  1.0115211e+03,  9.9864359e+02,  9.6401858e+02,
-       9.1024930e+02,  8.4835436e+02,  7.7530833e+02,  7.0141455e+02,
-       6.2384959e+02,  5.4721968e+02,  4.7387003e+02,  3.9972348e+02,
-       3.2726546e+02,  2.6394537e+02,  2.0233246e+02,  1.4286080e+02,
-       9.1074446e+01,  4.1308373e+01, -8.2303699e+00, -5.6391830e+01,
-      -1.0380366e+02, -1.4867461e+02, -1.9589518e+02, -2.3948198e+02,
-      -2.8179404e+02, -3.2661184e+02, -3.6849411e+02, -4.1204691e+02,
-      -4.6073272e+02, -5.1299590e+02, -5.7214182e+02, -6.3765069e+02,
-      -7.0266168e+02, -7.6246325e+02, -8.1462585e+02, -8.5309773e+02,
-      -8.7583931e+02, -8.7977551e+02, -8.6820607e+02, -8.3598008e+02,
-      -7.9127858e+02, -7.2555633e+02, -6.4513927e+02, -5.4928065e+02,
-      -4.4082024e+02, -3.2665281e+02, -2.0436663e+02, -8.1707251e+01,
-       4.7901921e+01,  1.7432524e+02,  2.9759562e+02,  4.1992512e+02,
-       5.3317251e+02,  6.4237470e+02,  7.4113789e+02,  8.2606130e+02,
-       8.9883116e+02,  9.5428904e+02,  9.8624536e+02,  9.9869004e+02,
-       9.8266888e+02,  9.4925619e+02,  8.9813116e+02,  8.3623319e+02,
-       7.6348823e+02,  6.8594085e+02,  6.0808870e+02,  5.2646492e+02,
-       4.4600650e+02,  3.6561691e+02,  2.8872526e+02,  2.1691496e+02,
-       1.4728773e+02,  8.4221474e+01,  2.6048470e+01, -3.4388207e+01,
-      -8.8802146e+01, -1.4299353e+02, -1.9336320e+02, -2.3710707e+02,
-      -2.8587475e+02, -3.3270152e+02, -3.7422945e+02, -4.1636455e+02,
-      -4.5573665e+02, -4.9726692e+02, -5.3543762e+02, -5.8065815e+02,
-      -6.2910888e+02, -6.7690028e+02, -7.2375327e+02, -7.6132305e+02,
-      -7.9286869e+02, -8.0958989e+02, -8.1191872e+02, -7.9586469e+02,
-      -7.7057333e+02, -7.2915594e+02, -6.7231474e+02, -6.0134288e+02,
-      -5.1986758e+02, -4.2496645e+02, -3.1966315e+02, -2.0893647e+02,
-      -9.4117050e+01,  2.1901801e+01,  1.4286090e+02,  2.5974483e+02,
-       3.7391931e+02,  4.8475194e+02,  5.8679531e+02,  6.8253551e+02,
-       7.6686124e+02,  8.3927651e+02,  9.0416036e+02,  9.4669654e+02,
-       9.6938119e+02,  9.6791538e+02,  9.4724817e+02,  9.0746962e+02,
-       8.5438295e+02,  7.8710681e+02,  7.1621104e+02,  6.3739148e+02 ],
-
-])
-
-LTPF_C5_TRANS = np.array([
-
-       5.5469254e+02,  4.6885623e+02,  3.8726237e+02,  3.0166318e+02,
-       2.2151059e+02,  1.4759060e+02,  7.6115964e+01,  1.2068315e+01,
-      -5.0213437e+01, -1.1124791e+02, -1.6496412e+02, -2.1495097e+02,
-      -2.6214927e+02, -3.0944212e+02, -3.5014054e+02, -3.8896389e+02,
-      -4.2617800e+02, -4.6406673e+02, -4.9691186e+02, -5.3314161e+02,
-      -5.6776429e+02, -6.0515795e+02, -6.4220526e+02, -6.7634237e+02,
-      -7.0656015e+02, -7.2541177e+02, -7.3535446e+02, -7.3265481e+02,
-      -7.1676788e+02, -6.8818630e+02, -6.4788454e+02, -5.9638237e+02,
-      -5.3135114e+02, -4.5622803e+02, -3.7085252e+02, -2.7464233e+02,
-      -1.7527279e+02, -6.8212512e+01,  3.6401489e+01,  1.4772605e+02,
-       2.5777143e+02,  3.5986766e+02,  4.6046220e+02,  5.5572298e+02,
-       6.4084254e+02,  7.2023220e+02,  7.9091946e+02,  8.4596132e+02,
-       8.8561830e+02,  9.0316671e+02,  9.0353933e+02,  8.8830570e+02,
-       8.5210347e+02,  8.0683786e+02,  7.4767132e+02,  6.8402115e+02,
-       6.1000133e+02,  5.3637714e+02,  4.5522905e+02,  3.7796360e+02,
-       2.9165202e+02,  2.1765490e+02,  1.3937214e+02,  6.9124279e+01,
-       5.1484918e+00, -5.7974304e+01, -1.1532103e+02, -1.7106128e+02,
-      -2.2144596e+02, -2.6763632e+02, -3.1235860e+02, -3.5424602e+02,
-      -3.9420762e+02, -4.2862777e+02, -4.6393345e+02, -5.0059201e+02,
-      -5.3052040e+02, -5.6462343e+02, -5.9733595e+02, -6.2614003e+02,
-      -6.5395598e+02, -6.7654796e+02, -6.8956058e+02, -6.9036404e+02,
-      -6.8409998e+02, -6.6772245e+02, -6.4131629e+02, -6.0423695e+02,
-      -5.5819928e+02, -5.0085813e+02, -4.3386043e+02, -3.5697723e+02,
-      -2.7353263e+02, -1.7946159e+02, -8.3894727e+01,  1.3870299e+01,
-       1.1661147e+02,  2.1714406e+02,  3.1916911e+02,  4.1466126e+02,
-       5.0807281e+02,  5.9699545e+02,  6.7493085e+02,  7.4602304e+02,
-       8.0681874e+02,  8.5162090e+02,  8.7732032e+02,  8.8801241e+02,
-       8.7733827e+02,  8.5711309e+02,  8.1939693e+02,  7.7020353e+02,
-       7.1415915e+02,  6.4639833e+02,  5.7422282e+02,  4.9782204e+02,
-       4.1400066e+02,  3.3290948e+02,  2.5272667e+02,  1.7094246e+02,
-       9.4679864e+01,  1.9214845e+01, -4.7039158e+01, -1.1560544e+02,
-      -1.7609914e+02, -2.3440216e+02, -2.8752910e+02, -3.3711616e+02,
-      -3.8273508e+02, -4.2591674e+02, -4.6397521e+02, -5.0230060e+02,
-      -5.3283216e+02, -5.6227577e+02, -5.9128189e+02, -6.1269433e+02,
-      -6.3821208e+02, -6.5457809e+02, -6.7024858e+02, -6.7829939e+02,
-      -6.7675317e+02, -6.6455106e+02, -6.4328023e+02, -6.1728360e+02,
-      -5.7844437e+02, -5.3391997e+02, -4.7785175e+02, -4.1395668e+02,
-      -3.3933775e+02, -2.5814915e+02, -1.7234111e+02, -7.9862248e+01,
-       1.5711615e+01,  1.1047993e+02,  2.0926197e+02,  3.0481978e+02,
-       4.0036676e+02,  4.9118016e+02,  5.7429258e+02,  6.5078515e+02,
-
-])
-
-### C.4.1.10  Output signal clipping
-
-X_HAT_CLIP_10M = np.array([
-
-    [  6.1525095e-01,  1.7117620e+00, -1.6200436e-01, -2.1107548e+00,
-       9.1636911e-01,  8.7583398e+00,  1.4291828e+01,  1.4615904e+01,
-       1.9312730e+01,  2.2178311e+01,  2.1178760e+01,  1.9939021e+01,
-       3.2745193e+01,  3.1385007e+01,  4.5642888e+01,  3.8185668e+01,
-       4.3452271e+01,  3.0174130e+01,  2.7510416e+01,  3.9048290e+00,
-      -4.1911157e+00, -3.4032526e+01, -4.3089457e+01, -7.1817465e+01,
-      -7.3319439e+01, -1.0231340e+02, -8.2645833e+01, -1.0207070e+02,
-      -7.4253115e+01, -9.5269932e+01, -1.2210097e+02, -1.3216707e+02,
-      -1.2665681e+02, -1.6033791e+02, -1.3067613e+02, -1.8796611e+01,
-       6.2097263e+01,  7.2290617e+00, -1.2550979e+02, -8.9649115e+01,
-       7.6135408e+02,  2.7072170e+03,  6.1558256e+03,  9.6522574e+03,
-       1.2566221e+04,  1.5421574e+04,  1.8329565e+04,  2.1102710e+04,
-       2.3323039e+04,  2.5451924e+04,  2.7365468e+04,  2.8891223e+04,
-       3.0341813e+04,  3.1380038e+04,  3.2134454e+04,  3.2606958e+04,
-       3.2683895e+04,  3.2518324e+04,  3.2036626e+04,  3.1278795e+04,
-       3.0136453e+04,  2.8694957e+04,  2.7009676e+04,  2.5075921e+04,
-       2.2940269e+04,  2.0564299e+04,  1.7956441e+04,  1.5202722e+04,
-       1.2352834e+04,  9.3639445e+03,  6.2714619e+03,  3.1112267e+03,
-      -1.0075267e+02, -3.3115389e+03, -6.4954073e+03, -9.6110111e+03,
-      -1.2594144e+04, -1.5477538e+04, -1.8214406e+04, -2.0797170e+04,
-      -2.3195615e+04, -2.5355124e+04, -2.7302557e+04, -2.8919494e+04,
-      -3.0273227e+04, -3.1357707e+04, -3.2152224e+04, -3.2625545e+04,
-      -3.2768000e+04, -3.2640407e+04, -3.2172861e+04, -3.1393517e+04,
-      -3.0316009e+04, -2.8922986e+04, -2.7290775e+04, -2.5351571e+04,
-      -2.3183937e+04, -2.0795771e+04, -1.8212609e+04, -1.5453610e+04,
-      -1.2543978e+04, -9.4923186e+03, -6.3663932e+03, -3.1740539e+03,
-       4.2407582e+01,  3.2479359e+03,  6.4369448e+03,  9.5564107e+03,
-       1.2589389e+04,  1.5494437e+04,  1.8238168e+04,  2.0814647e+04,
-       2.3183440e+04,  2.5340307e+04,  2.7236643e+04,  2.8879098e+04,
-       3.0219987e+04,  3.1304367e+04,  3.2073455e+04,  3.2541098e+04,
-       3.2660415e+04,  3.2490788e+04,  3.2004309e+04,  3.1215306e+04,
-       3.0131477e+04,  2.8751611e+04,  2.7114762e+04,  2.5198099e+04,
-       2.3044166e+04,  2.0655799e+04,  1.8068458e+04,  1.5312555e+04,
-       1.2400688e+04,  9.3834439e+03,  6.2670869e+03,  3.1043555e+03,
-      -9.0364008e+01, -3.2949280e+03, -6.4558716e+03, -9.5736656e+03,
-      -1.2593429e+04, -1.5487321e+04, -1.8231003e+04, -2.0808229e+04,
-      -2.3191386e+04, -2.5327915e+04, -2.7226934e+04, -2.8860268e+04,
-      -3.0235350e+04, -3.1313467e+04, -3.2092397e+04, -3.2585580e+04,
-      -3.2768000e+04, -3.2606534e+04, -3.2146234e+04, -3.1383325e+04,
-      -3.0295384e+04, -2.8926934e+04, -2.7297985e+04, -2.5343669e+04 ],
-
-    [ -2.3210612e+04, -2.0830109e+04, -1.8262528e+04, -1.5491202e+04,
-      -1.2640992e+04, -9.6308521e+03, -6.5120997e+03, -3.2981272e+03,
-      -1.0437273e+02,  3.1237705e+03,  6.3475523e+03,  9.4251090e+03,
-       1.2406927e+04,  1.5369182e+04,  1.8133410e+04,  2.0696011e+04,
-       2.3075625e+04,  2.5239168e+04,  2.7178122e+04,  2.8863775e+04,
-       3.0239107e+04,  3.1292540e+04,  3.2055374e+04,  3.2528526e+04,
-       3.2691792e+04,  3.2554032e+04,  3.2080858e+04,  3.1269701e+04,
-       3.0219849e+04,  2.8872409e+04,  2.7201446e+04,  2.5283194e+04,
-       2.3137397e+04,  2.0746427e+04,  1.8171258e+04,  1.5425482e+04,
-       1.2542866e+04,  9.5154094e+03,  6.4038902e+03,  3.2243297e+03,
-       1.8093877e+01, -3.2004786e+03, -6.3778766e+03, -9.4986572e+03,
-      -1.2530644e+04, -1.5438896e+04, -1.8199553e+04, -2.0786963e+04,
-      -2.3172801e+04, -2.5336869e+04, -2.7256938e+04, -2.8914212e+04,
-      -3.0292811e+04, -3.1379968e+04, -3.2163160e+04, -3.2636663e+04,
-      -3.2768000e+04, -3.2642190e+04, -3.2169708e+04, -3.1389768e+04,
-      -3.0307591e+04, -2.8933213e+04, -2.7277887e+04, -2.5361709e+04,
-      -2.3197556e+04, -2.0809583e+04, -1.8223444e+04, -1.5459240e+04,
-      -1.2547850e+04, -9.5147469e+03, -6.3913071e+03, -3.2061552e+03,
-       1.1474435e+01,  3.2280361e+03,  6.4146913e+03,  9.5376172e+03,
-       1.2570372e+04,  1.5481142e+04,  1.8242641e+04,  2.0827867e+04,
-       2.3213026e+04,  2.5375280e+04,  2.7293037e+04,  2.8947339e+04,
-       3.0323330e+04,  3.1406817e+04,  3.2187891e+04,  3.2660531e+04,
-       3.2767000e+04,  3.2659083e+04,  3.2187190e+04,  3.1402569e+04,
-       3.0317849e+04,  2.8939620e+04,  2.7281206e+04,  2.5360297e+04,
-       2.3194809e+04,  2.0805310e+04,  1.8215867e+04,  1.5450539e+04,
-       1.2535402e+04,  9.5045150e+03,  6.3825434e+03,  3.1961404e+03,
-      -1.9030604e+01, -3.2350097e+03, -6.4178539e+03, -9.5376996e+03,
-      -1.2564818e+04, -1.5468879e+04, -1.8222757e+04, -2.0799617e+04,
-      -2.3177492e+04, -2.5329860e+04, -2.7239548e+04, -2.8887388e+04,
-      -3.0257022e+04, -3.1336972e+04, -3.2115659e+04, -3.2585521e+04,
-      -3.2741691e+04, -3.2583544e+04, -3.2112334e+04, -3.1332786e+04,
-      -3.0253039e+04, -2.8881031e+04, -2.7231757e+04, -2.5321143e+04,
-      -2.3168095e+04, -2.0790131e+04, -1.8212051e+04, -1.5458479e+04,
-      -1.2554090e+04, -9.5278709e+03, -6.4088050e+03, -3.2282766e+03,
-      -1.5127187e+01,  3.1974595e+03,  6.3806758e+03,  9.5013370e+03,
-       1.2533377e+04,  1.5443257e+04,  1.8203988e+04,  2.0793445e+04,
-       2.3180292e+04,  2.5344634e+04,  2.7265053e+04,  2.8919963e+04,
-       3.0294320e+04,  3.1376795e+04,  3.2154178e+04,  3.2622959e+04,
-       3.2767000e+04,  3.2617286e+04,  3.2145842e+04,  3.1360660e+04,
-       3.0278348e+04,  2.8901877e+04,  2.7241369e+04,  2.5301056e+04 ],
-
-])
-
-X_HAT_CLIP_7M5 = np.array([
-
-     [ 9.0382948e-01,  2.8563300e+00,  2.0863167e+00,  3.2605273e+00,
-       1.8231017e+00, -2.6473031e+00, -7.7420704e+00, -1.6971743e+01,
-      -4.4169569e+00,  4.7473387e+00,  7.9882732e+00,  2.1090757e+00,
-       6.9477046e+00,  7.6294361e+00,  4.5069158e+00,  1.1288109e+00,
-       5.5301798e-01, -1.2320805e+00,  1.2696965e+01,  1.7998129e+01,
-       1.9997378e+01,  2.3310802e+01,  3.4116671e+01,  3.1619222e+01,
-       2.3643252e+01,  2.2595989e+01,  2.4150879e+01,  1.7561939e+01,
-       2.4167995e+01,  2.1868269e+01,  1.2021561e+01,  1.0810360e+01,
-      -1.1321816e+01, -1.3811836e+01, -2.7571991e+01, -3.3459505e+01,
-      -2.6720233e+01, -4.0425004e+01, -4.1666697e+01, -4.8106995e+01,
-      -7.1121739e+01, -8.5018856e+01, -6.4519501e+01, -6.1651047e+01,
-      -6.2001672e+01, -4.9054098e+01,  5.3605147e+00, -2.7222279e+00,
-      -6.3200946e+00, -2.8873822e+01, -5.6314175e+01, -5.9551902e+01,
-      -2.1183627e+01, -9.5007617e+01, -6.7674879e+01,  7.6546124e+01,
-       3.6355638e+02,  2.0908440e+02,  9.2290767e+01, -8.4453487e+01,
-      -2.0810832e+02, -1.9235273e+02, -4.0634578e+02, -2.2011977e+02,
-       6.2920459e+02,  3.1481663e+03,  6.2343351e+03,  9.4022080e+03,
-       1.2520451e+04,  1.5313131e+04,  1.8128985e+04,  2.0762454e+04,
-       2.3084787e+04,  2.5275848e+04,  2.7095495e+04,  2.8665301e+04,
-       3.0094623e+04,  3.1202047e+04,  3.2006678e+04,  3.2461623e+04,
-       3.2568832e+04,  3.2408327e+04,  3.1961953e+04,  3.1146555e+04,
-       3.0073949e+04,  2.8725124e+04,  2.7099832e+04,  2.5196695e+04,
-       2.3022972e+04,  2.0643354e+04,  1.8079103e+04,  1.5352852e+04,
-       1.2476728e+04,  9.4135962e+03,  6.2948219e+03,  3.1010477e+03,
-      -9.0897787e+01, -3.3383673e+03, -6.5093586e+03, -9.6214110e+03,
-      -1.2638625e+04, -1.5564780e+04, -1.8289238e+04, -2.0877731e+04,
-      -2.3274493e+04, -2.5456613e+04, -2.7372293e+04, -2.9018289e+04,
-      -3.0399516e+04, -3.1474248e+04, -3.2213279e+04, -3.2686770e+04,
-      -3.2768000e+04, -3.2655386e+04, -3.2161995e+04, -3.1363572e+04,
-      -3.0296725e+04, -2.8893704e+04, -2.7226660e+04, -2.5303018e+04 ],
-
-    [ -2.3111848e+04, -2.0718046e+04, -1.8145256e+04, -1.5386042e+04,
-      -1.2451683e+04, -9.4561229e+03, -6.3407390e+03, -3.2078423e+03,
-      -1.4231827e+01,  3.1871864e+03,  6.4028626e+03,  9.5288605e+03,
-       1.2522323e+04,  1.5403074e+04,  1.8148763e+04,  2.0738239e+04,
-       2.3124038e+04,  2.5248199e+04,  2.7157343e+04,  2.8821520e+04,
-       3.0197933e+04,  3.1280524e+04,  3.2068994e+04,  3.2535286e+04,
-       3.2674198e+04,  3.2505688e+04,  3.2021479e+04,  3.1217393e+04,
-       3.0139480e+04,  2.8742712e+04,  2.7081219e+04,  2.5164753e+04,
-       2.3005958e+04,  2.0625737e+04,  1.8051360e+04,  1.5314656e+04,
-       1.2403135e+04,  9.3958576e+03,  6.2866076e+03,  3.1150574e+03,
-      -8.3043055e+01, -3.2906309e+03, -6.4783209e+03, -9.5968770e+03,
-      -1.2627329e+04, -1.5543027e+04, -1.8297188e+04, -2.0856742e+04,
-      -2.3242831e+04, -2.5406524e+04, -2.7315500e+04, -2.8954115e+04,
-      -3.0311322e+04, -3.1397263e+04, -3.2199564e+04, -3.2663393e+04,
-      -3.2768000e+04, -3.2631820e+04, -3.2158938e+04, -3.1361864e+04,
-      -3.0286591e+04, -2.8909485e+04, -2.7252911e+04, -2.5338382e+04,
-      -2.3179744e+04, -2.0801866e+04, -1.8222214e+04, -1.5467629e+04,
-      -1.2551908e+04, -9.5234786e+03, -6.4050183e+03, -3.2234113e+03,
-      -1.0202956e+01,  3.2050827e+03,  6.3811438e+03,  9.5000550e+03,
-       1.2526624e+04,  1.5433267e+04,  1.8190970e+04,  2.0775689e+04,
-       2.3157406e+04,  2.5317424e+04,  2.7226647e+04,  2.8880016e+04,
-       3.0250964e+04,  3.1333324e+04,  3.2108157e+04,  3.2579815e+04,
-       3.2739171e+04,  3.2585501e+04,  3.2115570e+04,  3.1342610e+04,
-       3.0239307e+04,  2.8881574e+04,  2.7232557e+04,  2.5325302e+04,
-       2.3165560e+04,  2.0776212e+04,  1.8202819e+04,  1.5452512e+04,
-       1.2552870e+04,  9.5323059e+03,  6.4141989e+03,  3.2353808e+03,
-       3.2008805e+01, -3.1778835e+03, -6.3568255e+03, -9.4682388e+03,
-      -1.2490842e+04, -1.5403926e+04, -1.8154186e+04, -2.0745346e+04,
-      -2.3121161e+04, -2.5285375e+04, -2.7200767e+04, -2.8855100e+04,
-      -3.0236188e+04, -3.1303814e+04, -3.2106304e+04, -3.2610143e+04 ],
-
-])
-
-X_HAT_CLIP = [ X_HAT_CLIP_7M5, X_HAT_CLIP_10M ]
diff --git a/system/embdrv/lc3/test/arm/ltpf_arm.c b/system/embdrv/lc3/test/arm/ltpf_arm.c
deleted file mode 100644
index e7b8bfc..0000000
--- a/system/embdrv/lc3/test/arm/ltpf_arm.c
+++ /dev/null
@@ -1,114 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include "simd32.h"
-
-/* -------------------------------------------------------------------------- */
-
-#define TEST_ARM
-#include <ltpf.c>
-
-void lc3_put_bits_generic(lc3_bits_t *a, unsigned b, int c)
-{ (void)a, (void)b, (void)c; }
-
-unsigned lc3_get_bits_generic(struct lc3_bits *a, int b)
-{ return (void)a, (void)b, 0; }
-
-/* -------------------------------------------------------------------------- */
-
-static int check_resampler()
-{
-    int16_t __x[60+480], *x = __x + 60;
-    for (int i = -60; i < 480; i++)
-        x[i] = rand() & 0xffff;
-
-    struct lc3_ltpf_hp50_state hp50 = { 0 }, hp50_arm = { 0 };
-    int16_t y[128], y_arm[128];
-
-    resample_8k_12k8(&hp50, x, y, 128);
-    arm_resample_8k_12k8(&hp50_arm, x, y_arm, 128);
-    if (memcmp(y, y_arm, 128 * sizeof(*y)) != 0)
-        return -1;
-
-    resample_16k_12k8(&hp50, x, y, 128);
-    arm_resample_16k_12k8(&hp50_arm, x, y_arm, 128);
-    if (memcmp(y, y_arm, 128 * sizeof(*y)) != 0)
-        return -1;
-
-    resample_24k_12k8(&hp50, x, y, 128);
-    arm_resample_24k_12k8(&hp50_arm, x, y_arm, 128);
-    if (memcmp(y, y_arm, 128 * sizeof(*y)) != 0)
-        return -1;
-
-    resample_32k_12k8(&hp50, x, y, 128);
-    arm_resample_32k_12k8(&hp50_arm, x, y_arm, 128);
-    if (memcmp(y, y_arm, 128 * sizeof(*y)) != 0)
-        return -1;
-
-    resample_48k_12k8(&hp50, x, y, 128);
-    arm_resample_48k_12k8(&hp50_arm, x, y_arm, 128);
-    if (memcmp(y, y_arm, 128 * sizeof(*y)) != 0)
-        return -1;
-
-    return 0;
-}
-
-static int check_correlate()
-{
-    int16_t alignas(4) a[500], b[500];
-    float y[100], y_arm[100];
-
-    for (int i = 0; i < 500; i++) {
-        a[i] = rand() & 0xffff;
-        b[i] = rand() & 0xffff;
-    }
-
-    correlate(a, b+200, 128, y, 100);
-    arm_correlate(a, b+200, 128, y_arm, 100);
-    if (memcmp(y, y_arm, 100 * sizeof(*y)) != 0)
-        return -1;
-
-    correlate(a, b+199, 128, y, 99);
-    arm_correlate(a, b+199, 128, y_arm, 99);
-    if (memcmp(y, y_arm, 99 * sizeof(*y)) != 0)
-        return -1;
-
-    correlate(a, b+199, 128, y, 100);
-    arm_correlate(a, b+199, 128, y_arm, 100);
-    if (memcmp(y, y_arm, 100 * sizeof(*y)) != 0)
-        return -1;
-
-    return 0;
-}
-
-int check_ltpf(void)
-{
-    int ret;
-
-    if ((ret = check_resampler()) < 0)
-        return ret;
-
-    if ((ret = check_correlate()) < 0)
-        return ret;
-
-    return 0;
-}
diff --git a/system/embdrv/lc3/test/arm/simd32.h b/system/embdrv/lc3/test/arm/simd32.h
deleted file mode 100644
index fd17f71..0000000
--- a/system/embdrv/lc3/test/arm/simd32.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#if __ARM_FEATURE_SIMD32
-
-#include <arm_acle.h>
-
-#else
-#define __ARM_FEATURE_SIMD32 1
-
-#include <stdint.h>
-
-typedef int32_t int16x2_t;
-
-__attribute__((unused))
-static int16x2_t __pkhbt(int16x2_t a, int16x2_t b)
-{
-    uint32_t a_bot = (uint32_t)a & 0x0000ffffu;
-    uint32_t b_top = (uint32_t)b & 0xffff0000u;
-
-    return (int16x2_t)(a_bot | b_top);
-}
-
-__attribute__((unused))
-static int32_t __smlad(int16x2_t a, int16x2_t b, int32_t u)
-{
-    int16_t a_hi = a >> 16, a_lo = a & 0xffff;
-    int16_t b_hi = b >> 16, b_lo = b & 0xffff;
-
-    return u + (a_hi * b_hi) + (a_lo * b_lo);
-}
-
-__attribute__((unused))
-static int64_t __smlald(int16x2_t a, int16x2_t b, int64_t u)
-{
-    int16_t a_hi = a >> 16, a_lo = a & 0xffff;
-    int16_t b_hi = b >> 16, b_lo = b & 0xffff;
-    return u + (a_hi * b_hi) + (a_lo * b_lo);
-}
-
-__attribute__((unused))
-static int64_t __smlaldx(int16x2_t a, int16x2_t b, int64_t u)
-{
-    int16_t a_hi = a >> 16, a_lo = a & 0xffff;
-    int16_t b_hi = b >> 16, b_lo = b & 0xffff;
-    return u + (a_hi * b_lo) + (a_lo * b_hi);
-}
-
-#endif /* __ARM_FEATURE_SIMD32 */
diff --git a/system/embdrv/lc3/test/arm/test_arm.c b/system/embdrv/lc3/test/arm/test_arm.c
deleted file mode 100644
index 1e5c15b..0000000
--- a/system/embdrv/lc3/test/arm/test_arm.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <stdio.h>
-
-int check_ltpf(void);
-
-int main()
-{
-    int r, ret = 0;
-
-    printf("Checking LTPF ARM... "); fflush(stdout);
-    printf("%s\n", (r = check_ltpf()) == 0 ? "OK" : "Failed");
-    ret = ret || r;
-
-    return ret;
-}
diff --git a/system/embdrv/lc3/test/attdet.py b/system/embdrv/lc3/test/attdet.py
deleted file mode 100644
index c318e32..0000000
--- a/system/embdrv/lc3/test/attdet.py
+++ /dev/null
@@ -1,185 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-
-import build.lc3 as lc3
-import tables as T, appendix_c as C
-
-
-### ------------------------------------------------------------------------ ###
-
-class AttackDetector:
-
-    def __init__(self, dt, sr):
-
-        self.dt = dt
-        self.sr = sr
-        self.ms = T.DT_MS[dt]
-
-        self.xn1 = 0
-        self.xn2 = 0
-        self.en1 = 0
-        self.an1 = 0
-        self.p_att = 0
-
-    def is_enabled(self, nbytes):
-
-        c1 = self.dt == T.DT_10M and \
-             self.sr == T.SRATE_32K and nbytes > 80
-
-        c2 = self.dt == T.DT_10M and \
-             self.sr >= T.SRATE_48K and nbytes >= 100
-
-        c3 = self.dt == T.DT_7M5 and \
-             self.sr == T.SRATE_32K and nbytes >= 61 and nbytes < 150
-
-        c4 = self.dt == T.DT_7M5 and \
-             self.sr >= T.SRATE_48K and nbytes >= 75 and nbytes < 150
-
-        return c1 or c2 or c3 or c4
-
-    def run(self, nbytes, x):
-
-        ### 3.3.6.2 Downsampling and filtering input
-
-        mf = int(16 * self.ms)
-
-        r = len(x) // mf
-        x_att = np.array([ np.sum(x[i*r:(i+1)*r]) for i in range(mf) ])
-
-        x_hp = np.empty(mf)
-        x_hp[0 ] = 0.375 * x_att[0 ] - 0.5 * self.xn1    + 0.125 * self.xn2
-        x_hp[1 ] = 0.375 * x_att[1 ] - 0.5 * x_att[0   ] + 0.125 * self.xn1
-        x_hp[2:] = 0.375 * x_att[2:] - 0.5 * x_att[1:-1] + 0.125 * x_att[0:-2]
-        self.xn2 = x_att[-2]
-        self.xn1 = x_att[-1]
-
-        ### 3.3.6.3 Energy calculation
-
-        nb = int(self.ms / 2.5)
-
-        e_att = np.array([ np.sum(np.square(x_hp[40*i:40*(i+1)]))
-                           for i in range(nb) ])
-
-        a_att = np.empty(nb)
-        a_att[0] = np.maximum(0.25 * self.an1, self.en1)
-        for i in range(1,nb):
-            a_att[i] = np.maximum(0.25 * a_att[i-1], e_att[i-1])
-        self.en1 = e_att[-1]
-        self.an1 = a_att[-1]
-
-        ### 3.3.6.4 Attack Detection
-
-        p_att = -1
-        flags = [ (e_att[i] > 8.5 * a_att[i]) for i in range(nb) ]
-
-        for (i, f) in enumerate(flags):
-            if f: p_att = i
-
-        f_att = p_att >= 0 or self.p_att - 1 >= nb // 2
-        self.p_att = 1 + p_att
-
-        return self.is_enabled(nbytes) and f_att
-
-
-def initial_state():
-    return { 'en1': 0.0, 'an1': 0.0, 'p_att': 0 }
-
-### ------------------------------------------------------------------------ ###
-
-def check_enabling(rng, dt):
-
-    ok = True
-
-    for sr in range(T.SRATE_16K, T.NUM_SRATE):
-
-        attdet = AttackDetector(dt, sr)
-
-        for nbytes in [ 61, 61-1, 75-1, 75, 80, 80+1, 100-1, 100, 150-1, 150 ]:
-
-            f_att = lc3.attdet_run(dt, sr, nbytes,
-                initial_state(), 2 * rng.random(T.NS[dt][sr]+6) - 1)
-
-            ok = ok and f_att == attdet.is_enabled(nbytes)
-
-    return ok
-
-def check_unit(rng, dt, sr):
-
-    ns = T.NS[dt][sr]
-    ok = True
-
-    attdet = AttackDetector(dt, sr)
-
-    state_c = initial_state()
-    x_c = np.zeros(ns+6)
-
-    for run in range(100):
-
-        ### Generate noise, and an attack at random point
-
-        x = ((2 * rng.random(ns)) - 1) * (2 ** 8 - 1)
-        x[(ns * rng.random()).astype(int)] *= 2 ** 7
-
-        ### Check Implementation
-
-        f_att = attdet.run(100, x)
-
-        x_c = np.append(x_c[-6:], x)
-        f_att_c = lc3.attdet_run(dt, sr, 100, state_c, x_c)
-
-        ok = ok and f_att_c == f_att
-        ok = ok and np.amax(np.abs(1 - state_c['en1']/attdet.en1)) < 2
-        ok = ok and np.amax(np.abs(1 - state_c['an1']/attdet.an1)) < 2
-        ok = ok and state_c['p_att'] == attdet.p_att
-
-    return ok
-
-def check_appendix_c(dt):
-
-    sr = T.SRATE_48K
-
-    state = initial_state()
-
-    x = np.append(np.zeros(6), C.X_PCM_ATT[dt][0])
-    f_att = lc3.attdet_run(dt, sr, C.NBYTES_ATT[dt], state, x)
-    ok = f_att == C.F_ATT[dt][0]
-
-    x = np.append(x[-6:], C.X_PCM_ATT[dt][1])
-    f_att = lc3.attdet_run(dt, sr, C.NBYTES_ATT[dt], state, x)
-    ok = f_att == C.F_ATT[dt][1]
-
-    return ok
-
-def check():
-
-    rng = np.random.default_rng(1234)
-    ok = True
-
-    for dt in range(T.NUM_DT):
-        ok and check_enabling(rng, dt)
-
-    for dt in range(T.NUM_DT):
-        for sr in range(T.SRATE_32K, T.NUM_SRATE):
-            ok = ok and check_unit(rng, dt, sr)
-
-    for dt in range(T.NUM_DT):
-        ok = ok and check_appendix_c(dt)
-
-    return ok
-
-### ------------------------------------------------------------------------ ###
diff --git a/system/embdrv/lc3/test/attdet_py.c b/system/embdrv/lc3/test/attdet_py.c
deleted file mode 100644
index d85a8a5..0000000
--- a/system/embdrv/lc3/test/attdet_py.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <Python.h>
-#include <numpy/ndarrayobject.h>
-
-#include <attdet.c>
-#include "ctypes.h"
-
-static PyObject *attdet_run_py(PyObject *m, PyObject *args)
-{
-    unsigned dt, sr, nbytes;
-    PyObject *attdet_obj, *x_obj;
-    struct lc3_attdet_analysis attdet = { 0 };
-    int16_t *x;
-
-    if (!PyArg_ParseTuple(args, "IIIOO",
-                &dt, &sr, &nbytes, &attdet_obj, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-    CTYPES_CHECK(NULL, attdet_obj = to_attdet_analysis(attdet_obj, &attdet));
-
-    int ns = LC3_NS(dt, sr);
-
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_INT16, ns+6, &x));
-
-    int att = lc3_attdet_run(dt, sr, nbytes, &attdet, x+6);
-
-    from_attdet_analysis(attdet_obj, &attdet);
-    return Py_BuildValue("i", att);
-}
-
-static PyMethodDef methods[] = {
-    { "attdet_run", attdet_run_py, METH_VARARGS },
-    { NULL },
-};
-
-PyMODINIT_FUNC lc3_attdet_py_init(PyObject *m)
-{
-    import_array();
-
-    PyModule_AddFunctions(m, methods);
-
-    return m;
-}
diff --git a/system/embdrv/lc3/test/bitstream.py b/system/embdrv/lc3/test/bitstream.py
deleted file mode 100644
index 11edbd5..0000000
--- a/system/embdrv/lc3/test/bitstream.py
+++ /dev/null
@@ -1,240 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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 math
-
-class Bitstream:
-
-    def __init__(self, data):
-
-        self.bytes = data
-
-        self.bp_bw = len(data) - 1
-        self.mask_bw = 1
-
-        self.bp = 0
-        self.low = 0
-        self.range = 0xffffff
-
-    def dump(self):
-
-        b = self.bytes
-
-        for i in range(0, len(b), 20):
-            print(''.join('{:02x} '.format(x)
-                for x in b[i:min(i+20, len(b))] ))
-
-class BitstreamReader(Bitstream):
-
-    def __init__(self, data):
-
-        super().__init__(data)
-
-        self.low = ( (self.bytes[0] << 16) |
-                     (self.bytes[1] <<  8) |
-                     (self.bytes[2]      ) )
-        self.bp = 3
-
-    def read_bit(self):
-
-        bit = bool(self.bytes[self.bp_bw] & self.mask_bw)
-
-        self.mask_bw <<= 1
-        if self.mask_bw == 0x100:
-            self.mask_bw = 1
-            self.bp_bw -= 1
-
-        return bit
-
-    def read_uint(self, nbits):
-
-        val = 0
-        for k in range(nbits):
-            val |= self.read_bit() << k
-
-        return val
-
-    def ac_decode(self, cum_freqs, sym_freqs):
-
-        r = self.range >> 10
-        if self.low >= r << 10:
-            raise ValueError('Invalid ac bitstream')
-
-        val = len(cum_freqs) - 1
-        while self.low < r * cum_freqs[val]:
-            val -= 1
-
-        self.low -= r * cum_freqs[val]
-        self.range = r * sym_freqs[val]
-        while self.range < 0x10000:
-            self.range <<= 8
-
-            self.low <<= 8
-            self.low &= 0xffffff
-            self.low += self.bytes[self.bp]
-            self.bp += 1
-
-        return val
-
-    def get_bits_left(self):
-
-        nbits = 8 * len(self.bytes)
-
-        nbits_bw = nbits - \
-            (8*self.bp_bw + 8 - int(math.log2(self.mask_bw)))
-
-        nbits_ac = 8 * (self.bp - 3) + \
-            (25 - int(math.floor(math.log2(self.range))))
-
-        return nbits - (nbits_bw + nbits_ac)
-
-class BitstreamWriter(Bitstream):
-
-    def __init__(self, nbytes):
-
-        super().__init__(bytearray(nbytes))
-
-        self.cache = -1
-        self.carry = 0
-        self.carry_count = 0
-
-    def write_bit(self, bit):
-
-        mask = self.mask_bw
-        bp = self.bp_bw
-
-        if bit == 0:
-            self.bytes[bp] &= ~mask
-        else:
-            self.bytes[bp] |= mask
-
-        self.mask_bw <<= 1
-        if self.mask_bw == 0x100:
-            self.mask_bw = 1
-            self.bp_bw -= 1
-
-    def write_uint(self, val, nbits):
-
-        for k in range(nbits):
-            self.write_bit(val & 1)
-            val >>= 1
-
-    def ac_shift(self):
-
-        if self.low < 0xff0000 or self.carry == 1:
-
-            if self.cache >= 0:
-                self.bytes[self.bp] = self.cache + self.carry
-                self.bp += 1
-
-            while self.carry_count > 0:
-                self.bytes[self.bp] = (self.carry + 0xff) & 0xff
-                self.bp += 1
-                self.carry_count -= 1
-
-            self.cache = self.low >> 16
-            self.carry = 0
-
-        else:
-            self.carry_count += 1
-
-        self.low <<= 8
-        self.low &= 0xffffff
-
-    def ac_encode(self, cum_freq, sym_freq):
-
-        r = self.range >> 10
-        self.low += r * cum_freq
-        if (self.low >> 24) != 0:
-            self.carry = 1
-
-        self.low &= 0xffffff
-        self.range = r * sym_freq
-        while self.range < 0x10000:
-            self.range <<= 8;
-            self.ac_shift()
-
-    def get_bits_left(self):
-
-        nbits = 8 * len(self.bytes)
-
-        nbits_bw = nbits - \
-            (8*self.bp_bw + 8 - int(math.log2(self.mask_bw)))
-
-        nbits_ac = 8 * self.bp + (25 - int(math.floor(math.log2(self.range))))
-        if self.cache >= 0:
-            nbits_ac += 8
-        if self.carry_count > 0:
-            nbits_ac += 8 * self.carry_count
-
-        return nbits - (nbits_bw + nbits_ac)
-
-    def terminate(self):
-
-        bits = 1
-        while self.range >> (24 - bits) == 0:
-            bits += 1
-
-        mask = 0xffffff >> bits;
-        val = self.low + mask;
-
-        over1 = val >> 24
-        val &= 0x00ffffff
-        high = self.low + self.range
-        over2 = high >> 24
-        high &= 0x00ffffff
-        val = val & ~mask
-
-        if over1 == over2:
-
-            if val + mask >= high:
-                bits += 1
-                mask >>= 1
-                val = ((self.low + mask) & 0x00ffffff) & ~mask
-
-            if val < self.low:
-                self.carry = 1
-
-        self.low = val
-        while bits > 0:
-            self.ac_shift()
-            bits -= 8
-        bits += 8;
-
-        val = self.cache
-
-        if self.carry_count > 0:
-            self.bytes[self.bp] = self.cache
-            self.bp += 1
-
-            while self.carry_count > 1:
-                self.bytes[self.bp] = 0xff
-                self.bp += 1
-                self.carry_count -= 1
-
-            val = 0xff >> (8 - bits)
-
-        mask = 0x80;
-        for k in range(bits):
-
-            if val & mask == 0:
-                self.bytes[self.bp] &= ~mask
-            else:
-                self.bytes[self.bp] |= mask
-
-            mask >>= 1
-
-        return self.bytes
diff --git a/system/embdrv/lc3/test/bwdet.py b/system/embdrv/lc3/test/bwdet.py
deleted file mode 100644
index 2f55c87..0000000
--- a/system/embdrv/lc3/test/bwdet.py
+++ /dev/null
@@ -1,162 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-
-import build.lc3 as lc3
-import tables as T, appendix_c as C
-
-
-BW_START = [
-    [ [], [ 51 ], [ 45, 58 ], [ 42, 53, 60 ], [ 40, 51, 57, 61 ] ],
-    [ [], [ 53 ], [ 47, 59 ], [ 44, 54, 60 ], [ 41, 51, 57, 61 ] ]
-]
-
-BW_STOP = [
-    [ [], [ 63 ], [ 55, 63 ], [ 51, 58, 63 ], [ 48, 55, 60, 63 ] ],
-    [ [], [ 63 ], [ 56, 63 ], [ 52, 59, 63 ], [ 49, 55, 60, 63 ] ]
-]
-
-TQ = [ 20, 10, 10, 10 ]
-
-TC = [ 15, 23, 20, 20 ]
-L  = [ [ 4, 4, 3, 2 ], [ 4, 4, 3, 1 ] ]
-
-
-### ------------------------------------------------------------------------ ###
-
-class BandwidthDetector:
-
-    def __init__(self, dt, sr):
-
-        self.dt = dt
-        self.sr = sr
-
-    def run(self, e):
-
-        dt = self.dt
-        sr = self.sr
-
-        ### Stage 1, determine bw0 candidate
-
-        bw0 = 0
-
-        for bw in range(sr):
-            i0 = BW_START[dt][sr][bw]
-            i1 = BW_STOP[dt][sr][bw]
-            if np.mean(e[i0:i1+1]) >= TQ[bw]:
-                bw0 = bw + 1
-
-        ### Stage 2, Cut-off random coefficients at each steps
-
-        bw = bw0
-
-        if bw0 < sr:
-            l  = L[dt][bw0]
-            i0 = BW_START[dt][sr][bw0] - l
-            i1 = BW_START[dt][sr][bw0]
-
-            c = 10 * np.log10(1e-31 + e[i0-l+1:i1-l+2] / e[i0+1:i1+2])
-            if np.amax(c) <= TC[bw0]:
-                bw = sr
-
-        self.bw = bw
-        return self.bw
-
-    def get_nbits(self):
-
-        return 0 if self.sr == 0 else \
-               1 + np.log2(self.sr).astype(int)
-
-    def store_bw(self, b):
-
-        b.write_uint(self.bw, self.get_nbits())
-
-    def get_bw(self, b):
-
-        return b.read_uint(self.get_nbits())
-
-### ------------------------------------------------------------------------ ###
-
-def check_unit(rng, dt, sr):
-
-    ok = True
-
-    bwdet = BandwidthDetector(dt, sr)
-
-    for bw0 in range(sr+1):
-        for drop in range(10):
-
-            ### Generate random 'high' energy and
-            ### scale relevant bands to select 'bw0'
-
-            e = 20 + 100 * rng.random(64)
-
-            for i in range(sr):
-                if i+1 != bw0:
-                    i0 = BW_START[dt][sr][i]
-                    i1 = BW_STOP[dt][sr][i]
-                    e[i0:i1+1] /= (np.mean(e[i0:i1+1]) / TQ[i] + 1e-3)
-
-            ### Stage 2 Condition,
-            ### cut-off random coefficients at each steps
-
-            if bw0 < sr:
-                l  = L[dt][bw0]
-                i0 = BW_START[dt][sr][bw0] - l
-                i1 = BW_START[dt][sr][bw0]
-
-                e[i0-l+1:i1+2] /= np.power(10, np.arange(2*l+1) / (1 + drop))
-
-            ### Check with implementation
-
-            bw_c = lc3.bwdet_run(dt, sr, e)
-
-            ok = ok and bw_c == bwdet.run(e)
-
-    return ok
-
-def check_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ok = True
-
-    E_B  = C.E_B[dt]
-    P_BW = C.P_BW[dt]
-
-    bw = lc3.bwdet_run(dt, sr, E_B[0])
-    ok = ok and bw == P_BW[0]
-
-    bw = lc3.bwdet_run(dt, sr, E_B[1])
-    ok = ok and bw == P_BW[1]
-
-    return ok
-
-def check():
-
-    rng = np.random.default_rng(1234)
-
-    ok = True
-    for dt in range(T.NUM_DT):
-        for sr in range(T.NUM_SRATE):
-            ok = ok and check_unit(rng, dt, sr)
-
-    for dt in range(T.NUM_DT):
-        ok = ok and check_appendix_c(dt)
-
-    return ok
-
-### ------------------------------------------------------------------------ ###
diff --git a/system/embdrv/lc3/test/bwdet_py.c b/system/embdrv/lc3/test/bwdet_py.c
deleted file mode 100644
index c2decb3..0000000
--- a/system/embdrv/lc3/test/bwdet_py.c
+++ /dev/null
@@ -1,55 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <Python.h>
-#include <numpy/ndarrayobject.h>
-
-#include <bwdet.c>
-#include "ctypes.h"
-
-static PyObject *bwdet_run_py(PyObject *m, PyObject *args)
-{
-    unsigned dt, sr;
-    PyObject *e_obj;
-    float *e;
-
-    if (!PyArg_ParseTuple(args, "IIO", &dt, &sr, &e_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-    CTYPES_CHECK("e", to_1d_ptr(e_obj, NPY_FLOAT, LC3_NUM_BANDS, &e));
-
-    int bw = lc3_bwdet_run(dt, sr, e);
-
-    return Py_BuildValue("i", bw);
-}
-
-static PyMethodDef methods[] = {
-    { "bwdet_run", bwdet_run_py, METH_VARARGS },
-    { NULL },
-};
-
-PyMODINIT_FUNC lc3_bwdet_py_init(PyObject *m)
-{
-    import_array();
-
-    PyModule_AddFunctions(m, methods);
-
-    return m;
-}
diff --git a/system/embdrv/lc3/test/ctypes.h b/system/embdrv/lc3/test/ctypes.h
deleted file mode 100644
index 97a3add..0000000
--- a/system/embdrv/lc3/test/ctypes.h
+++ /dev/null
@@ -1,886 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#ifndef __CTYPES_H
-#define __CTYPES_H
-
-#include <Python.h>
-#include <numpy/ndarrayobject.h>
-
-#include <stdbool.h>
-
-
-#define CTYPES_CHECK(exc, t) \
-    do { \
-        if (!(t)) return (exc) ? PyErr_Format(PyExc_TypeError, exc) : NULL; \
-    } while(0)
-
-
-/**
- * From C types to Numpy Array types
- */
-
-#define to_scalar(obj, t, ptr) \
-    __to_scalar(obj, t, (void *)(ptr))
-
-#define to_1d_ptr(obj, t, n, ptr) \
-    __to_1d_ptr(obj, t, n, (void **)(ptr))
-
-#define to_2d_ptr(obj, t, n1, n2, ptr) \
-    __to_2d_ptr(obj, t, n1, n2, (void **)(ptr))
-
-#define to_1d_copy(obj, t, ptr, n) \
-    __to_1d_copy(obj, t, ptr, n)
-
-#define to_2d_copy(obj, t, ptr, n1, n2) \
-    __to_2d_copy(obj, t, ptr, n1, n2)
-
-
-/**
- * From Numpy Array types to C types
- */
-
-#define new_scalar(obj, ptr) \
-    __new_scalar(obj, ptr)
-
-#define new_1d_ptr(t, n, ptr) \
-    __new_1d_ptr(t, n, (void **)(ptr))
-
-#define new_2d_ptr(t, n1, n2, ptr) \
-    __new_2d_ptr(t, n1, n2, (void **)(ptr))
-
-#define new_1d_copy(t, n, src) \
-    __new_1d_copy(t, n, src)
-
-#define new_2d_copy(t, n1, n2, src) \
-    __new_2d_copy(t, n1, n2, src)
-
-
-/* -------------------------------------------------------------------------- */
-
-__attribute__((unused))
-static PyObject *__to_scalar(PyObject *obj, int t, void *ptr)
-{
-    obj = obj ? PyArray_FROMANY(obj, t, 0, 0, NPY_ARRAY_FORCECAST) : obj;
-    if (!obj)
-        return NULL;
-
-    memcpy(ptr, PyArray_DATA((PyArrayObject *)obj),
-        PyArray_NBYTES((PyArrayObject *)obj));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *__to_1d_ptr(PyObject *obj, int t, int n, void **ptr)
-{
-    obj = obj ? PyArray_FROMANY(obj,
-        t, 1, 1, NPY_ARRAY_FORCECAST|NPY_ARRAY_CARRAY) : obj;
-    if (!obj || (n && PyArray_SIZE((PyArrayObject *)obj) != n))
-        return NULL;
-
-    *ptr = PyArray_DATA((PyArrayObject *)obj);
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *__to_2d_ptr(PyObject *obj, int t, int n1, int n2, void **ptr)
-{
-    obj = obj ? PyArray_FROMANY(obj,
-        t, 2, 2, NPY_ARRAY_FORCECAST|NPY_ARRAY_CARRAY) : obj;
-    if (!obj || (n1 && PyArray_DIMS((PyArrayObject *)obj)[0] != n1)
-             || (n2 && PyArray_DIMS((PyArrayObject *)obj)[1] != n2))
-        return NULL;
-
-    *ptr = PyArray_DATA((PyArrayObject *)obj);
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *__to_1d_copy(PyObject *obj, int t, void *v, int n)
-{
-    void *src;
-
-    if ((obj = to_1d_ptr(obj, t, n, &src)))
-        memcpy(v, src, PyArray_NBYTES((PyArrayObject *)obj));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *__to_2d_copy(PyObject *obj, int t, void *v, int n1, int n2)
-{
-    void *src;
-
-    if ((obj = to_2d_ptr(obj, t, n1, n2, &src)))
-        memcpy(v, src, PyArray_NBYTES((PyArrayObject *)obj));
-
-    return obj;
-}
-
-/* -------------------------------------------------------------------------- */
-
-__attribute__((unused))
-static PyObject *__new_scalar(int t, const void *ptr)
-{
-    PyObject *obj = PyArray_SimpleNew(0, NULL, t);
-
-    memcpy(PyArray_DATA((PyArrayObject *)obj), ptr,
-        PyArray_NBYTES((PyArrayObject *)obj));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *__new_1d_ptr(int t, int n, void **ptr)
-{
-    PyObject *obj = PyArray_SimpleNew(1, (const npy_intp []){ n }, t);
-
-    *ptr = PyArray_DATA((PyArrayObject *)obj);
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *__new_2d_ptr(int t, int n1, int n2, void **ptr)
-{
-    PyObject *obj;
-
-    obj = PyArray_SimpleNew(2, ((const npy_intp []){ n1, n2 }), t);
-
-    *ptr = PyArray_DATA((PyArrayObject *)obj);
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *__new_1d_copy(int t, int n, const void *src)
-{
-    PyObject *obj;
-    void *dst;
-
-    if ((obj = new_1d_ptr(t, n, &dst)))
-        memcpy(dst, src, PyArray_NBYTES((PyArrayObject *)obj));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *__new_2d_copy(int t, int n1, int n2, const void *src)
-{
-    PyObject *obj;
-    void *dst;
-
-    if ((obj = new_2d_ptr(t, n1, n2, &dst)))
-        memcpy(dst, src, PyArray_NBYTES((PyArrayObject *)obj));
-
-    return obj;
-}
-
-/* -------------------------------------------------------------------------- */
-
-#include <lc3.h>
-
-__attribute__((unused))
-static PyObject *to_attdet_analysis(
-    PyObject *obj, struct lc3_attdet_analysis *attdet)
-{
-    CTYPES_CHECK("attdet", obj && PyDict_Check(obj));
-
-    CTYPES_CHECK("attdet.en1", to_scalar(
-        PyDict_GetItemString(obj, "en1"), NPY_INT32, &attdet->en1));
-
-    CTYPES_CHECK("attdet.an1", to_scalar(
-        PyDict_GetItemString(obj, "an1"), NPY_INT32, &attdet->an1));
-
-    CTYPES_CHECK("attdet.p_att", to_scalar(
-        PyDict_GetItemString(obj, "p_att"), NPY_INT, &attdet->p_att));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *from_attdet_analysis(
-    PyObject *obj, const struct lc3_attdet_analysis *attdet)
-{
-    if (!obj) obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "en1",
-        new_scalar(NPY_INT32, &attdet->en1));
-
-    PyDict_SetItemString(obj, "an1",
-        new_scalar(NPY_INT32, &attdet->an1));
-
-    PyDict_SetItemString(obj, "p_att",
-        new_scalar(NPY_INT, &attdet->p_att));
-
-    return obj;
-}
-
-/* -------------------------------------------------------------------------- */
-
-#include <ltpf.h>
-
-__attribute__((unused))
-static PyObject *to_ltpf_hp50_state(
-    PyObject *obj, struct lc3_ltpf_hp50_state *hp50)
-{
-    CTYPES_CHECK("hp50", obj && PyDict_Check(obj));
-
-    CTYPES_CHECK("hp50.s1", to_scalar(
-        PyDict_GetItemString(obj, "s1"), NPY_INT64, &hp50->s1));
-
-    CTYPES_CHECK("hp50.s2", to_scalar(
-        PyDict_GetItemString(obj, "s2"), NPY_INT64, &hp50->s2));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *from_ltpf_hp50_state(
-    PyObject *obj, const struct lc3_ltpf_hp50_state *hp50)
-{
-    PyDict_SetItemString(obj, "s1",
-        new_scalar(NPY_INT64, &hp50->s1));
-
-    PyDict_SetItemString(obj, "s2",
-        new_scalar(NPY_INT64, &hp50->s2));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_ltpf_analysis(
-    PyObject *obj, struct lc3_ltpf_analysis *ltpf)
-{
-    PyObject *nc_obj, *x_12k8_obj, *x_6k4_obj;
-    const int n_12k8 = sizeof(ltpf->x_12k8) / sizeof(*ltpf->x_12k8);
-    const int n_6k4 = sizeof(ltpf->x_6k4) / sizeof(*ltpf->x_6k4);
-
-    CTYPES_CHECK("ltpf", obj && PyDict_Check(obj));
-
-    CTYPES_CHECK("ltpf.active", to_scalar(
-        PyDict_GetItemString(obj, "active"), NPY_BOOL, &ltpf->active));
-
-    CTYPES_CHECK("ltpf.pitch", to_scalar(
-        PyDict_GetItemString(obj, "pitch"), NPY_INT, &ltpf->pitch));
-
-    CTYPES_CHECK("ltpf.nc", nc_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "nc"), NPY_FLOAT, ltpf->nc, 2));
-    PyDict_SetItemString(obj, "nc", nc_obj);
-
-    CTYPES_CHECK(NULL, to_ltpf_hp50_state(
-        PyDict_GetItemString(obj, "hp50"), &ltpf->hp50));
-
-    CTYPES_CHECK("ltpf.x_12k8", x_12k8_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "x_12k8"), NPY_INT16, ltpf->x_12k8, n_12k8));
-    PyDict_SetItemString(obj, "x_12k8", x_12k8_obj);
-
-    CTYPES_CHECK("ltpf.x_6k4", x_6k4_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "x_6k4"), NPY_INT16, ltpf->x_6k4, n_6k4));
-    PyDict_SetItemString(obj, "x_6k4", x_6k4_obj);
-
-    CTYPES_CHECK("ltpf.tc", to_scalar(
-        PyDict_GetItemString(obj, "tc"), NPY_INT, &ltpf->tc));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *from_ltpf_analysis(
-    PyObject *obj, const struct lc3_ltpf_analysis *ltpf)
-{
-    const int n_12k8 = sizeof(ltpf->x_12k8) / sizeof(*ltpf->x_12k8);
-    const int n_6k4 = sizeof(ltpf->x_6k4) / sizeof(*ltpf->x_6k4);
-
-    if (!obj) obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "active",
-        new_scalar(NPY_BOOL, &ltpf->active));
-
-    PyDict_SetItemString(obj, "pitch",
-        new_scalar(NPY_INT, &ltpf->pitch));
-
-    PyDict_SetItemString(obj, "nc",
-        new_1d_copy(NPY_FLOAT, 2, &ltpf->nc));
-
-    PyDict_SetItemString(obj, "hp50",
-        from_ltpf_hp50_state(PyDict_New(), &ltpf->hp50));
-
-    PyDict_SetItemString(obj, "x_12k8",
-        new_1d_copy(NPY_INT16, n_12k8, &ltpf->x_12k8));
-
-    PyDict_SetItemString(obj, "x_6k4",
-        new_1d_copy(NPY_INT16, n_6k4, &ltpf->x_6k4));
-
-    PyDict_SetItemString(obj, "tc",
-        new_scalar(NPY_INT, &ltpf->tc));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_ltpf_synthesis(
-    PyObject *obj, struct lc3_ltpf_synthesis *ltpf)
-{
-    PyObject *c_obj, *x_obj;
-
-    CTYPES_CHECK("ltpf", obj && PyDict_Check(obj));
-
-    CTYPES_CHECK("ltpf.active", to_scalar(
-        PyDict_GetItemString(obj, "active"), NPY_BOOL, &ltpf->active));
-
-    CTYPES_CHECK("ltpf.pitch", to_scalar(
-        PyDict_GetItemString(obj, "pitch"), NPY_INT, &ltpf->pitch));
-
-    CTYPES_CHECK("ltpf.c", c_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "c"), NPY_FLOAT, ltpf->c, 2*12));
-    PyDict_SetItemString(obj, "c", c_obj);
-
-    CTYPES_CHECK("ltpf.x", x_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "x"), NPY_FLOAT, ltpf->x, 12));
-    PyDict_SetItemString(obj, "x", x_obj);
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *from_ltpf_synthesis(
-    PyObject *obj, const struct lc3_ltpf_synthesis *ltpf)
-{
-    if (!obj) obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "active",
-        new_scalar(NPY_BOOL, &ltpf->active));
-
-    PyDict_SetItemString(obj, "pitch",
-        new_scalar(NPY_INT, &ltpf->pitch));
-
-    PyDict_SetItemString(obj, "c",
-        new_1d_copy(NPY_FLOAT, 2*12, &ltpf->c));
-
-    PyDict_SetItemString(obj, "x",
-        new_1d_copy(NPY_FLOAT, 12, &ltpf->x));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *new_ltpf_data(const struct lc3_ltpf_data *data)
-{
-    PyObject *obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "active",
-        new_scalar(NPY_BOOL, &data->active));
-
-    PyDict_SetItemString(obj, "pitch_index",
-        new_scalar(NPY_INT, &data->pitch_index));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_ltpf_data(
-     PyObject *obj, const struct lc3_ltpf_data *data)
-{
-    PyObject *item;
-
-    CTYPES_CHECK("ltpf", obj && PyDict_Check(obj));
-
-    if ((item = PyDict_GetItemString(obj, "active")))
-        CTYPES_CHECK("ltpf.active",
-            to_scalar(item, NPY_BOOL, &data->active));
-
-    if ((item = PyDict_GetItemString(obj, "pitch_index")))
-        CTYPES_CHECK("ltpf.pitch_index",
-            to_scalar(item, NPY_INT, &data->pitch_index));
-
-    return obj;
-}
-
-/* -------------------------------------------------------------------------- */
-
-#include <sns.h>
-
-__attribute__((unused))
-static PyObject *new_sns_data(const struct lc3_sns_data *data)
-{
-    PyObject *obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "lfcb",
-        new_scalar(NPY_INT, &data->lfcb));
-
-    PyDict_SetItemString(obj, "hfcb",
-        new_scalar(NPY_INT, &data->hfcb));
-
-    PyDict_SetItemString(obj, "shape",
-        new_scalar(NPY_INT, &data->shape));
-
-    PyDict_SetItemString(obj, "gain",
-        new_scalar(NPY_INT, &data->gain));
-
-    PyDict_SetItemString(obj, "idx_a",
-        new_scalar(NPY_INT, &data->idx_a));
-
-    PyDict_SetItemString(obj, "ls_a",
-        new_scalar(NPY_BOOL, &data->ls_a));
-
-    PyDict_SetItemString(obj, "idx_b",
-        new_scalar(NPY_INT, &data->idx_b));
-
-    PyDict_SetItemString(obj, "ls_b",
-        new_scalar(NPY_BOOL, &data->ls_b));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_sns_data(PyObject *obj, struct lc3_sns_data *data)
-{
-    PyObject *item;
-
-    CTYPES_CHECK("sns", obj && PyDict_Check(obj));
-
-    if ((item = PyDict_GetItemString(obj, "lfcb")))
-        CTYPES_CHECK("sns.lfcb", to_scalar(item, NPY_INT, &data->lfcb));
-
-    if ((item = PyDict_GetItemString(obj, "hfcb")))
-        CTYPES_CHECK("sns.hfcb", to_scalar(item, NPY_INT, &data->hfcb));
-
-    if ((item = PyDict_GetItemString(obj, "shape")))
-        CTYPES_CHECK("sns.shape", to_scalar(item, NPY_INT, &data->shape));
-
-    if ((item = PyDict_GetItemString(obj, "gain")))
-        CTYPES_CHECK("sns.gain", to_scalar(item, NPY_INT, &data->gain));
-
-    if ((item = PyDict_GetItemString(obj, "idx_a")))
-        CTYPES_CHECK("sns.idx_a", to_scalar(item, NPY_INT, &data->idx_a));
-
-    if ((item = PyDict_GetItemString(obj, "ls_a")))
-        CTYPES_CHECK("sns.ls_a", to_scalar(item, NPY_INT, &data->ls_a));
-
-    if ((item = PyDict_GetItemString(obj, "idx_b")))
-        CTYPES_CHECK("sns.idx_b", to_scalar(item, NPY_INT, &data->idx_b));
-
-    if ((item = PyDict_GetItemString(obj, "ls_b")))
-        CTYPES_CHECK("sns.ls_b", to_scalar(item, NPY_INT, &data->ls_b));
-
-    return obj;
-}
-
-/* -------------------------------------------------------------------------- */
-
-#include <tns.h>
-
-__attribute__((unused))
-static PyObject *new_tns_data(const struct lc3_tns_data *side)
-{
-    PyObject *obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "nfilters",
-        new_scalar(NPY_INT, &side->nfilters));
-
-    PyDict_SetItemString(obj, "lpc_weighting",
-        new_scalar(NPY_BOOL, &side->lpc_weighting));
-
-    PyDict_SetItemString(obj, "rc_order",
-        new_1d_copy(NPY_INT, 2, side->rc_order));
-
-    PyDict_SetItemString(obj, "rc",
-        new_2d_copy(NPY_INT, 2, 8, side->rc));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_tns_data(PyObject *obj, struct lc3_tns_data *side)
-{
-    PyObject *item;
-
-    CTYPES_CHECK("tns", obj && PyDict_Check(obj));
-
-    if ((item = PyDict_GetItemString(obj, "nfilters")))
-        CTYPES_CHECK("tns.nfilters",
-            to_scalar(item, NPY_INT, &side->nfilters));
-
-    if ((item = PyDict_GetItemString(obj, "lpc_weighting"))) {
-        CTYPES_CHECK("tns.lpc_weighting",
-            to_scalar(item, NPY_BOOL, &side->lpc_weighting));
-    }
-
-    if ((item = PyDict_GetItemString(obj, "rc_order"))) {
-        CTYPES_CHECK("tns.rc_order",
-            item = to_1d_copy(item, NPY_INT, side->rc_order, 2));
-        PyDict_SetItemString(obj, "rc_order", item);
-    }
-
-    if ((item = PyDict_GetItemString(obj, "rc"))) {
-        CTYPES_CHECK("tns.rc",
-            item = to_2d_copy(item, NPY_INT, side->rc, 2, 8));
-        PyDict_SetItemString(obj, "rc", item);
-    }
-
-    return obj;
-}
-
-/* -------------------------------------------------------------------------- */
-
-#include <spec.h>
-
-__attribute__((unused))
-static PyObject *from_spec_analysis(
-    PyObject *obj, const struct lc3_spec_analysis *spec)
-{
-    if (!obj) obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "nbits_off",
-        new_scalar(NPY_FLOAT, &spec->nbits_off));
-
-    PyDict_SetItemString(obj, "nbits_spare",
-        new_scalar(NPY_INT, &spec->nbits_spare));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_spec_analysis(
-    PyObject *obj, struct lc3_spec_analysis *spec)
-{
-    CTYPES_CHECK("spec", obj && PyDict_Check(obj));
-
-    CTYPES_CHECK("spec.nbits_off",
-        to_scalar(PyDict_GetItemString(obj, "nbits_off"),
-            NPY_FLOAT, &spec->nbits_off));
-
-    CTYPES_CHECK("spec.nbits_spare",
-        to_scalar(PyDict_GetItemString(obj, "nbits_spare"),
-            NPY_INT, &spec->nbits_spare));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *new_spec_side(const struct lc3_spec_side *side)
-{
-    PyObject *obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "g_idx",
-        new_scalar(NPY_INT, &side->g_idx));
-
-    PyDict_SetItemString(obj, "nq",
-        new_scalar(NPY_INT, &side->nq));
-
-    PyDict_SetItemString(obj, "lsb_mode",
-        new_scalar(NPY_BOOL, &side->lsb_mode));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_spec_data(
-    PyObject *obj, struct lc3_spec_side *side)
-{
-    PyObject *item;
-
-    CTYPES_CHECK("side", obj && PyDict_Check(obj));
-
-    if ((item = PyDict_GetItemString(obj, "g_idx")))
-        CTYPES_CHECK("side.g_idx",
-            to_scalar(item, NPY_INT, &side->g_idx));
-
-    if ((item = PyDict_GetItemString(obj, "nq")))
-        CTYPES_CHECK("side.nq",
-            to_scalar(item, NPY_INT, &side->nq));
-
-    if ((item = PyDict_GetItemString(obj, "lsb_mode")))
-        CTYPES_CHECK("side.lsb_mode",
-            to_scalar(item, NPY_BOOL, &side->lsb_mode));
-
-    return obj;
-}
-
-/* -------------------------------------------------------------------------- */
-
-#ifdef __CTYPES_LC3_C
-
-__attribute__((unused))
-static PyObject *new_side_data(const struct side_data *side)
-{
-    PyObject *obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "bw",
-        new_scalar(NPY_INT, &(int){ side->bw }));
-
-    PyDict_SetItemString(obj, "ltpf",
-        new_ltpf_data(&side->ltpf));
-
-    PyDict_SetItemString(obj, "sns",
-        new_sns_data(&side->sns));
-
-    PyDict_SetItemString(obj, "tns",
-        new_tns_data(&side->tns));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_side_data(PyObject *obj, struct side_data *side)
-{
-    PyObject *item;
-
-    CTYPES_CHECK("frame", obj && PyDict_Check(obj));
-
-    if ((item = PyDict_GetItemString(obj, "bw"))) {
-        int bw;
-        CTYPES_CHECK("frame.bw", to_scalar(item, NPY_INT, &bw));
-        side->bw = bw;
-    }
-
-    if ((item = PyDict_GetItemString(obj, "ltpf")))
-        to_ltpf_data(item, &side->ltpf);
-
-    if ((item = PyDict_GetItemString(obj, "sns")))
-        to_sns_data(item, &side->sns);
-
-    if ((item = PyDict_GetItemString(obj, "tns")))
-        to_tns_data(item, &side->tns);
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *new_plc_state(const struct lc3_plc_state *plc)
-{
-    PyObject *obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "seed",
-        new_scalar(NPY_UINT16, &plc->seed));
-
-    PyDict_SetItemString(obj, "count",
-        new_scalar(NPY_INT, &plc->count));
-
-    PyDict_SetItemString(obj, "alpha",
-        new_scalar(NPY_FLOAT, &plc->alpha));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_plc_state(
-    PyObject *obj, struct lc3_plc_state *plc)
-{
-    CTYPES_CHECK("plc", obj && PyDict_Check(obj));
-
-    CTYPES_CHECK("plc.seed", to_scalar(
-        PyDict_GetItemString(obj, "seed"), NPY_UINT16, &plc->seed));
-
-    CTYPES_CHECK("plc.count", to_scalar(
-        PyDict_GetItemString(obj, "count"), NPY_INT, &plc->count));
-
-    CTYPES_CHECK("plc.alpha", to_scalar(
-        PyDict_GetItemString(obj, "alpha"), NPY_FLOAT, &plc->alpha));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *from_encoder(PyObject *obj, const struct lc3_encoder *enc)
-{
-    unsigned dt = enc->dt, sr = enc->sr;
-    unsigned sr_pcm = enc->sr_pcm;
-    int ns = LC3_NS(dt, sr);
-    int nd = LC3_ND(dt, sr);
-    int nt = LC3_NT(sr);
-
-    if (!obj) obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "dt",
-        new_scalar(NPY_INT, &dt));
-
-    PyDict_SetItemString(obj, "sr",
-        new_scalar(NPY_INT, &sr));
-
-    PyDict_SetItemString(obj, "sr_pcm",
-        new_scalar(NPY_INT, &sr_pcm));
-
-    PyDict_SetItemString(obj, "attdet",
-        from_attdet_analysis(NULL, &enc->attdet));
-
-    PyDict_SetItemString(obj, "ltpf",
-        from_ltpf_analysis(NULL, &enc->ltpf));
-
-    PyDict_SetItemString(obj, "quant",
-        from_spec_analysis(NULL, &enc->spec));
-
-    PyDict_SetItemString(obj, "xt",
-        new_1d_copy(NPY_INT16, nt+ns, enc->xt-nt));
-
-    PyDict_SetItemString(obj, "xs",
-        new_1d_copy(NPY_FLOAT, ns, enc->xs));
-
-    PyDict_SetItemString(obj, "xd",
-        new_1d_copy(NPY_FLOAT, nd, enc->xd));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_encoder(PyObject *obj, struct lc3_encoder *enc)
-{
-    unsigned dt, sr, sr_pcm;
-    PyObject *xt_obj, *xs_obj, *xd_obj;
-
-    CTYPES_CHECK("encoder", obj && PyDict_Check(obj));
-
-    CTYPES_CHECK("encoder.dt", to_scalar(
-        PyDict_GetItemString(obj, "dt"), NPY_INT, &dt));
-    CTYPES_CHECK("encoder.dt", (unsigned)(enc->dt = dt) < LC3_NUM_DT);
-
-    CTYPES_CHECK("encoder.sr", to_scalar(
-        PyDict_GetItemString(obj, "sr"), NPY_INT, &sr));
-    CTYPES_CHECK("encoder.sr", (unsigned)(enc->sr = sr) < LC3_NUM_SRATE);
-
-    CTYPES_CHECK("encoder.sr_pcm", to_scalar(
-        PyDict_GetItemString(obj, "sr_pcm"), NPY_INT, &sr_pcm));
-    CTYPES_CHECK("encoder.s_pcmr",
-        (unsigned)(enc->sr_pcm = sr_pcm) < LC3_NUM_SRATE);
-
-    int ns = LC3_NS(dt, sr);
-    int nd = LC3_ND(dt, sr);
-    int nt = LC3_NT(sr);
-
-    CTYPES_CHECK(NULL, to_attdet_analysis(
-        PyDict_GetItemString(obj, "attdet"), &enc->attdet));
-
-    CTYPES_CHECK(NULL, to_ltpf_analysis(
-        PyDict_GetItemString(obj, "ltpf"), &enc->ltpf));
-
-    CTYPES_CHECK(NULL, to_spec_analysis(
-        PyDict_GetItemString(obj, "quant"), &enc->spec));
-
-    CTYPES_CHECK("encoder.xt", xt_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "xt"), NPY_INT16, enc->xt-nt, ns+nt));
-    PyDict_SetItemString(obj, "xt", xt_obj);
-
-    CTYPES_CHECK("encoder.xs", xs_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "xs"), NPY_FLOAT, enc->xs, ns));
-    PyDict_SetItemString(obj, "xs", xs_obj);
-
-    CTYPES_CHECK("encoder.xd", xd_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "xd"), NPY_FLOAT, enc->xd, nd));
-    PyDict_SetItemString(obj, "xd", xd_obj);
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *from_decoder(PyObject *obj, const struct lc3_decoder *dec)
-{
-    unsigned dt = dec->dt, sr = dec->sr;
-    unsigned sr_pcm = dec->sr_pcm;
-    unsigned xs_pos = dec->xs - dec->xh;
-    int nh = LC3_NH(dt, sr);
-    int ns = LC3_NS(dt, sr);
-    int nd = LC3_ND(dt, sr);
-
-    if (!obj) obj = PyDict_New();
-
-    PyDict_SetItemString(obj, "dt",
-        new_scalar(NPY_INT, &dt));
-
-    PyDict_SetItemString(obj, "sr",
-        new_scalar(NPY_INT, &sr));
-
-    PyDict_SetItemString(obj, "sr_pcm",
-        new_scalar(NPY_INT, &sr_pcm));
-
-    PyDict_SetItemString(obj, "ltpf",
-        from_ltpf_synthesis(NULL, &dec->ltpf));
-
-    PyDict_SetItemString(obj, "plc",
-        new_plc_state(&dec->plc));
-
-    PyDict_SetItemString(obj, "xh",
-        new_1d_copy(NPY_FLOAT, nh, dec->xh));
-
-    PyDict_SetItemString(obj, "xs_pos",
-        new_scalar(NPY_INT, &xs_pos));
-
-    PyDict_SetItemString(obj, "xd",
-        new_1d_copy(NPY_FLOAT, nd, dec->xd));
-
-    PyDict_SetItemString(obj, "xg",
-        new_1d_copy(NPY_FLOAT, ns, dec->xg));
-
-    return obj;
-}
-
-__attribute__((unused))
-static PyObject *to_decoder(PyObject *obj, struct lc3_decoder *dec)
-{
-    unsigned dt, sr, sr_pcm, xs_pos;
-    PyObject *xh_obj, *xd_obj, *xg_obj;
-
-    CTYPES_CHECK("decoder", obj && PyDict_Check(obj));
-
-    CTYPES_CHECK("decoder.dt", to_scalar(
-        PyDict_GetItemString(obj, "dt"), NPY_INT, &dt));
-    CTYPES_CHECK("decoder.dt", (unsigned)(dec->dt = dt) < LC3_NUM_DT);
-
-    CTYPES_CHECK("decoder.sr", to_scalar(
-        PyDict_GetItemString(obj, "sr"), NPY_INT, &sr));
-    CTYPES_CHECK("decoder.sr", (unsigned)(dec->sr = sr) < LC3_NUM_SRATE);
-
-    CTYPES_CHECK("decoder.sr_pcm", to_scalar(
-        PyDict_GetItemString(obj, "sr_pcm"), NPY_INT, &sr_pcm));
-    CTYPES_CHECK("decoder.sr_pcm",
-        (unsigned)(dec->sr_pcm = sr_pcm) < LC3_NUM_SRATE);
-
-    int nh = LC3_NH(dt, sr);
-    int ns = LC3_NS(dt, sr);
-    int nd = LC3_ND(dt, sr);
-
-    CTYPES_CHECK(NULL, to_ltpf_synthesis(
-        PyDict_GetItemString(obj, "ltpf"), &dec->ltpf));
-
-    CTYPES_CHECK(NULL, to_plc_state(
-        PyDict_GetItemString(obj, "plc"), &dec->plc));
-
-    CTYPES_CHECK("decoder.xh", xh_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "xh"), NPY_FLOAT, dec->xh, nh));
-    PyDict_SetItemString(obj, "xh", xh_obj);
-
-    CTYPES_CHECK("decoder.xs", to_scalar(
-        PyDict_GetItemString(obj, "xs_pos"), NPY_INT, &xs_pos));
-    dec->xs = dec->xh + xs_pos;
-
-    CTYPES_CHECK("decoder.xd", xd_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "xd"), NPY_FLOAT, dec->xd, nd));
-    PyDict_SetItemString(obj, "xd", xd_obj);
-
-    CTYPES_CHECK("decoder.xg", xg_obj = to_1d_copy(
-        PyDict_GetItemString(obj, "xg"), NPY_FLOAT, dec->xg, ns));
-    PyDict_SetItemString(obj, "xg", xg_obj);
-
-    return obj;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-#endif /* __CTYPES_LC3_C */
-
-#endif /* __CTYPES */
diff --git a/system/embdrv/lc3/test/decoder.py b/system/embdrv/lc3/test/decoder.py
deleted file mode 100755
index 93ed918..0000000
--- a/system/embdrv/lc3/test/decoder.py
+++ /dev/null
@@ -1,200 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-import scipy.signal as signal
-import scipy.io.wavfile as wavfile
-import struct
-import argparse
-
-import build.lc3 as lc3
-import tables as T, appendix_c as C
-
-import mdct, energy, bwdet, sns, tns, spec, ltpf
-import bitstream
-
-### ------------------------------------------------------------------------ ###
-
-class Decoder:
-
-    def __init__(self, dt_ms, sr_hz):
-
-        dt = { 7.5: T.DT_7M5, 10: T.DT_10M }[dt_ms]
-
-        sr = {  8000: T.SRATE_8K , 16000: T.SRATE_16K, 24000: T.SRATE_24K,
-               32000: T.SRATE_32K, 48000: T.SRATE_48K }[sr_hz]
-
-        self.sr = sr
-        self.ne = T.NE[dt][sr]
-        self.ns = T.NS[dt][sr]
-
-        self.mdct = mdct.MdctInverse(dt, sr)
-
-        self.bwdet = bwdet.BandwidthDetector(dt, sr)
-        self.spec = spec.SpectrumSynthesis(dt, sr)
-        self.tns = tns.TnsSynthesis(dt)
-        self.sns = sns.SnsSynthesis(dt, sr)
-        self.ltpf = ltpf.LtpfSynthesis(dt, sr)
-
-    def decode(self, data):
-
-        b = bitstream.BitstreamReader(data)
-
-        bw = self.bwdet.get_bw(b)
-        if bw > self.sr:
-            raise ValueError('Invalid bandwidth indication')
-
-        self.spec.load(b)
-
-        self.tns.load(b, bw, len(data))
-
-        pitch = b.read_bit()
-
-        self.sns.load(b)
-
-        if pitch:
-            self.ltpf.load(b)
-        else:
-            self.ltpf.disable()
-
-        x = self.spec.decode(b, bw, len(data))
-
-        return (x, bw, pitch)
-
-    def synthesize(self, x, bw, pitch, nbytes):
-
-        x = self.tns.run(x, bw)
-
-        x = self.sns.run(x)
-
-        x = np.append(x, np.zeros(self.ns - self.ne))
-        x = self.mdct.run(x)
-
-        x = self.ltpf.run(x, len(data))
-
-        return x
-
-    def run(self, data):
-
-        (x, bw, pitch) = self.decode(data)
-
-        x = self.synthesize(x, bw, pitch, len(data))
-
-        return x
-
-### ------------------------------------------------------------------------ ###
-
-def check_appendix_c(dt):
-
-    ok = True
-
-    dec_c = lc3.setup_decoder(int(T.DT_MS[dt] * 1000), 16000)
-
-    for i in range(len(C.BYTES_AC[dt])):
-
-        pcm = lc3.decode(dec_c, bytes(C.BYTES_AC[dt][i]))
-        ok = ok and np.max(np.abs(pcm - C.X_HAT_CLIP[dt][i])) < 1
-
-    return ok
-
-def check():
-
-    ok = True
-
-    for dt in range(T.NUM_DT):
-        ok = ok and check_appendix_c(dt)
-
-    return ok
-
-### ------------------------------------------------------------------------ ###
-
-if __name__ == "__main__":
-
-    parser = argparse.ArgumentParser(description='LC3 Decoder Test Framework')
-    parser.add_argument('lc3_file',
-        help='Input bitstream file', type=argparse.FileType('r'))
-    parser.add_argument('--pyout',
-        help='Python output file', type=argparse.FileType('w'))
-    parser.add_argument('--cout',
-        help='C output file', type=argparse.FileType('w'))
-    args = parser.parse_args()
-
-    ### File Header ###
-
-    f_lc3 = open(args.lc3_file.name, 'rb')
-
-    header = struct.unpack('=HHHHHHHI', f_lc3.read(18))
-
-    if header[0] != 0xcc1c:
-        raise ValueError('Invalid bitstream file')
-
-    if header[4] != 1:
-        raise ValueError('Unsupported number of channels')
-
-    sr_hz = header[2] * 100
-    bitrate = header[3] * 100
-    nchannels = header[4]
-    dt_ms = header[5] / 100
-
-    f_lc3.seek(header[1])
-
-    ### Setup ###
-
-    dec = Decoder(dt_ms, sr_hz)
-    dec_c = lc3.setup_decoder(int(dt_ms * 1000), sr_hz)
-
-    pcm_c  = np.empty(0).astype(np.int16)
-    pcm_py = np.empty(0).astype(np.int16)
-
-    ### Decoding loop ###
-
-    nframes = 0
-
-    while True:
-
-        data = f_lc3.read(2)
-        if len(data) != 2:
-            break
-
-        if nframes >= 1000:
-            break
-
-        (frame_nbytes,) = struct.unpack('=H', data)
-
-        print('Decoding frame %d' % nframes, end='\r')
-
-        data = f_lc3.read(frame_nbytes)
-
-        x = dec.run(data)
-        pcm_py = np.append(pcm_py,
-            np.clip(np.round(x), -32768, 32767).astype(np.int16))
-
-        x_c = lc3.decode(dec_c, data)
-        pcm_c = np.append(pcm_c, x_c)
-
-        nframes += 1
-
-    print('done ! %16s' % '')
-
-    ### Terminate ###
-
-    if args.pyout:
-        wavfile.write(args.pyout.name, sr_hz, pcm_py)
-    if args.cout:
-        wavfile.write(args.cout.name, sr_hz, pcm_c)
-
-### ------------------------------------------------------------------------ ###
diff --git a/system/embdrv/lc3/test/encoder.py b/system/embdrv/lc3/test/encoder.py
deleted file mode 100755
index 0382b96..0000000
--- a/system/embdrv/lc3/test/encoder.py
+++ /dev/null
@@ -1,213 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-import scipy.signal as signal
-import scipy.io.wavfile as wavfile
-import struct
-import argparse
-
-import build.lc3 as lc3
-import tables as T, appendix_c as C
-
-import attdet, ltpf
-import mdct, energy, bwdet, sns, tns, spec
-import bitstream
-
-### ------------------------------------------------------------------------ ###
-
-class Encoder:
-
-    def __init__(self, dt_ms, sr_hz):
-
-        dt = { 7.5: T.DT_7M5, 10: T.DT_10M }[dt_ms]
-
-        sr = {  8000: T.SRATE_8K , 16000: T.SRATE_16K, 24000: T.SRATE_24K,
-               32000: T.SRATE_32K, 48000: T.SRATE_48K }[sr_hz]
-
-        self.ne = T.NE[dt][sr]
-
-        self.attdet = attdet.AttackDetector(dt, sr)
-        self.ltpf = ltpf.Ltpf(dt, sr)
-
-        self.mdct = mdct.Mdct(dt, sr)
-        self.energy = e_energy.EnergyBand(dt, sr)
-        self.bwdet = bwdet.BandwidthDetector(dt, sr)
-        self.sns = sns.SnsAnalysis(dt, sr)
-        self.tns = tns.TnsAnalysis(dt)
-        self.spec = spec.SpectrumEncoder(dt, sr)
-
-    def analyse(self, x, nbytes):
-
-        att = self.attdet.run(nbytes, x)
-
-        pitch_present = self.ltpf.run(x)
-
-        x = self.mdct.forward(x)[:self.ne]
-
-        (e, nn_flag) = self.energy.compute(x)
-        if nn_flag:
-            self.ltpf.disable()
-
-        bw = self.bwdet.run(e)
-
-        x = self.sns.run(e, att, x)
-
-        x = self.tns.run(x, bw, nn_flag, nbytes)
-
-        (xq, lastnz, x) = self.spec.quantize(bw, nbytes,
-            self.bwdet.get_nbits(), self.ltpf.get_nbits(),
-            self.sns.get_nbits(), self.tns.get_nbits(), x)
-
-        return pitch_present
-
-    def encode(self, pitch_present, nbytes):
-
-        b = bitstream.BitstreamWriter(nbytes)
-
-        self.bwdet.store(b)
-
-        self.spec.store(b)
-
-        self.tns.store(b)
-
-        b.write_bit(pitch_present)
-
-        self.sns.store(b)
-
-        if pitch_present:
-            self.ltpf.store_data(b)
-
-        self.spec.encode(b)
-
-        return b.terminate()
-
-    def run(self, x, nbytes):
-
-        pitch_present = self.analyse(x, nbytes)
-
-        data = self.encode(pitch_present, nbytes)
-
-        return data
-
-### ------------------------------------------------------------------------ ###
-
-def check_appendix_c(dt):
-
-    ok = True
-
-    enc_c = lc3.setup_encoder(int(T.DT_MS[dt] * 1000), 16000)
-
-    for i in range(len(C.X_PCM[dt])):
-
-        data = lc3.encode(enc_c, C.X_PCM[dt][i], C.NBYTES[dt])
-        ok = ok and data == C.BYTES_AC[dt][i]
-        if not ok:
-            dump(data)
-            dump(C.BYTES_AC[dt][i])
-
-    return ok
-
-def check():
-
-    ok = True
-
-    for dt in range(T.NUM_DT):
-        ok = ok and check_appendix_c(dt)
-
-    return ok
-
-### ------------------------------------------------------------------------ ###
-
-def dump(data):
-    for i in range(0, len(data), 20):
-        print(''.join('{:02x} '.format(x)
-            for x in data[i:min(i+20, len(data))] ))
-
-if __name__ == "__main__":
-
-    parser = argparse.ArgumentParser(description='LC3 Encoder Test Framework')
-    parser.add_argument('wav_file',
-        help='Input wave file', type=argparse.FileType('r'))
-    parser.add_argument('--bitrate',
-        help='Bitrate in bps', type=int, required=True)
-    parser.add_argument('--dt',
-        help='Frame duration in ms', type=float, default=10)
-    parser.add_argument('--pyout',
-        help='Python output file', type=argparse.FileType('w'))
-    parser.add_argument('--cout',
-        help='C output file', type=argparse.FileType('w'))
-    args = parser.parse_args()
-
-    if args.bitrate < 16000 or args.bitrate > 320000:
-        raise ValueError('Invalid bitate %d bps' % args.bitrate)
-
-    if args.dt not in (7.5, 10):
-        raise ValueError('Invalid frame duration %.1f ms' % args.dt)
-
-    (sr_hz, pcm) = wavfile.read(args.wav_file.name)
-    if sr_hz not in (8000, 16000, 24000, 320000, 48000):
-        raise ValueError('Unsupported input samplerate: %d' % sr_hz)
-
-    ### Setup ###
-
-    enc = Encoder(args.dt, sr_hz)
-    enc_c = lc3.setup_encoder(int(args.dt * 1000), sr_hz)
-
-    frame_samples = int((args.dt * sr_hz) / 1000)
-    frame_nbytes = int((args.bitrate * args.dt) / (1000 * 8))
-
-    ### File Header ###
-
-    f_py = open(args.pyout.name, 'wb') if args.pyout else None
-    f_c  = open(args.cout.name , 'wb') if args.cout  else None
-
-    header = struct.pack('=HHHHHHHI', 0xcc1c, 18,
-        sr_hz // 100, args.bitrate // 100, 1, int(args.dt * 100), 0, len(pcm))
-
-    for f in (f_py, f_c):
-        if f: f.write(header)
-
-    ### Encoding loop ###
-
-    if len(pcm) % frame_samples > 0:
-        pcm = np.append(pcm, np.zeros(frame_samples - (len(pcm) % frame_samples)))
-
-    for i in range(0, len(pcm), frame_samples):
-
-        print('Encoding frame %d' % (i // frame_samples), end='\r')
-
-        frame_pcm = pcm[i:i+frame_samples]
-
-        data = enc.run(frame_pcm, frame_nbytes)
-        data_c = lc3.encode(enc_c, frame_pcm, frame_nbytes)
-
-        for f in (f_py, f_c):
-            if f: f.write(struct.pack('=H', frame_nbytes))
-
-        if f_py: f_py.write(data)
-        if f_c: f_c.write(data_c)
-
-    print('done ! %16s' % '')
-
-    ### Terminate ###
-
-    for f in (f_py, f_c):
-        if f: f.close()
-
-
-### ------------------------------------------------------------------------ ###
diff --git a/system/embdrv/lc3/test/energy.py b/system/embdrv/lc3/test/energy.py
deleted file mode 100644
index 13c6828..0000000
--- a/system/embdrv/lc3/test/energy.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-
-import build.lc3 as lc3
-import tables as T, appendix_c as C
-
-### ------------------------------------------------------------------------ ###
-
-class EnergyBand:
-
-    def __init__(self, dt, sr):
-
-        self.dt = dt
-        self.I = T.I[dt][sr]
-
-    def compute(self, x):
-
-        e = [ np.mean(np.square(x[self.I[i]:self.I[i+1]]))
-               for i in range(len(self.I)-1) ]
-
-        e_lo = np.sum(e[:len(e) - [4, 2][self.dt]])
-        e_hi = np.sum(e[len(e) - [4, 2][self.dt]:])
-
-        return np.append(e, np.zeros(64-len(e))), (e_hi > 30*e_lo)
-
-### ------------------------------------------------------------------------ ###
-
-def check_unit(rng, dt, sr):
-
-    ns = T.NS[dt][sr]
-    ok = True
-
-    nrg = EnergyBand(dt, sr)
-
-    x = (2 * rng.random(T.NS[dt][sr])) - 1
-
-    (e  , nn  ) = nrg.compute(x)
-    (e_c, nn_c) = lc3.energy_compute(dt, sr, x)
-    ok = ok and np.amax(np.abs(e_c - e)) < 1e-5 and nn_c == nn
-
-    x[15*ns//16:] *= 1e2;
-
-    (e  , nn  ) = nrg.compute(x)
-    (e_c, nn_c) = lc3.energy_compute(dt, sr, x)
-    ok = ok and np.amax(np.abs(e_c - e)) < 1e-3 and nn_c == nn
-
-    return ok
-
-def check_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ok = True
-
-    e  = lc3.energy_compute(dt, sr, C.X[dt][0])[0]
-    ok = ok and np.amax(np.abs(1 - e/C.E_B[dt][0])) < 1e-6
-
-    e  = lc3.energy_compute(dt, sr, C.X[dt][1])[0]
-    ok = ok and np.amax(np.abs(1 - e/C.E_B[dt][1])) < 1e-6
-
-    return ok
-
-def check():
-
-    rng = np.random.default_rng(1234)
-
-    ok = True
-
-    for dt in range(T.NUM_DT):
-        for sr in range(T.NUM_SRATE):
-            ok = ok and check_unit(rng, dt, sr)
-
-    for dt in range(T.NUM_DT):
-        ok = ok and check_appendix_c(dt)
-
-    return ok
-
-### ------------------------------------------------------------------------ ###
diff --git a/system/embdrv/lc3/test/energy_py.c b/system/embdrv/lc3/test/energy_py.c
deleted file mode 100644
index 3b65ba5..0000000
--- a/system/embdrv/lc3/test/energy_py.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "lc3.h"
-
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
-#include <numpy/ndarrayobject.h>
-
-#include <energy.c>
-#include "ctypes.h"
-
-static PyObject *energy_compute_py(PyObject *m, PyObject *args)
-{
-    unsigned dt, sr;
-    PyObject *x_obj, *e_obj;
-    float *x, *e;
-
-    if (!PyArg_ParseTuple(args, "IIO", &dt, &sr, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-
-    int ns = LC3_NS(dt, sr);
-
-    CTYPES_CHECK("x", to_1d_ptr(x_obj, NPY_FLOAT, ns, &x));
-    e_obj = new_1d_ptr(NPY_FLOAT, LC3_NUM_BANDS, &e);
-
-    int nn_flag = lc3_energy_compute(dt, sr, x, e);
-
-    return Py_BuildValue("Ni", e_obj, nn_flag);
-}
-
-static PyMethodDef methods[] = {
-    { "energy_compute", energy_compute_py, METH_VARARGS },
-    { NULL },
-};
-
-PyMODINIT_FUNC lc3_energy_py_init(PyObject *m)
-{
-    import_array();
-
-    PyModule_AddFunctions(m, methods);
-
-    return m;
-}
diff --git a/system/embdrv/lc3/test/lc3_py.c b/system/embdrv/lc3/test/lc3_py.c
deleted file mode 100644
index 2984beb..0000000
--- a/system/embdrv/lc3/test/lc3_py.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "lc3.h"
-
-#define PY_SSIZE_T_CLEAN
-#include <Python.h>
-#include <numpy/ndarrayobject.h>
-
-#include <lc3.c>
-
-#define __CTYPES_LC3_C
-#include "ctypes.h"
-
-static PyObject *setup_encoder_py(PyObject *m, PyObject *args)
-{
-    int dt_us, sr_hz;
-
-    if (!PyArg_ParseTuple(args, "ii", &dt_us, &sr_hz))
-        return NULL;
-
-    CTYPES_CHECK("dt_us", LC3_CHECK_DT_US(dt_us));
-    CTYPES_CHECK("sr_hz", LC3_CHECK_SR_HZ(sr_hz));
-
-    lc3_encoder_t encoder = lc3_setup_encoder(dt_us, sr_hz, 0,
-            malloc(lc3_encoder_size(dt_us, sr_hz)));
-
-    PyObject *encoder_obj = from_encoder(NULL, encoder);
-
-    free(encoder);
-
-    return Py_BuildValue("N", encoder_obj);
-}
-
-static PyObject *encode_py(PyObject *m, PyObject *args)
-{
-    PyObject *encoder_obj, *pcm_obj;
-    int nbytes;
-    int16_t *pcm;
-
-    if (!PyArg_ParseTuple(args, "OOi", &encoder_obj, &pcm_obj, &nbytes))
-        return NULL;
-
-    lc3_encoder_t encoder =
-        lc3_setup_encoder(10000, 48000, 0, &(lc3_encoder_mem_48k_t){ });
-
-    CTYPES_CHECK(NULL, encoder_obj = to_encoder(encoder_obj, encoder));
-
-    int ns = LC3_NS(encoder->dt, encoder->sr);
-
-    CTYPES_CHECK("x", pcm_obj = to_1d_ptr(pcm_obj, NPY_INT16, ns, &pcm));
-    CTYPES_CHECK("nbytes", nbytes >= 20 && nbytes <= 400);
-
-    uint8_t out[nbytes];
-
-    lc3_encode(encoder, LC3_PCM_FORMAT_S16, pcm, 1, nbytes, out);
-
-    from_encoder(encoder_obj, encoder);
-
-    return Py_BuildValue("N",
-        PyBytes_FromStringAndSize((const char *)out, nbytes));
-}
-
-static PyObject *setup_decoder_py(PyObject *m, PyObject *args)
-{
-    int dt_us, sr_hz;
-
-    if (!PyArg_ParseTuple(args, "ii", &dt_us, &sr_hz))
-        return NULL;
-
-    CTYPES_CHECK("dt_us", LC3_CHECK_DT_US(dt_us));
-    CTYPES_CHECK("sr_hz", LC3_CHECK_SR_HZ(sr_hz));
-
-    lc3_decoder_t decoder = lc3_setup_decoder(dt_us, sr_hz, 0,
-            malloc(lc3_decoder_size(dt_us, sr_hz)));
-
-    PyObject *decoder_obj = from_decoder(NULL, decoder);
-
-    free(decoder);
-
-    return Py_BuildValue("N", decoder_obj);
-}
-
-static PyObject *decode_py(PyObject *m, PyObject *args)
-{
-    PyObject *decoder_obj, *pcm_obj, *in_obj;
-    int16_t *pcm;
-
-    if (!PyArg_ParseTuple(args, "OO", &decoder_obj, &in_obj))
-        return NULL;
-
-    CTYPES_CHECK("in", in_obj == Py_None || PyBytes_Check(in_obj));
-
-    char *in = in_obj == Py_None ? NULL : PyBytes_AsString(in_obj);
-    int nbytes = in_obj == Py_None ? 0 : PyBytes_Size(in_obj);
-
-    lc3_decoder_t decoder =
-        lc3_setup_decoder(10000, 48000, 0, &(lc3_decoder_mem_48k_t){ });
-
-    CTYPES_CHECK(NULL, decoder_obj = to_decoder(decoder_obj, decoder));
-
-    int ns = LC3_NS(decoder->dt, decoder->sr);
-    pcm_obj = new_1d_ptr(NPY_INT16, ns, &pcm);
-
-    lc3_decode(decoder, in, nbytes, LC3_PCM_FORMAT_S16, pcm, 1);
-
-    from_decoder(decoder_obj, decoder);
-
-    return Py_BuildValue("N", pcm_obj);
-}
-
-static PyMethodDef methods[] = {
-    { "setup_encoder"      , setup_encoder_py      , METH_VARARGS },
-    { "encode"             , encode_py             , METH_VARARGS },
-    { "setup_decoder"      , setup_decoder_py      , METH_VARARGS },
-    { "decode"             , decode_py             , METH_VARARGS },
-    { NULL },
-};
-
-PyMODINIT_FUNC lc3_interface_py_init(PyObject *m)
-{
-    import_array();
-
-    PyModule_AddFunctions(m, methods);
-
-    return m;
-}
diff --git a/system/embdrv/lc3/test/ltpf.py b/system/embdrv/lc3/test/ltpf.py
deleted file mode 100644
index 1a852c8..0000000
--- a/system/embdrv/lc3/test/ltpf.py
+++ /dev/null
@@ -1,660 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-import scipy.signal as signal
-
-import build.lc3 as lc3
-import tables as T, appendix_c as C
-
-### ------------------------------------------------------------------------ ###
-
-class Resampler_12k8:
-
-    def __init__(self, dt, sr, history = 0):
-
-        self.sr = sr
-        self.p = 192 // T.SRATE_KHZ[sr]
-        self.w = 240 // self.p
-
-        self.n = ((T.DT_MS[dt] * 128) / 10).astype(int)
-        self.d = [ 44, 24 ][dt]
-
-        self.x = np.zeros(self.w + T.NS[dt][sr])
-        self.u = np.zeros(self.n + 2)
-        self.y = np.zeros(self.n + self.d + history)
-
-    def resample(self, x):
-
-        p = self.p
-        w = self.w
-        d = self.d
-        n = self.n
-
-        ### Sliding window
-
-        self.x[:w] = self.x[-w:]
-        self.x[w:] = x
-        self.u[:2] = self.u[-2:]
-
-        if len(self.y) > 2*n + d:
-            self.y[n+d:-n] = self.y[d+2*n:]
-        if len(self.y) > n + d:
-            self.y[-n:] = self.y[:n]
-        self.y[:d] = self.y[n:d+n]
-
-        x = self.x
-        u = self.u
-
-        ### 3.3.9.3 Resampling
-
-        h = np.zeros(240 + p)
-        h[-119:] = T.LTPF_H12K8[:119]
-        h[ :120] = T.LTPF_H12K8[119:]
-
-        for i in range(n):
-            e = (15 * i) // p
-            f = (15 * i)  % p
-            k = np.arange(-120, 120 + p, p) - f
-            u[2+i] = p * np.dot( x[e:e+w+1], np.take(h, k) )
-
-        if self.sr == T.SRATE_8K:
-            u = 0.5 * u
-
-        ### 3.3.9.4 High-pass filtering
-
-        b = [ 0.9827947082978771, -1.9655894165957540, 0.9827947082978771 ]
-        a = [ 1                 , -1.9652933726226904, 0.9658854605688177 ]
-
-        self.y[d:d+n] = b[0] * u[2:] + b[1] * u[1:-1] + b[2] * u[:-2]
-        for i in range(n):
-            self.y[d+i] -= a[1] * self.y[d+i-1] + a[2] * self.y[d+i-2]
-
-        return self.y
-
-
-class Resampler_6k4:
-
-    def __init__(self, n, history = 0):
-
-        self.x = np.zeros(n + 5)
-        self.n = n // 2
-
-        self.y = np.zeros(self.n + history)
-
-    def resample(self, x):
-
-        n = self.n
-
-        ### Sliding window
-
-        self.x[:3] = self.x[-5:-2]
-        self.x[3:] = x[:2*n+2]
-        x = self.x
-
-        if len(self.y) > 2*n:
-            self.y[n:-n] = self.y[2*n:]
-        if len(self.y) > n:
-            self.y[-n:] = self.y[:n]
-
-        ### 3.3.9.5 Downsampling to 6.4 KHz
-
-        h = [ 0.1236796411180537, 0.2353512128364889, 0.2819382920909148,
-              0.2353512128364889, 0.1236796411180537 ]
-
-        self.y[:n] = [ np.dot(x[2*i:2*i+5], h) for i in range(self.n) ]
-        return self.y
-
-
-def initial_hp50_state():
-    return { 's1': 0, 's2': 0 }
-
-### ------------------------------------------------------------------------ ###
-
-class Ltpf:
-
-    def __init__(self, dt, sr):
-
-        self.dt = dt
-        self.sr = sr
-
-        (self.pitch_present, self.pitch_index) = (None, None)
-
-    def get_data(self):
-
-        return { 'active' : self.active,
-                 'pitch_index' : self.pitch_index }
-
-    def get_nbits(self):
-
-        return 1 + 10 * int(self.pitch_present)
-
-
-class LtpfAnalysis(Ltpf):
-
-    def __init__(self, dt, sr):
-
-        super().__init__(dt, sr)
-
-        self.resampler_12k8 = Resampler_12k8(
-                dt, sr, history = 232)
-
-        self.resampler_6k4 = Resampler_6k4(
-                self.resampler_12k8.n, history = 114)
-
-        self.active = False
-        self.tc = 0
-        self.pitch = 0
-        self.nc = np.zeros(2)
-
-    def correlate(self, x, n, k0, k1):
-
-        return [ np.dot(x[:n], np.take(x, np.arange(n) - k)) \
-                    for k in range(k0, 1+k1) ]
-
-    def norm_corr(self, x, n, k):
-
-        u  = x[:n]
-        v  = np.take(x, np.arange(n) - k)
-        uv = np.dot(u, v)
-        return uv / np.sqrt(np.dot(u, u) * np.dot(v, v)) if uv > 0 else 0
-
-    def run(self, x):
-
-        ### 3.3.9.3-4 Resampling
-
-        x_12k8 = self.resampler_12k8.resample(x)
-
-        ### 3.3.9.5-6 Pitch detection algorithm
-
-        x = self.resampler_6k4.resample(x_12k8)
-        n = self.resampler_6k4.n
-
-        r  = self.correlate(x, n, 17, 114)
-        rw = r * (1 - 0.5 * np.arange(len(r)) / (len(r) - 1))
-
-        tc = self.tc
-        k0 = max(0, tc-4)
-        k1 = min(len(r)-1, tc+4)
-        t  = [ 17 + np.argmax(rw), 17 + k0 + np.argmax(r[k0:1+k1]) ]
-
-        nc = [ self.norm_corr(x, n, t[i]) for i in range(2) ]
-        ti = int(nc[1] > 0.85 * nc[0])
-        self.tc = t[ti] - 17
-
-        self.pitch_present = bool(nc[ti] > 0.6)
-
-        ### 3.3.9.7 Pitch-lag parameter
-
-        if self.pitch_present:
-            tc = self.tc + 17
-
-            x = x_12k8
-            n = self.resampler_12k8.n
-
-            k0 = max( 32, 2*tc-4)
-            k1 = min(228, 2*tc+4)
-            r  = self.correlate(x, n, k0-4, k1+4)
-            e  = k0 + np.argmax(r[4:-4])
-
-            h = np.zeros(42)
-            h[-15:] = T.LTPF_H4[:15]
-            h[ :16] = T.LTPF_H4[15:]
-
-            m = np.arange(-4, 5)
-            s = [ np.dot( np.take(r, e-k0+4 + m), np.take(h, 4*m-d) ) \
-                      for d in range(-3, 4) ]
-
-            f = np.argmax(s[3:])            if e <=  32 else \
-                -3 + np.argmax(s)           if e <  127 else \
-                -2 + 2*np.argmax(s[1:-1:2]) if e <  157 else 0
-
-            e -=   (f < 0)
-            f += 4*(f < 0)
-
-            self.pitch_index = 4*e + f    - 128 if e < 127 else \
-                               2*e + f//2 + 126 if e < 157 else e + 283
-
-        else:
-            e = f = 0
-            self.pitch_index = 0
-
-        ### 3.3.9.8 Activation bit
-
-        h = np.zeros(24)
-        h[-7:] = T.LTPF_HI[:7]
-        h[ :8] = T.LTPF_HI[7:]
-
-        k = np.arange(-2, 3)
-        u = [ np.dot( np.take(x, i-k), np.take(h, 4*k) ) \
-                  for i in range(n) ]
-        v = [ np.dot( np.take(x, i-k), np.take(h, 4*k-f) ) \
-                  for i in range(-e, n-e) ]
-
-        nc = max(0, np.dot(u, v)) / np.sqrt(np.dot(u, u) * np.dot(v, v)) \
-                if self.pitch_present else 0
-
-        pitch = e + f/4
-
-        if not self.active:
-            active = (self.dt == T.DT_10M or self.nc[1] > 0.94) \
-                     and self.nc[0] > 0.94 and nc > 0.94
-
-        else:
-            dp = abs(pitch - self.pitch)
-            dc = nc - self.nc[0]
-            active = nc > 0.9 or (dp < 2 and dc > -0.1 and nc > 0.84)
-
-        if not self.pitch_present:
-            active = False
-            pitch = 0
-            nc = 0
-
-        self.active = active
-        self.pitch = pitch
-        self.nc[1] = self.nc[0]
-        self.nc[0] = nc
-
-        return self.pitch_present
-
-    def disable(self):
-
-        self.active = False
-
-    def store(self, b):
-
-        b.write_uint(self.active, 1)
-        b.write_uint(self.pitch_index, 9)
-
-
-class LtpfSynthesis(Ltpf):
-
-    C_N = [ T.LTPF_N_8K , T.LTPF_N_16K,
-            T.LTPF_N_24K, T.LTPF_N_32K, T.LTPF_N_48K ]
-
-    C_D = [ T.LTPF_D_8K , T.LTPF_D_16K,
-            T.LTPF_D_24K, T.LTPF_D_32K, T.LTPF_D_48K ]
-
-    def __init__(self, dt, sr):
-
-        super().__init__(dt, sr)
-
-        self.C_N = LtpfSynthesis.C_N[sr]
-        self.C_D = LtpfSynthesis.C_D[sr]
-
-        ns = T.NS[dt][sr]
-
-        self.active = [ False, False ]
-        self.pitch_index = 0
-
-        max_pitch_12k8 = 228
-        max_pitch = max_pitch_12k8 * T.SRATE_KHZ[self.sr] / 12.8
-        max_pitch = np.ceil(max_pitch).astype(int)
-
-        self.x = np.zeros(ns)
-        self.y = np.zeros(max_pitch + len(self.C_D[0]))
-
-        self.p_e = [ 0, 0 ]
-        self.p_f = [ 0, 0 ]
-        self.c_n = [ None, None ]
-        self.c_d = [ None, None ]
-
-    def load(self, b):
-
-        self.active[0] = bool(b.read_uint(1))
-        self.pitch_index = b.read_uint(9)
-
-    def disable(self):
-
-        self.active[0] = False
-        self.pitch_index = 0
-
-    def run(self, x, nbytes):
-
-        sr = self.sr
-        dt = self.dt
-
-        ### 3.4.9.4 Filter parameters
-
-        pitch_index = self.pitch_index
-
-        if pitch_index >= 440:
-            p_e = pitch_index - 283
-            p_f = 0
-        elif pitch_index >= 380:
-            p_e = pitch_index // 2 - 63
-            p_f = 2*(pitch_index - 2*(p_e + 63))
-        else:
-            p_e = pitch_index // 4 + 32
-            p_f = pitch_index - 4*(p_e - 32)
-
-        p = (p_e + p_f / 4) * T.SRATE_KHZ[self.sr] / 12.8
-
-        self.p_e[0] = int(p * 4 + 0.5) // 4
-        self.p_f[0] = int(p * 4 + 0.5) - 4*self.p_e[0]
-
-        nbits = round(nbytes*80 / T.DT_MS[dt])
-        g_idx = max(nbits // 80, 3+sr) - (3+sr)
-
-        g = [ 0.4, 0.35, 0.3, 0.25 ][g_idx] if g_idx < 4 else 0
-        g_idx = min(g_idx, 3)
-
-        self.c_n[0] = 0.85 * g * LtpfSynthesis.C_N[sr][g_idx]
-        self.c_d[0] = g * LtpfSynthesis.C_D[sr][self.p_f[0]]
-
-        ### 3.4.9.2 Transition handling
-
-        n0 = (T.SRATE_KHZ[sr] * 1000) // 400
-        ns = T.NS[dt][sr]
-
-        x  = np.append(x, self.x)
-        y  = np.append(np.zeros(ns), self.y)
-        yc = y.copy()
-
-        c_n = self.c_n
-        c_d = self.c_d
-
-        l_n = len(c_n[0])
-        l_d = len(c_d[0])
-
-        d = [ self.p_e[0] - (l_d - 1) // 2,
-              self.p_e[1] - (l_d - 1) // 2 ]
-
-        for k in range(n0):
-
-            if not self.active[0] and not self.active[1]:
-                y[k] = x[k]
-
-            elif self.active[0] and not self.active[1]:
-                u = np.dot(c_n[0], np.take(x, k - np.arange(l_n))) - \
-                    np.dot(c_d[0], np.take(y, k - d[0] - np.arange(l_d)))
-                y[k] = x[k] - (k/n0) * u
-
-            elif not self.active[0] and self.active[1]:
-                u = np.dot(c_n[1], np.take(x, k - np.arange(l_n))) - \
-                    np.dot(c_d[1], np.take(y, k - d[1] - np.arange(l_d)))
-                y[k] = x[k] - (1 - k/n0) * u
-
-            elif self.p_e[0] == self.p_e[1] and self.p_f[0] == self.p_f[1]:
-                u = np.dot(c_n[0], np.take(x, k - np.arange(l_n))) - \
-                    np.dot(c_d[0], np.take(y, k - d[0] - np.arange(l_d)))
-                y[k] = x[k] - u
-
-            else:
-                u = np.dot(c_n[1], np.take(x, k - np.arange(l_n))) - \
-                    np.dot(c_d[1], np.take(y, k - d[1] - np.arange(l_d)))
-                yc[k] = x[k] - (1 - k/n0) * u
-
-                u = np.dot(c_n[0], np.take(yc, k - np.arange(l_n))) - \
-                    np.dot(c_d[0], np.take(y , k - d[0] - np.arange(l_d)))
-                y[k] = yc[k] - (k/n0) * u
-
-
-        ### 3.4.9.3 Remainder of the frame
-
-        for k in range(n0, ns):
-
-            if not self.active[0]:
-                y[k] = x[k]
-
-            else:
-                u = np.dot(c_n[0], np.take(x, k - np.arange(l_n))) - \
-                    np.dot(c_d[0], np.take(y, k - d[0] - np.arange(l_d)))
-                y[k] = x[k] - u
-
-        ### Sliding window
-
-        self.active[1] = self.active[0]
-        self.p_e[1] = self.p_e[0]
-        self.p_f[1] = self.p_f[0]
-        self.c_n[1] = self.c_n[0]
-        self.c_d[1] = self.c_d[0]
-
-        self.x = x[:ns]
-        self.y = np.append(self.y[ns:], y[:ns])
-
-        return y[:ns]
-
-def initial_state():
-    return { 'active' : False, 'pitch': 0, 'nc':  np.zeros(2),
-             'hp50' : initial_hp50_state(),
-             'x_12k8' : np.zeros(384), 'x_6k4' : np.zeros(178), 'tc' : 0 }
-
-def initial_sstate():
-    return { 'active': False, 'pitch': 0,
-             'c': np.zeros(2*12), 'x': np.zeros(12) }
-
-### ------------------------------------------------------------------------ ###
-
-def check_resampler(rng, dt, sr):
-
-    ns = T.NS[dt][sr]
-    nt = (5 * T.SRATE_KHZ[sr]) // 4
-    ok = True
-
-    r = Resampler_12k8(dt, sr)
-
-    hp50_c = initial_hp50_state()
-    x_c = np.zeros(nt)
-    y_c = np.zeros(384)
-
-    for run in range(10):
-
-        x = ((2 * rng.random(ns)) - 1) * (2 ** 15 - 1)
-        y = r.resample(x)
-
-        x_c = np.append(x_c[-nt:], x.astype(np.int16))
-        y_c[:-r.n] = y_c[r.n:]
-        y_c = lc3.ltpf_resample(dt, sr, hp50_c, x_c, y_c)
-
-        ok = ok and np.amax(np.abs(y_c[-r.d-r.n:] - y[:r.d+r.n]/2)) < 4
-
-    return ok
-
-def check_resampler_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ok = True
-
-    nt = (5 * T.SRATE_KHZ[sr]) // 4
-    n  = [ 96, 128 ][dt]
-    k  = [ 44,  24 ][dt] + n
-
-    state = initial_hp50_state()
-
-    x = np.append(np.zeros(nt), C.X_PCM[dt][0])
-    y = np.zeros(384)
-    y = lc3.ltpf_resample(dt, sr, state, x, y)
-    u = y[-k:len(C.X_TILDE_12K8D[dt][0])-k]
-
-    ok = ok and np.amax(np.abs(u - C.X_TILDE_12K8D[dt][0]/2)) < 2
-
-    x = np.append(x[-nt:], C.X_PCM[dt][1])
-    y[:-n] = y[n:]
-    y = lc3.ltpf_resample(dt, sr, state, x, y)
-    u = y[-k:len(C.X_TILDE_12K8D[dt][1])-k]
-
-    ok = ok and np.amax(np.abs(u - C.X_TILDE_12K8D[dt][1]/2)) < 2
-
-    return ok
-
-def check_analysis(rng, dt, sr):
-
-    ns = T.NS[dt][sr]
-    nt = (5 * T.SRATE_KHZ[sr]) // 4
-    ok = True
-
-    state_c = initial_state()
-    x_c = np.zeros(ns+nt)
-
-    ltpf = LtpfAnalysis(dt, sr)
-
-    t = np.arange(100 * ns) / (T.SRATE_KHZ[sr] * 1000)
-    s = signal.chirp(t, f0=10, f1=3e3, t1=t[-1], method='logarithmic')
-
-    for i in range(20):
-
-        x = s[i*ns:(i+1)*ns] * (2 ** 15 - 1)
-
-        pitch_present = ltpf.run(x)
-        data = ltpf.get_data()
-
-        x_c = np.append(x_c[-nt:], x.astype(np.int16))
-        (pitch_present_c, data_c) = lc3.ltpf_analyse(dt, sr, state_c, x_c)
-
-        ok = ok and (not pitch_present or state_c['tc'] == ltpf.tc)
-        ok = ok and np.amax(np.abs(state_c['nc'][0] - ltpf.nc[0])) < 1e-2
-        ok = ok and pitch_present_c == pitch_present
-        ok = ok and data_c['active'] == data['active']
-        ok = ok and data_c['pitch_index'] == data['pitch_index']
-        ok = ok and lc3.ltpf_get_nbits(pitch_present) == ltpf.get_nbits()
-
-    return ok
-
-def check_synthesis(rng, dt, sr):
-
-    ok = True
-
-    ns = T.NS[dt][sr]
-    nd = 18 * T.SRATE_KHZ[sr]
-
-    synthesis = LtpfSynthesis(dt, sr)
-
-    state_c = initial_sstate()
-    x_c = np.zeros(nd+ns)
-
-    for i in range(50):
-        pitch_present = bool(rng.integers(0, 10) >= 1)
-        if not pitch_present:
-            synthesis.disable()
-        else:
-            synthesis.active[0] = bool(rng.integers(0, 5) >= 1)
-            synthesis.pitch_index = rng.integers(0, 512)
-
-        data_c = None if not pitch_present else \
-            { 'active' : synthesis.active[0],
-              'pitch_index' : synthesis.pitch_index }
-
-        x = rng.random(ns) * 1e4
-        nbytes = rng.integers(10*(2+sr), 10*(6+sr))
-
-        x_c[:nd] = x_c[ns:]
-        x_c[nd:] = x
-
-        y = synthesis.run(x, nbytes)
-        x_c = lc3.ltpf_synthesize(dt, sr, nbytes, state_c, data_c, x_c)
-
-        ok = ok and np.amax(np.abs(x_c[nd:] - y)) < 1e-2
-
-    return ok
-
-def check_analysis_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    nt = (5 * T.SRATE_KHZ[sr]) // 4
-    ok = True
-
-    state = initial_state()
-
-    x = np.append(np.zeros(nt), C.X_PCM[dt][0])
-    (pitch_present, data) = lc3.ltpf_analyse(dt, sr, state, x)
-
-    ok = ok and C.T_CURR[dt][0] - state['tc'] == 17
-    ok = ok and np.amax(np.abs(state['nc'][0] - C.NC_LTPF[dt][0])) < 1e-5
-    ok = ok and pitch_present == C.PITCH_PRESENT[dt][0]
-    ok = ok and data['pitch_index'] == C.PITCH_INDEX[dt][0]
-    ok = ok and data['active'] == C.LTPF_ACTIVE[dt][0]
-
-    x = np.append(x[-nt:], C.X_PCM[dt][1])
-    (pitch_present, data) = lc3.ltpf_analyse(dt, sr, state, x)
-
-    ok = ok and C.T_CURR[dt][1] - state['tc'] == 17
-    ok = ok and np.amax(np.abs(state['nc'][0] - C.NC_LTPF[dt][1])) < 1e-5
-    ok = ok and pitch_present == C.PITCH_PRESENT[dt][1]
-    ok = ok and data['pitch_index'] == C.PITCH_INDEX[dt][1]
-    ok = ok and data['active'] == C.LTPF_ACTIVE[dt][1]
-
-    return ok
-
-def check_synthesis_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ok = True
-
-    if dt != T.DT_10M:
-        return ok
-
-    ns = T.NS[dt][sr]
-    nd = 18 * T.SRATE_KHZ[sr]
-
-    NBYTES = [ C.LTPF_C2_NBITS // 8, C.LTPF_C3_NBITS // 8,
-               C.LTPF_C4_NBITS // 8, C.LTPF_C5_NBITS // 8 ]
-
-    ACTIVE = [ C.LTPF_C2_ACTIVE, C.LTPF_C3_ACTIVE,
-               C.LTPF_C4_ACTIVE, C.LTPF_C5_ACTIVE ]
-
-    PITCH_INDEX = [ C.LTPF_C2_PITCH_INDEX, C.LTPF_C3_PITCH_INDEX,
-                    C.LTPF_C4_PITCH_INDEX, C.LTPF_C5_PITCH_INDEX ]
-
-    X = [ C.LTPF_C2_X, C.LTPF_C3_X,
-          C.LTPF_C4_X, C.LTPF_C5_X ]
-
-    PREV = [ C.LTPF_C2_PREV, C.LTPF_C3_PREV,
-             C.LTPF_C4_PREV, C.LTPF_C5_PREV  ]
-
-    TRANS = [ C.LTPF_C2_TRANS, C.LTPF_C3_TRANS,
-              C.LTPF_C4_TRANS, C.LTPF_C5_TRANS ]
-
-    for i in range(4):
-
-        state = initial_sstate()
-        nbytes = NBYTES[i]
-
-        data = { 'active' : ACTIVE[i][0], 'pitch_index' : PITCH_INDEX[i][0] }
-        x = np.append(np.zeros(nd), X[i][0])
-
-        lc3.ltpf_synthesize(dt, sr, nbytes, state, data, x)
-
-        data = { 'active' : ACTIVE[i][1], 'pitch_index' : PITCH_INDEX[i][1] }
-        x[  :nd-ns] = PREV[i][0][-nd+ns:]
-        x[nd-ns:nd] = PREV[i][1]
-        x[nd:nd+ns] = X[i][1]
-
-        y = lc3.ltpf_synthesize(dt, sr, nbytes, state, data, x)[nd:]
-
-        ok = ok and np.amax(np.abs(y - TRANS[i])) < 1e-3
-
-    return ok
-
-def check():
-
-    rng = np.random.default_rng(1234)
-    ok = True
-
-    for dt in range(T.NUM_DT):
-        for sr in range(T.NUM_SRATE):
-            ok = ok and check_resampler(rng, dt, sr)
-            ok = ok and check_analysis(rng, dt, sr)
-            ok = ok and check_synthesis(rng, dt, sr)
-
-    for dt in range(T.NUM_DT):
-        ok = ok and check_resampler_appendix_c(dt)
-        ok = ok and check_analysis_appendix_c(dt)
-        ok = ok and check_synthesis_appendix_c(dt)
-
-    return ok
-
-### ------------------------------------------------------------------------ ###
diff --git a/system/embdrv/lc3/test/ltpf_py.c b/system/embdrv/lc3/test/ltpf_py.c
deleted file mode 100644
index c51eadd..0000000
--- a/system/embdrv/lc3/test/ltpf_py.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <Python.h>
-#include <numpy/ndarrayobject.h>
-
-#include <ltpf.c>
-#include "ctypes.h"
-
-static PyObject *resample_py(PyObject *m, PyObject *args)
-{
-    unsigned dt, sr;
-    PyObject *hp50_obj, *x_obj, *y_obj;
-    struct lc3_ltpf_hp50_state hp50;
-    int16_t *x, *y;
-
-    if (!PyArg_ParseTuple(args, "IIOOO", &dt, &sr, &hp50_obj, &x_obj, &y_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-    CTYPES_CHECK(NULL, hp50_obj = to_ltpf_hp50_state(hp50_obj, &hp50));
-
-    int ns = LC3_NS(dt, sr), nt = LC3_NT(dt);
-    int ny = sizeof((struct lc3_ltpf_analysis){ }.x_12k8) / sizeof(int16_t);
-    int n  = dt == LC3_DT_7M5 ? 96 : 128;
-
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_INT16, ns+nt, &x));
-    CTYPES_CHECK("y", y_obj = to_1d_ptr(y_obj, NPY_INT16, ny, &y));
-
-    resample_12k8[sr](&hp50, x + nt, y + (ny - n), n);
-
-    from_ltpf_hp50_state(hp50_obj, &hp50);
-    return Py_BuildValue("O", y_obj);
-}
-
-static PyObject *analyse_py(PyObject *m, PyObject *args)
-{
-    PyObject *ltpf_obj, *x_obj;
-    unsigned dt, sr;
-    struct lc3_ltpf_analysis ltpf;
-    struct lc3_ltpf_data data = { 0 };
-    int16_t *x;
-
-    if (!PyArg_ParseTuple(args, "IIOO", &dt, &sr, &ltpf_obj, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", sr < LC3_NUM_SRATE);
-    CTYPES_CHECK(NULL, ltpf_obj = to_ltpf_analysis(ltpf_obj, &ltpf));
-
-    int ns = LC3_NS(dt, sr), nt = LC3_NT(sr);
-
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_INT16, ns+nt, &x));
-
-    int pitch_present =
-        lc3_ltpf_analyse(dt, sr, &ltpf, x + nt, &data);
-
-    from_ltpf_analysis(ltpf_obj, &ltpf);
-    return Py_BuildValue("iN", pitch_present, new_ltpf_data(&data));
-}
-
-static PyObject *synthesize_py(PyObject *m, PyObject *args)
-{
-    PyObject *ltpf_obj, *data_obj, *x_obj;
-    struct lc3_ltpf_synthesis ltpf;
-    struct lc3_ltpf_data data;
-    bool pitch_present;
-    unsigned dt, sr;
-    int nbytes;
-    float *x;
-
-    if (!PyArg_ParseTuple(args, "IIiOOO",
-                &dt, &sr, &nbytes, &ltpf_obj, &data_obj, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", sr < LC3_NUM_SRATE);
-    CTYPES_CHECK("nbytes", nbytes >= 20 && nbytes <= 400);
-    CTYPES_CHECK(NULL, ltpf_obj = to_ltpf_synthesis(ltpf_obj, &ltpf));
-
-    if ((pitch_present = (data_obj != Py_None)))
-        CTYPES_CHECK(NULL, data_obj = to_ltpf_data(data_obj, &data));
-
-    int ns = LC3_NS(dt,sr), nd = 18 * LC3_SRATE_KHZ(sr);
-
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, nd+ns, &x));
-
-    lc3_ltpf_synthesize(dt, sr, nbytes,
-        &ltpf, pitch_present ? &data : NULL, x, x + nd);
-
-    from_ltpf_synthesis(ltpf_obj, &ltpf);
-    return Py_BuildValue("O", x_obj);
-}
-
-static PyObject *get_nbits_py(PyObject *m, PyObject *args)
-{
-    int pitch_present;
-
-    if (!PyArg_ParseTuple(args, "i", &pitch_present))
-        return NULL;
-
-    int nbits = lc3_ltpf_get_nbits(pitch_present);
-
-    return Py_BuildValue("i", nbits);
-}
-
-static PyMethodDef methods[] = {
-    { "ltpf_resample"  , resample_py  , METH_VARARGS },
-    { "ltpf_analyse"   , analyse_py   , METH_VARARGS },
-    { "ltpf_synthesize", synthesize_py, METH_VARARGS },
-    { "ltpf_get_nbits" , get_nbits_py , METH_VARARGS },
-    { NULL },
-};
-
-PyMODINIT_FUNC lc3_ltpf_py_init(PyObject *m)
-{
-    import_array();
-
-    PyModule_AddFunctions(m, methods);
-
-    return m;
-}
diff --git a/system/embdrv/lc3/test/mdct.py b/system/embdrv/lc3/test/mdct.py
deleted file mode 100644
index aafba3f..0000000
--- a/system/embdrv/lc3/test/mdct.py
+++ /dev/null
@@ -1,198 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-import scipy.fft
-
-import build.lc3 as lc3
-import tables as T, appendix_c as C
-
-### ------------------------------------------------------------------------ ###
-
-class Mdct:
-
-    W = [ [ T.W_7M5_60, T.W_7M5_120, T.W_7M5_180, T.W_7M5_240, T.W_7M5_360 ],
-          [ T.W_10M_80, T.W_10M_160, T.W_10M_240, T.W_10M_320, T.W_10M_480 ] ]
-
-    def __init__(self, dt, sr):
-
-        self.ns = T.NS[dt][sr]
-        self.nd = T.ND[dt][sr]
-
-        self.t = np.zeros(2*self.ns)
-        self.w = Mdct.W[dt][sr]
-
-
-class MdctForward(Mdct):
-
-    def __init__(self, dt, sr):
-
-        super().__init__(dt, sr)
-
-    def run(self, x):
-
-        ns = self.ns
-        nd = self.nd
-
-        self.t[nd:nd+ns] = x
-        t = self.t * self.w
-        self.t[0:nd] = x[ns-nd:]
-
-        n  = len(t)
-        n2 = n // 2
-
-        z = t * np.exp(-2j * np.pi * np.arange(n) / (2*n))
-        z = scipy.fft.fft(z)[:n2]
-        z = z * np.exp(-2j * np.pi *
-                (n2/2 + 0.5) * (np.arange(n2) + 0.5) / (2 * n2))
-        return np.real(z) * np.sqrt(2/n2)
-
-
-class MdctInverse(Mdct):
-
-    def __init__(self, dt, sr):
-
-        super().__init__(dt, sr)
-
-    def run(self, x):
-
-        ns = self.ns
-        nd = self.nd
-
-        n = len(x)
-
-        x = np.append(x, -x[::-1])
-        z = x * np.exp(2j * np.pi * (n/2 + 0.5) * np.arange(2*n) / (2*n))
-        z = scipy.fft.ifft(z) * n
-        z = z * np.exp(2j * np.pi * (np.arange(2*n) + (n/2 + 0.5)) / (4*n))
-        t = np.real(z) * np.sqrt(2/n)
-
-        t = t * self.w[::-1]
-
-        y = np.empty(ns)
-        y[:nd] = t[ns-nd:ns] + self.t[2*ns-nd:]
-        y[nd:] = t[ns:2*ns-nd]
-        self.t = t
-
-        return y
-
-### ------------------------------------------------------------------------ ###
-
-def check_forward_unit(rng, dt, sr):
-
-    ns = T.NS[dt][sr]
-    nd = T.ND[dt][sr]
-    ok = True
-
-    x = (2 * rng.random(ns)) - 1
-
-    y   = [ None ] * 2
-    y_c = [ None ] * 2
-
-    mdct = MdctForward(dt, sr)
-    y[0] = mdct.run(x)
-    y[1] = mdct.run(x)
-
-    (y_c[0], d_c) = lc3.mdct_forward(dt, sr, x, np.zeros(nd))
-    y_c[1] = lc3.mdct_forward(dt, sr, x, d_c)[0]
-
-    ok = ok and np.amax(np.abs(y[0] - y_c[0])) < 1e-5
-    ok = ok and np.amax(np.abs(y[1] - y_c[1])) < 1e-5
-
-    return ok
-
-
-def check_forward_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ns = T.NS[dt][sr]
-    nd = T.ND[dt][sr]
-    ok = True
-
-    (y, d) = lc3.mdct_forward(dt, sr, C.X_PCM[dt][0], np.zeros(nd))
-    ok = ok and np.amax(np.abs(y - C.X[dt][0])) < 1e-1
-
-    (y, d) = lc3.mdct_forward(dt, sr, C.X_PCM[dt][1], d)
-    ok = ok and np.amax(np.abs(y - C.X[dt][1])) < 1e-1
-
-    return ok
-
-
-def check_inverse_unit(rng, dt, sr):
-
-    ns = T.NS[dt][sr]
-    nd = [ (23 * ns) // 30, (5 * ns) // 8 ][dt]
-    ok = True
-
-    x  = (2 * rng.random(ns)) - 1
-
-    y   = [ None ] * 2
-    y_c = [ None ] * 2
-
-    mdct = MdctInverse(dt, sr)
-    y[0] = mdct.run(x)
-    y[1] = mdct.run(x)
-
-    (y_c[0], d_c) = lc3.mdct_inverse(dt, sr, x, np.zeros(nd))
-    y_c[1] = lc3.mdct_inverse(dt, sr, x, d_c)[0]
-
-    ok = ok and np.amax(np.abs(y[0] - y_c[0])) < 1e-5
-    ok = ok and np.amax(np.abs(y[1] - y_c[1])) < 1e-5
-
-    return ok
-
-
-def check_inverse_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ns = T.NS[dt][sr]
-    nd = [ (23 * ns) // 30, (5 * ns) // 8 ][dt]
-    ok = True
-
-    (y, d0) = lc3.mdct_inverse(dt, sr, C.X_HAT_SNS[dt][0], np.zeros(nd))
-    yr = C.T_HAT_MDCT[dt][0][ns-nd:2*ns-nd]
-    dr = C.T_HAT_MDCT[dt][0][2*ns-nd:]
-
-    ok = ok and np.amax(np.abs(yr - y )) < 1e-1
-    ok = ok and np.amax(np.abs(dr - d0)) < 1e-1
-
-    (y, d1) = lc3.mdct_inverse(dt, sr, C.X_HAT_SNS[dt][1], d0)
-    yr[  :nd] = C.T_HAT_MDCT[dt][1][ns-nd:ns] + d0
-    yr[nd:ns] = C.T_HAT_MDCT[dt][1][ns:2*ns-nd]
-    dr        = C.T_HAT_MDCT[dt][1][2*ns-nd:]
-
-    ok = ok and np.amax(np.abs(yr - y )) < 1e-1
-    ok = ok and np.amax(np.abs(dr - d1)) < 1e-1
-
-    return ok
-
-
-def check():
-
-    rng = np.random.default_rng(1234)
-
-    ok  = True
-
-    for dt in range(T.NUM_DT):
-        for sr in range(T.NUM_SRATE):
-            ok = ok and check_forward_unit(rng, dt, sr)
-            ok = ok and check_inverse_unit(rng, dt, sr)
-
-    for dt in range(T.NUM_DT):
-        ok = ok and check_forward_appendix_c(dt)
-        ok = ok and check_inverse_appendix_c(dt)
-
-    return ok
diff --git a/system/embdrv/lc3/test/mdct_py.c b/system/embdrv/lc3/test/mdct_py.c
deleted file mode 100644
index 3479503..0000000
--- a/system/embdrv/lc3/test/mdct_py.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <Python.h>
-#include <numpy/ndarrayobject.h>
-
-#include <mdct.c>
-#include "ctypes.h"
-
-
-static PyObject *mdct_forward_py(PyObject *m, PyObject *args)
-{
-    PyObject *x_obj, *xd_obj, *y_obj, *d_obj;
-    enum lc3_dt dt;
-    enum lc3_srate sr;
-    float *x, *xd, *y, *d;
-
-    if (!PyArg_ParseTuple(args, "iiOO", &dt, &sr, &x_obj, &xd_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-
-    int ns = LC3_NS(dt, sr), nd = LC3_ND(dt, sr);
-
-    CTYPES_CHECK("x", to_1d_ptr(x_obj, NPY_FLOAT, ns, &x));
-    CTYPES_CHECK("xd", to_1d_ptr(xd_obj, NPY_FLOAT, nd, &xd));
-    d_obj = new_1d_ptr(NPY_FLOAT, nd, &d);
-    y_obj = new_1d_ptr(NPY_FLOAT, ns, &y);
-
-    memcpy(d, xd, nd * sizeof(float));
-
-    lc3_mdct_forward(dt, sr, sr, x, d, y);
-
-    return Py_BuildValue("NN", y_obj, d_obj);
-}
-
-static PyObject *mdct_inverse_py(PyObject *m, PyObject *args)
-{
-    PyObject *x_obj, *xd_obj, *d_obj, *y_obj;
-    enum lc3_dt dt;
-    enum lc3_srate sr;
-    float *x, *xd, *d, *y;
-
-    if (!PyArg_ParseTuple(args, "iiOO", &dt, &sr, &x_obj, &xd_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-
-    int ns = LC3_NS(dt, sr), nd = LC3_ND(dt, sr);
-
-    CTYPES_CHECK("x", to_1d_ptr(x_obj, NPY_FLOAT, ns, &x));
-    CTYPES_CHECK("xd", to_1d_ptr(xd_obj, NPY_FLOAT, nd, &xd));
-    d_obj = new_1d_ptr(NPY_FLOAT, nd, &d);
-    y_obj = new_1d_ptr(NPY_FLOAT, ns, &y);
-
-    memcpy(d, xd, nd * sizeof(float));
-
-    lc3_mdct_inverse(dt, sr, sr, x, d, y);
-
-    return Py_BuildValue("NN", y_obj, d_obj);
-}
-
-static PyMethodDef methods[] = {
-    { "mdct_forward", mdct_forward_py, METH_VARARGS },
-    { "mdct_inverse", mdct_inverse_py, METH_VARARGS },
-    { NULL },
-};
-
-PyMODINIT_FUNC lc3_mdct_py_init(PyObject *m)
-{
-    import_array();
-
-    PyModule_AddFunctions(m, methods);
-
-    return m;
-}
diff --git a/system/embdrv/lc3/test/module_py.c b/system/embdrv/lc3/test/module_py.c
deleted file mode 100644
index 6163a9c..0000000
--- a/system/embdrv/lc3/test/module_py.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <Python.h>
-
-static struct PyModuleDef module_def = {
-    PyModuleDef_HEAD_INIT,
-    .m_name = "LC3",
-    .m_doc = "LC3 Test Python Module",
-    .m_size = -1,
-};
-
-PyMODINIT_FUNC lc3_mdct_py_init(PyObject *);
-PyMODINIT_FUNC lc3_energy_py_init(PyObject *);
-PyMODINIT_FUNC lc3_attdet_py_init(PyObject *);
-PyMODINIT_FUNC lc3_bwdet_py_init(PyObject *);
-PyMODINIT_FUNC lc3_ltpf_py_init(PyObject *);
-PyMODINIT_FUNC lc3_sns_py_init(PyObject *);
-PyMODINIT_FUNC lc3_tns_py_init(PyObject *);
-PyMODINIT_FUNC lc3_spec_py_init(PyObject *);
-PyMODINIT_FUNC lc3_interface_py_init(PyObject *);
-
-PyMODINIT_FUNC PyInit_lc3(void)
-{
-    PyObject *m = PyModule_Create(&module_def);
-
-    if (m) m = lc3_mdct_py_init(m);
-    if (m) m = lc3_energy_py_init(m);
-    if (m) m = lc3_attdet_py_init(m);
-    if (m) m = lc3_bwdet_py_init(m);
-    if (m) m = lc3_ltpf_py_init(m);
-    if (m) m = lc3_sns_py_init(m);
-    if (m) m = lc3_tns_py_init(m);
-    if (m) m = lc3_spec_py_init(m);
-    if (m) m = lc3_interface_py_init(m);
-
-    return m;
-}
diff --git a/system/embdrv/lc3/test/neon/ltpf_neon.c b/system/embdrv/lc3/test/neon/ltpf_neon.c
deleted file mode 100644
index 0577bd1..0000000
--- a/system/embdrv/lc3/test/neon/ltpf_neon.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "neon.h"
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-/* -------------------------------------------------------------------------- */
-
-#define TEST_NEON
-#include <ltpf.c>
-
-void lc3_put_bits_generic(lc3_bits_t *a, unsigned b, int c)
-{ (void)a, (void)b, (void)c; }
-
-unsigned lc3_get_bits_generic(struct lc3_bits *a, int b)
-{ return (void)a, (void)b, 0; }
-
-/* -------------------------------------------------------------------------- */
-
-static int check_resampler()
-{
-    int16_t __x[60+480], *x = __x + 60;
-    for (int i = -60; i < 480; i++)
-          x[i] = rand() & 0xffff;
-
-    struct lc3_ltpf_hp50_state hp50 = { 0 }, hp50_neon = { 0 };
-    int16_t y[128], y_neon[128];
-
-    resample_16k_12k8(&hp50, x, y, 128);
-    neon_resample_16k_12k8(&hp50_neon, x, y_neon, 128);
-    if (memcmp(y, y_neon, 128 * sizeof(*y)) != 0)
-        return printf("Error\n"), -1;
-
-    resample_32k_12k8(&hp50, x, y, 128);
-    neon_resample_32k_12k8(&hp50_neon, x, y_neon, 128);
-    if (memcmp(y, y_neon, 128 * sizeof(*y)) != 0)
-        return printf("Error\n"), -1;
-
-    resample_48k_12k8(&hp50, x, y, 128);
-    neon_resample_48k_12k8(&hp50_neon, x, y_neon, 128);
-    if (memcmp(y, y_neon, 128 * sizeof(*y)) != 0)
-        return -1;
-
-    return 0;
-}
-
-static int check_dot()
-{
-    int16_t x[200];
-    for (int i = 0; i < 200; i++)
-        x[i] = rand() & 0xffff;
-
-    float y = dot(x, x+3, 128);
-    float y_neon = neon_dot(x, x+3, 128);
-    if (y != y_neon)
-        return -1;
-
-    return 0;
-}
-
-static int check_correlate()
-{
-    int16_t alignas(4) a[500], b[500];
-    float y[100], y_neon[100];
-
-    for (int i = 0; i < 500; i++) {
-        a[i] = rand() & 0xffff;
-        b[i] = rand() & 0xffff;
-    }
-
-    correlate(a, b+200, 128, y, 100);
-    neon_correlate(a, b+200, 128, y_neon, 100);
-    if (memcmp(y, y_neon, 100 * sizeof(*y)) != 0)
-        return -1;
-
-    correlate(a, b+199, 128, y, 99);
-    neon_correlate(a, b+199, 128, y_neon, 99);
-    if (memcmp(y, y_neon, 99 * sizeof(*y)) != 0)
-        return -1;
-
-    return 0;
-}
-
-int check_ltpf(void)
-{
-    int ret;
-
-    if ((ret = check_resampler()) < 0)
-        return ret;
-
-    if ((ret = check_dot()) < 0)
-        return ret;
-
-    if ((ret = check_correlate()) < 0)
-        return ret;
-
-    return 0;
-}
diff --git a/system/embdrv/lc3/test/neon/mdct_neon.c b/system/embdrv/lc3/test/neon/mdct_neon.c
deleted file mode 100644
index d5f3888..0000000
--- a/system/embdrv/lc3/test/neon/mdct_neon.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "neon.h"
-
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-/* -------------------------------------------------------------------------- */
-
-#define TEST_NEON
-#include <mdct.c>
-
-/* -------------------------------------------------------------------------- */
-
-static int check_fft(void)
-{
-    struct lc3_complex x[240];
-    struct lc3_complex y[240], y_neon[240];
-
-    for (int i = 0; i < 240; i++) {
-          x[i].re = (double)rand() / RAND_MAX;
-          x[i].im = (double)rand() / RAND_MAX;
-    }
-
-    fft_5(x, y, 240/5);
-    neon_fft_5(x, y_neon, 240/5);
-    for (int i = 0; i < 240; i++)
-        if (fabsf(y[i].re - y_neon[i].re) > 1e-6f ||
-            fabsf(y[i].im - y_neon[i].im) > 1e-6f   )
-            return -1;
-
-    fft_bf3(lc3_fft_twiddles_bf3[0], x, y, 240/15);
-    neon_fft_bf3(lc3_fft_twiddles_bf3[0], x, y_neon, 240/15);
-    for (int i = 0; i < 240; i++)
-        if (fabsf(y[i].re - y_neon[i].re) > 1e-6f ||
-            fabsf(y[i].im - y_neon[i].im) > 1e-6f   )
-            return -1;
-
-    fft_bf2(lc3_fft_twiddles_bf2[0][1], x, y, 240/30);
-    neon_fft_bf2(lc3_fft_twiddles_bf2[0][1], x, y_neon, 240/30);
-    for (int i = 0; i < 240; i++)
-        if (fabsf(y[i].re - y_neon[i].re) > 1e-6f ||
-            fabsf(y[i].im - y_neon[i].im) > 1e-6f   )
-            return -1;
-
-    return 0;
-}
-
-int check_mdct(void)
-{
-    int ret;
-
-    if ((ret = check_fft()) < 0)
-        return ret;
-
-    return 0;
-}
diff --git a/system/embdrv/lc3/test/neon/neon.h b/system/embdrv/lc3/test/neon/neon.h
deleted file mode 100644
index acd2392..0000000
--- a/system/embdrv/lc3/test/neon/neon.h
+++ /dev/null
@@ -1,330 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#if __ARM_NEON
-
-#include <arm_neon.h>
-
-#else
-#define __ARM_NEON 1
-
-#include <stdint.h>
-
-
-/* ----------------------------------------------------------------------------
- *  Integer
- * -------------------------------------------------------------------------- */
-
-typedef struct { int16_t e[4]; } int16x4_t;
-
-typedef struct { int16_t e[8]; } int16x8_t;
-typedef struct { int32_t e[4]; } int32x4_t;
-typedef struct { int64_t e[2]; } int64x2_t;
-
-
-/**
- * Load / Store
- */
-
-__attribute__((unused))
-static int16x4_t vld1_s16(const int16_t *p)
-{
-    return (int16x4_t){ { p[0], p[1], p[2], p[3] } };
-}
-
-
-/**
- * Arithmetic
- */
-
-__attribute__((unused))
-static int32x4_t vmull_s16(int16x4_t a, int16x4_t b)
-{
-    return (int32x4_t){ { a.e[0] * b.e[0], a.e[1] * b.e[1],
-                          a.e[2] * b.e[2], a.e[3] * b.e[3]  } };
-}
-
-__attribute__((unused))
-static int32x4_t vmlal_s16(int32x4_t r, int16x4_t a, int16x4_t b)
-{
-    return (int32x4_t){ {
-        r.e[0] + a.e[0] * b.e[0], r.e[1] + a.e[1] * b.e[1],
-        r.e[2] + a.e[2] * b.e[2], r.e[3] + a.e[3] * b.e[3] } };
-}
-
-__attribute__((unused))
-static int64x2_t vpadalq_s32(int64x2_t a, int32x4_t b)
-{
-    int64x2_t r;
-
-    r.e[0] = a.e[0] + ((int64_t)b.e[0] + b.e[1]);
-    r.e[1] = a.e[1] + ((int64_t)b.e[2] + b.e[3]);
-
-    return r;
-}
-
-
-/**
- * Reduce
- */
-
-__attribute__((unused))
-static int32_t vaddvq_s32(int32x4_t v)
-{
-    return v.e[0] + v.e[1] + v.e[2] + v.e[3];
-}
-
-__attribute__((unused))
-static int64_t vaddvq_s64(int64x2_t v)
-{
-    return v.e[0] + v.e[1];
-}
-
-
-/**
- * Manipulation
- */
-
-__attribute__((unused))
-static int16x4_t vext_s16(int16x4_t a, int16x4_t b, const int n)
-{
-    int16_t x[] = { a.e[0], a.e[1], a.e[2], a.e[3],
-                    b.e[0], b.e[1], b.e[2], b.e[3] };
-
-    return (int16x4_t){ { x[n], x[n+1], x[n+2], x[n+3] } };
-}
-
-__attribute__((unused))
-static int32x4_t vmovq_n_s32(uint32_t v)
-{
-    return (int32x4_t){ { v, v, v, v } };
-}
-
-__attribute__((unused))
-static int64x2_t vmovq_n_s64(int64_t v)
-{
-    return (int64x2_t){ { v, v, } };
-}
-
-
-
-/* ----------------------------------------------------------------------------
- *  Floating Point
- * -------------------------------------------------------------------------- */
-
-typedef struct { float e[2]; } float32x2_t;
-typedef struct { float e[4]; } float32x4_t;
-
-typedef struct { float32x2_t val[2]; } float32x2x2_t;
-typedef struct { float32x4_t val[2]; } float32x4x2_t;
-
-
-/**
- * Load / Store
- */
-
-__attribute__((unused))
-static float32x2_t vld1_f32(const float *p)
-{
-    return (float32x2_t){ { p[0], p[1] } };
-}
-
-__attribute__((unused))
-static float32x4_t vld1q_f32(const float *p)
-{
-    return (float32x4_t){ { p[0], p[1], p[2], p[3] } };
-}
-
-__attribute__((unused))
-static float32x4_t vld1q_dup_f32(const float *p)
-{
-    return (float32x4_t){ { p[0], p[0], p[0], p[0] } };
-}
-
-__attribute__((unused))
-static float32x2x2_t vld2_f32(const float *p)
-{
-    return (float32x2x2_t){ .val[0] = { { p[0], p[2] } },
-                            .val[1] = { { p[1], p[3] } } };
-}
-
-__attribute__((unused))
-static float32x4x2_t vld2q_f32(const float *p)
-{
-    return (float32x4x2_t){ .val[0] = { { p[0], p[2], p[4], p[6] } },
-                            .val[1] = { { p[1], p[3], p[5], p[7] } } };
-}
-
-__attribute__((unused))
-static void vst1_f32(float *p, float32x2_t v)
-{
-    p[0] = v.e[0], p[1] = v.e[1];
-}
-
-__attribute__((unused))
-static void vst1q_f32(float *p, float32x4_t v)
-{
-    p[0] = v.e[0], p[1] = v.e[1], p[2] = v.e[2], p[3] = v.e[3];
-}
-
-/**
- * Arithmetic
- */
-
-__attribute__((unused))
-static float32x2_t vneg_f32(float32x2_t a)
-{
-    return (float32x2_t){ { -a.e[0], -a.e[1] } };
-}
-
-__attribute__((unused))
-static float32x4_t vnegq_f32(float32x4_t a)
-{
-    return (float32x4_t){ { -a.e[0], -a.e[1], -a.e[2], -a.e[3] } };
-}
-
-__attribute__((unused))
-static float32x4_t vaddq_f32(float32x4_t a, float32x4_t b)
-{
-    return (float32x4_t){ { a.e[0] + b.e[0], a.e[1] + b.e[1],
-                            a.e[2] + b.e[2], a.e[3] + b.e[3] } };
-}
-
-__attribute__((unused))
-static float32x4_t vsubq_f32(float32x4_t a, float32x4_t b)
-{
-    return (float32x4_t){ { a.e[0] - b.e[0], a.e[1] - b.e[1],
-                            a.e[2] - b.e[2], a.e[3] - b.e[3] } };
-}
-
-__attribute__((unused))
-static float32x2_t vfma_f32(float32x2_t a, float32x2_t b, float32x2_t c)
-{
-    return (float32x2_t){ {
-        a.e[0] + b.e[0] * c.e[0], a.e[1] + b.e[1] * c.e[1] } };
-}
-
-__attribute__((unused))
-static float32x4_t vfmaq_f32(float32x4_t a, float32x4_t b, float32x4_t c)
-{
-    return (float32x4_t){ {
-        a.e[0] + b.e[0] * c.e[0], a.e[1] + b.e[1] * c.e[1],
-        a.e[2] + b.e[2] * c.e[2], a.e[3] + b.e[3] * c.e[3] } };
-}
-
-__attribute__((unused))
-static float32x2_t vfms_f32(float32x2_t a, float32x2_t b, float32x2_t c)
-{
-    return (float32x2_t){ {
-        a.e[0] - b.e[0] * c.e[0], a.e[1] - b.e[1] * c.e[1] } };
-}
-
-__attribute__((unused))
-static float32x4_t vfmsq_f32(float32x4_t a, float32x4_t b, float32x4_t c)
-{
-    return (float32x4_t){ {
-        a.e[0] - b.e[0] * c.e[0], a.e[1] - b.e[1] * c.e[1],
-        a.e[2] - b.e[2] * c.e[2], a.e[3] - b.e[3] * c.e[3] } };
-}
-
-
-/**
- * Manipulation
- */
-
-__attribute__((unused))
-static float32x2_t vcreate_f32(uint64_t u)
-{
-    float *f = (float *)&u;
-    return (float32x2_t){ { f[0] , f[1] } };
-}
-
-__attribute__((unused))
-static float32x4_t vcombine_f32(float32x2_t a, float32x2_t b)
-{
-    return (float32x4_t){ { a.e[0], a.e[1], b.e[0], b.e[1] } };
-}
-
-__attribute__((unused))
-static float32x2_t vget_low_f32(float32x4_t a)
-{
-    return (float32x2_t){ { a.e[0], a.e[1] } };
-}
-
-__attribute__((unused))
-static float32x2_t vget_high_f32(float32x4_t a)
-{
-    return (float32x2_t){ { a.e[2], a.e[3] } };
-}
-
-__attribute__((unused))
-static float32x4_t vmovq_n_f32(float v)
-{
-    return (float32x4_t){ { v, v, v, v } };
-}
-
-__attribute__((unused))
-static float32x2_t vrev64_f32(float32x2_t v)
-{
-    return (float32x2_t){ { v.e[1], v.e[0] } };
-}
-
-__attribute__((unused))
-static float32x4_t vrev64q_f32(float32x4_t v)
-{
-    return (float32x4_t){ { v.e[1], v.e[0], v.e[3], v.e[2] } };
-}
-
-__attribute__((unused))
-static float32x2_t vtrn1_f32(float32x2_t a, float32x2_t b)
-{
-    return (float32x2_t){ { a.e[0], b.e[0] } };
-}
-
-__attribute__((unused))
-static float32x2_t vtrn2_f32(float32x2_t a, float32x2_t b)
-{
-    return (float32x2_t){ { a.e[1], b.e[1] } };
-}
-
-__attribute__((unused))
-static float32x4_t vtrn1q_f32(float32x4_t a, float32x4_t b)
-{
-    return (float32x4_t){ { a.e[0], b.e[0], a.e[2], b.e[2] } };
-}
-
-__attribute__((unused))
-static float32x4_t vtrn2q_f32(float32x4_t a, float32x4_t b)
-{
-    return (float32x4_t){ { a.e[1], b.e[1], a.e[3], b.e[3] } };
-}
-
-__attribute__((unused))
-static float32x4_t vzip1q_f32(float32x4_t a, float32x4_t b)
-{
-    return (float32x4_t){ { a.e[0], b.e[0], a.e[1], b.e[1] } };
-}
-
-__attribute__((unused))
-static float32x4_t vzip2q_f32(float32x4_t a, float32x4_t b)
-{
-    return (float32x4_t){ { a.e[2], b.e[2], a.e[3], b.e[3] } };
-}
-
-
-#endif /* __ARM_NEON */
diff --git a/system/embdrv/lc3/test/neon/test_neon.c b/system/embdrv/lc3/test/neon/test_neon.c
deleted file mode 100644
index 50622c7..0000000
--- a/system/embdrv/lc3/test/neon/test_neon.c
+++ /dev/null
@@ -1,37 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <stdio.h>
-
-int check_ltpf(void);
-int check_mdct(void);
-
-int main()
-{
-    int r, ret = 0;
-
-    printf("Checking LTPF Neon... "); fflush(stdout);
-    printf("%s\n", (r = check_ltpf()) == 0 ? "OK" : "Failed");
-    ret = ret || r;
-
-    printf("Checking MDCT Neon... "); fflush(stdout);
-    printf("%s\n", (r = check_mdct()) == 0 ? "OK" : "Failed");
-    ret = ret || r;
-
-    return ret;
-}
diff --git a/system/embdrv/lc3/test/run.py b/system/embdrv/lc3/test/run.py
deleted file mode 100755
index 4b61f7a..0000000
--- a/system/embdrv/lc3/test/run.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2022 Google LLC
-#
-# 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 mdct, energy, bwdet, attdet
-import ltpf, sns, tns, spec, encoder, decoder
-
-ok = True
-
-for m in [ ( mdct    , "MDCT"                   ),
-           ( energy  , "Energy Band"            ),
-           ( bwdet   , "Bandwidth Detector"     ),
-           ( attdet  , "Attack Detector"        ),
-           ( ltpf    , "Long Term Postfilter"   ),
-           ( sns     , "Spectral Noise Shaping" ),
-           ( tns     , "Temporal Noise Shaping" ),
-           ( spec    , "Spectral Quantization"  ),
-           ( encoder , "Encoder"                ),
-           ( decoder , "Decoder"                ) ]:
-
-    print('[{:^6}] {:}'.format('...', m[1]), end='\r', flush=True)
-    ret = m[0].check()
-    print('[{:^6}] {:}'.format('OK' if ret else 'FAILED', m[1]))
-
-    ok = ok and ret
-
-exit(0 if ok else 1);
diff --git a/system/embdrv/lc3/test/setup.py b/system/embdrv/lc3/test/setup.py
deleted file mode 100755
index 8e12891..0000000
--- a/system/embdrv/lc3/test/setup.py
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright 2022 Google LLC
-#
-# 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.
-#
-
-from distutils.core import setup, Extension
-import os, sys, glob
-
-if len(sys.argv) <= 1:
-  sys.argv = sys.argv + [
-    'build', '--build-base', os.getcwd() + os.sep + 'build',
-    'install', '--install-lib', os.getcwd() + os.sep + 'build' ]
-
-INC_DIR = '..' + os.sep + 'include'
-SRC_DIR = '..' + os.sep + 'src'
-
-sources = glob.glob('*_py.c') + \
-          [ SRC_DIR + os.sep + 'tables.c',
-            SRC_DIR + os.sep + 'bits.c',
-            SRC_DIR + os.sep + 'plc.c' ]
-
-depends = [ 'ctypes.h' ] + \
-          glob.glob(INC_DIR + os.sep + '*.h') + \
-          glob.glob(SRC_DIR + os.sep + '*.[c,h]')
-
-includes = [ SRC_DIR, INC_DIR ]
-
-ctiming = Extension('lc3',
-  extra_compile_args = ['-std=c11'],
-  define_macros = [ ('NPY_NO_DEPRECATED_API', 'NPY_1_7_API_VERSION') ],
-  sources = sources,
-  depends = depends,
-  include_dirs = includes)
-
-setup(name = 'LC3',
-      version = '1.0',
-      description = 'LC3 Test Python Module',
-      ext_modules = [ctiming])
diff --git a/system/embdrv/lc3/test/sns.py b/system/embdrv/lc3/test/sns.py
deleted file mode 100644
index 897ed79..0000000
--- a/system/embdrv/lc3/test/sns.py
+++ /dev/null
@@ -1,594 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-import scipy.fftpack as fftpack
-
-import build.lc3 as lc3
-import tables as T, appendix_c as C
-
-### ------------------------------------------------------------------------ ###
-
-class Sns:
-
-    def __init__(self, dt, sr):
-
-        self.dt = dt
-        self.sr = sr
-
-        (self.ind_lf, self.ind_hf, self.shape, self.gain) = \
-            (None, None, None, None)
-
-        (self.idx_a, self.ls_a, self.idx_b, self.ls_b) = \
-            (None, None, None, None)
-
-    def get_data(self):
-
-        data = { 'lfcb' : self.ind_lf, 'hfcb' : self.ind_hf,
-                 'shape' : self.shape, 'gain' : self.gain,
-                 'idx_a' : self.idx_a, 'ls_a' : self.ls_a }
-
-        if self.idx_b is not None:
-            data.update({ 'idx_b' : self.idx_b, 'ls_b' : self.ls_b })
-
-        return data
-
-    def get_nbits(self):
-
-        return 38
-
-    def spectral_shaping(self, scf, inv, x):
-
-        ## 3.3.7.4 Scale factors interpolation
-
-        scf_i = np.empty(4*len(scf))
-        scf_i[0     ] = scf[0]
-        scf_i[1     ] = scf[0]
-        scf_i[2:62:4] = scf[:15] + 1/8 * (scf[1:] - scf[:15])
-        scf_i[3:63:4] = scf[:15] + 3/8 * (scf[1:] - scf[:15])
-        scf_i[4:64:4] = scf[:15] + 5/8 * (scf[1:] - scf[:15])
-        scf_i[5:64:4] = scf[:15] + 7/8 * (scf[1:] - scf[:15])
-        scf_i[62    ] = scf[15 ] + 1/8 * (scf[15] - scf[14 ])
-        scf_i[63    ] = scf[15 ] + 3/8 * (scf[15] - scf[14 ])
-
-        n2 = 64 - min(len(x), 64)
-
-        for i in range(n2):
-            scf_i[i] = 0.5 * (scf_i[2*i] + scf_i[2*i+1])
-        scf_i = np.append(scf_i[:n2], scf_i[2*n2:])
-
-        g_sns = np.power(2, [ -scf_i, scf_i ][inv])
-
-        ## 3.3.7.4 Spectral shaping
-
-        y = np.empty(len(x))
-        I = T.I[self.dt][self.sr]
-
-        for b in range(len(g_sns)):
-            y[I[b]:I[b+1]] = x[I[b]:I[b+1]] * g_sns[b]
-
-        return y
-
-
-class SnsAnalysis(Sns):
-
-    def __init__(self, dt, sr):
-
-        super().__init__(dt, sr)
-
-    def compute_scale_factors(self, e, att):
-
-        dt = self.dt
-
-        ## 3.3.7.2.1 Padding
-
-        n2 = 64 - len(e)
-
-        e = np.append(np.empty(n2), e)
-        for i in range(n2):
-            e[2*i+0] = e[2*i+1] = e[n2+i]
-
-        ## 3.3.7.2.2 Smoothing
-
-        e_s = np.zeros(len(e))
-        e_s[0   ] = 0.75 * e[0   ] + 0.25 * e[1   ]
-        e_s[1:63] = 0.25 * e[0:62] + 0.5  * e[1:63] + 0.25 * e[2:64]
-        e_s[  63] = 0.25 * e[  62] + 0.75 * e[  63]
-
-        ## 3.3.7.2.3 Pre-emphasis
-
-        g_tilt = [ 14, 18, 22, 26, 30 ][self.sr]
-        e_p = e_s * (10 ** ((np.arange(64) * g_tilt) / 630))
-
-        ## 3.3.7.2.4 Noise floor
-
-        noise_floor = max(np.average(e_p) * (10 ** (-40/10)), 2 ** -32)
-        e_p = np.fmax(e_p, noise_floor * np.ones(len(e)))
-
-        ## 3.3.7.2.5 Logarithm
-
-        e_l = np.log2(10 ** -31 + e_p) / 2
-
-        ## 3.3.7.2.6 Band energy grouping
-
-        w = [ 1/12, 2/12, 3/12, 3/12, 2/12, 1/12 ]
-
-        e_4 = np.zeros(len(e_l) // 4)
-        e_4[0   ] = w[0] * e_l[0] + np.sum(w[1:] * e_l[:5])
-        e_4[1:15] = [ np.sum(w * e_l[4*i-1:4*i+5]) for i in range(1, 15) ]
-        e_4[  15] = np.sum(w[:5] * e_l[59:64]) + w[5] * e_l[63]
-
-        ## 3.3.7.2.7 Mean removal and scaling, attack handling
-
-        scf = 0.85 * (e_4 - np.average(e_4))
-
-        scf_a = np.zeros(len(scf))
-        scf_a[0   ] = np.average(scf[:3])
-        scf_a[1   ] = np.average(scf[:4])
-        scf_a[2:14] = [ np.average(scf[i:i+5]) for i in range(12) ]
-        scf_a[  14] = np.average(scf[12:])
-        scf_a[  15] = np.average(scf[13:])
-
-        scf_a = (0.5 if self.dt == T.DT_10M else 0.3) * \
-                (scf_a - np.average(scf_a))
-
-        return scf_a if att else scf
-
-    def enum_mpvq(self, v):
-
-        sign = None
-        index = 0
-        x = 0
-
-        for (n, vn) in enumerate(v[::-1]):
-
-            if sign is not None and vn != 0:
-                index = 2*index + sign
-            if vn != 0:
-                sign = 1 if vn < 0 else 0
-
-            index += T.SNS_MPVQ_OFFSETS[n][x]
-            x += abs(vn)
-
-        return (index, bool(sign))
-
-    def quantize(self, scf):
-
-        ## 3.3.7.3.2 Stage 1
-
-        dmse_lf = [ np.sum((scf[:8] - T.SNS_LFCB[i]) ** 2) for i in range(32) ]
-        dmse_hf = [ np.sum((scf[8:] - T.SNS_HFCB[i]) ** 2) for i in range(32) ]
-
-        self.ind_lf = np.argmin(dmse_lf)
-        self.ind_hf = np.argmin(dmse_hf)
-
-        st1 = np.append(T.SNS_LFCB[self.ind_lf], T.SNS_HFCB[self.ind_hf])
-        r1 = scf - st1
-
-        ## 3.3.7.3.3 Stage 2
-
-        t2_rot = fftpack.dct(r1, norm = 'ortho')
-        x = np.abs(t2_rot)
-
-        ## 3.3.7.3.3 Stage 2 Shape search, step 1
-
-        K = 6
-
-        proj_fac = (K - 1) / sum(np.abs(t2_rot))
-        y3 = np.floor(x * proj_fac).astype(int)
-
-        ## 3.3.7.3.3 Stage 2 Shape search, step 2
-
-        corr_xy = np.sum(y3 * x)
-        energy_y = np.sum(y3 * y3)
-
-        k0 = sum(y3)
-        for k in range(k0, K):
-            q_pvq = ((corr_xy + x) ** 2) / (energy_y + 2*y3 + 1)
-            n_best = np.argmax(q_pvq)
-
-            corr_xy += x[n_best]
-            energy_y += 2*y3[n_best] + 1
-            y3[n_best] += 1
-
-        ## 3.3.7.3.3 Stage 2 Shape search, step 3
-
-        K = 8
-
-        y2 = y3.copy()
-
-        for k in range(sum(y2), K):
-            q_pvq = ((corr_xy + x) ** 2) / (energy_y + 2*y2 + 1)
-            n_best = np.argmax(q_pvq)
-
-            corr_xy += x[n_best]
-            energy_y += 2*y2[n_best] + 1
-            y2[n_best] += 1
-
-
-        ## 3.3.7.3.3 Stage 2 Shape search, step 4
-
-        y1 = np.append(y2[:10], [0] * 6)
-
-        ## 3.3.7.3.3 Stage 2 Shape search, step 5
-
-        corr_xy -= sum(y2[10:] * x[10:])
-        energy_y -= sum(y2[10:] * y2[10:])
-
-        ## 3.3.7.3.3 Stage 2 Shape search, step 6
-
-        K = 10
-
-        for k in range(sum(y1), K):
-            q_pvq = ((corr_xy + x[:10]) ** 2) / (energy_y + 2*y1[:10] + 1)
-            n_best = np.argmax(q_pvq)
-
-            corr_xy += x[n_best]
-            energy_y += 2*y1[n_best] + 1
-            y1[n_best] += 1
-
-        ## 3.3.7.3.3 Stage 2 Shape search, step 7
-
-        y0 = np.append(y1[:10], [ 0 ] * 6)
-
-        q_pvq = ((corr_xy + x[10:]) ** 2) / (energy_y + 2*y0[10:] + 1)
-        n_best = 10 + np.argmax(q_pvq)
-
-        y0[n_best] += 1
-
-        ## 3.3.7.3.3 Stage 2 Shape search, step 8
-
-        y0 *= np.sign(t2_rot).astype(int)
-        y1 *= np.sign(t2_rot).astype(int)
-        y2 *= np.sign(t2_rot).astype(int)
-        y3 *= np.sign(t2_rot).astype(int)
-
-        ## 3.3.7.3.3 Stage 2 Shape search, step 9
-
-        xq = [ y / np.sqrt(sum(y ** 2)) for y in (y0, y1, y2, y3) ]
-
-        ## 3.3.7.3.3 Shape and gain combination determination
-
-        G = [ T.SNS_VQ_REG_ADJ_GAINS, T.SNS_VQ_REG_LF_ADJ_GAINS,
-              T.SNS_VQ_NEAR_ADJ_GAINS, T.SNS_VQ_FAR_ADJ_GAINS ]
-
-        dMSE = [ [ sum((t2_rot - G[j][i] * xq[j]) ** 2)
-                   for i in range(len(G[j])) ] for j in range(4) ]
-
-        self.shape = np.argmin([ np.min(dMSE[j]) for j in range(4) ])
-        self.gain = np.argmin(dMSE[self.shape])
-
-        gain = G[self.shape][self.gain]
-
-        ## 3.3.7.3.3 Enumeration of the selected PVQ pulse configurations
-
-        if self.shape == 0:
-            (self.idx_a, self.ls_a) = self.enum_mpvq(y0[:10])
-            (self.idx_b, self.ls_b) = self.enum_mpvq(y0[10:])
-        elif self.shape == 1:
-            (self.idx_a, self.ls_a) = self.enum_mpvq(y1[:10])
-            (self.idx_b, self.ls_b) = (None, None)
-        elif self.shape == 2:
-            (self.idx_a, self.ls_a) = self.enum_mpvq(y2)
-            (self.idx_b, self.ls_b) = (None, None)
-        elif self.shape == 3:
-            (self.idx_a, self.ls_a) = self.enum_mpvq(y3)
-            (self.idx_b, self.ls_b) = (None, None)
-
-        ## 3.3.7.3.4 Synthesis of the Quantized scale factor
-
-        scf_q = st1 + gain * fftpack.idct(xq[self.shape], norm = 'ortho')
-
-        return scf_q
-
-    def run(self, eb, att, x):
-
-        scf = self.compute_scale_factors(eb, att)
-        scf_q = self.quantize(scf)
-        y = self.spectral_shaping(scf_q, False, x)
-
-        return y
-
-    def store(self, b):
-
-        shape = self.shape
-        gain_msb_bits = np.array([ 1, 1, 2, 2 ])[shape]
-        gain_lsb_bits = np.array([ 0, 1, 0, 1 ])[shape]
-
-        b.write_uint(self.ind_lf, 5)
-        b.write_uint(self.ind_hf, 5)
-
-        b.write_bit(shape >> 1)
-
-        b.write_uint(self.gain >> gain_lsb_bits, gain_msb_bits)
-
-        b.write_bit(self.ls_a)
-
-        if self.shape == 0:
-            sz_shape_a = 2390004
-            index_joint = self.idx_a + \
-                (2 * self.idx_b + self.ls_b + 2) * sz_shape_a
-
-        elif self.shape == 1:
-            sz_shape_a = 2390004
-            index_joint = self.idx_a + (self.gain & 1) * sz_shape_a
-
-        elif self.shape == 2:
-            index_joint = self.idx_a
-
-        elif self.shape == 3:
-            sz_shape_a = 15158272
-            index_joint = sz_shape_a + (self.gain & 1) + 2 * self.idx_a
-
-        b.write_uint(index_joint, 14 - gain_msb_bits)
-        b.write_uint(index_joint >> (14 - gain_msb_bits), 12)
-
-
-class SnsSynthesis(Sns):
-
-    def __init__(self, dt, sr):
-
-        super().__init__(dt, sr)
-
-    def deenum_mpvq(self, index, ls, npulses, n):
-
-        y = np.zeros(n, dtype=np.int)
-        pos = 0
-
-        for i in range(len(y)-1, -1, -1):
-
-            if index > 0:
-                yi = 0
-                while index < T.SNS_MPVQ_OFFSETS[i][npulses - yi]: yi += 1
-                index -= T.SNS_MPVQ_OFFSETS[i][npulses - yi]
-            else:
-                yi = npulses
-
-            y[pos] = [ yi, -yi ][int(ls)]
-            pos += 1
-
-            npulses -= yi
-            if npulses <= 0:
-                break
-
-            if yi > 0:
-                ls = index & 1
-                index >>= 1
-
-        return y
-
-    def unquantize(self):
-
-        ## 3.7.4.2.1-2  SNS VQ Decoding
-
-        y = np.empty(16, dtype=np.int)
-
-        if self.shape == 0:
-            y[:10] = self.deenum_mpvq(self.idx_a, self.ls_a, 10, 10)
-            y[10:] = self.deenum_mpvq(self.idx_b, self.ls_b,  1,  6)
-        elif self.shape == 1:
-            y[:10] = self.deenum_mpvq(self.idx_a, self.ls_a, 10, 10)
-            y[10:] = np.zeros(6, dtype=np.int)
-        elif self.shape == 2:
-            y = self.deenum_mpvq(self.idx_a, self.ls_a, 8, 16)
-        elif self.shape == 3:
-            y = self.deenum_mpvq(self.idx_a, self.ls_a, 6, 16)
-
-        ## 3.7.4.2.3  Unit energy normalization
-
-        y = y / np.sqrt(sum(y ** 2))
-
-        ## 3.7.4.2.4  Reconstruction of the quantized scale factors
-
-        G = [ T.SNS_VQ_REG_ADJ_GAINS, T.SNS_VQ_REG_LF_ADJ_GAINS,
-              T.SNS_VQ_NEAR_ADJ_GAINS, T.SNS_VQ_FAR_ADJ_GAINS ]
-
-        gain = G[self.shape][self.gain]
-
-        scf = np.append(T.SNS_LFCB[self.ind_lf], T.SNS_HFCB[self.ind_hf]) \
-                + gain * fftpack.idct(y, norm = 'ortho')
-
-        return scf
-
-    def load(self, b):
-
-        self.ind_lf = b.read_uint(5)
-        self.ind_hf = b.read_uint(5)
-
-        shape_msb = b.read_bit()
-
-        gain_msb_bits = 1 + shape_msb
-        self.gain = b.read_uint(gain_msb_bits)
-
-        self.ls_a = b.read_bit()
-
-        index_joint  = b.read_uint(14 - gain_msb_bits)
-        index_joint |= b.read_uint(12) << (14 - gain_msb_bits)
-
-        if shape_msb == 0:
-            sz_shape_a = 2390004
-
-            if index_joint >= sz_shape_a * 14:
-                raise ValueError('Invalide SNS joint index')
-
-            self.idx_a = index_joint % sz_shape_a
-            index_joint = index_joint // sz_shape_a
-            if index_joint >= 2:
-                self.shape = 0
-                self.idx_b = (index_joint - 2) // 2
-                self.ls_b =  (index_joint - 2)  % 2
-            else:
-                self.shape = 1
-                self.gain = (self.gain << 1) + (index_joint & 1)
-
-        else:
-            sz_shape_a = 15158272
-            if index_joint >= sz_shape_a + 1549824:
-                raise ValueError('Invalide SNS joint index')
-
-            if index_joint < sz_shape_a:
-                self.shape = 2
-                self.idx_a = index_joint
-            else:
-                self.shape = 3
-                index_joint -= sz_shape_a
-                self.gain = (self.gain << 1) + (index_joint % 2)
-                self.idx_a = index_joint // 2
-
-    def run(self, x):
-
-        scf = self.unquantize()
-        y = self.spectral_shaping(scf, True, x)
-
-        return y
-
-### ------------------------------------------------------------------------ ###
-
-def check_analysis(rng, dt, sr):
-
-    ok = True
-
-    analysis = SnsAnalysis(dt, sr)
-
-    for i in range(10):
-        x = rng.random(T.NE[dt][sr]) * 1e4
-        e = rng.random(min(len(x), 64)) * 1e10
-
-        for att in (0, 1):
-            y = analysis.run(e, att, x)
-            data = analysis.get_data()
-
-            (y_c, data_c) = lc3.sns_analyze(dt, sr, e, att, x)
-
-            for k in data.keys():
-                ok = ok and data_c[k] == data[k]
-
-            ok = ok and lc3.sns_get_nbits() == analysis.get_nbits()
-            ok = ok and np.amax(np.abs(y - y_c)) < 1e-1
-
-    return ok
-
-def check_synthesis(rng, dt, sr):
-
-    ok = True
-
-    synthesis = SnsSynthesis(dt, sr)
-
-    for i in range(100):
-
-        synthesis.ind_lf = rng.integers(0, 32)
-        synthesis.ind_hf = rng.integers(0, 32)
-
-        shape = rng.integers(0, 4)
-        sz_shape_a = [ 2390004, 2390004, 15158272, 774912 ][shape]
-        sz_shape_b = [ 6, 1, 0, 0 ][shape]
-        synthesis.shape = shape
-        synthesis.gain = rng.integers(0, [ 2, 4, 4, 8 ][shape])
-        synthesis.idx_a = rng.integers(0, sz_shape_a, endpoint=True)
-        synthesis.ls_a = bool(rng.integers(0, 1, endpoint=True))
-        synthesis.idx_b = rng.integers(0, sz_shape_b, endpoint=True)
-        synthesis.ls_b = bool(rng.integers(0, 1, endpoint=True))
-
-        x = rng.random(T.NE[dt][sr]) * 1e4
-
-        y = synthesis.run(x)
-        y_c = lc3.sns_synthesize(dt, sr, synthesis.get_data(), x)
-        ok = ok and np.amax(np.abs(y - y_c)) < 1e0
-
-    return ok
-
-def check_analysis_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ok = True
-
-    for i in range(len(C.E_B[dt])):
-
-        scf = lc3.sns_compute_scale_factors(dt, sr, C.E_B[dt][i], False)
-        ok = ok and np.amax(np.abs(scf - C.SCF[dt][i])) < 1e-4
-
-        (lf, hf) = lc3.sns_resolve_codebooks(scf)
-        ok = ok and lf == C.IND_LF[dt][i] and hf == C.IND_HF[dt][i]
-
-        (y, yn, shape, gain) = lc3.sns_quantize(scf, lf, hf)
-        ok = ok and np.any(y[0][:16] - C.SNS_Y0[dt][i] == 0)
-        ok = ok and np.any(y[1][:10] - C.SNS_Y1[dt][i] == 0)
-        ok = ok and np.any(y[2][:16] - C.SNS_Y2[dt][i] == 0)
-        ok = ok and np.any(y[3][:16] - C.SNS_Y3[dt][i] == 0)
-        ok = ok and shape == 2*C.SUBMODE_MSB[dt][i] + C.SUBMODE_LSB[dt][i]
-        ok = ok and gain == C.G_IND[dt][i]
-
-        scf_q = lc3.sns_unquantize(lf, hf, yn[shape], shape, gain)
-        ok = ok and np.amax(np.abs(scf_q - C.SCF_Q[dt][i])) < 1e-5
-
-        x = lc3.sns_spectral_shaping(dt, sr, C.SCF_Q[dt][i], False, C.X[dt][i])
-        ok = ok and np.amax(np.abs(1 - x/C.X_S[dt][i])) < 1e-5
-
-        (x, data) = lc3.sns_analyze(dt, sr, C.E_B[dt][i], False, C.X[dt][i])
-        ok = ok and data['lfcb'] == C.IND_LF[dt][i]
-        ok = ok and data['hfcb'] == C.IND_HF[dt][i]
-        ok = ok and data['shape'] == \
-            2*C.SUBMODE_MSB[dt][i] + C.SUBMODE_LSB[dt][i]
-        ok = ok and data['gain'] == C.G_IND[dt][i]
-        ok = ok and data['idx_a'] == C.IDX_A[dt][i]
-        ok = ok and data['ls_a'] == C.LS_IND_A[dt][i]
-        ok = ok and (C.IDX_B[dt][i] is None or
-            data['idx_b'] == C.IDX_B[dt][i])
-        ok = ok and (C.LS_IND_B[dt][i] is None or
-            data['ls_b'] == C.LS_IND_B[dt][i])
-        ok = ok and np.amax(np.abs(1 - x/C.X_S[dt][i])) < 1e-5
-
-    return ok
-
-def check_synthesis_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ok = True
-
-    for i in range(len(C.X_HAT_TNS[dt])):
-
-        data = {
-            'lfcb'  : C.IND_LF[dt][i], 'hfcb' : C.IND_HF[dt][i],
-            'shape' : 2*C.SUBMODE_MSB[dt][i] + C.SUBMODE_LSB[dt][i],
-            'gain'  : C.G_IND[dt][i],
-            'idx_a' : C.IDX_A[dt][i],
-            'ls_a'  : C.LS_IND_A[dt][i],
-            'idx_b' : C.IDX_B[dt][i] if C.IDX_B[dt][i] is not None else 0,
-            'ls_b'  : C.LS_IND_B[dt][i] if C.LS_IND_B[dt][i] is not None else 0,
-        }
-
-        x = lc3.sns_synthesize(dt, sr, data, C.X_HAT_TNS[dt][i])
-        ok = ok and np.amax(np.abs(x - C.X_HAT_SNS[dt][i])) < 1e0
-
-    return ok
-
-def check():
-
-    rng = np.random.default_rng(1234)
-    ok = True
-
-    for dt in range(T.NUM_DT):
-        for sr in range(T.NUM_SRATE):
-            ok = ok and check_analysis(rng, dt, sr)
-            ok = ok and check_synthesis(rng, dt, sr)
-
-    for dt in range(T.NUM_DT):
-        ok = ok and check_analysis_appendix_c(dt)
-        ok = ok and check_synthesis_appendix_c(dt)
-
-    return ok
-
-### ------------------------------------------------------------------------ ###
diff --git a/system/embdrv/lc3/test/sns_py.c b/system/embdrv/lc3/test/sns_py.c
deleted file mode 100644
index 2ef3e83..0000000
--- a/system/embdrv/lc3/test/sns_py.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "lc3.h"
-#include <Python.h>
-#include <numpy/ndarrayobject.h>
-
-#include <sns.c>
-#include "ctypes.h"
-
-static PyObject *compute_scale_factors_py(PyObject *m, PyObject *args)
-{
-    unsigned dt, sr;
-    PyObject *eb_obj, *scf_obj;
-    float *eb, *scf;
-    int att;
-
-    if (!PyArg_ParseTuple(args, "IIOp", &dt, &sr, &eb_obj, &att))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-
-    int nb = LC3_MIN(lc3_band_lim[dt][sr][LC3_NUM_BANDS], LC3_NUM_BANDS);
-
-    CTYPES_CHECK("eb", to_1d_ptr(eb_obj, NPY_FLOAT, nb, &eb));
-    scf_obj = new_1d_ptr(NPY_FLOAT, 16, &scf);
-
-    compute_scale_factors(dt, sr, eb, att, scf);
-
-    return Py_BuildValue("N", scf_obj);
-}
-
-static PyObject *resolve_codebooks_py(PyObject *m, PyObject *args)
-{
-    PyObject *scf_obj;
-    float *scf;
-    int lfcb_idx, hfcb_idx;
-
-    if (!PyArg_ParseTuple(args, "O", &scf_obj))
-        return NULL;
-
-    CTYPES_CHECK("eb", to_1d_ptr(scf_obj, NPY_FLOAT, 16, &scf));
-
-    resolve_codebooks(scf, &lfcb_idx, &hfcb_idx);
-
-    return Py_BuildValue("ii", lfcb_idx, hfcb_idx);
-}
-
-static PyObject *quantize_py(PyObject *m, PyObject *args)
-{
-    PyObject *scf_obj, *y_obj, *yn_obj;
-    float *scf;
-    int lfcb_idx, hfcb_idx;
-    int shape_idx, gain_idx;
-    float (*yn)[16];
-    int (*y)[16];
-
-    if (!PyArg_ParseTuple(args, "Oii", &scf_obj, &lfcb_idx, &hfcb_idx))
-        return NULL;
-
-    CTYPES_CHECK("scf", to_1d_ptr(scf_obj, NPY_FLOAT, 16, &scf));
-    CTYPES_CHECK("lfcb_idx", (unsigned)lfcb_idx < 32);
-    CTYPES_CHECK("hfcb_idx", (unsigned)hfcb_idx < 32);
-
-    y_obj = new_2d_ptr(NPY_INT, 4, 16, &y);
-    yn_obj = new_2d_ptr(NPY_FLOAT, 4, 16, &yn);
-
-    quantize(scf, lfcb_idx, hfcb_idx,
-        y, yn, &shape_idx, &gain_idx);
-
-    return Py_BuildValue("NNii", y_obj, yn_obj, shape_idx, gain_idx);
-}
-
-static PyObject *unquantize_py(PyObject *m, PyObject *args)
-{
-    PyObject *y_obj, *scf_obj;
-    int lfcb_idx, hfcb_idx;
-    int shape, gain;
-    float *y, *scf;
-
-    if (!PyArg_ParseTuple(args, "iiOii",
-                &lfcb_idx, &hfcb_idx, &y_obj, &shape, &gain))
-        return NULL;
-
-    CTYPES_CHECK("lfcb_idx", (unsigned)lfcb_idx < 32);
-    CTYPES_CHECK("hfcb_idx", (unsigned)hfcb_idx < 32);
-    CTYPES_CHECK("y", to_1d_ptr(y_obj, NPY_FLOAT, 16, &y));
-    CTYPES_CHECK("shape", (unsigned)shape < 4);
-    CTYPES_CHECK("gain",
-        (unsigned)gain < (unsigned)lc3_sns_vq_gains[shape].count);
-
-    scf_obj = new_1d_ptr(NPY_FLOAT, 16, &scf);
-
-    unquantize(lfcb_idx, hfcb_idx, y, shape, gain, scf);
-
-    return Py_BuildValue("N", scf_obj);
-}
-
-static PyObject *spectral_shaping_py(PyObject *m, PyObject *args)
-{
-    PyObject *scf_q_obj, *x_obj;
-    unsigned dt, sr;
-    float *scf_q, *x;
-    int inv;
-
-    if (!PyArg_ParseTuple(args, "IIOpO", &dt, &sr, &scf_q_obj, &inv, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-
-    int ne = LC3_NE(dt, sr);
-
-    CTYPES_CHECK("scf_q", to_1d_ptr(scf_q_obj, NPY_FLOAT, 16, &scf_q));
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
-
-    spectral_shaping(dt, sr, scf_q, inv, x, x);
-
-    return Py_BuildValue("O", x_obj);
-}
-
-static PyObject *analyze_py(PyObject *m, PyObject *args)
-{
-    PyObject *eb_obj, *x_obj;
-    struct lc3_sns_data data = { 0 };
-    unsigned dt, sr;
-    float *eb, *x;
-    int att;
-
-    if (!PyArg_ParseTuple(args, "IIOpO", &dt, &sr, &eb_obj, &att, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-
-    int ne = LC3_NE(dt, sr);
-    int nb = LC3_MIN(ne, LC3_NUM_BANDS);
-
-    CTYPES_CHECK("eb", to_1d_ptr(eb_obj, NPY_FLOAT, nb, &eb));
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
-
-    lc3_sns_analyze(dt, sr, eb, att, &data, x, x);
-
-    return Py_BuildValue("ON", x_obj, new_sns_data(&data));
-}
-
-static PyObject *synthesize_py(PyObject *m, PyObject *args)
-{
-    PyObject *data_obj, *x_obj;
-    struct lc3_sns_data data;
-    unsigned dt, sr;
-    float *x;
-
-    if (!PyArg_ParseTuple(args, "IIOO", &dt, &sr, &data_obj, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-    CTYPES_CHECK(NULL, data_obj = to_sns_data(data_obj, &data));
-
-    int ne = LC3_NE(dt, sr);
-
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
-
-    lc3_sns_synthesize(dt, sr, &data, x, x);
-
-    return Py_BuildValue("O", x_obj);
-}
-
-static PyObject *get_nbits_py(PyObject *m, PyObject *args)
-{
-    if (!PyArg_ParseTuple(args, ""))
-        return NULL;
-
-    int nbits = lc3_sns_get_nbits();
-
-    return Py_BuildValue("i", nbits);
-}
-
-static PyMethodDef methods[] = {
-    { "sns_compute_scale_factors", compute_scale_factors_py, METH_VARARGS },
-    { "sns_resolve_codebooks"    , resolve_codebooks_py    , METH_VARARGS },
-    { "sns_quantize"             , quantize_py             , METH_VARARGS },
-    { "sns_unquantize"           , unquantize_py           , METH_VARARGS },
-    { "sns_spectral_shaping"     , spectral_shaping_py     , METH_VARARGS },
-    { "sns_analyze"              , analyze_py              , METH_VARARGS },
-    { "sns_synthesize"           , synthesize_py           , METH_VARARGS },
-    { "sns_get_nbits"            , get_nbits_py            , METH_VARARGS },
-    { NULL },
-};
-
-PyMODINIT_FUNC lc3_sns_py_init(PyObject *m)
-{
-    import_array();
-
-    PyModule_AddFunctions(m, methods);
-
-    return m;
-}
diff --git a/system/embdrv/lc3/test/spec.py b/system/embdrv/lc3/test/spec.py
deleted file mode 100644
index 0117d57..0000000
--- a/system/embdrv/lc3/test/spec.py
+++ /dev/null
@@ -1,812 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-
-import build.lc3 as lc3
-import tables as T, appendix_c as C
-
-import bwdet as m_bwdet
-import ltpf as m_ltpf
-import sns as m_sns
-import tns as m_tns
-
-### ------------------------------------------------------------------------ ###
-
-class SpectrumQuantization:
-
-    def __init__(self, dt, sr):
-
-        self.dt = dt
-        self.sr = sr
-
-    def get_gain_offset(self, nbytes):
-
-        g_off = (nbytes * 8) // (10 * (1 + self.sr))
-        g_off = -min(115, g_off) - (105 + 5*(1 + self.sr))
-
-        return g_off
-
-    def get_noise_indices(self, bw, xq, lastnz):
-
-        nf_start = [ 18, 24 ][self.dt]
-        nf_width = [  2,  3 ][self.dt]
-
-        bw_stop = int([ 80, 160, 240, 320, 400 ][bw] * (T.DT_MS[self.dt] / 10))
-
-        xq = np.append(xq[:lastnz], np.zeros(len(xq) - lastnz))
-
-        i_nf = [ np.all(xq[k-nf_width:min(bw_stop, k+nf_width+1)] == 0)
-                    for k in range(nf_start, bw_stop) ]
-
-        return (i_nf, nf_start, bw_stop)
-
-
-class SpectrumAnalysis(SpectrumQuantization):
-
-    def __init__(self, dt, sr):
-
-        super().__init__(dt, sr)
-
-        self.reset_off  = 0
-        self.nbits_off  = 0
-        self.nbits_spec = 0
-        self.nbits_est  = 0
-
-        self.g_idx = None
-
-    def estimate_gain(self, x, nbits_spec, nbits_off, g_off):
-
-        nbits = int(nbits_spec + nbits_off + 0.5)
-
-        ### Energy (dB) by 4 MDCT coefficients
-
-        e = [ np.sum(x[4*k:4*(k+1)] ** 2) for k in range(len(x) // 4) ]
-        e = 10 * np.log10(2**-31 + np.array(e))
-
-        ### Compute gain index
-
-        g_idx = 255
-
-        for i in range(8):
-            factor = 1 << (7 - i)
-            g_idx -= factor
-            tmp = 0
-            iszero = 1
-
-            for ei in e[-1::-1]:
-
-                if ei * 28/20 < g_idx + g_off:
-                    if iszero == 0:
-                        tmp += 2.7*28/20
-                else:
-                    if g_idx + g_off < (ei - 43) * 28/20:
-                        tmp += 2*ei*28/20 - 2*(g_idx + g_off) - 36*28/20
-                    else:
-                        tmp += ei*28/20 - (g_idx + g_off) + 7*28/20
-                    iszero = 0
-
-            if tmp > nbits * 1.4 * 28/20 and iszero == 0:
-                g_idx += factor
-
-        ### Limit gain index
-
-        x_max = np.amax(np.abs(x))
-        if x_max > 0:
-            g_min = 28 * np.log10(x_max / (32768 - 0.375))
-            g_min = np.ceil(g_min).astype(int) - g_off
-            reset_off = g_idx < g_min
-        else:
-            g_min = 0
-            reset_off = True
-
-        if reset_off:
-            g_idx = g_min
-
-        return (g_idx + g_off, reset_off)
-
-    def quantize(self, g_int, x):
-
-        xg = x / 10 ** (g_int / 28)
-
-        xq = np.where(xg < 0, np.ceil(xg - 0.375), np.floor(xg + 0.375))
-        xq = xq.astype(int)
-        xq = np.fmin(np.fmax(xq, -32768), 32767)
-
-        nz_pairs = np.any([ xq[::2] != 0, xq[1::2] != 0 ], axis=0)
-        lastnz = len(xq) - 2 * np.argmax(nz_pairs[-1::-1])
-        if not np.any(nz_pairs):
-            lastnz = 0
-
-        return (xg, xq, lastnz)
-
-    def compute_nbits(self, nbytes, x, lastnz, nbits_spec):
-
-        mode =   1 if nbytes >= 20 * (3 + self.sr) else 0
-        rate = 512 if nbytes >  20 * (1 + self.sr) else 0
-
-        nbits_est = 0
-        nbits_trunc = 0
-        nbits_lsb = 0
-        lastnz_trunc = 2
-        c = 0
-
-        for n in range(0, lastnz, 2):
-            t = c + rate
-            if n > len(x) // 2:
-                t += 256
-
-            a = abs(x[n  ])
-            b = abs(x[n+1])
-            lev = 0
-            while max(a, b) >= 4:
-                nbits_est += \
-                    T.AC_SPEC_BITS[T.AC_SPEC_LOOKUP[t + lev*1024]][16];
-                if lev == 0 and mode == 1:
-                    nbits_lsb += 2
-                else:
-                    nbits_est += 2 * 2048
-
-                a >>= 1
-                b >>= 1
-                lev = min(lev + 1, 3)
-
-            nbits_est += \
-                T.AC_SPEC_BITS[T.AC_SPEC_LOOKUP[t + lev*1024]][a + 4*b]
-
-            a_lsb = abs(x[n  ])
-            b_lsb = abs(x[n+1])
-            nbits_est += (min(a_lsb, 1) + min(b_lsb, 1)) * 2048
-            if lev > 0 and mode == 1:
-                a_lsb >>= 1;
-                b_lsb >>= 1;
-                nbits_lsb += int(a_lsb == 0 and x[n  ] != 0)
-                nbits_lsb += int(b_lsb == 0 and x[n+1] != 0)
-
-            if (x[n] != 0 or x[n+1] != 0) and \
-                    (nbits_est <= nbits_spec * 2048):
-                lastnz_trunc = n + 2;
-                nbits_trunc = nbits_est
-
-            t = 1 + (a + b) * (lev + 1) if lev <= 1 else 12 + lev;
-            c = (c & 15) * 16 + t;
-
-        nbits_est = (nbits_est + 2047) // 2048 + nbits_lsb;
-        nbits_trunc = (nbits_trunc + 2047) // 2048
-
-        self.rate = rate
-        self.lsb_mode = mode == 1 and nbits_est > nbits_spec
-
-        return (nbits_est, nbits_trunc, lastnz_trunc, self.lsb_mode)
-
-    def adjust_gain(self, g_idx, nbits, nbits_spec):
-
-        T1 = [  80,  230,  380,  530,  680 ]
-        T2 = [ 500, 1025, 1550, 2075, 2600 ]
-        T3 = [ 850, 1700, 2550, 3400, 4250 ]
-
-        sr = self.sr
-
-        if nbits < T1[sr]:
-            delta = (nbits + 48) / 16
-
-        elif nbits < T2[sr]:
-            a = T1[sr] / 16 + 3
-            b = T2[sr] / 48
-            delta = a + (nbits - T1[sr]) * (b - a) / (T2[sr] - T1[sr])
-
-        elif nbits < T3[sr]:
-            delta = nbits / 48
-
-        else:
-            delta = T3[sr] / 48;
-
-        delta = np.fix(delta + 0.5).astype(int)
-
-        if (g_idx < 255 and nbits > nbits_spec) or \
-           (g_idx >   0 and nbits < nbits_spec - (delta + 2)):
-
-            if nbits < nbits_spec - (delta + 2):
-                return - 1
-
-            if g_idx == 254 or nbits < nbits_spec + delta:
-                return 1
-
-            else:
-                return 2
-
-        return 0
-
-    def estimate_noise(self, bw, xq, lastnz, x):
-
-        (i_nf, nf_start, nf_stop) = self.get_noise_indices(bw, xq, lastnz)
-
-        nf = 8 - 16 * sum(abs(x[nf_start:nf_stop] * i_nf)) / sum(i_nf) \
-                if sum(i_nf) > 0 else 0
-
-        return min(max(np.rint(nf).astype(int), 0), 7)
-
-    def run(self,
-        bw, nbytes, nbits_bw, nbits_ltpf, nbits_sns, nbits_tns, x):
-
-        sr = self.sr
-
-        ### Bit budget
-
-        nbits_gain = 8
-        nbits_nf   = 3
-
-        nbits_ari  = np.ceil(np.log2(len(x) / 2)).astype(int)
-        nbits_ari += 3 + min((8*nbytes - 1) // 1280, 2)
-
-        nbits_spec = 8*nbytes - \
-            nbits_bw - nbits_ltpf - nbits_sns - nbits_tns - \
-            nbits_gain - nbits_nf - nbits_ari
-
-        ### Global gain estimation
-
-        nbits_off = self.nbits_off + self.nbits_spec - self.nbits_est
-        nbits_off = min(40, max(-40, nbits_off))
-
-        nbits_off = 0 if self.reset_off else \
-                    0.8 * self.nbits_off + 0.2 * nbits_off
-
-        g_off = self.get_gain_offset(nbytes)
-
-        (g_int, self.reset_off) = \
-            self.estimate_gain(x, nbits_spec, nbits_off, g_off)
-        self.nbits_off = nbits_off
-        self.nbits_spec = nbits_spec
-
-        ### Quantization
-
-        (xg, xq, lastnz) = self.quantize(g_int, x)
-
-        (nbits_est, nbits_trunc, lastnz_trunc, _) = \
-            self.compute_nbits(nbytes, xq, lastnz, nbits_spec)
-
-        self.nbits_est = nbits_est
-
-        ### Adjust gain and requantize
-
-        g_adj = self.adjust_gain(g_int - g_off, nbits_est, nbits_spec)
-
-        (xg, xq, lastnz) = self.quantize(g_adj, xg)
-
-        (nbits_est, nbits_trunc, lastnz_trunc, lsb_mode) = \
-            self.compute_nbits(nbytes, xq, lastnz, nbits_spec)
-
-        self.g_idx = g_int + g_adj - g_off
-        self.xq = xq
-        self.lastnz = lastnz_trunc
-
-        self.nbits_residual_max = nbits_spec - nbits_trunc + 4
-        self.xg = xg
-
-        ### Noise factor
-
-        self.noise_factor = self.estimate_noise(bw, xq, lastnz, x)
-
-        return (self.xq, self.lastnz, self.xg)
-
-    def store(self, b):
-
-        ne = T.NE[self.dt][self.sr]
-        nbits_lastnz = np.ceil(np.log2(ne/2)).astype(int)
-
-        b.write_uint((self.lastnz >> 1) - 1, nbits_lastnz)
-        b.write_uint(self.lsb_mode, 1)
-
-    def encode(self, bits):
-
-        ### Noise factor
-
-        bits.write_uint(self.noise_factor, 3)
-
-        ### Quantized data
-
-        lsbs = []
-
-        x = self.xq
-        c = 0
-
-        for n in range(0, self.lastnz, 2):
-            t = c + self.rate
-            if n > len(x) // 2:
-                t += 256
-
-            a = abs(x[n  ])
-            b = abs(x[n+1])
-            lev = 0
-            while max(a, b) >= 4:
-
-                bits.ac_encode(
-                    T.AC_SPEC_CUMFREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][16],
-                    T.AC_SPEC_FREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][16])
-
-                if lev == 0 and self.lsb_mode:
-                    lsb_0 = a & 1
-                    lsb_1 = b & 1
-                else:
-                    bits.write_bit(a & 1)
-                    bits.write_bit(b & 1)
-
-                a >>= 1
-                b >>= 1
-                lev = min(lev + 1, 3)
-
-            bits.ac_encode(
-                T.AC_SPEC_CUMFREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][a + 4*b],
-                T.AC_SPEC_FREQ[T.AC_SPEC_LOOKUP[t + lev*1024]][a + 4*b])
-
-            a_lsb = abs(x[n  ])
-            b_lsb = abs(x[n+1])
-            if lev > 0 and self.lsb_mode:
-                a_lsb >>= 1
-                b_lsb >>= 1
-
-                lsbs.append(lsb_0)
-                if a_lsb == 0 and x[n+0] != 0:
-                    lsbs.append(int(x[n+0] < 0))
-
-                lsbs.append(lsb_1)
-                if b_lsb == 0 and x[n+1] != 0:
-                    lsbs.append(int(x[n+1] < 0))
-
-            if a_lsb > 0:
-                bits.write_bit(int(x[n+0] < 0))
-
-            if b_lsb > 0:
-                bits.write_bit(int(x[n+1] < 0))
-
-            t = 1 + (a + b) * (lev + 1) if lev <= 1 else 12 + lev;
-            c = (c & 15) * 16 + t;
-
-        ### Residual data
-
-        if self.lsb_mode == 0:
-            nbits_residual = min(bits.get_bits_left(), self.nbits_residual_max)
-
-            for i in range(len(self.xg)):
-
-                if self.xq[i] == 0:
-                    continue
-
-                bits.write_bit(self.xg[i] >= self.xq[i])
-                nbits_residual -= 1
-                if nbits_residual <= 0:
-                    break
-
-        else:
-            nbits_residual = min(bits.get_bits_left(), len(lsbs))
-            for lsb in lsbs[:nbits_residual]:
-                bits.write_bit(lsb)
-
-
-class SpectrumSynthesis(SpectrumQuantization):
-
-    def __init__(self, dt, sr):
-
-        super().__init__(dt, sr)
-
-    def fill_noise(self, bw, x, lastnz, f_nf, nf_seed):
-
-        (i_nf, nf_start, nf_stop) = self.get_noise_indices(bw, x, lastnz)
-
-        k_nf = nf_start +  np.argwhere(i_nf)
-        l_nf = (8 - f_nf)/16
-
-        for k in k_nf:
-            nf_seed = (13849 + nf_seed * 31821) & 0xffff
-            x[k] = [ -l_nf, l_nf ][nf_seed < 0x8000]
-
-        return x
-
-    def load(self, b):
-
-        ne = T.NE[self.dt][self.sr]
-        nbits_lastnz = np.ceil(np.log2(ne/2)).astype(int)
-
-        self.lastnz = (b.read_uint(nbits_lastnz) + 1) << 1
-        self.lsb_mode = b.read_uint(1)
-        self.g_idx = b.read_uint(8)
-
-        if self.lastnz > ne:
-            raise ValueError('Invalid count of coded samples')
-
-    def decode(self, bits, bw, nbytes):
-
-        ### Noise factor
-
-        f_nf = bits.read_uint(3)
-
-        ### Quantized data
-
-        x = np.zeros(T.NE[self.dt][self.sr])
-        rate = 512 if nbytes >  20 * (1 + self.sr) else 0
-
-        levs = np.zeros(len(x), dtype=np.int)
-        c = 0
-
-        for n in range(0, self.lastnz, 2):
-            t = c + rate
-            if n > len(x) // 2:
-                t += 256
-
-            for lev in range(14):
-
-                s = t + min(lev, 3) * 1024
-
-                sym = bits.ac_decode(
-                    T.AC_SPEC_CUMFREQ[T.AC_SPEC_LOOKUP[s]],
-                    T.AC_SPEC_FREQ[T.AC_SPEC_LOOKUP[s]])
-
-                if sym < 16:
-                    break
-
-                if self.lsb_mode == 0 or lev > 0:
-                    x[n  ] += bits.read_bit() << lev
-                    x[n+1] += bits.read_bit() << lev
-
-            if lev >= 14:
-                raise ValueError('Out of range value')
-
-            a = sym %  4
-            b = sym // 4
-
-            levs[n  ] = lev
-            levs[n+1] = lev
-
-            x[n  ] += a << lev
-            x[n+1] += b << lev
-
-            if x[n] and bits.read_bit():
-                x[n] = -x[n]
-
-            if x[n+1] and bits.read_bit():
-                x[n+1] = -x[n+1]
-
-            lev = min(lev, 3)
-            t = 1 + (a + b) * (lev + 1) if lev <= 1 else 12 + lev;
-            c = (c & 15) * 16 + t;
-
-        ### Residual data
-
-        nbits_residual = bits.get_bits_left()
-        if nbits_residual < 0:
-            raise ValueError('Out of bitstream')
-
-        if self.lsb_mode == 0:
-
-            xr = np.zeros(len(x), dtype=np.bool)
-
-            for i in range(len(x)):
-
-                if nbits_residual <= 0:
-                    xr.resize(i)
-                    break
-
-                if x[i] == 0:
-                    continue
-
-                xr[i] = bits.read_bit()
-                nbits_residual -= 1
-
-        else:
-
-            for i in range(len(levs)):
-
-                if nbits_residual <= 0:
-                    break
-
-                if levs[i] <= 0:
-                    continue
-
-                lsb = bits.read_bit()
-                nbits_residual -= 1
-                if not lsb:
-                    continue
-
-                sign = int(x[i] < 0)
-
-                if x[i] == 0:
-
-                    if nbits_residual <= 0:
-                        break
-
-                    sign = bits.read_bit()
-                    nbits_residual -= 1
-
-                x[i] += [ 1, -1 ][sign]
-
-        ### Set residual and noise
-
-        nf_seed = sum(abs(x.astype(np.int)) * range(len(x)))
-
-        zero_frame = (self.lastnz <= 2 and x[0] == 0 and x[1] == 0
-                      and self.g_idx <= 0 and nf >= 7)
-
-        if self.lsb_mode == 0:
-
-            for i in range(len(xr)):
-
-                if x[i] and xr[i] == 0:
-                    x[i] += [ -0.1875, -0.3125 ][x[i] < 0]
-                elif x[i]:
-                    x[i] += [  0.1875,  0.3125 ][x[i] > 0]
-
-        if not zero_frame:
-            x = self.fill_noise(bw, x, self.lastnz, f_nf, nf_seed)
-
-        ### Rescale coefficients
-
-        g_int = self.get_gain_offset(nbytes) + self.g_idx
-        x *= 10 ** (g_int / 28)
-
-        return x
-
-
-def initial_state():
-    return { 'nbits_off' : 0.0, 'nbits_spare' : 0 }
-
-
-### ------------------------------------------------------------------------ ###
-
-def check_estimate_gain(rng, dt, sr):
-
-    ne = T.I[dt][sr][-1]
-    ok = True
-
-    analysis = SpectrumAnalysis(dt, sr)
-
-    for i in range(10):
-        x = rng.random(ne) * i * 1e2
-
-        nbytes = 20 + int(rng.random() * 100)
-        nbits_budget = 8 * nbytes - int(rng.random() * 100)
-        nbits_off = rng.random() * 10
-        g_off = 10 - int(rng.random() * 20)
-
-        (g_int, reset_off) = \
-            analysis.estimate_gain(x, nbits_budget, nbits_off, g_off)
-
-        (g_int_c, reset_off_c) = lc3.spec_estimate_gain(
-            dt, sr, x, nbits_budget, nbits_off, -g_off)
-
-        ok = ok and g_int_c == g_int
-        ok = ok and reset_off_c == reset_off
-
-    return ok
-
-def check_quantization(rng, dt, sr):
-
-    ne = T.I[dt][sr][-1]
-    ok = True
-
-    analysis = SpectrumAnalysis(dt, sr)
-
-    for g_int in range(-128, 128):
-
-        x = rng.random(ne) *  1e2
-        nbytes = 20 + int(rng.random() * 30)
-
-        (xg, xq, nq) = analysis.quantize(g_int, x)
-        (xg_c, xq_c, nq_c) = lc3.spec_quantize(dt, sr, g_int, x)
-
-        ok = ok and np.amax(np.abs(1 - xg_c/xg)) < 1e-6
-        ok = ok and np.any(abs(xq_c - xq) < 1)
-        ok = ok and nq_c == nq
-
-    return ok
-
-def check_compute_nbits(rng, dt, sr):
-
-    ne = T.I[dt][sr][-1]
-    ok = True
-
-    analysis = SpectrumAnalysis(dt, sr)
-
-    for nbytes in range(20, 150):
-
-        nbits_budget = nbytes * 8 - int(rng.random() * 100)
-        xq = (rng.random(ne) * 8).astype(int)
-        nq = ne // 2 + int(rng.random() * ne // 2)
-
-        nq = nq - nq % 2
-        if xq[nq-2] == 0 and xq[nq-1] == 0:
-            xq[nq-2] = 1
-
-        (nbits, nbits_trunc, nq_trunc, lsb_mode) = \
-            analysis.compute_nbits(nbytes, xq, nq, nbits_budget)
-
-        (nbits_c, nq_c, _) = \
-            lc3.spec_compute_nbits(dt, sr, nbytes, xq, nq, 0)
-
-        (nbits_trunc_c, nq_trunc_c, lsb_mode_c) = \
-            lc3.spec_compute_nbits(dt, sr, nbytes, xq, nq, nbits_budget)
-
-        ok = ok and nbits_c == nbits
-        ok = ok and nbits_trunc_c == nbits_trunc
-        ok = ok and nq_trunc_c == nq_trunc
-        ok = ok and lsb_mode_c == lsb_mode
-
-    return ok
-
-def check_adjust_gain(rng, dt, sr):
-
-    ne = T.I[dt][sr][-1]
-    ok = True
-
-    analysis = SpectrumAnalysis(dt, sr)
-
-    for g_idx in (0, 128, 254, 255):
-        for nbits in range(50, 5000, 5):
-            nbits_budget = int(nbits * (0.95 + (rng.random() * 0.1)))
-
-            g_adj = analysis.adjust_gain(g_idx, nbits, nbits_budget)
-
-            g_adj_c = lc3.spec_adjust_gain(sr, g_idx, nbits, nbits_budget)
-
-            ok = ok and g_adj_c == g_adj
-
-    return ok
-
-def check_unit(rng, dt, sr):
-
-    ns = T.NS[dt][sr]
-    ne = T.I[dt][sr][-1]
-    ok = True
-
-    state_c = initial_state()
-
-    bwdet = m_bwdet.BandwidthDetector(dt, sr)
-    ltpf = m_ltpf.LtpfAnalysis(dt, sr)
-    tns = m_tns.TnsAnalysis(dt)
-    sns = m_sns.SnsAnalysis(dt, sr)
-    analysis = SpectrumAnalysis(dt, sr)
-
-    nbytes = 100
-
-    for i in range(10):
-
-        x = rng.random(ns) * 1e4
-        e = rng.random(min(len(x), 64)) * 1e10
-
-        bwdet.run(e)
-        pitch_present = ltpf.run(x)
-        tns.run(x[:ne], sr, False, nbytes)
-        sns.run(e, False, x)
-
-        (xq, nq, _) = analysis.run(sr, nbytes, bwdet.get_nbits(),
-            ltpf.get_nbits(), sns.get_nbits(), tns.get_nbits(), x[:ne])
-
-        (_, xq_c, side_c) = lc3.spec_analyze(
-            dt, sr, nbytes, pitch_present, tns.get_data(), state_c, x[:ne])
-
-        ok = ok and side_c['g_idx'] == analysis.g_idx
-        ok = ok and side_c['nq'] == nq
-        ok = ok and np.any(abs(xq_c - xq) < 1)
-
-    return ok
-
-def check_noise(rng, dt, bw):
-
-    ne = T.NE[dt][bw]
-    ok = True
-
-    analysis = SpectrumAnalysis(dt, bw)
-
-    for i in range(10):
-
-        xq = ((rng.random(ne) - 0.5) * 10 ** (0.5)).astype(int)
-        nq = ne - int(rng.random() * 5)
-        x  = rng.random(ne) * i * 1e-1
-
-        nf = analysis.estimate_noise(bw, xq, nq, x)
-        nf_c = lc3.spec_estimate_noise(dt, bw, xq, nq, x)
-
-        ok = ok and nf_c == nf
-
-    return ok
-
-def check_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ne = T.NE[dt][sr]
-    ok = True
-
-    state_c = initial_state()
-
-    for i in range(len(C.X_F[dt])):
-
-        g_int = lc3.spec_estimate_gain(dt, sr, C.X_F[dt][i],
-            C.NBITS_SPEC[dt][i], C.NBITS_OFFSET[dt][i], -C.GG_OFF[dt][i])[0]
-        ok = ok and g_int == C.GG_IND[dt][i] + C.GG_OFF[dt][i]
-
-        (_, xq, nq) = lc3.spec_quantize(dt, sr,
-            C.GG_IND[dt][i] + C.GG_OFF[dt][i], C.X_F[dt][i])
-        ok = ok and np.any((xq - C.X_Q[dt][i]) == 0)
-        ok = ok and nq == C.LASTNZ[dt][i]
-
-        nbits = lc3.spec_compute_nbits(dt, sr,
-            C.NBYTES[dt], C.X_Q[dt][i], C.LASTNZ[dt][i], 0)[0]
-        ok = ok and nbits == C.NBITS_EST[dt][i]
-
-        g_adj = lc3.spec_adjust_gain(sr,
-            C.GG_IND[dt][i], C.NBITS_EST[dt][i], C.NBITS_SPEC[dt][i])
-        ok = ok and g_adj == C.GG_IND_ADJ[dt][i] - C.GG_IND[dt][i]
-
-        if C.GG_IND_ADJ[dt][i] != C.GG_IND[dt][i]:
-
-            (_, xq, nq) = lc3.spec_quantize(dt, sr,
-                C.GG_IND_ADJ[dt][i] + C.GG_OFF[dt][i], C.X_F[dt][i])
-            lastnz = C.LASTNZ_REQ[dt][i]
-            ok = ok and np.any(((xq - C.X_Q_REQ[dt][i])[:lastnz]) == 0)
-
-        tns_data = {
-            'nfilters' : C.NUM_TNS_FILTERS[dt][i],
-            'lpc_weighting' : [ True, True ],
-            'rc_order' : [ C.RC_ORDER[dt][i][0], 0 ],
-            'rc' : [ C.RC_I_1[dt][i] - 8, np.zeros(8, dtype = np.int) ]
-        }
-
-        (x, xq, side) = lc3.spec_analyze(dt, sr, C.NBYTES[dt],
-            C.PITCH_PRESENT[dt][i], tns_data, state_c, C.X_F[dt][i])
-
-        ok = ok and np.abs(state_c['nbits_off'] - C.NBITS_OFFSET[dt][i]) < 1e-5
-        if C.GG_IND_ADJ[dt][i] != C.GG_IND[dt][i]:
-            xq = C.X_Q_REQ[dt][i]
-            nq = C.LASTNZ_REQ[dt][i]
-            ok = ok and side['g_idx'] == C.GG_IND_ADJ[dt][i]
-            ok = ok and side['nq'] == nq
-            ok = ok and np.any(((xq[:nq] - xq[:nq])) == 0)
-        else:
-            xq = C.X_Q[dt][i]
-            nq = C.LASTNZ[dt][i]
-            ok = ok and side['g_idx'] == C.GG_IND[dt][i]
-            ok = ok and side['nq'] == nq
-            ok = ok and np.any((xq[:nq] - C.X_Q[dt][i][:nq]) == 0)
-        ok = ok and side['lsb_mode'] == C.LSB_MODE[dt][i]
-
-        gg = C.GG[dt][i] if C.GG_IND_ADJ[dt][i] == C.GG_IND[dt][i] \
-                else C.GG_ADJ[dt][i]
-
-        nf = lc3.spec_estimate_noise(dt, C.P_BW[dt][i],
-                xq, nq, C.X_F[dt][i] / gg)
-        ok = ok and nf == C.F_NF[dt][i]
-
-    return ok
-
-def check():
-
-    rng = np.random.default_rng(1234)
-    ok = True
-
-    for dt in range(T.NUM_DT):
-        for sr in range(T.NUM_SRATE):
-            ok = ok and check_estimate_gain(rng, dt, sr)
-            ok = ok and check_quantization(rng, dt, sr)
-            ok = ok and check_compute_nbits(rng, dt, sr)
-            ok = ok and check_adjust_gain(rng, dt, sr)
-            ok = ok and check_unit(rng, dt, sr)
-            ok = ok and check_noise(rng, dt, sr)
-
-    for dt in range(T.NUM_DT):
-        ok = ok and check_appendix_c(dt)
-
-    return ok
-
-### ------------------------------------------------------------------------ ###
diff --git a/system/embdrv/lc3/test/spec_py.c b/system/embdrv/lc3/test/spec_py.c
deleted file mode 100644
index 814f253..0000000
--- a/system/embdrv/lc3/test/spec_py.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "lc3.h"
-#include <Python.h>
-#include <numpy/ndarrayobject.h>
-
-#include <spec.c>
-#include "ctypes.h"
-
-static PyObject *estimate_gain_py(PyObject *m, PyObject *args)
-{
-    PyObject *x_obj;
-    unsigned dt, sr;
-    float *x;
-    int nbits_budget;
-    float nbits_off;
-    int g_off;
-    bool reset_off;
-
-    if (!PyArg_ParseTuple(args, "IIOifi", &dt, &sr,
-                &x_obj, &nbits_budget, &nbits_off, &g_off))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-
-    int ne = LC3_NE(dt, sr);
-
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
-
-    int g_int = estimate_gain(dt, sr,
-        x, nbits_budget, nbits_off, g_off, &reset_off);
-
-    return Py_BuildValue("ii", g_int, reset_off);
-}
-
-static PyObject *adjust_gain_py(PyObject *m, PyObject *args)
-{
-    unsigned sr;
-    int g_idx, nbits, nbits_budget;
-
-    if (!PyArg_ParseTuple(args, "Iiii", &sr, &g_idx, &nbits, &nbits_budget))
-        return NULL;
-
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-    CTYPES_CHECK("g_idx", g_idx >= 0 && g_idx <= 255);
-
-    g_idx = adjust_gain(sr, g_idx, nbits, nbits_budget);
-
-    return Py_BuildValue("i", g_idx);
-}
-
-static PyObject *quantize_py(PyObject *m, PyObject *args)
-{
-    PyObject *x_obj, *xq_obj;
-    unsigned dt, sr;
-    float *x;
-    int16_t *xq;
-    int g_int, nq;
-
-    if (!PyArg_ParseTuple(args, "IIiO", &dt, &sr, &g_int, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-    CTYPES_CHECK("g_int", g_int >= -255 && g_int <= 255);
-
-    int ne = LC3_NE(dt, sr);
-
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
-
-    xq_obj = new_1d_ptr(NPY_INT16, ne, &xq);
-    uint16_t __xq[ne];
-
-    quantize(dt, sr, g_int, x, __xq, &nq);
-
-    for (int i = 0; i < nq; i++)
-        xq[i] = __xq[i] & 1 ? -(__xq[i] >> 1) : (__xq[i] >> 1);
-
-    return Py_BuildValue("ONi", x_obj, xq_obj, nq);
-}
-
-static PyObject *compute_nbits_py(PyObject *m, PyObject *args)
-{
-    PyObject *xq_obj;
-    unsigned dt, sr, nbytes;
-    int16_t *xq;
-    int nq, nbits_budget;
-    bool lsb_mode;
-
-    if (!PyArg_ParseTuple(args, "IIIOii", &dt, &sr,
-                &nbytes, &xq_obj, &nq, &nbits_budget))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-
-    int ne = LC3_NE(dt, sr);
-
-    CTYPES_CHECK("xq", xq_obj = to_1d_ptr(xq_obj, NPY_INT16, ne, &xq));
-
-    uint16_t __xq[ne];
-    for (int i = 0; i < ne; i++)
-        __xq[i] = xq[i] < 0 ? (-xq[i] << 1) + 1 : (xq[i] << 1);
-
-    int nbits = compute_nbits(
-        dt, sr, nbytes, __xq, &nq, nbits_budget, &lsb_mode);
-
-    return Py_BuildValue("iii", nbits, nq, lsb_mode);
-}
-
-static PyObject *analyze_py(PyObject *m, PyObject *args)
-{
-    PyObject *tns_obj, *spec_obj, *x_obj, *xq_obj;
-    struct lc3_tns_data tns = { 0 };
-    struct lc3_spec_analysis spec = { 0 };
-    struct lc3_spec_side side = { 0 };
-    unsigned dt, sr, nbytes;
-    int pitch;
-    float *x;
-    int16_t *xq;
-
-    if (!PyArg_ParseTuple(args, "IIIpOOO", &dt, &sr, &nbytes,
-                &pitch, &tns_obj, &spec_obj, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)sr < LC3_NUM_SRATE);
-
-    int ne = LC3_NE(dt, sr);
-
-    CTYPES_CHECK(NULL, tns_obj = to_tns_data(tns_obj, &tns));
-    CTYPES_CHECK(NULL, spec_obj = to_spec_analysis(spec_obj, &spec));
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
-
-    xq_obj = new_1d_ptr(NPY_INT16, ne, &xq);
-    uint16_t __xq[ne];
-
-    lc3_spec_analyze(dt, sr, nbytes, pitch, &tns, &spec, x, __xq, &side);
-
-    for (int i = 0; i < ne; i++)
-        xq[i] = __xq[i] & 1 ? -(__xq[i] >> 1) : (__xq[i] >> 1);
-
-    from_spec_analysis(spec_obj, &spec);
-    return Py_BuildValue("ONN", x_obj, xq_obj, new_spec_side(&side));
-}
-
-static PyObject *estimate_noise_py(PyObject *m, PyObject *args)
-{
-    PyObject *x_obj, *xq_obj;
-    unsigned dt, bw;
-    int16_t *xq;
-    float *x;
-    int nq;
-
-    if (!PyArg_ParseTuple(args, "IIOIO", &dt, &bw, &xq_obj, &nq, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("bw", (unsigned)bw < LC3_NUM_BANDWIDTH);
-
-    int ne = LC3_NE(dt, bw);
-
-    CTYPES_CHECK("xq", xq_obj = to_1d_ptr(xq_obj, NPY_INT16, ne, &xq));
-    CTYPES_CHECK("x" , x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x ));
-
-    uint16_t __xq[nq];
-    for (int i = 0; i < nq; i++)
-        __xq[i] = xq[i] < 0 ? (-xq[i] << 1) + 1 : (xq[i] << 1);
-
-    int noise_factor = estimate_noise(dt, bw, __xq, nq, x);
-
-    return Py_BuildValue("i", noise_factor);
-}
-
-static PyMethodDef methods[] = {
-    { "spec_estimate_gain" , estimate_gain_py , METH_VARARGS },
-    { "spec_adjust_gain"   , adjust_gain_py   , METH_VARARGS },
-    { "spec_quantize"      , quantize_py      , METH_VARARGS },
-    { "spec_compute_nbits" , compute_nbits_py , METH_VARARGS },
-    { "spec_analyze"       , analyze_py       , METH_VARARGS },
-    { "spec_estimate_noise", estimate_noise_py, METH_VARARGS },
-    { NULL },
-};
-
-PyMODINIT_FUNC lc3_spec_py_init(PyObject *m)
-{
-    import_array();
-
-    PyModule_AddFunctions(m, methods);
-
-    return m;
-}
diff --git a/system/embdrv/lc3/test/tables.py b/system/embdrv/lc3/test/tables.py
deleted file mode 100644
index 4886586..0000000
--- a/system/embdrv/lc3/test/tables.py
+++ /dev/null
@@ -1,2709 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-
-
-### Generic ###
-
-DT_7M5 = 0
-DT_10M = 1
-NUM_DT = 2
-
-DT_MS = np.array([ 7.5, 10 ])
-
-
-SRATE_8K  = 0
-SRATE_16K = 1
-SRATE_24K = 2
-SRATE_32K = 3
-SRATE_48K = 4
-NUM_SRATE = 5
-
-SRATE_KHZ = np.array([ 8, 16, 24, 32, 48 ])
-
-
-NS = [ (SRATE_KHZ * DT_MS[dt]).astype(int) for dt in range(NUM_DT) ]
-NE = [ np.append(NS[dt][:-1], (NS[dt][-1] * 5) // 6) for dt in range(NUM_DT) ]
-
-ND = [ (23 * NS[0]) // 30, (5 * NS[1]) // 8 ]
-
-
-### 3.7.1/2 ###
-
-I_10M_8K = np.array([
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-     10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-     20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
-     30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
-     40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
-     51,  53,  55,  57,  59,  61,  63,  65,  67,  69,
-     71,  73,  75,  77,  80
-])
-
-I_10M_16K = np.array([
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-     10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-     20,  21,  22,  23,  24,  25,  26,  27,  28,  30,
-     32,  34,  36,  38,  40,  42,  44,  46,  48,  50,
-     52,  55,  58,  61,  64,  67,  70,  73,  76,  80,
-     84,  88,  92,  96, 101, 106, 111, 116, 121, 127,
-    133, 139, 146, 153, 160
-])
-
-I_10M_24K = np.array([
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-     10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-     20,  21,  22,  23,  25,  27,  29,  31,  33,  35,
-     37,  39,  41,  43,  46,  49,  52,  55,  58,  61,
-     64,  68,  72,  76,  80,  85,  90,  95, 100, 106,
-    112, 118, 125, 132, 139, 147, 155, 164, 173, 183,
-    193, 204, 215, 227, 240
-])
-
-I_10M_32K = np.array([
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-     10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-     20,  22,  24,  26,  28,  30,  32,  34,  36,  38,
-     41,  44,  47,  50,  53,  56,  60,  64,  68,  72,
-     76,  81,  86,  91,  97, 103, 109, 116, 123, 131,
-    139, 148, 157, 166, 176, 187, 199, 211, 224, 238,
-    252, 268, 284, 302, 320
-])
-
-I_10M_48K = np.array([
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-     10,  11,  12,  13,  14,  15,  16,  17,  18,  20,
-     22,  24,  26,  28,  30,  32,  34,  36,  39,  42,
-     45,  48,  51,  55,  59,  63,  67,  71,  76,  81,
-     86,  92,  98, 105, 112, 119, 127, 135, 144, 154,
-    164, 175, 186, 198, 211, 225, 240, 256, 273, 291,
-    310, 330, 352, 375, 400
-])
-
-I_7M5_8K = np.array([
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-     10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-     20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
-     30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
-     40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
-     50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
-     60
-])
-
-I_7M5_16K = np.array([
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-     10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-     20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
-     30,  31,  32,  33,  34,  36,  38,  40,  42,  44,
-     46,  48,  50,  52,  54,  56,  58,  60,  62,  65,
-     68,  71,  74,  77,  80,  83,  86,  90,  94,  98,
-    102, 106, 110, 115, 120
-])
-
-I_7M5_24K = np.array([
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-     10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-     20,  21,  22,  23,  24,  25,  26,  27,  29,  31,
-     33,  35,  37,  39,  41,  43,  45,  47,  49,  52,
-     55,  58,  61,  64,  67,  70,  74,  78,  82,  86,
-     90,  95, 100, 105, 110, 115, 121, 127, 134, 141,
-    148, 155, 163, 171, 180
-])
-
-I_7M5_32K = np.array([
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-     10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-     20,  21,  22,  23,  24,  26,  28,  30,  32,  34,
-     36,  38,  40,  42,  45,  48,  51,  54,  57,  60,
-     63,  67,  71,  75,  79,  84,  89,  94,  99, 105,
-    111, 117, 124, 131, 138, 146, 154, 163, 172, 182,
-    192, 203, 215, 227, 240
-])
-
-I_7M5_48K = np.array([
-      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
-     10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
-     20,  21,  22,  24,  26,  28,  30,  32,  34,  36,
-     38,  40,  43,  46,  49,  52,  55,  59,  63,  67,
-     71,  75,  80,  85,  90,  96, 102, 108, 115, 122,
-    129, 137, 146, 155, 165, 175, 186, 197, 209, 222,
-    236, 251, 266, 283, 300
-])
-
-I = [ [ I_7M5_8K, I_7M5_16K, I_7M5_24K, I_7M5_32K, I_7M5_48K ],
-      [ I_10M_8K, I_10M_16K, I_10M_24K, I_10M_32K, I_10M_48K ] ]
-
-
-### 3.7.3 ###
-
-W_10M_80 = np.array([
-    -7.07854671e-04, -2.09819773e-03, -4.52519808e-03, -8.23397633e-03,
-    -1.33771310e-02, -1.99972156e-02, -2.80090946e-02, -3.72150208e-02,
-    -4.73176826e-02, -5.79465483e-02, -6.86760675e-02, -7.90464744e-02,
-    -8.85970547e-02, -9.68830362e-02, -1.03496124e-01, -1.08076646e-01,
-    -1.10324226e-01, -1.09980985e-01, -1.06817214e-01, -1.00619042e-01,
-    -9.11645251e-02, -7.82061748e-02, -6.14668812e-02, -4.06336286e-02,
-    -1.53632952e-02,  1.47015507e-02,  4.98973651e-02,  9.05036926e-02,
-     1.36691102e-01,  1.88468639e-01,  2.45645680e-01,  3.07778908e-01,
-     3.74164237e-01,  4.43811480e-01,  5.15473546e-01,  5.87666172e-01,
-     6.58761977e-01,  7.27057670e-01,  7.90875299e-01,  8.48664336e-01,
-     8.99132024e-01,  9.41334815e-01,  9.74763483e-01,  9.99411473e-01,
-     1.01576037e+00,  1.02473616e+00,  1.02763429e+00,  1.02599149e+00,
-     1.02142721e+00,  1.01543986e+00,  1.00936693e+00,  1.00350816e+00,
-     9.98889821e-01,  9.95313390e-01,  9.92594392e-01,  9.90577196e-01,
-     9.89137162e-01,  9.88179075e-01,  9.87624927e-01,  9.87405628e-01,
-     9.87452485e-01,  9.87695113e-01,  9.88064062e-01,  9.88492687e-01,
-     9.88923003e-01,  9.89307497e-01,  9.89614633e-01,  9.89831927e-01,
-     9.89969310e-01,  9.90060335e-01,  9.90157502e-01,  9.90325529e-01,
-     9.90630379e-01,  9.91129889e-01,  9.91866549e-01,  9.92861973e-01,
-     9.94115607e-01,  9.95603378e-01,  9.97279311e-01,  9.99078484e-01,
-     1.00092237e+00,  1.00272811e+00,  1.00441604e+00,  1.00591922e+00,
-     1.00718935e+00,  1.00820015e+00,  1.00894949e+00,  1.00945824e+00,
-     1.00976898e+00,  1.00994034e+00,  1.01003945e+00,  1.01013232e+00,
-     1.01027252e+00,  1.01049435e+00,  1.01080807e+00,  1.01120107e+00,
-     1.01164127e+00,  1.01208013e+00,  1.01245818e+00,  1.01270696e+00,
-     1.01275501e+00,  1.01253013e+00,  1.01196233e+00,  1.01098214e+00,
-     1.00951244e+00,  1.00746086e+00,  1.00470868e+00,  1.00111141e+00,
-     9.96504102e-01,  9.90720000e-01,  9.82376587e-01,  9.70882175e-01,
-     9.54673298e-01,  9.32155386e-01,  9.01800368e-01,  8.62398408e-01,
-     8.13281737e-01,  7.54455197e-01,  6.86658072e-01,  6.11348804e-01,
-     5.30618165e-01,  4.47130985e-01,  3.63911468e-01,  2.84164703e-01,
-     2.11020945e-01,  1.47228797e-01,  9.48266535e-02,  5.48243661e-02,
-     2.70146141e-02,  9.99674359e-03,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-])
-
-W_10M_160 = np.array([
-    -4.61989875e-04, -9.74716672e-04, -1.66447310e-03, -2.59710692e-03,
-    -3.80628516e-03, -5.32460872e-03, -7.17588528e-03, -9.38248086e-03,
-    -1.19527030e-02, -1.48952816e-02, -1.82066640e-02, -2.18757093e-02,
-    -2.58847194e-02, -3.02086274e-02, -3.48159779e-02, -3.96706799e-02,
-    -4.47269805e-02, -4.99422586e-02, -5.52633479e-02, -6.06371724e-02,
-    -6.60096152e-02, -7.13196627e-02, -7.65117823e-02, -8.15296401e-02,
-    -8.63113754e-02, -9.08041129e-02, -9.49537776e-02, -9.87073651e-02,
-    -1.02020268e-01, -1.04843883e-01, -1.07138231e-01, -1.08869014e-01,
-    -1.09996966e-01, -1.10489847e-01, -1.10322584e-01, -1.09462175e-01,
-    -1.07883429e-01, -1.05561251e-01, -1.02465016e-01, -9.85701457e-02,
-    -9.38468492e-02, -8.82630999e-02, -8.17879272e-02, -7.43878560e-02,
-    -6.60218980e-02, -5.66565564e-02, -4.62445689e-02, -3.47458578e-02,
-    -2.21158161e-02, -8.31042570e-03,  6.71769764e-03,  2.30064206e-02,
-     4.06010646e-02,  5.95323909e-02,  7.98335419e-02,  1.01523314e-01,
-     1.24617139e-01,  1.49115252e-01,  1.75006740e-01,  2.02269985e-01,
-     2.30865538e-01,  2.60736512e-01,  2.91814469e-01,  3.24009570e-01,
-     3.57217518e-01,  3.91314689e-01,  4.26157164e-01,  4.61592545e-01,
-     4.97447159e-01,  5.33532682e-01,  5.69654673e-01,  6.05608382e-01,
-     6.41183084e-01,  6.76165350e-01,  7.10340055e-01,  7.43494372e-01,
-     7.75428189e-01,  8.05943723e-01,  8.34858937e-01,  8.62010834e-01,
-     8.87259971e-01,  9.10486312e-01,  9.31596250e-01,  9.50522086e-01,
-     9.67236671e-01,  9.81739750e-01,  9.94055718e-01,  1.00424751e+00,
-     1.01240743e+00,  1.01865099e+00,  1.02311884e+00,  1.02597245e+00,
-     1.02739752e+00,  1.02758583e+00,  1.02673867e+00,  1.02506178e+00,
-     1.02275651e+00,  1.02000914e+00,  1.01699650e+00,  1.01391595e+00,
-     1.01104487e+00,  1.00777386e+00,  1.00484875e+00,  1.00224501e+00,
-     9.99939317e-01,  9.97905542e-01,  9.96120338e-01,  9.94559753e-01,
-     9.93203161e-01,  9.92029727e-01,  9.91023065e-01,  9.90166895e-01,
-     9.89448837e-01,  9.88855636e-01,  9.88377852e-01,  9.88005163e-01,
-     9.87729546e-01,  9.87541274e-01,  9.87432981e-01,  9.87394992e-01,
-     9.87419705e-01,  9.87497321e-01,  9.87620124e-01,  9.87778192e-01,
-     9.87963798e-01,  9.88167801e-01,  9.88383520e-01,  9.88602222e-01,
-     9.88818277e-01,  9.89024798e-01,  9.89217866e-01,  9.89392368e-01,
-     9.89546334e-01,  9.89677201e-01,  9.89785920e-01,  9.89872536e-01,
-     9.89941079e-01,  9.89994556e-01,  9.90039402e-01,  9.90081472e-01,
-     9.90129379e-01,  9.90190227e-01,  9.90273445e-01,  9.90386228e-01,
-     9.90537983e-01,  9.90734883e-01,  9.90984259e-01,  9.91290512e-01,
-     9.91658694e-01,  9.92090615e-01,  9.92588721e-01,  9.93151653e-01,
-     9.93779087e-01,  9.94466818e-01,  9.95211663e-01,  9.96006862e-01,
-     9.96846133e-01,  9.97720337e-01,  9.98621352e-01,  9.99538258e-01,
-     1.00046196e+00,  1.00138055e+00,  1.00228487e+00,  1.00316385e+00,
-     1.00400915e+00,  1.00481138e+00,  1.00556397e+00,  1.00625986e+00,
-     1.00689557e+00,  1.00746662e+00,  1.00797244e+00,  1.00841147e+00,
-     1.00878601e+00,  1.00909776e+00,  1.00935176e+00,  1.00955240e+00,
-     1.00970709e+00,  1.00982209e+00,  1.00990696e+00,  1.00996902e+00,
-     1.01001789e+00,  1.01006081e+00,  1.01010656e+00,  1.01016113e+00,
-     1.01023108e+00,  1.01031948e+00,  1.01043047e+00,  1.01056410e+00,
-     1.01072136e+00,  1.01089966e+00,  1.01109699e+00,  1.01130817e+00,
-     1.01152919e+00,  1.01175301e+00,  1.01197388e+00,  1.01218284e+00,
-     1.01237303e+00,  1.01253506e+00,  1.01266098e+00,  1.01274058e+00,
-     1.01276592e+00,  1.01272696e+00,  1.01261590e+00,  1.01242289e+00,
-     1.01214046e+00,  1.01175881e+00,  1.01126996e+00,  1.01066368e+00,
-     1.00993075e+00,  1.00905825e+00,  1.00803431e+00,  1.00684335e+00,
-     1.00547001e+00,  1.00389477e+00,  1.00209885e+00,  1.00006069e+00,
-     9.97760020e-01,  9.95174643e-01,  9.92286108e-01,  9.89075787e-01,
-     9.84736245e-01,  9.79861353e-01,  9.74137862e-01,  9.67333198e-01,
-     9.59253976e-01,  9.49698408e-01,  9.38463416e-01,  9.25356797e-01,
-     9.10198679e-01,  8.92833832e-01,  8.73143784e-01,  8.51042044e-01,
-     8.26483991e-01,  7.99468149e-01,  7.70043128e-01,  7.38302860e-01,
-     7.04381434e-01,  6.68461648e-01,  6.30775533e-01,  5.91579959e-01,
-     5.51170316e-01,  5.09891542e-01,  4.68101711e-01,  4.26177297e-01,
-     3.84517234e-01,  3.43522867e-01,  3.03600465e-01,  2.65143468e-01,
-     2.28528397e-01,  1.94102191e-01,  1.62173542e-01,  1.33001524e-01,
-     1.06784043e-01,  8.36505724e-02,  6.36518811e-02,  4.67653841e-02,
-     3.28807275e-02,  2.18305756e-02,  1.33638143e-02,  6.75812489e-03,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-])
-
-W_10M_240 = np.array([
-    -3.61349642e-04, -7.07854671e-04, -1.07444364e-03, -1.53347854e-03,
-    -2.09819773e-03, -2.77842087e-03, -3.58412992e-03, -4.52519808e-03,
-    -5.60932724e-03, -6.84323454e-03, -8.23397633e-03, -9.78531476e-03,
-    -1.14988030e-02, -1.33771310e-02, -1.54218168e-02, -1.76297991e-02,
-    -1.99972156e-02, -2.25208056e-02, -2.51940630e-02, -2.80090946e-02,
-    -3.09576509e-02, -3.40299627e-02, -3.72150208e-02, -4.05005325e-02,
-    -4.38721922e-02, -4.73176826e-02, -5.08232534e-02, -5.43716664e-02,
-    -5.79465483e-02, -6.15342620e-02, -6.51170816e-02, -6.86760675e-02,
-    -7.21944781e-02, -7.56569598e-02, -7.90464744e-02, -8.23444256e-02,
-    -8.55332458e-02, -8.85970547e-02, -9.15209110e-02, -9.42884745e-02,
-    -9.68830362e-02, -9.92912326e-02, -1.01500847e-01, -1.03496124e-01,
-    -1.05263700e-01, -1.06793998e-01, -1.08076646e-01, -1.09099730e-01,
-    -1.09852449e-01, -1.10324226e-01, -1.10508462e-01, -1.10397741e-01,
-    -1.09980985e-01, -1.09249277e-01, -1.08197423e-01, -1.06817214e-01,
-    -1.05099580e-01, -1.03036011e-01, -1.00619042e-01, -9.78412002e-02,
-    -9.46930422e-02, -9.11645251e-02, -8.72464453e-02, -8.29304391e-02,
-    -7.82061748e-02, -7.30614243e-02, -6.74846818e-02, -6.14668812e-02,
-    -5.49949726e-02, -4.80544442e-02, -4.06336286e-02, -3.27204559e-02,
-    -2.43012258e-02, -1.53632952e-02, -5.89143427e-03,  4.12659586e-03,
-     1.47015507e-02,  2.58473819e-02,  3.75765277e-02,  4.98973651e-02,
-     6.28203403e-02,  7.63539773e-02,  9.05036926e-02,  1.05274712e-01,
-     1.20670347e-01,  1.36691102e-01,  1.53334389e-01,  1.70595471e-01,
-     1.88468639e-01,  2.06944996e-01,  2.26009300e-01,  2.45645680e-01,
-     2.65834602e-01,  2.86554381e-01,  3.07778908e-01,  3.29476944e-01,
-     3.51617148e-01,  3.74164237e-01,  3.97073959e-01,  4.20304305e-01,
-     4.43811480e-01,  4.67544229e-01,  4.91449863e-01,  5.15473546e-01,
-     5.39555764e-01,  5.63639982e-01,  5.87666172e-01,  6.11569531e-01,
-     6.35289059e-01,  6.58761977e-01,  6.81923097e-01,  7.04709282e-01,
-     7.27057670e-01,  7.48906896e-01,  7.70199019e-01,  7.90875299e-01,
-     8.10878869e-01,  8.30157914e-01,  8.48664336e-01,  8.66354816e-01,
-     8.83189685e-01,  8.99132024e-01,  9.14154056e-01,  9.28228255e-01,
-     9.41334815e-01,  9.53461939e-01,  9.64604825e-01,  9.74763483e-01,
-     9.83943539e-01,  9.92152910e-01,  9.99411473e-01,  1.00574608e+00,
-     1.01118397e+00,  1.01576037e+00,  1.01951507e+00,  1.02249094e+00,
-     1.02473616e+00,  1.02630410e+00,  1.02725098e+00,  1.02763429e+00,
-     1.02751106e+00,  1.02694280e+00,  1.02599149e+00,  1.02471615e+00,
-     1.02317598e+00,  1.02142721e+00,  1.01952157e+00,  1.01751012e+00,
-     1.01543986e+00,  1.01346092e+00,  1.01165490e+00,  1.00936693e+00,
-     1.00726318e+00,  1.00531319e+00,  1.00350816e+00,  1.00184079e+00,
-     1.00030393e+00,  9.98889821e-01,  9.97591528e-01,  9.96401528e-01,
-     9.95313390e-01,  9.94320108e-01,  9.93415896e-01,  9.92594392e-01,
-     9.91851028e-01,  9.91179799e-01,  9.90577196e-01,  9.90038105e-01,
-     9.89559439e-01,  9.89137162e-01,  9.88768437e-01,  9.88449792e-01,
-     9.88179075e-01,  9.87952836e-01,  9.87769137e-01,  9.87624927e-01,
-     9.87517995e-01,  9.87445813e-01,  9.87405628e-01,  9.87395112e-01,
-     9.87411537e-01,  9.87452485e-01,  9.87514989e-01,  9.87596889e-01,
-     9.87695113e-01,  9.87807582e-01,  9.87931200e-01,  9.88064062e-01,
-     9.88203257e-01,  9.88347108e-01,  9.88492687e-01,  9.88638659e-01,
-     9.88782558e-01,  9.88923003e-01,  9.89058172e-01,  9.89186767e-01,
-     9.89307497e-01,  9.89419640e-01,  9.89522076e-01,  9.89614633e-01,
-     9.89697035e-01,  9.89769260e-01,  9.89831927e-01,  9.89885257e-01,
-     9.89930764e-01,  9.89969310e-01,  9.90002569e-01,  9.90032156e-01,
-     9.90060335e-01,  9.90088981e-01,  9.90120659e-01,  9.90157502e-01,
-     9.90202395e-01,  9.90257541e-01,  9.90325529e-01,  9.90408791e-01,
-     9.90509649e-01,  9.90630379e-01,  9.90772711e-01,  9.90938744e-01,
-     9.91129889e-01,  9.91347632e-01,  9.91592856e-01,  9.91866549e-01,
-     9.92169132e-01,  9.92501085e-01,  9.92861973e-01,  9.93251918e-01,
-     9.93670021e-01,  9.94115607e-01,  9.94587315e-01,  9.95083740e-01,
-     9.95603378e-01,  9.96143992e-01,  9.96703453e-01,  9.97279311e-01,
-     9.97869086e-01,  9.98469709e-01,  9.99078484e-01,  9.99691901e-01,
-     1.00030819e+00,  1.00092237e+00,  1.00153264e+00,  1.00213546e+00,
-     1.00272811e+00,  1.00330745e+00,  1.00387093e+00,  1.00441604e+00,
-     1.00494055e+00,  1.00544214e+00,  1.00591922e+00,  1.00637030e+00,
-     1.00679393e+00,  1.00718935e+00,  1.00755557e+00,  1.00789267e+00,
-     1.00820015e+00,  1.00847842e+00,  1.00872788e+00,  1.00894949e+00,
-     1.00914411e+00,  1.00931322e+00,  1.00945824e+00,  1.00958128e+00,
-     1.00968409e+00,  1.00976898e+00,  1.00983831e+00,  1.00989455e+00,
-     1.00994034e+00,  1.00997792e+00,  1.01001023e+00,  1.01003945e+00,
-     1.01006820e+00,  1.01009839e+00,  1.01013232e+00,  1.01017166e+00,
-     1.01021810e+00,  1.01027252e+00,  1.01033649e+00,  1.01041022e+00,
-     1.01049435e+00,  1.01058887e+00,  1.01069350e+00,  1.01080807e+00,
-     1.01093144e+00,  1.01106288e+00,  1.01120107e+00,  1.01134470e+00,
-     1.01149190e+00,  1.01164127e+00,  1.01179028e+00,  1.01193757e+00,
-     1.01208013e+00,  1.01221624e+00,  1.01234291e+00,  1.01245818e+00,
-     1.01255888e+00,  1.01264286e+00,  1.01270696e+00,  1.01274895e+00,
-     1.01276580e+00,  1.01275501e+00,  1.01271380e+00,  1.01263978e+00,
-     1.01253013e+00,  1.01238231e+00,  1.01219407e+00,  1.01196233e+00,
-     1.01168517e+00,  1.01135914e+00,  1.01098214e+00,  1.01055072e+00,
-     1.01006213e+00,  1.00951244e+00,  1.00889869e+00,  1.00821592e+00,
-     1.00746086e+00,  1.00662774e+00,  1.00571234e+00,  1.00470868e+00,
-     1.00361147e+00,  1.00241429e+00,  1.00111141e+00,  9.99696165e-01,
-     9.98162595e-01,  9.96504102e-01,  9.94714888e-01,  9.92789191e-01,
-     9.90720000e-01,  9.88479371e-01,  9.85534766e-01,  9.82376587e-01,
-     9.78974733e-01,  9.75162381e-01,  9.70882175e-01,  9.66080552e-01,
-     9.60697640e-01,  9.54673298e-01,  9.47947935e-01,  9.40460905e-01,
-     9.32155386e-01,  9.22977548e-01,  9.12874535e-01,  9.01800368e-01,
-     8.89716328e-01,  8.76590897e-01,  8.62398408e-01,  8.47120080e-01,
-     8.30747973e-01,  8.13281737e-01,  7.94729145e-01,  7.75110884e-01,
-     7.54455197e-01,  7.32796355e-01,  7.10179084e-01,  6.86658072e-01,
-     6.62296243e-01,  6.37168412e-01,  6.11348804e-01,  5.84920660e-01,
-     5.57974743e-01,  5.30618165e-01,  5.02952396e-01,  4.75086883e-01,
-     4.47130985e-01,  4.19204992e-01,  3.91425291e-01,  3.63911468e-01,
-     3.36783777e-01,  3.10162784e-01,  2.84164703e-01,  2.58903371e-01,
-     2.34488060e-01,  2.11020945e-01,  1.88599764e-01,  1.67310081e-01,
-     1.47228797e-01,  1.28422307e-01,  1.10942255e-01,  9.48266535e-02,
-     8.00991437e-02,  6.67676585e-02,  5.48243661e-02,  4.42458885e-02,
-     3.49936100e-02,  2.70146141e-02,  2.02437018e-02,  1.46079676e-02,
-     9.99674359e-03,  5.30523510e-03,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-])
-
-W_10M_320 = np.array([
-    -3.02115349e-04, -5.86773749e-04, -8.36650400e-04, -1.12663536e-03,
-    -1.47049294e-03, -1.87347339e-03, -2.33929236e-03, -2.87200807e-03,
-    -3.47625639e-03, -4.15596382e-03, -4.91456379e-03, -5.75517250e-03,
-    -6.68062338e-03, -7.69381692e-03, -8.79676075e-03, -9.99050307e-03,
-    -1.12757412e-02, -1.26533415e-02, -1.41243899e-02, -1.56888962e-02,
-    -1.73451209e-02, -1.90909737e-02, -2.09254671e-02, -2.28468479e-02,
-    -2.48520772e-02, -2.69374670e-02, -2.90995249e-02, -3.13350463e-02,
-    -3.36396073e-02, -3.60082097e-02, -3.84360174e-02, -4.09174603e-02,
-    -4.34465489e-02, -4.60178672e-02, -4.86259851e-02, -5.12647420e-02,
-    -5.39264475e-02, -5.66038431e-02, -5.92911675e-02, -6.19826820e-02,
-    -6.46702555e-02, -6.73454222e-02, -7.00009902e-02, -7.26305701e-02,
-    -7.52278496e-02, -7.77852594e-02, -8.02948025e-02, -8.27492454e-02,
-    -8.51412546e-02, -8.74637912e-02, -8.97106934e-02, -9.18756408e-02,
-    -9.39517698e-02, -9.59313774e-02, -9.78084326e-02, -9.95785130e-02,
-    -1.01236117e-01, -1.02774104e-01, -1.04186122e-01, -1.05468025e-01,
-    -1.06616088e-01, -1.07625538e-01, -1.08491230e-01, -1.09208742e-01,
-    -1.09773615e-01, -1.10180886e-01, -1.10427188e-01, -1.10510836e-01,
-    -1.10428147e-01, -1.10173922e-01, -1.09743736e-01, -1.09135313e-01,
-    -1.08346734e-01, -1.07373994e-01, -1.06213016e-01, -1.04860615e-01,
-    -1.03313240e-01, -1.01567316e-01, -9.96200551e-02, -9.74680323e-02,
-    -9.51072362e-02, -9.25330338e-02, -8.97412522e-02, -8.67287769e-02,
-    -8.34921384e-02, -8.00263990e-02, -7.63267954e-02, -7.23880616e-02,
-    -6.82057680e-02, -6.37761143e-02, -5.90938600e-02, -5.41531632e-02,
-    -4.89481272e-02, -4.34734711e-02, -3.77246130e-02, -3.16958761e-02,
-    -2.53817983e-02, -1.87768910e-02, -1.18746138e-02, -4.66909925e-03,
-     2.84409675e-03,  1.06697612e-02,  1.88135595e-02,  2.72815601e-02,
-     3.60781047e-02,  4.52070276e-02,  5.46723880e-02,  6.44786605e-02,
-     7.46286220e-02,  8.51249057e-02,  9.59698399e-02,  1.07165078e-01,
-     1.18711585e-01,  1.30610107e-01,  1.42859645e-01,  1.55458473e-01,
-     1.68404161e-01,  1.81694789e-01,  1.95327388e-01,  2.09296321e-01,
-     2.23594564e-01,  2.38216022e-01,  2.53152972e-01,  2.68396157e-01,
-     2.83936139e-01,  2.99762426e-01,  3.15861908e-01,  3.32221055e-01,
-     3.48826468e-01,  3.65664038e-01,  3.82715297e-01,  3.99961186e-01,
-     4.17384327e-01,  4.34966962e-01,  4.52687640e-01,  4.70524201e-01,
-     4.88453925e-01,  5.06454555e-01,  5.24500675e-01,  5.42567437e-01,
-     5.60631204e-01,  5.78667265e-01,  5.96647704e-01,  6.14545890e-01,
-     6.32336194e-01,  6.49992632e-01,  6.67487403e-01,  6.84793267e-01,
-     7.01883546e-01,  7.18732254e-01,  7.35312821e-01,  7.51600199e-01,
-     7.67569925e-01,  7.83197457e-01,  7.98458386e-01,  8.13329535e-01,
-     8.27789227e-01,  8.41817856e-01,  8.55396130e-01,  8.68506898e-01,
-     8.81133444e-01,  8.93259678e-01,  9.04874884e-01,  9.15965761e-01,
-     9.26521530e-01,  9.36533999e-01,  9.45997703e-01,  9.54908841e-01,
-     9.63265812e-01,  9.71068890e-01,  9.78320416e-01,  9.85022676e-01,
-     9.91179208e-01,  9.96798994e-01,  1.00189402e+00,  1.00647434e+00,
-     1.01055206e+00,  1.01414254e+00,  1.01726259e+00,  1.01992884e+00,
-     1.02215987e+00,  1.02397632e+00,  1.02540073e+00,  1.02645534e+00,
-     1.02716451e+00,  1.02755273e+00,  1.02764446e+00,  1.02746325e+00,
-     1.02703590e+00,  1.02638907e+00,  1.02554820e+00,  1.02453713e+00,
-     1.02338080e+00,  1.02210370e+00,  1.02072836e+00,  1.01927533e+00,
-     1.01776518e+00,  1.01621736e+00,  1.01466531e+00,  1.01324907e+00,
-     1.01194801e+00,  1.01018909e+00,  1.00855796e+00,  1.00701129e+00,
-     1.00554876e+00,  1.00416842e+00,  1.00286727e+00,  1.00164177e+00,
-     1.00048907e+00,  9.99406080e-01,  9.98389887e-01,  9.97437085e-01,
-     9.96544484e-01,  9.95709855e-01,  9.94930241e-01,  9.94202405e-01,
-     9.93524160e-01,  9.92893043e-01,  9.92306810e-01,  9.91763378e-01,
-     9.91259764e-01,  9.90795450e-01,  9.90367789e-01,  9.89975161e-01,
-     9.89616034e-01,  9.89289016e-01,  9.88992851e-01,  9.88726033e-01,
-     9.88486872e-01,  9.88275104e-01,  9.88089217e-01,  9.87927711e-01,
-     9.87789826e-01,  9.87674344e-01,  9.87580750e-01,  9.87507202e-01,
-     9.87452945e-01,  9.87416974e-01,  9.87398469e-01,  9.87395830e-01,
-     9.87408003e-01,  9.87434340e-01,  9.87473624e-01,  9.87524314e-01,
-     9.87585620e-01,  9.87656379e-01,  9.87735892e-01,  9.87822558e-01,
-     9.87915097e-01,  9.88013273e-01,  9.88115695e-01,  9.88221131e-01,
-     9.88328903e-01,  9.88437831e-01,  9.88547679e-01,  9.88656841e-01,
-     9.88764587e-01,  9.88870854e-01,  9.88974432e-01,  9.89074727e-01,
-     9.89171004e-01,  9.89263102e-01,  9.89350722e-01,  9.89433065e-01,
-     9.89509692e-01,  9.89581081e-01,  9.89646747e-01,  9.89706737e-01,
-     9.89760693e-01,  9.89809448e-01,  9.89853013e-01,  9.89891471e-01,
-     9.89925419e-01,  9.89955420e-01,  9.89982449e-01,  9.90006512e-01,
-     9.90028481e-01,  9.90049748e-01,  9.90070956e-01,  9.90092836e-01,
-     9.90116392e-01,  9.90142748e-01,  9.90173428e-01,  9.90208733e-01,
-     9.90249864e-01,  9.90298369e-01,  9.90354850e-01,  9.90420508e-01,
-     9.90495930e-01,  9.90582515e-01,  9.90681257e-01,  9.90792209e-01,
-     9.90916546e-01,  9.91055074e-01,  9.91208461e-01,  9.91376861e-01,
-     9.91560583e-01,  9.91760421e-01,  9.91976718e-01,  9.92209110e-01,
-     9.92457914e-01,  9.92723123e-01,  9.93004954e-01,  9.93302728e-01,
-     9.93616108e-01,  9.93945371e-01,  9.94289515e-01,  9.94648168e-01,
-     9.95020303e-01,  9.95405817e-01,  9.95803871e-01,  9.96213027e-01,
-     9.96632469e-01,  9.97061531e-01,  9.97499058e-01,  9.97943743e-01,
-     9.98394057e-01,  9.98849312e-01,  9.99308343e-01,  9.99768922e-01,
-     1.00023113e+00,  1.00069214e+00,  1.00115201e+00,  1.00160853e+00,
-     1.00206049e+00,  1.00250721e+00,  1.00294713e+00,  1.00337891e+00,
-     1.00380137e+00,  1.00421381e+00,  1.00461539e+00,  1.00500462e+00,
-     1.00538063e+00,  1.00574328e+00,  1.00609151e+00,  1.00642491e+00,
-     1.00674243e+00,  1.00704432e+00,  1.00733022e+00,  1.00759940e+00,
-     1.00785206e+00,  1.00808818e+00,  1.00830803e+00,  1.00851125e+00,
-     1.00869814e+00,  1.00886952e+00,  1.00902566e+00,  1.00916672e+00,
-     1.00929336e+00,  1.00940640e+00,  1.00950702e+00,  1.00959526e+00,
-     1.00967215e+00,  1.00973908e+00,  1.00979668e+00,  1.00984614e+00,
-     1.00988808e+00,  1.00992409e+00,  1.00995538e+00,  1.00998227e+00,
-     1.01000630e+00,  1.01002862e+00,  1.01005025e+00,  1.01007195e+00,
-     1.01009437e+00,  1.01011892e+00,  1.01014650e+00,  1.01017711e+00,
-     1.01021176e+00,  1.01025100e+00,  1.01029547e+00,  1.01034523e+00,
-     1.01040032e+00,  1.01046156e+00,  1.01052862e+00,  1.01060152e+00,
-     1.01067979e+00,  1.01076391e+00,  1.01085343e+00,  1.01094755e+00,
-     1.01104595e+00,  1.01114849e+00,  1.01125440e+00,  1.01136308e+00,
-     1.01147330e+00,  1.01158500e+00,  1.01169742e+00,  1.01180892e+00,
-     1.01191926e+00,  1.01202724e+00,  1.01213215e+00,  1.01223273e+00,
-     1.01232756e+00,  1.01241638e+00,  1.01249789e+00,  1.01257043e+00,
-     1.01263330e+00,  1.01268528e+00,  1.01272556e+00,  1.01275258e+00,
-     1.01276506e+00,  1.01276236e+00,  1.01274338e+00,  1.01270648e+00,
-     1.01265084e+00,  1.01257543e+00,  1.01247947e+00,  1.01236111e+00,
-     1.01221981e+00,  1.01205436e+00,  1.01186400e+00,  1.01164722e+00,
-     1.01140252e+00,  1.01112965e+00,  1.01082695e+00,  1.01049292e+00,
-     1.01012635e+00,  1.00972589e+00,  1.00929006e+00,  1.00881730e+00,
-     1.00830503e+00,  1.00775283e+00,  1.00715783e+00,  1.00651805e+00,
-     1.00583140e+00,  1.00509559e+00,  1.00430863e+00,  1.00346750e+00,
-     1.00256950e+00,  1.00161271e+00,  1.00059427e+00,  9.99511170e-01,
-     9.98360922e-01,  9.97140929e-01,  9.95848886e-01,  9.94481854e-01,
-     9.93037528e-01,  9.91514656e-01,  9.89913680e-01,  9.88193062e-01,
-     9.85942259e-01,  9.83566790e-01,  9.81142303e-01,  9.78521444e-01,
-     9.75663604e-01,  9.72545344e-01,  9.69145663e-01,  9.65440618e-01,
-     9.61404362e-01,  9.57011307e-01,  9.52236767e-01,  9.47054884e-01,
-     9.41440374e-01,  9.35369161e-01,  9.28819009e-01,  9.21766289e-01,
-     9.14189628e-01,  9.06069468e-01,  8.97389168e-01,  8.88133200e-01,
-     8.78289389e-01,  8.67846957e-01,  8.56797064e-01,  8.45133465e-01,
-     8.32854281e-01,  8.19959478e-01,  8.06451101e-01,  7.92334648e-01,
-     7.77620449e-01,  7.62320618e-01,  7.46448649e-01,  7.30020573e-01,
-     7.13056738e-01,  6.95580544e-01,  6.77617323e-01,  6.59195531e-01,
-     6.40348643e-01,  6.21107220e-01,  6.01504928e-01,  5.81578761e-01,
-     5.61367451e-01,  5.40918863e-01,  5.20273683e-01,  4.99478073e-01,
-     4.78577418e-01,  4.57617260e-01,  4.36649021e-01,  4.15722146e-01,
-     3.94885659e-01,  3.74190319e-01,  3.53686890e-01,  3.33426002e-01,
-     3.13458647e-01,  2.93833790e-01,  2.74599264e-01,  2.55803064e-01,
-     2.37490219e-01,  2.19703603e-01,  2.02485542e-01,  1.85874992e-01,
-     1.69906780e-01,  1.54613227e-01,  1.40023821e-01,  1.26163740e-01,
-     1.13053443e-01,  1.00708497e-01,  8.91402439e-02,  7.83561210e-02,
-     6.83582123e-02,  5.91421154e-02,  5.06989301e-02,  4.30171776e-02,
-     3.60802073e-02,  2.98631634e-02,  2.43372266e-02,  1.94767524e-02,
-     1.52571017e-02,  1.16378749e-02,  8.43308778e-03,  4.44966900e-03,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-])
-
-W_10M_480 = np.array([
-    -2.35303215e-04, -4.61989875e-04, -6.26293154e-04, -7.92918043e-04,
-    -9.74716672e-04, -1.18025689e-03, -1.40920904e-03, -1.66447310e-03,
-    -1.94659161e-03, -2.25708173e-03, -2.59710692e-03, -2.96760762e-03,
-    -3.37045488e-03, -3.80628516e-03, -4.27687377e-03, -4.78246990e-03,
-    -5.32460872e-03, -5.90340381e-03, -6.52041973e-03, -7.17588528e-03,
-    -7.87142282e-03, -8.60658604e-03, -9.38248086e-03, -1.01982718e-02,
-    -1.10552055e-02, -1.19527030e-02, -1.28920591e-02, -1.38726348e-02,
-    -1.48952816e-02, -1.59585662e-02, -1.70628856e-02, -1.82066640e-02,
-    -1.93906598e-02, -2.06135542e-02, -2.18757093e-02, -2.31752632e-02,
-    -2.45122745e-02, -2.58847194e-02, -2.72926374e-02, -2.87339090e-02,
-    -3.02086274e-02, -3.17144037e-02, -3.32509886e-02, -3.48159779e-02,
-    -3.64089241e-02, -3.80274232e-02, -3.96706799e-02, -4.13357542e-02,
-    -4.30220337e-02, -4.47269805e-02, -4.64502229e-02, -4.81889149e-02,
-    -4.99422586e-02, -5.17069080e-02, -5.34816204e-02, -5.52633479e-02,
-    -5.70512315e-02, -5.88427175e-02, -6.06371724e-02, -6.24310403e-02,
-    -6.42230355e-02, -6.60096152e-02, -6.77896227e-02, -6.95599687e-02,
-    -7.13196627e-02, -7.30658127e-02, -7.47975891e-02, -7.65117823e-02,
-    -7.82071142e-02, -7.98801069e-02, -8.15296401e-02, -8.31523735e-02,
-    -8.47472895e-02, -8.63113754e-02, -8.78437445e-02, -8.93416436e-02,
-    -9.08041129e-02, -9.22279576e-02, -9.36123287e-02, -9.49537776e-02,
-    -9.62515531e-02, -9.75028462e-02, -9.87073651e-02, -9.98627129e-02,
-    -1.00968022e-01, -1.02020268e-01, -1.03018380e-01, -1.03959636e-01,
-    -1.04843883e-01, -1.05668684e-01, -1.06434282e-01, -1.07138231e-01,
-    -1.07779996e-01, -1.08357063e-01, -1.08869014e-01, -1.09313559e-01,
-    -1.09690356e-01, -1.09996966e-01, -1.10233226e-01, -1.10397281e-01,
-    -1.10489847e-01, -1.10508642e-01, -1.10453743e-01, -1.10322584e-01,
-    -1.10114583e-01, -1.09827693e-01, -1.09462175e-01, -1.09016396e-01,
-    -1.08490885e-01, -1.07883429e-01, -1.07193718e-01, -1.06419636e-01,
-    -1.05561251e-01, -1.04616281e-01, -1.03584904e-01, -1.02465016e-01,
-    -1.01256900e-01, -9.99586457e-02, -9.85701457e-02, -9.70891114e-02,
-    -9.55154582e-02, -9.38468492e-02, -9.20830006e-02, -9.02217102e-02,
-    -8.82630999e-02, -8.62049382e-02, -8.40474215e-02, -8.17879272e-02,
-    -7.94262503e-02, -7.69598078e-02, -7.43878560e-02, -7.17079700e-02,
-    -6.89199478e-02, -6.60218980e-02, -6.30134942e-02, -5.98919191e-02,
-    -5.66565564e-02, -5.33040616e-02, -4.98342724e-02, -4.62445689e-02,
-    -4.25345569e-02, -3.87019577e-02, -3.47458578e-02, -3.06634152e-02,
-    -2.64542508e-02, -2.21158161e-02, -1.76474054e-02, -1.30458136e-02,
-    -8.31042570e-03, -3.43826866e-03,  1.57031548e-03,  6.71769764e-03,
-     1.20047702e-02,  1.74339832e-02,  2.30064206e-02,  2.87248142e-02,
-     3.45889635e-02,  4.06010646e-02,  4.67610292e-02,  5.30713391e-02,
-     5.95323909e-02,  6.61464781e-02,  7.29129318e-02,  7.98335419e-02,
-     8.69080741e-02,  9.41381377e-02,  1.01523314e-01,  1.09065152e-01,
-     1.16762655e-01,  1.24617139e-01,  1.32627295e-01,  1.40793819e-01,
-     1.49115252e-01,  1.57592141e-01,  1.66222480e-01,  1.75006740e-01,
-     1.83943194e-01,  1.93031818e-01,  2.02269985e-01,  2.11656743e-01,
-     2.21188852e-01,  2.30865538e-01,  2.40683799e-01,  2.50642064e-01,
-     2.60736512e-01,  2.70965907e-01,  2.81325902e-01,  2.91814469e-01,
-     3.02427028e-01,  3.13160350e-01,  3.24009570e-01,  3.34971959e-01,
-     3.46042294e-01,  3.57217518e-01,  3.68491565e-01,  3.79859512e-01,
-     3.91314689e-01,  4.02853287e-01,  4.14468833e-01,  4.26157164e-01,
-     4.37911390e-01,  4.49725632e-01,  4.61592545e-01,  4.73506703e-01,
-     4.85460018e-01,  4.97447159e-01,  5.09459723e-01,  5.21490984e-01,
-     5.33532682e-01,  5.45578981e-01,  5.57621716e-01,  5.69654673e-01,
-     5.81668558e-01,  5.93656062e-01,  6.05608382e-01,  6.17519206e-01,
-     6.29379661e-01,  6.41183084e-01,  6.52920354e-01,  6.64584079e-01,
-     6.76165350e-01,  6.87657395e-01,  6.99051154e-01,  7.10340055e-01,
-     7.21514933e-01,  7.32569177e-01,  7.43494372e-01,  7.54284633e-01,
-     7.64931365e-01,  7.75428189e-01,  7.85767017e-01,  7.95941465e-01,
-     8.05943723e-01,  8.15768707e-01,  8.25408622e-01,  8.34858937e-01,
-     8.44112583e-01,  8.53165119e-01,  8.62010834e-01,  8.70645634e-01,
-     8.79063156e-01,  8.87259971e-01,  8.95231329e-01,  9.02975168e-01,
-     9.10486312e-01,  9.17762555e-01,  9.24799743e-01,  9.31596250e-01,
-     9.38149486e-01,  9.44458839e-01,  9.50522086e-01,  9.56340292e-01,
-     9.61911452e-01,  9.67236671e-01,  9.72315664e-01,  9.77150119e-01,
-     9.81739750e-01,  9.86086587e-01,  9.90190638e-01,  9.94055718e-01,
-     9.97684240e-01,  1.00108096e+00,  1.00424751e+00,  1.00718858e+00,
-     1.00990665e+00,  1.01240743e+00,  1.01469470e+00,  1.01677466e+00,
-     1.01865099e+00,  1.02033046e+00,  1.02181733e+00,  1.02311884e+00,
-     1.02424026e+00,  1.02518972e+00,  1.02597245e+00,  1.02659694e+00,
-     1.02706918e+00,  1.02739752e+00,  1.02758790e+00,  1.02764895e+00,
-     1.02758583e+00,  1.02740852e+00,  1.02712299e+00,  1.02673867e+00,
-     1.02626166e+00,  1.02570100e+00,  1.02506178e+00,  1.02435398e+00,
-     1.02358239e+00,  1.02275651e+00,  1.02188060e+00,  1.02096387e+00,
-     1.02000914e+00,  1.01902729e+00,  1.01801944e+00,  1.01699650e+00,
-     1.01595743e+00,  1.01492344e+00,  1.01391595e+00,  1.01304757e+00,
-     1.01221613e+00,  1.01104487e+00,  1.00991459e+00,  1.00882489e+00,
-     1.00777386e+00,  1.00676170e+00,  1.00578665e+00,  1.00484875e+00,
-     1.00394608e+00,  1.00307885e+00,  1.00224501e+00,  1.00144473e+00,
-     1.00067619e+00,  9.99939317e-01,  9.99232085e-01,  9.98554813e-01,
-     9.97905542e-01,  9.97284268e-01,  9.96689095e-01,  9.96120338e-01,
-     9.95576126e-01,  9.95056572e-01,  9.94559753e-01,  9.94086038e-01,
-     9.93633779e-01,  9.93203161e-01,  9.92792187e-01,  9.92401518e-01,
-     9.92029727e-01,  9.91676778e-01,  9.91340877e-01,  9.91023065e-01,
-     9.90721643e-01,  9.90436680e-01,  9.90166895e-01,  9.89913101e-01,
-     9.89673564e-01,  9.89448837e-01,  9.89237484e-01,  9.89040193e-01,
-     9.88855636e-01,  9.88684347e-01,  9.88524761e-01,  9.88377852e-01,
-     9.88242327e-01,  9.88118564e-01,  9.88005163e-01,  9.87903202e-01,
-     9.87811174e-01,  9.87729546e-01,  9.87657198e-01,  9.87594984e-01,
-     9.87541274e-01,  9.87496906e-01,  9.87460625e-01,  9.87432981e-01,
-     9.87412641e-01,  9.87400475e-01,  9.87394992e-01,  9.87396916e-01,
-     9.87404906e-01,  9.87419705e-01,  9.87439972e-01,  9.87466328e-01,
-     9.87497321e-01,  9.87533893e-01,  9.87574654e-01,  9.87620124e-01,
-     9.87668980e-01,  9.87722156e-01,  9.87778192e-01,  9.87837649e-01,
-     9.87899199e-01,  9.87963798e-01,  9.88030030e-01,  9.88098468e-01,
-     9.88167801e-01,  9.88239030e-01,  9.88310769e-01,  9.88383520e-01,
-     9.88456016e-01,  9.88529420e-01,  9.88602222e-01,  9.88674940e-01,
-     9.88746626e-01,  9.88818277e-01,  9.88888248e-01,  9.88957438e-01,
-     9.89024798e-01,  9.89091125e-01,  9.89155170e-01,  9.89217866e-01,
-     9.89277956e-01,  9.89336519e-01,  9.89392368e-01,  9.89446283e-01,
-     9.89497212e-01,  9.89546334e-01,  9.89592362e-01,  9.89636265e-01,
-     9.89677201e-01,  9.89716220e-01,  9.89752029e-01,  9.89785920e-01,
-     9.89817027e-01,  9.89846207e-01,  9.89872536e-01,  9.89897514e-01,
-     9.89920005e-01,  9.89941079e-01,  9.89960061e-01,  9.89978226e-01,
-     9.89994556e-01,  9.90010350e-01,  9.90024832e-01,  9.90039402e-01,
-     9.90053211e-01,  9.90067475e-01,  9.90081472e-01,  9.90096693e-01,
-     9.90112245e-01,  9.90129379e-01,  9.90147465e-01,  9.90168060e-01,
-     9.90190227e-01,  9.90215190e-01,  9.90242442e-01,  9.90273445e-01,
-     9.90307127e-01,  9.90344891e-01,  9.90386228e-01,  9.90432448e-01,
-     9.90482565e-01,  9.90537983e-01,  9.90598060e-01,  9.90664037e-01,
-     9.90734883e-01,  9.90812038e-01,  9.90894786e-01,  9.90984259e-01,
-     9.91079525e-01,  9.91181924e-01,  9.91290512e-01,  9.91406471e-01,
-     9.91528801e-01,  9.91658694e-01,  9.91795272e-01,  9.91939622e-01,
-     9.92090615e-01,  9.92249503e-01,  9.92415240e-01,  9.92588721e-01,
-     9.92768871e-01,  9.92956911e-01,  9.93151653e-01,  9.93353924e-01,
-     9.93562689e-01,  9.93779087e-01,  9.94001643e-01,  9.94231202e-01,
-     9.94466818e-01,  9.94709344e-01,  9.94957285e-01,  9.95211663e-01,
-     9.95471264e-01,  9.95736795e-01,  9.96006862e-01,  9.96282303e-01,
-     9.96561799e-01,  9.96846133e-01,  9.97133827e-01,  9.97425669e-01,
-     9.97720337e-01,  9.98018509e-01,  9.98318587e-01,  9.98621352e-01,
-     9.98925543e-01,  9.99231731e-01,  9.99538258e-01,  9.99846116e-01,
-     1.00015391e+00,  1.00046196e+00,  1.00076886e+00,  1.00107561e+00,
-     1.00138055e+00,  1.00168424e+00,  1.00198543e+00,  1.00228487e+00,
-     1.00258098e+00,  1.00287441e+00,  1.00316385e+00,  1.00345006e+00,
-     1.00373157e+00,  1.00400915e+00,  1.00428146e+00,  1.00454934e+00,
-     1.00481138e+00,  1.00506827e+00,  1.00531880e+00,  1.00556397e+00,
-     1.00580227e+00,  1.00603455e+00,  1.00625986e+00,  1.00647902e+00,
-     1.00669054e+00,  1.00689557e+00,  1.00709305e+00,  1.00728380e+00,
-     1.00746662e+00,  1.00764273e+00,  1.00781104e+00,  1.00797244e+00,
-     1.00812588e+00,  1.00827260e+00,  1.00841147e+00,  1.00854357e+00,
-     1.00866802e+00,  1.00878601e+00,  1.00889653e+00,  1.00900077e+00,
-     1.00909776e+00,  1.00918888e+00,  1.00927316e+00,  1.00935176e+00,
-     1.00942394e+00,  1.00949118e+00,  1.00955240e+00,  1.00960889e+00,
-     1.00965997e+00,  1.00970709e+00,  1.00974924e+00,  1.00978774e+00,
-     1.00982209e+00,  1.00985371e+00,  1.00988150e+00,  1.00990696e+00,
-     1.00992957e+00,  1.00995057e+00,  1.00996902e+00,  1.00998650e+00,
-     1.01000236e+00,  1.01001789e+00,  1.01003217e+00,  1.01004672e+00,
-     1.01006081e+00,  1.01007567e+00,  1.01009045e+00,  1.01010656e+00,
-     1.01012323e+00,  1.01014176e+00,  1.01016113e+00,  1.01018264e+00,
-     1.01020559e+00,  1.01023108e+00,  1.01025795e+00,  1.01028773e+00,
-     1.01031948e+00,  1.01035408e+00,  1.01039064e+00,  1.01043047e+00,
-     1.01047227e+00,  1.01051710e+00,  1.01056410e+00,  1.01061427e+00,
-     1.01066629e+00,  1.01072136e+00,  1.01077842e+00,  1.01083825e+00,
-     1.01089966e+00,  1.01096373e+00,  1.01102919e+00,  1.01109699e+00,
-     1.01116586e+00,  1.01123661e+00,  1.01130817e+00,  1.01138145e+00,
-     1.01145479e+00,  1.01152919e+00,  1.01160368e+00,  1.01167880e+00,
-     1.01175301e+00,  1.01182748e+00,  1.01190094e+00,  1.01197388e+00,
-     1.01204489e+00,  1.01211499e+00,  1.01218284e+00,  1.01224902e+00,
-     1.01231210e+00,  1.01237303e+00,  1.01243046e+00,  1.01248497e+00,
-     1.01253506e+00,  1.01258168e+00,  1.01262347e+00,  1.01266098e+00,
-     1.01269276e+00,  1.01271979e+00,  1.01274058e+00,  1.01275575e+00,
-     1.01276395e+00,  1.01276592e+00,  1.01276030e+00,  1.01274782e+00,
-     1.01272696e+00,  1.01269861e+00,  1.01266140e+00,  1.01261590e+00,
-     1.01256083e+00,  1.01249705e+00,  1.01242289e+00,  1.01233923e+00,
-     1.01224492e+00,  1.01214046e+00,  1.01202430e+00,  1.01189756e+00,
-     1.01175881e+00,  1.01160845e+00,  1.01144516e+00,  1.01126996e+00,
-     1.01108126e+00,  1.01087961e+00,  1.01066368e+00,  1.01043418e+00,
-     1.01018968e+00,  1.00993075e+00,  1.00965566e+00,  1.00936525e+00,
-     1.00905825e+00,  1.00873476e+00,  1.00839308e+00,  1.00803431e+00,
-     1.00765666e+00,  1.00726014e+00,  1.00684335e+00,  1.00640701e+00,
-     1.00594915e+00,  1.00547001e+00,  1.00496799e+00,  1.00444353e+00,
-     1.00389477e+00,  1.00332190e+00,  1.00272313e+00,  1.00209885e+00,
-     1.00144728e+00,  1.00076851e+00,  1.00006069e+00,  9.99324268e-01,
-     9.98557350e-01,  9.97760020e-01,  9.96930604e-01,  9.96069427e-01,
-     9.95174643e-01,  9.94246644e-01,  9.93283713e-01,  9.92286108e-01,
-     9.91252309e-01,  9.90182742e-01,  9.89075787e-01,  9.87931302e-01,
-     9.86355322e-01,  9.84736245e-01,  9.83175095e-01,  9.81558334e-01,
-     9.79861353e-01,  9.78061749e-01,  9.76157432e-01,  9.74137862e-01,
-     9.71999011e-01,  9.69732741e-01,  9.67333198e-01,  9.64791512e-01,
-     9.62101150e-01,  9.59253976e-01,  9.56242718e-01,  9.53060091e-01,
-     9.49698408e-01,  9.46149812e-01,  9.42407161e-01,  9.38463416e-01,
-     9.34311297e-01,  9.29944987e-01,  9.25356797e-01,  9.20540463e-01,
-     9.15489628e-01,  9.10198679e-01,  9.04662060e-01,  8.98875519e-01,
-     8.92833832e-01,  8.86533719e-01,  8.79971272e-01,  8.73143784e-01,
-     8.66047653e-01,  8.58681252e-01,  8.51042044e-01,  8.43129723e-01,
-     8.34943514e-01,  8.26483991e-01,  8.17750537e-01,  8.08744982e-01,
-     7.99468149e-01,  7.89923516e-01,  7.80113773e-01,  7.70043128e-01,
-     7.59714574e-01,  7.49133097e-01,  7.38302860e-01,  7.27229876e-01,
-     7.15920192e-01,  7.04381434e-01,  6.92619693e-01,  6.80643883e-01,
-     6.68461648e-01,  6.56083014e-01,  6.43517927e-01,  6.30775533e-01,
-     6.17864165e-01,  6.04795463e-01,  5.91579959e-01,  5.78228937e-01,
-     5.64753589e-01,  5.51170316e-01,  5.37490509e-01,  5.23726350e-01,
-     5.09891542e-01,  4.96000807e-01,  4.82066294e-01,  4.68101711e-01,
-     4.54121700e-01,  4.40142182e-01,  4.26177297e-01,  4.12241789e-01,
-     3.98349961e-01,  3.84517234e-01,  3.70758372e-01,  3.57088679e-01,
-     3.43522867e-01,  3.30076376e-01,  3.16764033e-01,  3.03600465e-01,
-     2.90599616e-01,  2.77775850e-01,  2.65143468e-01,  2.52716188e-01,
-     2.40506985e-01,  2.28528397e-01,  2.16793343e-01,  2.05313990e-01,
-     1.94102191e-01,  1.83168087e-01,  1.72522195e-01,  1.62173542e-01,
-     1.52132068e-01,  1.42405280e-01,  1.33001524e-01,  1.23926066e-01,
-     1.15185830e-01,  1.06784043e-01,  9.87263751e-02,  9.10137900e-02,
-     8.36505724e-02,  7.66350831e-02,  6.99703341e-02,  6.36518811e-02,
-     5.76817602e-02,  5.20524422e-02,  4.67653841e-02,  4.18095054e-02,
-     3.71864025e-02,  3.28807275e-02,  2.88954850e-02,  2.52098057e-02,
-     2.18305756e-02,  1.87289619e-02,  1.59212782e-02,  1.33638143e-02,
-     1.10855888e-02,  8.94347419e-03,  6.75812489e-03,  3.50443813e-03,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-])
-
-W_7M5_60 = np.array([
-     2.95060859e-03,  7.17541132e-03,  1.37695374e-02,  2.30953556e-02,
-     3.54036230e-02,  5.08289304e-02,  6.94696293e-02,  9.13884278e-02,
-     1.16604575e-01,  1.45073546e-01,  1.76711174e-01,  2.11342953e-01,
-     2.48768614e-01,  2.88701102e-01,  3.30823871e-01,  3.74814544e-01,
-     4.20308013e-01,  4.66904918e-01,  5.14185341e-01,  5.61710041e-01,
-     6.09026346e-01,  6.55671016e-01,  7.01218384e-01,  7.45240679e-01,
-     7.87369206e-01,  8.27223833e-01,  8.64513675e-01,  8.98977415e-01,
-     9.30407518e-01,  9.58599937e-01,  9.83447719e-01,  1.00488283e+00,
-     1.02285381e+00,  1.03740495e+00,  1.04859791e+00,  1.05656184e+00,
-     1.06149371e+00,  1.06362578e+00,  1.06325973e+00,  1.06074505e+00,
-     1.05643590e+00,  1.05069500e+00,  1.04392435e+00,  1.03647725e+00,
-     1.02872867e+00,  1.02106486e+00,  1.01400658e+00,  1.00727455e+00,
-     1.00172250e+00,  9.97309592e-01,  9.93985158e-01,  9.91683335e-01,
-     9.90325325e-01,  9.89822613e-01,  9.90074734e-01,  9.90975314e-01,
-     9.92412851e-01,  9.94273149e-01,  9.96439157e-01,  9.98791616e-01,
-     1.00120985e+00,  1.00357357e+00,  1.00575984e+00,  1.00764515e+00,
-     1.00910687e+00,  1.01002476e+00,  1.01028203e+00,  1.00976919e+00,
-     1.00838641e+00,  1.00605124e+00,  1.00269767e+00,  9.98280464e-01,
-     9.92777987e-01,  9.86186892e-01,  9.77634164e-01,  9.67447270e-01,
-     9.55129725e-01,  9.40389877e-01,  9.22959280e-01,  9.02607350e-01,
-     8.79202689e-01,  8.52641750e-01,  8.22881272e-01,  7.89971715e-01,
-     7.54030328e-01,  7.15255742e-01,  6.73936911e-01,  6.30414716e-01,
-     5.85078858e-01,  5.38398518e-01,  4.90833753e-01,  4.42885823e-01,
-     3.95091024e-01,  3.48004343e-01,  3.02196710e-01,  2.58227431e-01,
-     2.16641416e-01,  1.77922122e-01,  1.42480547e-01,  1.10652194e-01,
-     8.26995967e-02,  5.88334516e-02,  3.92030848e-02,  2.38629107e-02,
-     1.26976223e-02,  5.35665361e-03,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-])
-
-W_7M5_120 = np.array([
-     2.20824874e-03,  3.81014420e-03,  5.91552473e-03,  8.58361457e-03,
-     1.18759723e-02,  1.58335301e-02,  2.04918652e-02,  2.58883593e-02,
-     3.20415894e-02,  3.89616721e-02,  4.66742169e-02,  5.51849337e-02,
-     6.45038384e-02,  7.46411071e-02,  8.56000162e-02,  9.73846703e-02,
-     1.09993603e-01,  1.23419277e-01,  1.37655457e-01,  1.52690437e-01,
-     1.68513363e-01,  1.85093105e-01,  2.02410419e-01,  2.20450365e-01,
-     2.39167941e-01,  2.58526168e-01,  2.78498539e-01,  2.99038432e-01,
-     3.20104862e-01,  3.41658622e-01,  3.63660034e-01,  3.86062695e-01,
-     4.08815272e-01,  4.31871046e-01,  4.55176988e-01,  4.78676593e-01,
-     5.02324813e-01,  5.26060916e-01,  5.49831283e-01,  5.73576883e-01,
-     5.97241338e-01,  6.20770242e-01,  6.44099662e-01,  6.67176382e-01,
-     6.89958854e-01,  7.12379980e-01,  7.34396372e-01,  7.55966688e-01,
-     7.77036981e-01,  7.97558114e-01,  8.17490856e-01,  8.36796950e-01,
-     8.55447310e-01,  8.73400798e-01,  8.90635719e-01,  9.07128770e-01,
-     9.22848784e-01,  9.37763323e-01,  9.51860206e-01,  9.65130600e-01,
-     9.77556541e-01,  9.89126209e-01,  9.99846919e-01,  1.00970073e+00,
-     1.01868229e+00,  1.02681455e+00,  1.03408981e+00,  1.04051196e+00,
-     1.04610837e+00,  1.05088565e+00,  1.05486289e+00,  1.05807221e+00,
-     1.06053414e+00,  1.06227662e+00,  1.06333815e+00,  1.06375557e+00,
-     1.06356632e+00,  1.06282156e+00,  1.06155996e+00,  1.05981709e+00,
-     1.05765876e+00,  1.05512006e+00,  1.05223985e+00,  1.04908779e+00,
-     1.04569860e+00,  1.04210831e+00,  1.03838099e+00,  1.03455276e+00,
-     1.03067200e+00,  1.02679167e+00,  1.02295558e+00,  1.01920733e+00,
-     1.01587289e+00,  1.01221017e+00,  1.00884559e+00,  1.00577851e+00,
-     1.00300262e+00,  1.00051460e+00,  9.98309229e-01,  9.96378601e-01,
-     9.94718132e-01,  9.93316216e-01,  9.92166957e-01,  9.91258603e-01,
-     9.90581104e-01,  9.90123118e-01,  9.89873712e-01,  9.89818707e-01,
-     9.89946800e-01,  9.90243175e-01,  9.90695564e-01,  9.91288540e-01,
-     9.92009469e-01,  9.92842693e-01,  9.93775067e-01,  9.94790398e-01,
-     9.95875534e-01,  9.97014367e-01,  9.98192871e-01,  9.99394506e-01,
-     1.00060586e+00,  1.00181040e+00,  1.00299457e+00,  1.00414155e+00,
-     1.00523688e+00,  1.00626393e+00,  1.00720890e+00,  1.00805489e+00,
-     1.00878802e+00,  1.00939182e+00,  1.00985296e+00,  1.01015529e+00,
-     1.01028602e+00,  1.01022988e+00,  1.00997541e+00,  1.00950846e+00,
-     1.00881848e+00,  1.00789488e+00,  1.00672876e+00,  1.00530991e+00,
-     1.00363456e+00,  1.00169363e+00,  9.99485663e-01,  9.97006370e-01,
-     9.94254687e-01,  9.91231967e-01,  9.87937115e-01,  9.84375125e-01,
-     9.79890963e-01,  9.75269879e-01,  9.70180498e-01,  9.64580027e-01,
-     9.58425534e-01,  9.51684014e-01,  9.44320232e-01,  9.36290624e-01,
-     9.27580507e-01,  9.18153414e-01,  9.07976524e-01,  8.97050058e-01,
-     8.85351360e-01,  8.72857927e-01,  8.59579819e-01,  8.45502615e-01,
-     8.30619943e-01,  8.14946648e-01,  7.98489378e-01,  7.81262450e-01,
-     7.63291769e-01,  7.44590843e-01,  7.25199287e-01,  7.05153668e-01,
-     6.84490545e-01,  6.63245210e-01,  6.41477162e-01,  6.19235334e-01,
-     5.96559133e-01,  5.73519989e-01,  5.50173851e-01,  5.26568538e-01,
-     5.02781159e-01,  4.78860889e-01,  4.54877894e-01,  4.30898123e-01,
-     4.06993964e-01,  3.83234031e-01,  3.59680098e-01,  3.36408100e-01,
-     3.13496418e-01,  2.91010565e-01,  2.69019585e-01,  2.47584348e-01,
-     2.26788433e-01,  2.06677771e-01,  1.87310343e-01,  1.68739644e-01,
-     1.51012382e-01,  1.34171842e-01,  1.18254662e-01,  1.03290734e-01,
-     8.93117360e-02,  7.63429787e-02,  6.44077291e-02,  5.35243715e-02,
-     4.37084453e-02,  3.49667099e-02,  2.72984629e-02,  2.06895808e-02,
-     1.51125125e-02,  1.05228754e-02,  6.85547314e-03,  4.02351119e-03,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-])
-
-W_7M5_180 = np.array([
-     1.97084908e-03,  2.95060859e-03,  4.12447721e-03,  5.52688664e-03,
-     7.17541132e-03,  9.08757730e-03,  1.12819105e-02,  1.37695374e-02,
-     1.65600266e-02,  1.96650895e-02,  2.30953556e-02,  2.68612894e-02,
-     3.09632560e-02,  3.54036230e-02,  4.01915610e-02,  4.53331403e-02,
-     5.08289304e-02,  5.66815448e-02,  6.28935304e-02,  6.94696293e-02,
-     7.64106314e-02,  8.37160016e-02,  9.13884278e-02,  9.94294008e-02,
-     1.07834725e-01,  1.16604575e-01,  1.25736503e-01,  1.35226811e-01,
-     1.45073546e-01,  1.55273819e-01,  1.65822194e-01,  1.76711174e-01,
-     1.87928776e-01,  1.99473180e-01,  2.11342953e-01,  2.23524554e-01,
-     2.36003100e-01,  2.48768614e-01,  2.61813811e-01,  2.75129161e-01,
-     2.88701102e-01,  3.02514034e-01,  3.16558805e-01,  3.30823871e-01,
-     3.45295567e-01,  3.59963992e-01,  3.74814544e-01,  3.89831817e-01,
-     4.05001010e-01,  4.20308013e-01,  4.35739515e-01,  4.51277817e-01,
-     4.66904918e-01,  4.82609041e-01,  4.98375466e-01,  5.14185341e-01,
-     5.30021478e-01,  5.45869352e-01,  5.61710041e-01,  5.77528151e-01,
-     5.93304696e-01,  6.09026346e-01,  6.24674189e-01,  6.40227555e-01,
-     6.55671016e-01,  6.70995935e-01,  6.86184559e-01,  7.01218384e-01,
-     7.16078449e-01,  7.30756084e-01,  7.45240679e-01,  7.59515122e-01,
-     7.73561955e-01,  7.87369206e-01,  8.00923138e-01,  8.14211386e-01,
-     8.27223833e-01,  8.39952374e-01,  8.52386102e-01,  8.64513675e-01,
-     8.76324079e-01,  8.87814288e-01,  8.98977415e-01,  9.09803319e-01,
-     9.20284312e-01,  9.30407518e-01,  9.40169652e-01,  9.49567795e-01,
-     9.58599937e-01,  9.67260260e-01,  9.75545166e-01,  9.83447719e-01,
-     9.90971957e-01,  9.98119269e-01,  1.00488283e+00,  1.01125773e+00,
-     1.01724436e+00,  1.02285381e+00,  1.02808734e+00,  1.03293706e+00,
-     1.03740495e+00,  1.04150164e+00,  1.04523236e+00,  1.04859791e+00,
-     1.05160340e+00,  1.05425505e+00,  1.05656184e+00,  1.05853400e+00,
-     1.06017414e+00,  1.06149371e+00,  1.06249943e+00,  1.06320577e+00,
-     1.06362578e+00,  1.06376487e+00,  1.06363778e+00,  1.06325973e+00,
-     1.06264695e+00,  1.06180496e+00,  1.06074505e+00,  1.05948492e+00,
-     1.05804533e+00,  1.05643590e+00,  1.05466218e+00,  1.05274047e+00,
-     1.05069500e+00,  1.04853894e+00,  1.04627898e+00,  1.04392435e+00,
-     1.04149540e+00,  1.03901003e+00,  1.03647725e+00,  1.03390793e+00,
-     1.03131989e+00,  1.02872867e+00,  1.02614832e+00,  1.02358988e+00,
-     1.02106486e+00,  1.01856262e+00,  1.01655770e+00,  1.01400658e+00,
-     1.01162953e+00,  1.00938590e+00,  1.00727455e+00,  1.00529616e+00,
-     1.00344526e+00,  1.00172250e+00,  1.00012792e+00,  9.98657533e-01,
-     9.97309592e-01,  9.96083571e-01,  9.94976569e-01,  9.93985158e-01,
-     9.93107530e-01,  9.92341305e-01,  9.91683335e-01,  9.91130070e-01,
-     9.90678325e-01,  9.90325325e-01,  9.90067562e-01,  9.89901282e-01,
-     9.89822613e-01,  9.89827845e-01,  9.89913241e-01,  9.90074734e-01,
-     9.90308256e-01,  9.90609852e-01,  9.90975314e-01,  9.91400330e-01,
-     9.91880966e-01,  9.92412851e-01,  9.92991779e-01,  9.93613381e-01,
-     9.94273149e-01,  9.94966958e-01,  9.95690370e-01,  9.96439157e-01,
-     9.97208572e-01,  9.97994275e-01,  9.98791616e-01,  9.99596062e-01,
-     1.00040410e+00,  1.00120985e+00,  1.00200976e+00,  1.00279924e+00,
-     1.00357357e+00,  1.00432828e+00,  1.00505850e+00,  1.00575984e+00,
-     1.00642767e+00,  1.00705768e+00,  1.00764515e+00,  1.00818549e+00,
-     1.00867427e+00,  1.00910687e+00,  1.00947916e+00,  1.00978659e+00,
-     1.01002476e+00,  1.01018954e+00,  1.01027669e+00,  1.01028203e+00,
-     1.01020174e+00,  1.01003208e+00,  1.00976919e+00,  1.00940939e+00,
-     1.00894931e+00,  1.00838641e+00,  1.00771780e+00,  1.00694031e+00,
-     1.00605124e+00,  1.00504879e+00,  1.00393183e+00,  1.00269767e+00,
-     1.00134427e+00,  9.99872092e-01,  9.98280464e-01,  9.96566569e-01,
-     9.94731737e-01,  9.92777987e-01,  9.90701374e-01,  9.88504165e-01,
-     9.86186892e-01,  9.83711989e-01,  9.80584643e-01,  9.77634164e-01,
-     9.74455033e-01,  9.71062916e-01,  9.67447270e-01,  9.63593926e-01,
-     9.59491398e-01,  9.55129725e-01,  9.50501326e-01,  9.45592810e-01,
-     9.40389877e-01,  9.34886760e-01,  9.29080559e-01,  9.22959280e-01,
-     9.16509579e-01,  9.09724456e-01,  9.02607350e-01,  8.95155084e-01,
-     8.87356154e-01,  8.79202689e-01,  8.70699698e-01,  8.61847424e-01,
-     8.52641750e-01,  8.43077833e-01,  8.33154905e-01,  8.22881272e-01,
-     8.12257597e-01,  8.01285439e-01,  7.89971715e-01,  7.78318177e-01,
-     7.66337710e-01,  7.54030328e-01,  7.41407991e-01,  7.28477501e-01,
-     7.15255742e-01,  7.01751739e-01,  6.87975632e-01,  6.73936911e-01,
-     6.59652573e-01,  6.45139489e-01,  6.30414716e-01,  6.15483622e-01,
-     6.00365852e-01,  5.85078858e-01,  5.69649536e-01,  5.54084810e-01,
-     5.38398518e-01,  5.22614738e-01,  5.06756805e-01,  4.90833753e-01,
-     4.74866033e-01,  4.58876566e-01,  4.42885823e-01,  4.26906539e-01,
-     4.10970973e-01,  3.95091024e-01,  3.79291327e-01,  3.63587417e-01,
-     3.48004343e-01,  3.32563201e-01,  3.17287485e-01,  3.02196710e-01,
-     2.87309403e-01,  2.72643992e-01,  2.58227431e-01,  2.44072856e-01,
-     2.30208977e-01,  2.16641416e-01,  2.03398481e-01,  1.90486162e-01,
-     1.77922122e-01,  1.65726674e-01,  1.53906397e-01,  1.42480547e-01,
-     1.31453980e-01,  1.20841778e-01,  1.10652194e-01,  1.00891734e-01,
-     9.15718851e-02,  8.26995967e-02,  7.42815529e-02,  6.63242382e-02,
-     5.88334516e-02,  5.18140676e-02,  4.52698346e-02,  3.92030848e-02,
-     3.36144159e-02,  2.85023308e-02,  2.38629107e-02,  1.96894227e-02,
-     1.59720527e-02,  1.26976223e-02,  9.84937739e-03,  7.40724463e-03,
-     5.35665361e-03,  3.83226552e-03,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-])
-
-W_7M5_240 = np.array([
-     1.84833037e-03,  2.56481839e-03,  3.36762118e-03,  4.28736617e-03,
-     5.33830143e-03,  6.52679223e-03,  7.86112587e-03,  9.34628179e-03,
-     1.09916868e-02,  1.28011172e-02,  1.47805911e-02,  1.69307043e-02,
-     1.92592307e-02,  2.17696937e-02,  2.44685983e-02,  2.73556543e-02,
-     3.04319230e-02,  3.36980464e-02,  3.71583577e-02,  4.08148180e-02,
-     4.46708068e-02,  4.87262995e-02,  5.29820633e-02,  5.74382470e-02,
-     6.20968580e-02,  6.69609767e-02,  7.20298364e-02,  7.73039146e-02,
-     8.27825574e-02,  8.84682102e-02,  9.43607566e-02,  1.00460272e-01,
-     1.06763824e-01,  1.13273679e-01,  1.19986420e-01,  1.26903521e-01,
-     1.34020853e-01,  1.41339557e-01,  1.48857211e-01,  1.56573685e-01,
-     1.64484622e-01,  1.72589077e-01,  1.80879090e-01,  1.89354320e-01,
-     1.98012244e-01,  2.06854141e-01,  2.15875319e-01,  2.25068672e-01,
-     2.34427407e-01,  2.43948314e-01,  2.53627993e-01,  2.63464061e-01,
-     2.73450494e-01,  2.83582189e-01,  2.93853469e-01,  3.04257373e-01,
-     3.14790914e-01,  3.25449123e-01,  3.36227410e-01,  3.47118760e-01,
-     3.58120177e-01,  3.69224663e-01,  3.80427793e-01,  3.91720023e-01,
-     4.03097022e-01,  4.14551955e-01,  4.26081719e-01,  4.37676318e-01,
-     4.49330196e-01,  4.61034855e-01,  4.72786043e-01,  4.84576777e-01,
-     4.96401707e-01,  5.08252458e-01,  5.20122078e-01,  5.32002077e-01,
-     5.43888090e-01,  5.55771601e-01,  5.67645739e-01,  5.79502786e-01,
-     5.91335035e-01,  6.03138367e-01,  6.14904172e-01,  6.26623941e-01,
-     6.38288834e-01,  6.49893375e-01,  6.61432360e-01,  6.72902514e-01,
-     6.84293750e-01,  6.95600460e-01,  7.06811784e-01,  7.17923425e-01,
-     7.28931386e-01,  7.39832773e-01,  7.50618982e-01,  7.61284053e-01,
-     7.71818919e-01,  7.82220992e-01,  7.92481330e-01,  8.02599448e-01,
-     8.12565230e-01,  8.22377129e-01,  8.32030518e-01,  8.41523208e-01,
-     8.50848313e-01,  8.60002412e-01,  8.68979881e-01,  8.77778347e-01,
-     8.86395904e-01,  8.94829421e-01,  9.03077626e-01,  9.11132652e-01,
-     9.18993585e-01,  9.26652937e-01,  9.34111420e-01,  9.41364344e-01,
-     9.48412967e-01,  9.55255630e-01,  9.61892013e-01,  9.68316363e-01,
-     9.74530156e-01,  9.80528338e-01,  9.86313928e-01,  9.91886049e-01,
-     9.97246345e-01,  1.00239190e+00,  1.00731946e+00,  1.01202707e+00,
-     1.01651654e+00,  1.02079430e+00,  1.02486082e+00,  1.02871471e+00,
-     1.03235170e+00,  1.03577375e+00,  1.03898432e+00,  1.04198786e+00,
-     1.04478564e+00,  1.04737818e+00,  1.04976743e+00,  1.05195405e+00,
-     1.05394290e+00,  1.05573463e+00,  1.05734177e+00,  1.05875726e+00,
-     1.05998674e+00,  1.06103672e+00,  1.06190651e+00,  1.06260369e+00,
-     1.06313289e+00,  1.06350237e+00,  1.06370981e+00,  1.06376322e+00,
-     1.06366765e+00,  1.06343012e+00,  1.06305656e+00,  1.06255421e+00,
-     1.06192235e+00,  1.06116702e+00,  1.06029469e+00,  1.05931469e+00,
-     1.05823465e+00,  1.05705891e+00,  1.05578948e+00,  1.05442979e+00,
-     1.05298793e+00,  1.05147505e+00,  1.04989930e+00,  1.04826213e+00,
-     1.04656691e+00,  1.04481699e+00,  1.04302125e+00,  1.04118768e+00,
-     1.03932339e+00,  1.03743168e+00,  1.03551757e+00,  1.03358511e+00,
-     1.03164371e+00,  1.02969955e+00,  1.02775944e+00,  1.02582719e+00,
-     1.02390791e+00,  1.02200805e+00,  1.02013910e+00,  1.01826310e+00,
-     1.01687901e+00,  1.01492195e+00,  1.01309662e+00,  1.01134205e+00,
-     1.00965912e+00,  1.00805036e+00,  1.00651754e+00,  1.00505799e+00,
-     1.00366956e+00,  1.00235327e+00,  1.00110981e+00,  9.99937523e-01,
-     9.98834524e-01,  9.97800606e-01,  9.96835756e-01,  9.95938881e-01,
-     9.95108459e-01,  9.94343411e-01,  9.93642921e-01,  9.93005832e-01,
-     9.92430984e-01,  9.91917493e-01,  9.91463898e-01,  9.91068214e-01,
-     9.90729218e-01,  9.90446225e-01,  9.90217819e-01,  9.90041963e-01,
-     9.89917085e-01,  9.89841975e-01,  9.89815048e-01,  9.89834329e-01,
-     9.89898211e-01,  9.90005403e-01,  9.90154189e-01,  9.90342427e-01,
-     9.90568459e-01,  9.90830953e-01,  9.91128038e-01,  9.91457566e-01,
-     9.91817881e-01,  9.92207559e-01,  9.92624757e-01,  9.93067358e-01,
-     9.93533398e-01,  9.94021410e-01,  9.94529685e-01,  9.95055964e-01,
-     9.95598351e-01,  9.96155580e-01,  9.96725627e-01,  9.97306092e-01,
-     9.97895214e-01,  9.98491441e-01,  9.99092890e-01,  9.99697063e-01,
-     1.00030303e+00,  1.00090793e+00,  1.00151084e+00,  1.00210923e+00,
-     1.00270118e+00,  1.00328513e+00,  1.00385926e+00,  1.00442111e+00,
-     1.00496860e+00,  1.00550040e+00,  1.00601455e+00,  1.00650869e+00,
-     1.00698104e+00,  1.00743004e+00,  1.00785364e+00,  1.00824962e+00,
-     1.00861604e+00,  1.00895138e+00,  1.00925390e+00,  1.00952134e+00,
-     1.00975175e+00,  1.00994371e+00,  1.01009550e+00,  1.01020488e+00,
-     1.01027007e+00,  1.01028975e+00,  1.01026227e+00,  1.01018562e+00,
-     1.01005820e+00,  1.00987882e+00,  1.00964593e+00,  1.00935753e+00,
-     1.00901228e+00,  1.00860959e+00,  1.00814837e+00,  1.00762674e+00,
-     1.00704343e+00,  1.00639775e+00,  1.00568877e+00,  1.00491559e+00,
-     1.00407768e+00,  1.00317429e+00,  1.00220424e+00,  1.00116684e+00,
-     1.00006248e+00,  9.98891422e-01,  9.97652252e-01,  9.96343856e-01,
-     9.94967462e-01,  9.93524663e-01,  9.92013927e-01,  9.90433283e-01,
-     9.88785147e-01,  9.87072681e-01,  9.85297443e-01,  9.83401161e-01,
-     9.80949418e-01,  9.78782729e-01,  9.76468238e-01,  9.74042850e-01,
-     9.71498848e-01,  9.68829968e-01,  9.66030974e-01,  9.63095104e-01,
-     9.60018198e-01,  9.56795738e-01,  9.53426267e-01,  9.49903482e-01,
-     9.46222115e-01,  9.42375820e-01,  9.38361702e-01,  9.34177798e-01,
-     9.29823124e-01,  9.25292320e-01,  9.20580120e-01,  9.15679793e-01,
-     9.10590604e-01,  9.05315030e-01,  8.99852756e-01,  8.94199497e-01,
-     8.88350152e-01,  8.82301631e-01,  8.76054874e-01,  8.69612385e-01,
-     8.62972799e-01,  8.56135198e-01,  8.49098179e-01,  8.41857024e-01,
-     8.34414055e-01,  8.26774617e-01,  8.18939244e-01,  8.10904891e-01,
-     8.02675318e-01,  7.94253751e-01,  7.85641662e-01,  7.76838609e-01,
-     7.67853193e-01,  7.58685181e-01,  7.49330658e-01,  7.39809171e-01,
-     7.30109944e-01,  7.20247781e-01,  7.10224161e-01,  7.00044326e-01,
-     6.89711890e-01,  6.79231154e-01,  6.68608179e-01,  6.57850997e-01,
-     6.46965718e-01,  6.35959617e-01,  6.24840336e-01,  6.13603503e-01,
-     6.02265091e-01,  5.90829083e-01,  5.79309408e-01,  5.67711124e-01,
-     5.56037416e-01,  5.44293664e-01,  5.32489768e-01,  5.20636084e-01,
-     5.08743273e-01,  4.96811166e-01,  4.84849881e-01,  4.72868107e-01,
-     4.60875918e-01,  4.48881081e-01,  4.36891039e-01,  4.24912022e-01,
-     4.12960603e-01,  4.01035896e-01,  3.89157867e-01,  3.77322199e-01,
-     3.65543767e-01,  3.53832356e-01,  3.42196115e-01,  3.30644820e-01,
-     3.19187559e-01,  3.07833309e-01,  2.96588182e-01,  2.85463717e-01,
-     2.74462409e-01,  2.63609584e-01,  2.52883101e-01,  2.42323489e-01,
-     2.31925746e-01,  2.21690837e-01,  2.11638058e-01,  2.01766920e-01,
-     1.92082236e-01,  1.82589160e-01,  1.73305997e-01,  1.64229200e-01,
-     1.55362654e-01,  1.46717079e-01,  1.38299391e-01,  1.30105078e-01,
-     1.22145310e-01,  1.14423458e-01,  1.06941076e-01,  9.97025893e-02,
-     9.27124283e-02,  8.59737427e-02,  7.94893311e-02,  7.32616579e-02,
-     6.72934102e-02,  6.15874081e-02,  5.61458003e-02,  5.09700747e-02,
-     4.60617047e-02,  4.14220117e-02,  3.70514189e-02,  3.29494666e-02,
-     2.91153327e-02,  2.55476401e-02,  2.22437711e-02,  1.92000659e-02,
-     1.64122205e-02,  1.38747611e-02,  1.15806353e-02,  9.52213664e-03,
-     7.69137380e-03,  6.07207833e-03,  4.62581217e-03,  3.60685164e-03,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-])
-
-W_7M5_360 = np.array([
-     1.72152668e-03,  2.20824874e-03,  2.68901752e-03,  3.22613342e-03,
-     3.81014420e-03,  4.45371932e-03,  5.15369240e-03,  5.91552473e-03,
-     6.73869158e-03,  7.62861841e-03,  8.58361457e-03,  9.60938437e-03,
-     1.07060753e-02,  1.18759723e-02,  1.31190130e-02,  1.44390108e-02,
-     1.58335301e-02,  1.73063081e-02,  1.88584711e-02,  2.04918652e-02,
-     2.22061476e-02,  2.40057166e-02,  2.58883593e-02,  2.78552326e-02,
-     2.99059145e-02,  3.20415894e-02,  3.42610013e-02,  3.65680973e-02,
-     3.89616721e-02,  4.14435824e-02,  4.40140796e-02,  4.66742169e-02,
-     4.94214625e-02,  5.22588489e-02,  5.51849337e-02,  5.82005143e-02,
-     6.13059845e-02,  6.45038384e-02,  6.77913923e-02,  7.11707833e-02,
-     7.46411071e-02,  7.82028053e-02,  8.18549521e-02,  8.56000162e-02,
-     8.94357617e-02,  9.33642589e-02,  9.73846703e-02,  1.01496718e-01,
-     1.05698760e-01,  1.09993603e-01,  1.14378287e-01,  1.18853508e-01,
-     1.23419277e-01,  1.28075997e-01,  1.32820581e-01,  1.37655457e-01,
-     1.42578648e-01,  1.47590522e-01,  1.52690437e-01,  1.57878853e-01,
-     1.63152529e-01,  1.68513363e-01,  1.73957969e-01,  1.79484737e-01,
-     1.85093105e-01,  1.90784835e-01,  1.96556497e-01,  2.02410419e-01,
-     2.08345433e-01,  2.14359825e-01,  2.20450365e-01,  2.26617296e-01,
-     2.32856279e-01,  2.39167941e-01,  2.45550642e-01,  2.52003951e-01,
-     2.58526168e-01,  2.65118408e-01,  2.71775911e-01,  2.78498539e-01,
-     2.85284606e-01,  2.92132459e-01,  2.99038432e-01,  3.06004256e-01,
-     3.13026529e-01,  3.20104862e-01,  3.27237324e-01,  3.34423210e-01,
-     3.41658622e-01,  3.48944976e-01,  3.56279252e-01,  3.63660034e-01,
-     3.71085146e-01,  3.78554327e-01,  3.86062695e-01,  3.93610554e-01,
-     4.01195225e-01,  4.08815272e-01,  4.16468460e-01,  4.24155411e-01,
-     4.31871046e-01,  4.39614744e-01,  4.47384019e-01,  4.55176988e-01,
-     4.62990138e-01,  4.70824619e-01,  4.78676593e-01,  4.86545433e-01,
-     4.94428714e-01,  5.02324813e-01,  5.10229471e-01,  5.18142927e-01,
-     5.26060916e-01,  5.33982818e-01,  5.41906817e-01,  5.49831283e-01,
-     5.57751234e-01,  5.65667636e-01,  5.73576883e-01,  5.81476666e-01,
-     5.89364661e-01,  5.97241338e-01,  6.05102013e-01,  6.12946170e-01,
-     6.20770242e-01,  6.28572094e-01,  6.36348526e-01,  6.44099662e-01,
-     6.51820973e-01,  6.59513822e-01,  6.67176382e-01,  6.74806795e-01,
-     6.82400711e-01,  6.89958854e-01,  6.97475722e-01,  7.04950145e-01,
-     7.12379980e-01,  7.19765434e-01,  7.27103833e-01,  7.34396372e-01,
-     7.41638561e-01,  7.48829639e-01,  7.55966688e-01,  7.63049259e-01,
-     7.70072273e-01,  7.77036981e-01,  7.83941108e-01,  7.90781257e-01,
-     7.97558114e-01,  8.04271381e-01,  8.10914901e-01,  8.17490856e-01,
-     8.23997094e-01,  8.30432785e-01,  8.36796950e-01,  8.43089298e-01,
-     8.49305847e-01,  8.55447310e-01,  8.61511037e-01,  8.67496281e-01,
-     8.73400798e-01,  8.79227518e-01,  8.84972438e-01,  8.90635719e-01,
-     8.96217173e-01,  9.01716414e-01,  9.07128770e-01,  9.12456578e-01,
-     9.17697261e-01,  9.22848784e-01,  9.27909917e-01,  9.32882596e-01,
-     9.37763323e-01,  9.42553356e-01,  9.47252428e-01,  9.51860206e-01,
-     9.56376060e-01,  9.60800602e-01,  9.65130600e-01,  9.69366689e-01,
-     9.73508812e-01,  9.77556541e-01,  9.81507226e-01,  9.85364580e-01,
-     9.89126209e-01,  9.92794201e-01,  9.96367545e-01,  9.99846919e-01,
-     1.00322812e+00,  1.00651341e+00,  1.00970073e+00,  1.01279029e+00,
-     1.01578293e+00,  1.01868229e+00,  1.02148657e+00,  1.02419772e+00,
-     1.02681455e+00,  1.02933598e+00,  1.03176043e+00,  1.03408981e+00,
-     1.03632326e+00,  1.03846361e+00,  1.04051196e+00,  1.04246831e+00,
-     1.04433331e+00,  1.04610837e+00,  1.04779018e+00,  1.04938334e+00,
-     1.05088565e+00,  1.05229923e+00,  1.05362522e+00,  1.05486289e+00,
-     1.05601521e+00,  1.05708746e+00,  1.05807221e+00,  1.05897524e+00,
-     1.05979447e+00,  1.06053414e+00,  1.06119412e+00,  1.06177366e+00,
-     1.06227662e+00,  1.06270324e+00,  1.06305569e+00,  1.06333815e+00,
-     1.06354800e+00,  1.06368607e+00,  1.06375557e+00,  1.06375743e+00,
-     1.06369358e+00,  1.06356632e+00,  1.06337707e+00,  1.06312782e+00,
-     1.06282156e+00,  1.06245782e+00,  1.06203634e+00,  1.06155996e+00,
-     1.06102951e+00,  1.06044797e+00,  1.05981709e+00,  1.05914163e+00,
-     1.05842136e+00,  1.05765876e+00,  1.05685377e+00,  1.05600761e+00,
-     1.05512006e+00,  1.05419505e+00,  1.05323346e+00,  1.05223985e+00,
-     1.05121668e+00,  1.05016637e+00,  1.04908779e+00,  1.04798366e+00,
-     1.04685334e+00,  1.04569860e+00,  1.04452056e+00,  1.04332348e+00,
-     1.04210831e+00,  1.04087907e+00,  1.03963603e+00,  1.03838099e+00,
-     1.03711403e+00,  1.03583813e+00,  1.03455276e+00,  1.03326200e+00,
-     1.03196750e+00,  1.03067200e+00,  1.02937564e+00,  1.02808244e+00,
-     1.02679167e+00,  1.02550635e+00,  1.02422655e+00,  1.02295558e+00,
-     1.02169299e+00,  1.02044475e+00,  1.01920733e+00,  1.01799992e+00,
-     1.01716022e+00,  1.01587289e+00,  1.01461783e+00,  1.01339738e+00,
-     1.01221017e+00,  1.01105652e+00,  1.00993444e+00,  1.00884559e+00,
-     1.00778956e+00,  1.00676790e+00,  1.00577851e+00,  1.00482173e+00,
-     1.00389592e+00,  1.00300262e+00,  1.00214091e+00,  1.00131213e+00,
-     1.00051460e+00,  9.99748988e-01,  9.99013486e-01,  9.98309229e-01,
-     9.97634934e-01,  9.96991885e-01,  9.96378601e-01,  9.95795982e-01,
-     9.95242217e-01,  9.94718132e-01,  9.94222122e-01,  9.93755313e-01,
-     9.93316216e-01,  9.92905809e-01,  9.92522422e-01,  9.92166957e-01,
-     9.91837704e-01,  9.91535508e-01,  9.91258603e-01,  9.91007878e-01,
-     9.90781723e-01,  9.90581104e-01,  9.90404336e-01,  9.90252267e-01,
-     9.90123118e-01,  9.90017726e-01,  9.89934325e-01,  9.89873712e-01,
-     9.89834110e-01,  9.89816359e-01,  9.89818707e-01,  9.89841998e-01,
-     9.89884438e-01,  9.89946800e-01,  9.90027287e-01,  9.90126680e-01,
-     9.90243175e-01,  9.90377594e-01,  9.90528134e-01,  9.90695564e-01,
-     9.90878043e-01,  9.91076302e-01,  9.91288540e-01,  9.91515602e-01,
-     9.91755666e-01,  9.92009469e-01,  9.92275155e-01,  9.92553486e-01,
-     9.92842693e-01,  9.93143533e-01,  9.93454080e-01,  9.93775067e-01,
-     9.94104689e-01,  9.94443742e-01,  9.94790398e-01,  9.95145361e-01,
-     9.95506800e-01,  9.95875534e-01,  9.96249681e-01,  9.96629919e-01,
-     9.97014367e-01,  9.97403799e-01,  9.97796404e-01,  9.98192871e-01,
-     9.98591286e-01,  9.98992436e-01,  9.99394506e-01,  9.99798247e-01,
-     1.00020179e+00,  1.00060586e+00,  1.00100858e+00,  1.00141070e+00,
-     1.00181040e+00,  1.00220846e+00,  1.00260296e+00,  1.00299457e+00,
-     1.00338148e+00,  1.00376444e+00,  1.00414155e+00,  1.00451348e+00,
-     1.00487832e+00,  1.00523688e+00,  1.00558730e+00,  1.00593027e+00,
-     1.00626393e+00,  1.00658905e+00,  1.00690380e+00,  1.00720890e+00,
-     1.00750238e+00,  1.00778498e+00,  1.00805489e+00,  1.00831287e+00,
-     1.00855700e+00,  1.00878802e+00,  1.00900405e+00,  1.00920593e+00,
-     1.00939182e+00,  1.00956244e+00,  1.00971590e+00,  1.00985296e+00,
-     1.00997177e+00,  1.01007317e+00,  1.01015529e+00,  1.01021893e+00,
-     1.01026225e+00,  1.01028602e+00,  1.01028842e+00,  1.01027030e+00,
-     1.01022988e+00,  1.01016802e+00,  1.01008292e+00,  1.00997541e+00,
-     1.00984369e+00,  1.00968863e+00,  1.00950846e+00,  1.00930404e+00,
-     1.00907371e+00,  1.00881848e+00,  1.00853675e+00,  1.00822947e+00,
-     1.00789488e+00,  1.00753391e+00,  1.00714488e+00,  1.00672876e+00,
-     1.00628393e+00,  1.00581146e+00,  1.00530991e+00,  1.00478053e+00,
-     1.00422177e+00,  1.00363456e+00,  1.00301719e+00,  1.00237067e+00,
-     1.00169363e+00,  1.00098749e+00,  1.00025108e+00,  9.99485663e-01,
-     9.98689592e-01,  9.97863666e-01,  9.97006370e-01,  9.96119199e-01,
-     9.95201404e-01,  9.94254687e-01,  9.93277595e-01,  9.92270651e-01,
-     9.91231967e-01,  9.90163286e-01,  9.89064394e-01,  9.87937115e-01,
-     9.86779736e-01,  9.85592773e-01,  9.84375125e-01,  9.83129288e-01,
-     9.81348463e-01,  9.79890963e-01,  9.78400459e-01,  9.76860435e-01,
-     9.75269879e-01,  9.73627353e-01,  9.71931341e-01,  9.70180498e-01,
-     9.68372652e-01,  9.66506952e-01,  9.64580027e-01,  9.62592318e-01,
-     9.60540986e-01,  9.58425534e-01,  9.56244393e-01,  9.53998416e-01,
-     9.51684014e-01,  9.49301185e-01,  9.46846884e-01,  9.44320232e-01,
-     9.41718404e-01,  9.39042580e-01,  9.36290624e-01,  9.33464050e-01,
-     9.30560854e-01,  9.27580507e-01,  9.24519592e-01,  9.21378471e-01,
-     9.18153414e-01,  9.14844696e-01,  9.11451652e-01,  9.07976524e-01,
-     9.04417545e-01,  9.00776308e-01,  8.97050058e-01,  8.93238398e-01,
-     8.89338681e-01,  8.85351360e-01,  8.81274023e-01,  8.77109638e-01,
-     8.72857927e-01,  8.68519505e-01,  8.64092796e-01,  8.59579819e-01,
-     8.54976007e-01,  8.50285220e-01,  8.45502615e-01,  8.40630470e-01,
-     8.35667925e-01,  8.30619943e-01,  8.25482007e-01,  8.20258909e-01,
-     8.14946648e-01,  8.09546696e-01,  8.04059978e-01,  7.98489378e-01,
-     7.92831417e-01,  7.87090668e-01,  7.81262450e-01,  7.75353947e-01,
-     7.69363613e-01,  7.63291769e-01,  7.57139016e-01,  7.50901711e-01,
-     7.44590843e-01,  7.38205136e-01,  7.31738075e-01,  7.25199287e-01,
-     7.18588225e-01,  7.11905687e-01,  7.05153668e-01,  6.98332634e-01,
-     6.91444101e-01,  6.84490545e-01,  6.77470119e-01,  6.70388375e-01,
-     6.63245210e-01,  6.56045780e-01,  6.48788627e-01,  6.41477162e-01,
-     6.34114323e-01,  6.26702000e-01,  6.19235334e-01,  6.11720596e-01,
-     6.04161612e-01,  5.96559133e-01,  5.88914401e-01,  5.81234783e-01,
-     5.73519989e-01,  5.65770616e-01,  5.57988067e-01,  5.50173851e-01,
-     5.42330194e-01,  5.34460798e-01,  5.26568538e-01,  5.18656324e-01,
-     5.10728813e-01,  5.02781159e-01,  4.94819491e-01,  4.86845139e-01,
-     4.78860889e-01,  4.70869928e-01,  4.62875144e-01,  4.54877894e-01,
-     4.46882512e-01,  4.38889325e-01,  4.30898123e-01,  4.22918322e-01,
-     4.14950878e-01,  4.06993964e-01,  3.99052648e-01,  3.91134614e-01,
-     3.83234031e-01,  3.75354653e-01,  3.67502060e-01,  3.59680098e-01,
-     3.51887312e-01,  3.44130166e-01,  3.36408100e-01,  3.28728966e-01,
-     3.21090505e-01,  3.13496418e-01,  3.05951565e-01,  2.98454319e-01,
-     2.91010565e-01,  2.83621109e-01,  2.76285415e-01,  2.69019585e-01,
-     2.61812445e-01,  2.54659232e-01,  2.47584348e-01,  2.40578694e-01,
-     2.33647009e-01,  2.26788433e-01,  2.20001992e-01,  2.13301325e-01,
-     2.06677771e-01,  2.00140409e-01,  1.93683630e-01,  1.87310343e-01,
-     1.81027384e-01,  1.74839476e-01,  1.68739644e-01,  1.62737273e-01,
-     1.56825277e-01,  1.51012382e-01,  1.45298230e-01,  1.39687469e-01,
-     1.34171842e-01,  1.28762544e-01,  1.23455562e-01,  1.18254662e-01,
-     1.13159677e-01,  1.08171439e-01,  1.03290734e-01,  9.85202978e-02,
-     9.38600023e-02,  8.93117360e-02,  8.48752103e-02,  8.05523737e-02,
-     7.63429787e-02,  7.22489246e-02,  6.82699120e-02,  6.44077291e-02,
-     6.06620003e-02,  5.70343711e-02,  5.35243715e-02,  5.01334690e-02,
-     4.68610790e-02,  4.37084453e-02,  4.06748365e-02,  3.77612269e-02,
-     3.49667099e-02,  3.22919275e-02,  2.97357669e-02,  2.72984629e-02,
-     2.49787186e-02,  2.27762542e-02,  2.06895808e-02,  1.87178169e-02,
-     1.68593418e-02,  1.51125125e-02,  1.34757094e-02,  1.19462709e-02,
-     1.05228754e-02,  9.20130941e-03,  7.98124316e-03,  6.85547314e-03,
-     5.82657334e-03,  4.87838525e-03,  4.02351119e-03,  3.15418663e-03,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-     0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-])
-
-
-### 3.7.4 ###
-
-SNS_LFCB = np.array([
-
-    [  2.26283366e+00,  8.13311269e-01, -5.30193495e-01, -1.35664836e+00,
-      -1.59952177e+00, -1.44098768e+00, -1.14381648e+00, -7.55203768e-01 ],
-
-    [  2.94516479e+00,  2.41143318e+00,  9.60455106e-01, -4.43226488e-01,
-      -1.22913612e+00, -1.55590039e+00, -1.49688656e+00, -1.11689987e+00 ],
-
-    [ -2.18610707e+00, -1.97152136e+00, -1.78718620e+00, -1.91865896e+00,
-      -1.79399122e+00, -1.35738404e+00, -7.05444279e-01, -4.78172945e-02 ],
-
-    [  6.93688237e-01,  9.55609857e-01,  5.75230787e-01, -1.14603419e-01,
-      -6.46050637e-01, -9.52351370e-01, -1.07405247e+00, -7.58087707e-01 ],
-
-    [ -1.29752132e+00, -7.40369057e-01, -3.45372484e-01, -3.13285696e-01,
-      -4.02977243e-01, -3.72020853e-01, -7.83414177e-02,  9.70441304e-02 ],
-
-    [  9.14652038e-01,  1.74293043e+00,  1.90906627e+00,  1.54408484e+00,
-       1.09344961e+00,  6.47479550e-01,  3.61790752e-02, -2.97092807e-01 ],
-
-    [ -2.51428813e+00, -2.89175271e+00, -2.00450667e+00, -7.50912274e-01,
-       4.41202105e-01,  1.20190988e+00,  1.32742857e+00,  1.22049081e+00 ],
-
-    [ -9.22188405e-01,  6.32495141e-01,  1.08736431e+00,  6.08628625e-01,
-       1.31174568e-01, -2.96149158e-01, -2.07013517e-01,  1.34924917e-01 ],
-
-    [  7.90322288e-01,  6.28401262e-01,  3.93117924e-01,  4.80007711e-01,
-       4.47815138e-01,  2.09734215e-01,  6.56691996e-03, -8.61242342e-02 ],
-
-    [  1.44775580e+00,  2.72399952e+00,  2.31083269e+00,  9.35051270e-01,
-      -2.74743911e-01, -9.02077697e-01, -9.40681512e-01, -6.33697039e-01 ],
-
-    [  7.93354526e-01,  1.43931186e-02, -5.67834845e-01, -6.54760468e-01,
-      -4.79458998e-01, -1.73894662e-01,  6.80162706e-02,  2.95125948e-01 ],
-
-    [  2.72425347e+00,  2.95947572e+00,  1.84953559e+00,  5.63284922e-01,
-       1.39917088e-01,  3.59641093e-01,  6.89461355e-01,  6.39790177e-01 ],
-
-    [ -5.30830198e-01, -2.12690683e-01,  5.76613628e-03,  4.24871484e-01,
-       4.73128952e-01,  8.58894199e-01,  1.19111161e+00,  9.96189670e-01 ],
-
-    [  1.68728411e+00,  2.43614509e+00,  2.33019429e+00,  1.77983778e+00,
-       1.44411295e+00,  1.51995177e+00,  1.47199394e+00,  9.77682474e-01 ],
-
-    [ -2.95183273e+00, -1.59393497e+00, -1.09918773e-01,  3.88609073e-01,
-       5.12932650e-01,  6.28112597e-01,  8.22621796e-01,  8.75891425e-01 ],
-
-    [  1.01878343e-01,  5.89857324e-01,  6.19047647e-01,  1.26731314e+00,
-       2.41961048e+00,  2.25174253e+00,  5.26537031e-01, -3.96591513e-01 ],
-
-    [  2.68254575e+00,  1.32738011e+00,  1.30185274e-01, -3.38533089e-01,
-      -3.68219236e-01, -1.91689947e-01, -1.54782377e-01, -2.34207178e-01 ],
-
-    [  4.82697924e+00,  3.11947804e+00,  1.39513671e+00,  2.50295316e-01,
-      -3.93613839e-01, -6.43458173e-01, -6.42570737e-01, -7.23193223e-01 ],
-
-    [  8.78419936e-02, -5.69586840e-01, -1.14506016e+00, -1.66968488e+00,
-      -1.84534418e+00, -1.56468027e+00, -1.11746759e+00, -5.33981663e-01 ],
-
-    [  1.39102308e+00,  1.98146479e+00,  1.11265796e+00, -2.20107509e-01,
-      -7.74965612e-01, -5.94063874e-01,  1.36937681e-01,  8.18242891e-01 ],
-
-    [  3.84585894e-01, -1.60588786e-01, -5.39366810e-01, -5.29309079e-01,
-       1.90433547e-01,  2.56062918e+00,  2.81896398e+00,  6.56670876e-01 ],
-
-    [  1.93227399e+00,  3.01030180e+00,  3.06543894e+00,  2.50110161e+00,
-       1.93089593e+00,  5.72153811e-01, -8.11741794e-01, -1.17641811e+00 ],
-
-    [  1.75080463e-01, -7.50522832e-01, -1.03943893e+00, -1.13577509e+00,
-      -1.04197904e+00, -1.52060099e-02,  2.07048392e+00,  3.42948918e+00 ],
-
-    [ -1.18817020e+00,  3.66792874e-01,  1.30957830e+00,  1.68330687e+00,
-       1.25100924e+00,  9.42375752e-01,  8.26250483e-01,  4.39952741e-01 ],
-
-    [  2.53322203e+00,  2.11274643e+00,  1.26288412e+00,  7.61513512e-01,
-       5.22117938e-01,  1.18680070e-01, -4.52346828e-01, -7.00352426e-01 ],
-
-    [  3.99889837e+00,  4.07901751e+00,  2.82285661e+00,  1.72607213e+00,
-       6.47144377e-01, -3.31148521e-01, -8.84042571e-01, -1.12697341e+00 ],
-
-    [  5.07902593e-01,  1.58838450e+00,  1.72899024e+00,  1.00692230e+00,
-       3.77121232e-01,  4.76370767e-01,  1.08754740e+00,  1.08756266e+00 ],
-
-    [  3.16856825e+00,  3.25853458e+00,  2.42230591e+00,  1.79446078e+00,
-       1.52177911e+00,  1.17196707e+00,  4.89394597e-01, -6.22795716e-02 ],
-
-    [  1.89414767e+00,  1.25108695e+00,  5.90451211e-01,  6.08358583e-01,
-       8.78171010e-01,  1.11912511e+00,  1.01857662e+00,  6.20453891e-01 ],
-
-    [  9.48880605e-01,  2.13239439e+00,  2.72345350e+00,  2.76986077e+00,
-       2.54286973e+00,  2.02046264e+00,  8.30045859e-01, -2.75569174e-02 ],
-
-    [ -1.88026757e+00, -1.26431073e+00,  3.11424977e-01,  1.83670210e+00,
-       2.25634192e+00,  2.04818998e+00,  2.19526837e+00,  2.02659614e+00 ],
-
-    [  2.46375746e-01,  9.55621773e-01,  1.52046777e+00,  1.97647400e+00,
-       1.94043867e+00,  2.23375847e+00,  1.98835978e+00,  1.27232673e+00 ],
-
-])
-
-SNS_HFCB = np.array([
-
-    [  2.32028419e-01, -1.00890271e+00, -2.14223503e+00, -2.37533814e+00,
-      -2.23041933e+00, -2.17595881e+00, -2.29065914e+00, -2.53286398e+00 ],
-
-    [ -1.29503937e+00, -1.79929965e+00, -1.88703148e+00, -1.80991660e+00,
-      -1.76340038e+00, -1.83418428e+00, -1.80480981e+00, -1.73679545e+00 ],
-
-    [  1.39285716e-01, -2.58185126e-01, -6.50804573e-01, -1.06815732e+00,
-      -1.61928742e+00, -2.18762566e+00, -2.63757587e+00, -2.97897750e+00 ],
-
-    [ -3.16513102e-01, -4.77747657e-01, -5.51162076e-01, -4.84788283e-01,
-      -2.38388394e-01, -1.43024507e-01,  6.83186674e-02,  8.83061717e-02 ],
-
-    [  8.79518405e-01,  2.98340096e-01, -9.15386396e-01, -2.20645975e+00,
-      -2.74142181e+00, -2.86139074e+00, -2.88841597e+00, -2.95182608e+00 ],
-
-    [ -2.96701922e-01, -9.75004919e-01, -1.35857500e+00, -9.83721106e-01,
-      -6.52956939e-01, -9.89986993e-01, -1.61467225e+00, -2.40712302e+00 ],
-
-    [  3.40981100e-01,  2.68899789e-01,  5.63335685e-02,  4.99114047e-02,
-      -9.54130727e-02, -7.60166146e-01, -2.32758120e+00, -3.77155485e+00 ],
-
-    [ -1.41229759e+00, -1.48522119e+00, -1.18603580e+00, -6.25001634e-01,
-       1.53902497e-01,  5.76386498e-01,  7.95092604e-01,  5.96564632e-01 ],
-
-    [ -2.28839512e-01, -3.33719070e-01, -8.09321359e-01, -1.63587877e+00,
-      -1.88486397e+00, -1.64496691e+00, -1.40515778e+00, -1.46666471e+00 ],
-
-    [ -1.07148629e+00, -1.41767015e+00, -1.54891762e+00, -1.45296062e+00,
-      -1.03182970e+00, -6.90642640e-01, -4.28843805e-01, -4.94960215e-01 ],
-
-    [ -5.90988511e-01, -7.11737759e-02,  3.45719523e-01,  3.00549461e-01,
-      -1.11865218e+00, -2.44089151e+00, -2.22854732e+00, -1.89509228e+00 ],
-
-    [ -8.48434099e-01, -5.83226811e-01,  9.00423688e-02,  8.45025008e-01,
-       1.06572385e+00,  7.37582999e-01,  2.56590452e-01, -4.91963360e-01 ],
-
-    [  1.14069146e+00,  9.64016892e-01,  3.81461206e-01, -4.82849341e-01,
-      -1.81632721e+00, -2.80279513e+00, -3.23385725e+00, -3.45908714e+00 ],
-
-    [ -3.76283238e-01,  4.25675462e-02,  5.16547697e-01,  2.51716882e-01,
-      -2.16179968e-01, -5.34074091e-01, -6.40786096e-01, -8.69745032e-01 ],
-
-    [  6.65004121e-01,  1.09790765e+00,  1.38342667e+00,  1.34327359e+00,
-       8.22978837e-01,  2.15876799e-01, -4.04925753e-01, -1.07025606e+00 ],
-
-    [ -8.26265954e-01, -6.71181233e-01, -2.28495593e-01,  5.18980853e-01,
-       1.36721896e+00,  2.18023038e+00,  2.53596093e+00,  2.20121099e+00 ],
-
-    [  1.41008327e+00,  7.54441908e-01, -1.30550585e+00, -1.87133711e+00,
-      -1.24008685e+00, -1.26712925e+00, -2.03670813e+00, -2.89685162e+00 ],
-
-    [  3.61386818e-01, -2.19991705e-02, -5.79368834e-01, -8.79427961e-01,
-      -8.50685023e-01, -7.79397050e-01, -7.32182927e-01, -8.88348515e-01 ],
-
-    [  4.37469239e-01,  3.05440420e-01, -7.38786566e-03, -4.95649855e-01,
-      -8.06651271e-01, -1.22431892e+00, -1.70157770e+00, -2.24491914e+00 ],
-
-    [  6.48100319e-01,  6.82299134e-01,  2.53247464e-01,  7.35842144e-02,
-       3.14216709e-01,  2.34729881e-01,  1.44600134e-01, -6.82120179e-02 ],
-
-    [  1.11919833e+00,  1.23465533e+00,  5.89170238e-01, -1.37192460e+00,
-      -2.37095707e+00, -2.00779783e+00, -1.66688540e+00, -1.92631846e+00 ],
-
-    [  1.41847497e-01, -1.10660071e-01, -2.82824593e-01, -6.59813475e-03,
-       2.85929280e-01,  4.60445530e-02, -6.02596416e-01, -2.26568729e+00 ],
-
-    [  5.04046955e-01,  8.26982163e-01,  1.11981236e+00,  1.17914044e+00,
-       1.07987429e+00,  6.97536239e-01, -9.12548817e-01, -3.57684747e+00 ],
-
-    [ -5.01076050e-01, -3.25678006e-01,  2.80798195e-02,  2.62054555e-01,
-       3.60590806e-01,  6.35623722e-01,  9.59012467e-01,  1.30745157e+00 ],
-
-    [  3.74970983e+00,  1.52342612e+00, -4.57715662e-01, -7.98711008e-01,
-      -3.86819329e-01, -3.75901062e-01, -6.57836900e-01, -1.28163964e+00 ],
-
-    [ -1.15258991e+00, -1.10800886e+00, -5.62615117e-01, -2.20562124e-01,
-      -3.49842880e-01, -7.53432770e-01, -9.88596593e-01, -1.28790472e+00 ],
-
-    [  1.02827246e+00,  1.09770519e+00,  7.68645546e-01,  2.06081978e-01,
-      -3.42805735e-01, -7.54939405e-01, -1.04196178e+00, -1.50335653e+00 ],
-
-    [  1.28831972e-01,  6.89439395e-01,  1.12346905e+00,  1.30934523e+00,
-       1.35511965e+00,  1.42311381e+00,  1.15706449e+00,  4.06319438e-01 ],
-
-    [  1.34033030e+00,  1.38996825e+00,  1.04467922e+00,  6.35822746e-01,
-      -2.74733756e-01, -1.54923372e+00, -2.44239710e+00, -3.02457607e+00 ],
-
-    [  2.13843105e+00,  4.24711267e+00,  2.89734110e+00,  9.32730658e-01,
-      -2.92822250e-01, -8.10404297e-01, -7.88868099e-01, -9.35353149e-01 ],
-
-    [  5.64830487e-01,  1.59184978e+00,  2.39771699e+00,  3.03697344e+00,
-       2.66424350e+00,  1.39304485e+00,  4.03834024e-01, -6.56270971e-01 ],
-
-    [ -4.22460548e-01,  3.26149625e-01,  1.39171313e+00,  2.23146615e+00,
-       2.61179442e+00,  2.66540340e+00,  2.40103554e+00,  1.75920380e+00 ],
-
-])
-
-SNS_VQ_REG_ADJ_GAINS = \
-    np.array([ 8915, 12054 ]) / 4096
-
-SNS_VQ_REG_LF_ADJ_GAINS = \
-    np.array([ 6245, 15043, 17861, 21014 ]) / 4096
-
-SNS_VQ_NEAR_ADJ_GAINS = \
-    np.array([ 7099,  9132, 11253, 14808 ]) / 4096
-
-SNS_VQ_FAR_ADJ_GAINS = \
-    np.array([ 4336,  5067,  5895,  8149, 10235, 12825, 16868, 19882 ]) / 4096
-
-SNS_MPVQ_OFFSETS = np.array([
-    [ 0, 1,  1,   1,    1,     1,      1,      1,      1,       1,       1 ],
-    [ 0, 1,  3,   5,    7,     9,     11,     13,     15,      17,      19 ],
-    [ 0, 1,  5,  13,   25,    41,     61,     85,    113,     145,     181 ],
-    [ 0, 1,  7,  25,   63,   129,    231,    377,    575,     833,    1159 ],
-    [ 0, 1,  9,  41,  129,   321,    681,   1289,   2241,    3649,    5641 ],
-    [ 0, 1, 11,  61,  231,   681,   1683,   3653,   7183,   13073  , 22363 ],
-    [ 0, 1, 13,  85,  377,  1289,   3653,   8989,  19825,   40081,   75517 ],
-    [ 0, 1, 15, 113,  575,  2241,   7183,  19825,  48639,  108545,  224143 ],
-    [ 0, 1, 17, 145,  833,  3649,  13073,  40081, 108545,  265729,  598417 ],
-    [ 0, 1, 19, 181, 1159,  5641,  22363,  75517, 224143,  598417, 1462563 ],
-    [ 0, 1, 21, 221, 1561,  8361,  36365, 134245, 433905, 1256465, 3317445 ],
-    [ 0, 1, 23, 265, 2047, 11969,  56695, 227305, 795455, 2485825, 7059735 ],
-    [ 0, 1, 25, 313, 2625, 16641,  85305, 369305,1392065, 4673345,14218905 ],
-    [ 0, 1, 27, 365, 3303, 22569, 124515, 579125,2340495, 8405905,27298155 ],
-    [ 0, 1, 29, 421, 4089, 29961, 177045, 880685,3800305,14546705,50250765 ],
-    [ 0, 1, 31, 481, 4991, 39041, 246047,1303777,5984767,24331777,89129247 ],
-])
-
-### 3.7.5 ###
-
-TNS_ORDER_BITS = np.array([
-    [ 0, 17234, 13988, 11216, 8694, 6566, 4977, 3961, 3040 ],
-    [ 0, 12683,  9437,  6874, 5541, 5121, 5170, 5359, 5056 ]
-])
-
-TNS_ORDER_FREQ = np.array([
-    [  3,  9,  23,  54, 111, 190, 268, 366 ],
-    [ 14, 42, 100, 157, 181, 178, 167, 185 ]
-])
-
-TNS_ORDER_CUMFREQ = np.array([
-    [ 0,  3, 12,  35,  89, 200, 390, 658 ],
-    [ 0, 14, 56, 156, 313, 494, 672, 839 ]
-])
-
-TNS_COEF_BITS = np.array([
-
-    [ 20480, 15725, 12479, 10334,  8694,  7320,  6964,  6335,
-       5504,  5637,  6566,  6758,  8433, 11348, 15186, 20480, 20480 ],
-
-    [ 20480, 20480, 20480, 20480, 12902,  9368,  7057,  5901,
-       5254,  5485,  5598,  6076,  7608, 10742, 15186, 20480, 20480 ],
-
-    [ 20480, 20480, 20480, 20480, 13988,  9368,  6702,  4841,
-       4585,  4682,  5859,  7764, 12109, 20480, 20480, 20480, 20480 ],
-
-    [ 20480, 20480, 20480, 20480, 18432, 13396,  8982,  4767,
-       3779,  3658,  6335,  9656, 13988, 20480, 20480, 20480, 20480 ],
-
-    [ 20480, 20480, 20480, 20480, 20480, 14731,  9437,  4275,
-       3249,  3493,  8483, 13988, 17234, 20480, 20480, 20480, 20480 ],
-
-    [ 20480, 20480, 20480, 20480, 20480, 20480, 12902,  4753,
-       3040,  2953,  9105, 15725, 20480, 20480, 20480, 20480, 20480 ],
-
-    [ 20480, 20480, 20480, 20480, 20480, 20480, 12902,  3821,
-       3346,  3000, 12109, 20480, 20480, 20480, 20480, 20480, 20480 ],
-
-    [ 20480, 20480, 20480, 20480, 20480, 20480, 15725,  3658,
-      20480,  1201, 10854, 18432, 20480, 20480, 20480, 20480, 20480 ]
-
-])
-
-TNS_COEF_FREQ = np.array([
-    [ 1, 5, 15, 31, 54, 86,  97, 120, 159, 152, 111, 104, 59, 22, 6, 1, 1 ],
-    [ 1, 1,  1,  1, 13, 43,  94, 139, 173, 160, 154, 131, 78, 27, 6, 1, 1 ],
-    [ 1, 1,  1,  1,  9, 43, 106, 199, 217, 210, 141,  74, 17,  1, 1, 1, 1 ],
-    [ 1, 1,  1,  1,  2, 11,  49, 204, 285, 297, 120,  39,  9,  1, 1, 1, 1 ],
-    [ 1, 1,  1,  1,  1,  7,  42, 241, 341, 314,  58,   9,  3,  1, 1, 1, 1 ],
-    [ 1, 1,  1,  1,  1,  1,  13, 205, 366, 377,  47,   5,  1,  1, 1, 1, 1 ],
-    [ 1, 1,  1,  1,  1,  1,  13, 281, 330, 371,  17,   1,  1,  1, 1, 1, 1 ],
-    [ 1, 1,  1,  1,  1,  1,   5, 297,   1, 682,  26,   2,  1,  1, 1, 1, 1 ]
-])
-
-TNS_COEF_CUMFREQ = np.array([
-
-    [    0,    1,    6,   21,   52,  106,  192,  289,
-       409,  568,  720,  831,  935,  994, 1016, 1022, 1023 ],
-
-    [    0,    1,    2,    3,    4,   17,   60,  154,
-       293,  466,  626,  780,  911,  989, 1016, 1022, 1023 ],
-
-    [    0,    1,    2,    3,    4,   13,   56,  162,
-       361,  578,  788,  929, 1003, 1020, 1021, 1022, 1023 ],
-
-    [    0,    1,    2,    3,    4,    6,   17,   66,
-       270,  555,  852,  972, 1011, 1020, 1021, 1022, 1023 ],
-
-    [    0,    1,    2,    3,    4,    5,   12,   54,
-       295,  636,  950, 1008, 1017, 1020, 1021, 1022, 1023 ],
-
-    [    0,    1,    2,    3,    4,    5,    6,   19,
-       224,  590,  967, 1014, 1019, 1020, 1021, 1022, 1023 ],
-
-    [    0,    1,    2,    3,    4,    5,    6,   19,
-       300,  630, 1001, 1018, 1019, 1020, 1021, 1022, 1023 ],
-
-    [    0,    1,    2,    3,    4,    5,    6,   11,
-       308,  309,  991, 1017, 1019, 1020, 1021, 1022, 1023 ],
-
-])
-
-
-### 3.7.6 ###
-
-LTPF_H12K8 = np.array([
-    -2.04305583e-05, -4.46345894e-05, -7.16366399e-05, -1.00101113e-04,
-    -1.28372848e-04, -1.54543830e-04, -1.76544567e-04, -1.92256960e-04,
-    -1.99643819e-04, -1.96888686e-04, -1.82538332e-04, -1.55639427e-04,
-    -1.15860365e-04, -6.35893034e-05,  2.81006480e-19,  7.29218021e-05,
-     1.52397076e-04,  2.34920777e-04,  3.16378650e-04,  3.92211738e-04,
-     4.57623849e-04,  5.07824294e-04,  5.38295523e-04,  5.45072918e-04,
-     5.25022155e-04,  4.76098424e-04,  3.97571380e-04,  2.90200217e-04,
-     1.56344667e-04, -5.81880142e-19, -1.73252713e-04, -3.56385965e-04,
-    -5.41155231e-04, -7.18414023e-04, -8.78505232e-04, -1.01171451e-03,
-    -1.10876706e-03, -1.16134522e-03, -1.16260169e-03, -1.10764097e-03,
-    -9.93941563e-04, -8.21692190e-04, -5.94017766e-04, -3.17074654e-04,
-     9.74695082e-19,  3.45293760e-04,  7.04480871e-04,  1.06133447e-03,
-     1.39837473e-03,  1.69763080e-03,  1.94148675e-03,  2.11357591e-03,
-     2.19968245e-03,  2.18860625e-03,  2.07294546e-03,  1.84975249e-03,
-     1.52102188e-03,  1.09397426e-03,  5.81108062e-04, -1.42248266e-18,
-    -6.27153730e-04, -1.27425140e-03, -1.91223839e-03, -2.51026925e-03,
-    -3.03703830e-03, -3.46222687e-03, -3.75800672e-03, -3.90053247e-03,
-    -3.87135231e-03, -3.65866558e-03, -3.25835851e-03, -2.67475555e-03,
-    -1.92103305e-03, -1.01925433e-03,  1.86962369e-18,  1.09841545e-03,
-     2.23113197e-03,  3.34830927e-03,  4.39702277e-03,  5.32342672e-03,
-     6.07510531e-03,  6.60352025e-03,  6.86645399e-03,  6.83034270e-03,
-     6.47239234e-03,  5.78237521e-03,  4.76401273e-03,  3.43586351e-03,
-     1.83165284e-03, -2.25189837e-18, -1.99647619e-03, -4.08266886e-03,
-    -6.17308037e-03, -8.17444895e-03, -9.98882386e-03, -1.15169871e-02,
-    -1.26621006e-02, -1.33334458e-02, -1.34501120e-02, -1.29444881e-02,
-    -1.17654154e-02, -9.88086732e-03, -7.28003640e-03, -3.97473021e-03,
-     2.50961778e-18,  4.58604422e-03,  9.70324900e-03,  1.52512477e-02,
-     2.11120585e-02,  2.71533724e-02,  3.32324245e-02,  3.92003203e-02,
-     4.49066644e-02,  5.02043309e-02,  5.49542017e-02,  5.90297032e-02,
-     6.23209727e-02,  6.47385023e-02,  6.62161245e-02,  6.67132287e-02,
-     6.62161245e-02,  6.47385023e-02,  6.23209727e-02,  5.90297032e-02,
-     5.49542017e-02,  5.02043309e-02,  4.49066644e-02,  3.92003203e-02,
-     3.32324245e-02,  2.71533724e-02,  2.11120585e-02,  1.52512477e-02,
-     9.70324900e-03,  4.58604422e-03,  2.50961778e-18, -3.97473021e-03,
-    -7.28003640e-03, -9.88086732e-03, -1.17654154e-02, -1.29444881e-02,
-    -1.34501120e-02, -1.33334458e-02, -1.26621006e-02, -1.15169871e-02,
-    -9.98882386e-03, -8.17444895e-03, -6.17308037e-03, -4.08266886e-03,
-    -1.99647619e-03, -2.25189837e-18,  1.83165284e-03,  3.43586351e-03,
-     4.76401273e-03,  5.78237521e-03,  6.47239234e-03,  6.83034270e-03,
-     6.86645399e-03,  6.60352025e-03,  6.07510531e-03,  5.32342672e-03,
-     4.39702277e-03,  3.34830927e-03,  2.23113197e-03,  1.09841545e-03,
-     1.86962369e-18, -1.01925433e-03, -1.92103305e-03, -2.67475555e-03,
-    -3.25835851e-03, -3.65866558e-03, -3.87135231e-03, -3.90053247e-03,
-    -3.75800672e-03, -3.46222687e-03, -3.03703830e-03, -2.51026925e-03,
-    -1.91223839e-03, -1.27425140e-03, -6.27153730e-04, -1.42248266e-18,
-     5.81108062e-04,  1.09397426e-03,  1.52102188e-03,  1.84975249e-03,
-     2.07294546e-03,  2.18860625e-03,  2.19968245e-03,  2.11357591e-03,
-     1.94148675e-03,  1.69763080e-03,  1.39837473e-03,  1.06133447e-03,
-     7.04480871e-04,  3.45293760e-04,  9.74695082e-19, -3.17074654e-04,
-    -5.94017766e-04, -8.21692190e-04, -9.93941563e-04, -1.10764097e-03,
-    -1.16260169e-03, -1.16134522e-03, -1.10876706e-03, -1.01171451e-03,
-    -8.78505232e-04, -7.18414023e-04, -5.41155231e-04, -3.56385965e-04,
-    -1.73252713e-04, -5.81880142e-19,  1.56344667e-04,  2.90200217e-04,
-     3.97571380e-04,  4.76098424e-04,  5.25022155e-04,  5.45072918e-04,
-     5.38295523e-04,  5.07824294e-04,  4.57623849e-04,  3.92211738e-04,
-     3.16378650e-04,  2.34920777e-04,  1.52397076e-04,  7.29218021e-05,
-     2.81006480e-19, -6.35893034e-05, -1.15860365e-04, -1.55639427e-04,
-    -1.82538332e-04, -1.96888686e-04, -1.99643819e-04, -1.92256960e-04,
-    -1.76544567e-04, -1.54543830e-04, -1.28372848e-04, -1.00101113e-04,
-    -7.16366399e-05, -4.46345894e-05, -2.04305583e-05
-])
-
-LTPF_H4 = np.array([
-    -2.87456116e-03, -3.00125103e-03,  2.74547165e-03,  1.53572770e-02,
-     2.86823405e-02,  2.95038503e-02,  4.59833449e-03, -4.72963246e-02,
-    -1.05835916e-01, -1.30305021e-01, -7.54404636e-02,  8.35788573e-02,
-     3.30182571e-01,  6.03297008e-01,  8.17488686e-01,  8.98638285e-01,
-     8.17488686e-01,  6.03297008e-01,  3.30182571e-01,  8.35788573e-02,
-    -7.54404636e-02, -1.30305021e-01, -1.05835916e-01, -4.72963246e-02,
-     4.59833449e-03,  2.95038503e-02,  2.86823405e-02,  1.53572770e-02,
-     2.74547165e-03, -3.00125103e-03, -2.87456116e-03
-])
-
-LTPF_HI = np.array([
-     6.69885837e-03,  3.96711478e-02,  1.06999186e-01,  2.09880463e-01,
-     3.35690625e-01,  4.59220930e-01,  5.50075002e-01,  5.83527575e-01,
-     5.50075002e-01,  4.59220930e-01,  3.35690625e-01,  2.09880463e-01,
-     1.06999186e-01,  3.96711478e-02,  6.69885837e-03
-])
-
-LTPF_N_8K = np.array([
-    [  6.02361821e-01,  4.19760926e-01, -1.88342453e-02 ],
-    [  5.99476858e-01,  4.19760926e-01, -1.59492828e-02 ],
-    [  5.96776466e-01,  4.19760926e-01, -1.32488910e-02 ],
-    [  5.94241012e-01,  4.19760926e-01, -1.07134366e-02 ],
-])
-
-LTPF_N_16K = np.array([
-    [  6.02361821e-01,  4.19760926e-01, -1.88342453e-02 ],
-    [  5.99476858e-01,  4.19760926e-01, -1.59492828e-02 ],
-    [  5.96776466e-01,  4.19760926e-01, -1.32488910e-02 ],
-    [  5.94241012e-01,  4.19760926e-01, -1.07134366e-02 ],
-])
-
-LTPF_N_24K = np.array([
-
-    [  3.98969559e-01,  5.14250861e-01,  1.00438297e-01, -1.27889396e-02,
-      -1.57228008e-03 ],
-
-    [  3.94863491e-01,  5.12381921e-01,  1.04319493e-01, -1.09199996e-02,
-      -1.34740833e-03 ],
-
-    [  3.90984448e-01,  5.10605352e-01,  1.07983252e-01, -9.14343107e-03,
-      -1.13212462e-03 ],
-
-    [  3.87309389e-01,  5.08912208e-01,  1.11451738e-01, -7.45028713e-03,
-      -9.25551405e-04 ],
-
-])
-
-LTPF_N_32K = np.array([
-
-    [  2.98237945e-01,  4.65280920e-01,  2.10599743e-01,  3.76678038e-02,
-      -1.01569616e-02, -2.53588100e-03, -3.18294617e-04 ],
-
-    [  2.94383415e-01,  4.61929400e-01,  2.12946577e-01,  4.06617500e-02,
-      -8.69327230e-03, -2.17830711e-03, -2.74288806e-04 ],
-
-    [  2.90743921e-01,  4.58746191e-01,  2.15145697e-01,  4.35010477e-02,
-      -7.29549535e-03, -1.83439564e-03, -2.31692019e-04 ],
-
-    [  2.87297585e-01,  4.55714889e-01,  2.17212695e-01,  4.62008888e-02,
-      -5.95746380e-03, -1.50293428e-03, -1.90385191e-04 ],
-
-])
-
-LTPF_N_48K = np.array([
-
-    [  1.98136374e-01,  3.52449490e-01,  2.51369527e-01,  1.42414624e-01,
-       5.70473102e-02,  9.29336624e-03, -7.22602537e-03, -3.17267989e-03,
-      -1.12183596e-03, -2.90295724e-04, -4.27081559e-05 ],
-
-    [  1.95070943e-01,  3.48466041e-01,  2.50998846e-01,  1.44116741e-01,
-       5.92894732e-02,  1.10892383e-02, -6.19290811e-03, -2.72670551e-03,
-      -9.66712583e-04, -2.50810092e-04, -3.69993877e-05 ],
-
-    [  1.92181006e-01,  3.44694556e-01,  2.50622009e-01,  1.45710245e-01,
-       6.14113213e-02,  1.27994140e-02, -5.20372109e-03, -2.29732451e-03,
-      -8.16560813e-04, -2.12385575e-04, -3.14127133e-05 ],
-
-    [  1.89448531e-01,  3.41113925e-01,  2.50240688e-01,  1.47206563e-01,
-       6.34247723e-02,  1.44320343e-02, -4.25444914e-03, -1.88308147e-03,
-      -6.70961906e-04, -1.74936334e-04, -2.59386474e-05 ],
-
-])
-
-LTPF_D_8K = np.array([
-
-    [  0.00000000e+00,  2.09880463e-01,  5.83527575e-01,  2.09880463e-01,
-       0.00000000e+00 ],
-
-    [  0.00000000e+00,  1.06999186e-01,  5.50075002e-01,  3.35690625e-01,
-       6.69885837e-03 ],
-
-    [  0.00000000e+00,  3.96711478e-02,  4.59220930e-01,  4.59220930e-01,
-       3.96711478e-02 ],
-
-    [  0.00000000e+00,  6.69885837e-03,  3.35690625e-01,  5.50075002e-01,
-       1.06999186e-01 ],
-
-])
-
-LTPF_D_16K = np.array([
-
-    [  0.00000000e+00,  2.09880463e-01,  5.83527575e-01,  2.09880463e-01,
-       0.00000000e+00 ],
-
-    [  0.00000000e+00,  1.06999186e-01,  5.50075002e-01,  3.35690625e-01,
-       6.69885837e-03 ],
-
-    [  0.00000000e+00,  3.96711478e-02,  4.59220930e-01,  4.59220930e-01,
-       3.96711478e-02 ],
-
-    [  0.00000000e+00,  6.69885837e-03,  3.35690625e-01,  5.50075002e-01,
-       1.06999186e-01 ],
-
-])
-
-LTPF_D_24K = np.array([
-
-    [  0.00000000e+00,  6.32223163e-02,  2.50730961e-01,  3.71390943e-01,
-       2.50730961e-01,  6.32223163e-02,  0.00000000e+00 ],
-
-    [  0.00000000e+00,  3.45927217e-02,  1.98651560e-01,  3.62641173e-01,
-       2.98675055e-01,  1.01309287e-01,  4.26354371e-03 ],
-
-    [  0.00000000e+00,  1.53574678e-02,  1.47434488e-01,  3.37425955e-01,
-       3.37425955e-01,  1.47434488e-01,  1.53574678e-02 ],
-
-    [  0.00000000e+00,  4.26354371e-03,  1.01309287e-01,  2.98675055e-01,
-       3.62641173e-01,  1.98651560e-01,  3.45927217e-02 ],
-
-])
-
-LTPF_D_32K = np.array([
-
-    [  0.00000000e+00,  2.90040188e-02,  1.12985742e-01,  2.21202403e-01,
-       2.72390947e-01,  2.21202403e-01,  1.12985742e-01,  2.90040188e-02,
-       0.00000000e+00 ],
-
-    [  0.00000000e+00,  1.70315342e-02,  8.72250379e-02,  1.96140776e-01,
-       2.68923798e-01,  2.42499910e-01,  1.40577336e-01,  4.47487717e-02,
-       3.12703024e-03 ],
-
-    [  0.00000000e+00,  8.56367375e-03,  6.42622294e-02,  1.68767671e-01,
-       2.58744594e-01,  2.58744594e-01,  1.68767671e-01,  6.42622294e-02,
-       8.56367375e-03 ],
-
-    [  0.00000000e+00,  3.12703024e-03,  4.47487717e-02,  1.40577336e-01,
-       2.42499910e-01,  2.68923798e-01,  1.96140776e-01,  8.72250379e-02,
-       1.70315342e-02 ],
-
-])
-
-LTPF_D_48K = np.array([
-
-    [  0.00000000e+00,  1.08235939e-02,  3.60896922e-02,  7.67640147e-02,
-       1.24153058e-01,  1.62759644e-01,  1.77677142e-01,  1.62759644e-01,
-       1.24153058e-01,  7.67640147e-02,  3.60896922e-02,  1.08235939e-02,
-       0.00000000e+00 ],
-
-    [  0.00000000e+00,  7.04140493e-03,  2.81970232e-02,  6.54704494e-02,
-       1.12464799e-01,  1.54841896e-01,  1.76712238e-01,  1.69150721e-01,
-       1.35290158e-01,  8.85142501e-02,  4.49935385e-02,  1.55761371e-02,
-       2.03972196e-03 ],
-
-    [  0.00000000e+00,  4.14699847e-03,  2.13575731e-02,  5.48273558e-02,
-       1.00497144e-01,  1.45606034e-01,  1.73843984e-01,  1.73843984e-01,
-       1.45606034e-01,  1.00497144e-01,  5.48273558e-02,  2.13575731e-02,
-       4.14699847e-03 ],
-
-    [  0.00000000e+00,  2.03972196e-03,  1.55761371e-02,  4.49935385e-02,
-       8.85142501e-02,  1.35290158e-01,  1.69150721e-01,  1.76712238e-01,
-       1.54841896e-01,  1.12464799e-01,  6.54704494e-02,  2.81970232e-02,
-       7.04140493e-03 ],
-
-])
-
-### 3.7.7 ###
-
-AC_SPEC_LOOKUP = np.array([
-     1, 39,  7, 25, 22, 22, 28, 22, 22, 22, 22, 28, 28, 28, 34, 31,
-    31, 40, 43, 46, 49, 52, 14, 17, 36, 36, 36, 38,  0, 57, 38, 22,
-     0,  8,  9, 11, 47, 14, 14, 17, 36, 36, 36, 38, 59, 59, 38, 22,
-    22, 26, 46, 29, 30, 32, 33, 35, 36, 36, 36, 38,  0, 59, 23, 22,
-    46, 46, 45, 47, 48, 50, 50, 18, 54, 54, 54, 38, 59, 59, 59, 22,
-     0, 62, 63,  3, 33,  2,  2, 61, 20, 20, 20, 21, 59, 59, 39, 28,
-    28, 63, 63,  3, 33,  2,  2, 61, 38, 38, 38, 21, 59, 59, 39, 28,
-    28,  6,  6,  6,  2, 18, 61, 20, 21, 21, 21, 59, 39, 39,  7, 34,
-    34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
-    34, 51, 51, 51, 53, 54, 20, 38, 38, 57, 39, 39, 39,  7, 24, 34,
-     4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,  4,
-     4,  4,  4,  4,  4, 56, 38, 57, 57, 59,  7,  7,  7, 42, 42, 34,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  5,  4,  4,  5, 21, 21, 59,  7,  7,  7,  7, 25, 25, 25, 34,
-     4,  4,  4,  4,  5, 23, 23, 39,  7,  7,  7, 42, 25, 25, 22, 31,
-    31, 39, 39, 39, 39,  7,  7, 42,  0, 25, 22, 22, 22, 28, 34, 31,
-    55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
-    55, 55, 40,  8,  9, 49, 49, 52, 17, 17, 17,  4,  0, 20, 17, 60,
-    40, 40,  8, 43, 27, 49, 49, 14, 17, 17, 17, 36, 42, 42, 17, 57,
-    57, 40,  8, 26, 27, 49, 12, 14, 17, 17, 17, 36,  0, 38, 36,  1,
-     8,  8, 43,  9, 11, 49, 12, 14, 14, 33, 50, 50, 50, 61, 36, 39,
-     8,  8, 43, 46, 49, 52, 30, 14, 14, 33, 50, 50, 50, 50, 18, 25,
-     8,  8, 43, 46, 49, 52, 30, 14, 14, 18,  5,  5,  5, 61, 18, 23,
-    43, 43, 43,  9, 49, 52,  3, 14, 14, 50, 50, 50, 50, 61, 17, 24,
-    43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
-    43, 43, 43,  9, 11, 52, 52, 14, 14, 17, 61, 61, 61, 54, 17, 39,
-    45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
-    45, 45, 44, 27, 29, 52, 48, 52, 52, 17, 17, 17, 17,  2, 17,  7,
-    27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
-    27, 27,  9, 27, 27, 12, 52, 14, 14, 58, 41, 41, 41,  6, 17, 37,
-     9,  9,  9, 27, 11, 49, 12, 52, 14, 14, 14, 50,  0, 53, 17, 28,
-    52, 52, 49, 52, 12, 52, 30, 14, 14, 17,  2,  2,  2, 38, 38, 34,
-    31, 34, 34, 31, 31, 31, 31, 19, 19, 19, 19, 19, 19, 19, 31, 19,
-    44, 44, 62, 30, 32, 58, 35, 36, 36, 38,  0, 59,  7,  7, 39, 34,
-    34, 45, 47, 48, 33, 35, 35, 36, 38, 38, 38, 59,  7,  7, 39, 34,
-    34, 62, 30, 15, 50, 53, 53, 54, 21, 21, 21, 59,  7,  7,  7, 34,
-    30, 30, 48, 33, 58, 18, 18, 56, 23, 23, 23, 59,  7,  7, 24, 34,
-    34,  6,  6, 58, 53, 54, 54, 21, 59, 59, 59, 39,  7,  7, 42, 34,
-     6,  6, 33, 58, 53, 54, 61, 21, 59, 59, 59, 39,  7,  7, 42, 34,
-    34, 51, 51, 53, 54, 56, 56, 57, 39, 39, 39,  7, 42, 42, 25, 31,
-    31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
-    31,  4,  4,  4,  5, 23, 23, 39,  7,  7,  7, 42, 25, 25, 22, 31,
-    31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
-    31,  5,  5,  5,  5, 57, 57, 39, 24, 24, 24, 42, 22, 22, 28, 31,
-    31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
-    31, 41, 41, 41, 41, 39, 39,  7, 42, 42, 42, 25, 28, 28, 28, 31,
-    31, 41, 41, 41, 41, 39, 39, 24, 25, 25, 25, 22, 28, 28, 34, 31,
-    31, 10, 10, 10, 10, 10, 10, 28, 34, 34, 34, 34, 34, 34, 31, 19,
-     8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,
-     8,  8,  9, 11, 47, 32, 50, 18, 18, 20, 21, 21, 21, 39, 59, 34,
-    26, 26, 27, 29, 30, 33, 50, 18, 18, 20, 57, 57, 57, 59, 59, 34,
-    27, 27, 11, 12, 48, 50, 58, 61, 61, 56, 57, 57, 57, 59, 39, 34,
-    45, 45, 12, 30, 32,  2,  2, 61, 38, 38, 38, 57,  0, 59, 39, 34,
-    63, 63,  3, 32, 58, 18, 18, 20, 21, 21, 21, 59, 39, 39,  7, 31,
-    31,  3,  3, 33, 58, 18, 18, 20, 21, 21, 21, 59,  7,  7,  7, 31,
-     6,  6, 51, 51, 53, 54, 54, 38, 57, 57, 57, 39,  7,  7, 42, 31,
-    31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
-    31, 51, 53, 53, 54, 56, 56, 57, 59, 59, 59,  7, 24, 24, 25, 31,
-    31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
-    31,  4,  4,  4, 54, 21, 21, 57, 39, 39, 39,  7, 42, 42, 22, 31,
-    31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
-    31,  5,  5,  5,  5, 23, 23, 59,  7,  7,  7, 42, 22, 22, 28, 31,
-    31,  4,  4,  4,  5, 23, 23, 39, 24, 24, 24, 25, 28, 28, 34, 31,
-    31, 10, 10, 10, 10, 10, 10, 28, 34, 34, 34, 31, 31, 31, 31, 19,
-    13, 13, 13, 13, 13, 13, 13, 13, 60, 60, 60, 60, 60, 60, 60, 16,
-    16,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 60,
-    60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 60,
-    60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
-    60,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 60,
-    60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
-    60,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 13, 60,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 60,
-    60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 16,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13,  0, 13,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13,  0, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13,  0, 13, 60,
-    60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13,  0,  0, 13,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 60,  0,
-     0, 16,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 37,
-     0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 60,
-     0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 60,
-     0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 13, 60,
-     0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 13, 60,
-     0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 60, 16,
-     0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 60, 16,
-     0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 60, 16,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 60, 16,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 60, 60, 16,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 60, 60, 16,
-    13, 13, 13, 13, 13, 13, 13, 13, 60, 60, 60, 60, 60, 60, 60, 16,
-    60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 16, 16, 16, 16, 16, 37,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 16,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 16,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 16,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 16,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 16,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 16,
-     0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 60, 16,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 60, 16,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0, 13,  0, 13, 13, 13, 13, 13, 13, 13, 60, 16,
-    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
-    16,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 13, 60, 60, 60, 16,
-    13, 13, 13, 13, 13, 13, 13, 13, 60, 60, 60, 60, 60, 60, 16, 16,
-    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 37,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13,  0, 13, 13,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13,  0, 13, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0, 60,  0,  0,  0,  0, 13, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13,  0, 13, 13,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13,  0, 19,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13, 13, 13,  0,  0,  0,  0,  0,  0,  0,  0, 19, 13, 13, 13,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 19,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13, 13, 13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-    60, 13, 13, 13, 13, 13, 13, 60, 60, 60, 60, 60, 60, 60, 60, 16,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 60, 60, 60, 16,
-    16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
-    16,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 60,
-     0,  0,  0,  0,  0,  0,  0, 13, 13, 13, 13, 13, 13, 13, 13, 60,
-    13, 13, 13, 13, 13, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-    13, 13, 13, 13, 13,  0,  0,  0,  0,  0, 13, 13, 13, 13,  0,  0,
-    13, 13, 13, 13, 13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
-    13, 13, 13, 13, 13,  0,  0,  0,  0,  0,  0,  0, 13, 13, 13,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13, 60,
-    60,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-    13,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 13,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-     0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-])
-
-AC_SPEC_BITS = np.array([
-
-    [ 20480, 20480,  5220,  9042, 20480, 20480,  6619,  9892,
-       5289,  6619,  9105, 11629,  8982,  9892, 11629, 13677,  4977 ],
-
-    [ 11940, 10854, 12109, 13677, 10742,  9812, 11090, 12288,
-      11348, 10240, 11348, 12683, 12109, 10854, 11629, 12902,  1197 ],
-
-    [  7886,  7120,  8982, 10970,  7496,  6815,  8334, 10150,
-       9437,  8535,  9656, 11216, 11348, 10431, 11348, 12479,  4051 ],
-
-    [  5485,  6099,  9168, 11940,  6311,  6262,  8640, 11090,
-       9233,  8640, 10334, 12479, 11781, 11090, 12479, 13988,  6009 ],
-
-    [  7886,  7804, 10150, 11940,  7886,  7685,  9368, 10854,
-      10061,  9300, 10431, 11629, 11629, 10742, 11485, 12479,  2763 ],
-
-    [  9042,  8383, 10240, 11781,  8483,  8013,  9437, 10742,
-      10334,  9437, 10431, 11485, 11781, 10742, 11485, 12288,  2346 ],
-
-    [  5922,  6619,  9368, 11940,  6566,  6539,  8750, 10970,
-       9168,  8640, 10240, 12109, 11485, 10742, 11940, 13396,  5009 ],
-
-    [ 12288, 11090, 11348, 12109, 11090,  9892, 10334, 10970,
-      11629, 10431, 10970, 11629, 12479, 11348, 11781, 12288,  1289 ],
-
-    [  1685,  5676, 13138, 18432,  5598,  7804, 13677, 18432,
-      12683, 13396, 17234, 20480, 17234, 17234, 20480, 20480, 15725 ],
-
-    [  2793,  5072, 10970, 15725,  5204,  6487, 11216, 15186,
-      10970, 11216, 14336, 17234, 15186, 15186, 17234, 18432, 12109 ],
-
-    [ 12902, 11485, 11940, 13396, 11629, 10531, 11348, 12479,
-      12683, 11629, 12288, 13138, 13677, 12683, 13138, 13677,   854 ],
-
-    [  3821,  5088,  9812, 13988,  5289,  5901,  9812, 13677,
-       9976,  9892, 12479, 15186, 13988, 13677, 15186, 17234,  9812 ],
-
-    [  4856,  5412,  9168, 12902,  5598,  5736,  8863, 12288,
-       9368,  8982, 11090, 13677, 12902, 12288, 13677, 15725,  8147 ],
-
-    [ 20480, 20480,  7088,  9300, 20480, 20480,  7844,  9733,
-       7320,  7928,  9368, 10970,  9581,  9892, 10970, 12288,  2550 ],
-
-    [  6031,  5859,  8192, 10635,  6410,  6286,  8433, 10742,
-       9656,  9042, 10531, 12479, 12479, 11629, 12902, 14336,  5756 ],
-
-    [  6144,  6215,  8982, 11940,  6262,  6009,  8433, 11216,
-       8982,  8433, 10240, 12479, 11781, 11090, 12479, 13988,  5817 ],
-
-    [ 20480, 20480, 11216, 12109, 20480, 20480, 11216, 11940,
-      11629, 11485, 11940, 12479, 12479, 12109, 12683, 13138,   704 ],
-
-    [  7928,  6994,  8239,  9733,  7218,  6539,  8147,  9892,
-       9812,  9105, 10240, 11629, 12109, 11216, 12109, 13138,  4167 ],
-
-    [  8640,  7724,  9233, 10970,  8013,  7185,  8483, 10150,
-       9656,  8694,  9656, 10970, 11348, 10334, 11090, 12288,  3391 ],
-
-    [ 20480, 18432, 18432, 18432, 18432, 18432, 18432, 18432,
-      18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432,    91 ],
-
-    [ 10061,  8863,  9733, 11090,  8982,  7970,  8806,  9976,
-      10061,  9105,  9812, 10742, 11485, 10334, 10970, 11781,  2557 ],
-
-    [ 10431,  9368, 10240, 11348,  9368,  8433,  9233, 10334,
-      10431,  9437, 10061, 10970, 11781, 10635, 11216, 11940,  2119 ],
-
-    [ 13988, 12479, 12683, 12902, 12683, 11348, 11485, 11940,
-      12902, 11629, 11940, 12288, 13396, 12109, 12479, 12683,   828 ],
-
-    [ 10431,  9300, 10334, 11629,  9508,  8483,  9437, 10635,
-      10635,  9656, 10431, 11348, 11940, 10854, 11485, 12288,  1946 ],
-
-    [ 12479, 11216, 11629, 12479, 11348, 10150, 10635, 11348,
-      11940, 10854, 11216, 11940, 12902, 11629, 11940, 12479,  1146 ],
-
-    [ 13396, 12109, 12288, 12902, 12109, 10854, 11216, 11781,
-      12479, 11348, 11629, 12109, 13138, 11940, 12288, 12683,   928 ],
-
-    [  2443,  5289, 11629, 16384,  5170,  6730, 11940, 16384,
-      11216, 11629, 14731, 18432, 15725, 15725, 18432, 20480, 13396 ],
-
-    [  3328,  5009, 10531, 15186,  5040,  6031, 10531, 14731,
-      10431, 10431, 13396, 16384, 15186, 14731, 16384, 18432, 11629 ],
-
-    [ 14336, 12902, 12902, 13396, 12902, 11629, 11940, 12288,
-      13138, 12109, 12288, 12902, 13677, 12683, 12902, 13138,   711 ],
-
-    [  4300,  5204,  9437, 13396,  5430,  5776,  9300, 12902,
-       9656,  9437, 11781, 14731, 13396, 12902, 14731, 16384,  8982 ],
-
-    [  5394,  5776,  8982, 12288,  5922,  5901,  8640, 11629,
-       9105,  8694, 10635, 13138, 12288, 11629, 13138, 14731,  6844 ],
-
-    [ 17234, 15725, 15725, 15725, 15725, 14731, 14731, 14731,
-      16384, 14731, 14731, 15186, 16384, 15186, 15186, 15186,   272 ],
-
-    [  6461,  6286,  8806, 11348,  6566,  6215,  8334, 10742,
-       9233,  8535, 10061, 12109, 11781, 10970, 12109, 13677,  5394 ],
-
-    [  6674,  6487,  8863, 11485,  6702,  6286,  8334, 10635,
-       9168,  8483,  9976, 11940, 11629, 10854, 11940, 13396,  5105 ],
-
-    [ 15186, 13677, 13677, 13988, 13677, 12479, 12479, 12683,
-      13988, 12683, 12902, 13138, 14336, 13138, 13396, 13677,   565 ],
-
-    [  7844,  7252,  8922, 10854,  7389,  6815,  8383, 10240,
-       9508,  8750,  9892, 11485, 11629, 10742, 11629, 12902,  3842 ],
-
-    [  9233,  8239,  9233, 10431,  8334,  7424,  8483,  9892,
-      10061,  9105, 10061, 11216, 11781, 10742, 11485, 12479,  2906 ],
-
-    [ 20480, 20480, 14731, 14731, 20480, 20480, 14336, 14336,
-      15186, 14336, 14731, 14731, 15186, 14731, 14731, 15186,   266 ],
-
-    [ 10531,  9300,  9976, 11090,  9437,  8286,  9042, 10061,
-      10431,  9368,  9976, 10854, 11781, 10531, 11090, 11781,  2233 ],
-
-    [ 11629, 10334, 10970, 12109, 10431,  9368, 10061, 10970,
-      11348, 10240, 10854, 11485, 12288, 11216, 11629, 12288,  1469 ],
-
-    [   952,  6787, 15725, 20480,  6646,  9733, 16384, 20480,
-      14731, 15725, 18432, 20480, 18432, 20480, 20480, 20480, 18432 ],
-
-    [  9437,  8806, 10742, 12288,  8982,  8483,  9892, 11216,
-      10742,  9892, 10854, 11940, 12109, 11090, 11781, 12683,  1891 ],
-
-    [ 12902, 11629, 11940, 12479, 11781, 10531, 10854, 11485,
-      12109, 10970, 11348, 11940, 12902, 11781, 12109, 12479,  1054 ],
-
-    [  2113,  5323, 11781, 16384,  5579,  7252, 12288, 16384,
-      11781, 12288, 15186, 18432, 15725, 16384, 18432, 20480, 12902 ],
-
-    [  2463,  5965, 11348, 15186,  5522,  6934, 11216, 14731,
-      10334, 10635, 13677, 16384, 13988, 13988, 15725, 18432, 10334 ],
-
-    [  3779,  5541,  9812, 13677,  5467,  6122,  9656, 13138,
-       9581,  9437, 11940, 14731, 13138, 12683, 14336, 16384,  8982 ],
-
-    [  3181,  5154, 10150, 14336,  5448,  6311, 10334, 13988,
-      10334, 10431, 13138, 15725, 14336, 13988, 15725, 18432, 10431 ],
-
-    [  4841,  5560,  9105, 12479,  5756,  5944,  8922, 12109,
-       9300,  8982, 11090, 13677, 12479, 12109, 13677, 15186,  7460 ],
-
-    [  5859,  6009,  8922, 11940,  6144,  5987,  8483, 11348,
-       9042,  8535, 10334, 12683, 11940, 11216, 12683, 14336,  6215 ],
-
-    [  4250,  4916,  8587, 12109,  5901,  6191,  9233, 12288,
-      10150,  9892, 11940, 14336, 13677, 13138, 14731, 16384,  8383 ],
-
-    [  7153,  6702,  8863, 11216,  6904,  6410,  8239, 10431,
-       9233,  8433,  9812, 11629, 11629, 10742, 11781, 13138,  4753 ],
-
-    [  6674,  7057,  9508, 11629,  7120,  6964,  8806, 10635,
-       9437,  8750, 10061, 11629, 11485, 10531, 11485, 12683,  4062 ],
-
-    [  5341,  5289,  8013, 10970,  6311,  6262,  8640, 11090,
-      10061,  9508, 11090, 13138, 12902, 12288, 13396, 15186,  6539 ],
-
-    [  8057,  7533,  9300, 11216,  7685,  7057,  8535, 10334,
-       9508,  8694,  9812, 11216, 11485, 10431, 11348, 12479,  3541 ],
-
-    [  9168,  8239,  9656, 11216,  8483,  7608,  8806, 10240,
-       9892,  8982,  9812, 11090, 11485, 10431, 11090, 12109,  2815 ],
-
-    [   558,  7928, 18432, 20480,  7724, 12288, 20480, 20480,
-      18432, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480 ],
-
-    [  9892,  8806,  9976, 11348,  9042,  8057,  9042, 10240,
-      10240,  9233,  9976, 11090, 11629, 10531, 11216, 12109,  2371 ],
-
-    [ 11090,  9812, 10531, 11629,  9976,  8863,  9508, 10531,
-      10854,  9733, 10334, 11090, 11940, 10742, 11216, 11940,  1821 ],
-
-    [  7354,  6964,  9042, 11216,  7153,  6592,  8334, 10431,
-       9233,  8483,  9812, 11485, 11485, 10531, 11629, 12902,  4349 ],
-
-    [ 11348, 10150, 10742, 11629, 10150,  9042,  9656, 10431,
-      10854,  9812, 10431, 11216, 12109, 10970, 11485, 12109,  1700 ],
-
-    [ 20480, 20480,  8694, 10150, 20480, 20480,  8982, 10240,
-       8982,  9105,  9976, 10970, 10431, 10431, 11090, 11940,  1610 ],
-
-    [  9233,  8192,  9368, 10970,  8286,  7496,  8587,  9976,
-       9812,  8863,  9733, 10854, 11348, 10334, 11090, 11940,  3040 ],
-
-    [  4202,  5716,  9733, 13138,  5598,  6099,  9437, 12683,
-       9300,  9168, 11485, 13988, 12479, 12109, 13988, 15725,  7804 ],
-
-    [  4400,  5965,  9508, 12479,  6009,  6360,  9105, 11781,
-       9300,  8982, 10970, 13138, 12109, 11629, 13138, 14731,  6994 ]
-])
-
-AC_SPEC_CUMFREQ = np.array([
-
-    [    0,    1,    2,  177,  225,  226,  227,  336,
-       372,  543,  652,  699,  719,  768,  804,  824,  834 ],
-
-    [    0,   18,   44,   61,   71,   98,  135,  159,
-       175,  197,  229,  251,  265,  282,  308,  328,  341 ],
-
-    [    0,   71,  163,  212,  237,  318,  420,  481,
-       514,  556,  613,  652,  675,  697,  727,  749,  764 ],
-
-    [    0,  160,  290,  336,  354,  475,  598,  653,
-       677,  722,  777,  808,  823,  842,  866,  881,  890 ],
-
-    [    0,   71,  144,  177,  195,  266,  342,  385,
-       411,  445,  489,  519,  539,  559,  586,  607,  622 ],
-
-    [    0,   48,  108,  140,  159,  217,  285,  327,
-       354,  385,  427,  457,  478,  497,  524,  545,  561 ],
-
-    [    0,  138,  247,  290,  308,  419,  531,  584,
-       609,  655,  710,  742,  759,  780,  807,  825,  836 ],
-
-    [    0,   16,   40,   62,   79,  103,  139,  170,
-       195,  215,  245,  270,  290,  305,  327,  346,  362 ],
-
-    [    0,  579,  729,  741,  743,  897,  970,  980,
-       982,  996, 1007, 1010, 1011, 1014, 1017, 1018, 1019 ],
-
-    [    0,  398,  582,  607,  612,  788,  902,  925,
-       931,  956,  979,  987,  990,  996, 1002, 1005, 1007 ],
-
-    [    0,   13,   34,   52,   63,   83,  112,  134,
-       149,  163,  183,  199,  211,  221,  235,  247,  257 ],
-
-    [    0,  281,  464,  501,  510,  681,  820,  857,
-       867,  902,  938,  953,  959,  968,  978,  984,  987 ],
-
-    [    0,  198,  362,  408,  421,  575,  722,  773,
-       789,  832,  881,  905,  915,  928,  944,  954,  959 ],
-
-    [    0,    1,    2,   95,  139,  140,  141,  213,
-       251,  337,  407,  450,  475,  515,  551,  576,  592 ],
-
-    [    0,  133,  274,  338,  366,  483,  605,  664,
-       691,  730,  778,  807,  822,  837,  857,  870,  878 ],
-
-    [    0,  128,  253,  302,  320,  443,  577,  636,
-       659,  708,  767,  799,  814,  833,  857,  872,  881 ],
-
-    [    0,    1,    2,   25,   42,   43,   44,   67,
-        85,  105,  126,  144,  159,  174,  191,  205,  217 ],
-
-    [    0,   70,  166,  229,  267,  356,  468,  533,
-       569,  606,  653,  685,  705,  722,  745,  762,  774 ],
-
-    [    0,   55,  130,  175,  200,  268,  358,  416,
-       449,  488,  542,  581,  606,  628,  659,  683,  699 ],
-
-    [    0,    1,    3,    5,    7,    9,   11,   13,
-        15,   17,   19,   21,   23,   25,   27,   29,   31 ],
-
-    [    0,   34,   85,  123,  147,  196,  265,  317,
-       352,  386,  433,  470,  497,  518,  549,  574,  593 ],
-
-    [    0,   30,   73,  105,  127,  170,  229,  274,
-       305,  335,  377,  411,  436,  455,  483,  506,  524 ],
-
-    [    0,    9,   24,   38,   51,   65,   87,  108,
-       126,  139,  159,  177,  193,  204,  221,  236,  250 ],
-
-    [    0,   30,   74,  105,  125,  166,  224,  266,
-       294,  322,  361,  391,  413,  431,  457,  478,  494 ],
-
-    [    0,   15,   38,   58,   73,   95,  128,  156,
-       178,  196,  222,  245,  263,  276,  296,  314,  329 ],
-
-    [    0,   11,   28,   44,   57,   74,  100,  123,
-       142,  157,  179,  199,  216,  228,  246,  262,  276 ],
-
-    [    0,  448,  619,  639,  643,  821,  926,  944,
-       948,  971,  991,  998, 1000, 1005, 1010, 1012, 1013 ],
-
-    [    0,  332,  520,  549,  555,  741,  874,  903,
-       910,  940,  970,  981,  985,  991,  998, 1002, 1004 ],
-
-    [    0,    8,   21,   34,   45,   58,   78,   96,
-       112,  124,  141,  157,  170,  180,  194,  207,  219 ],
-
-    [    0,  239,  415,  457,  468,  631,  776,  820,
-       833,  872,  914,  933,  940,  951,  964,  971,  975 ],
-
-    [    0,  165,  310,  359,  375,  513,  652,  707,
-       727,  774,  828,  856,  868,  884,  904,  916,  923 ],
-
-    [    0,    3,    8,   13,   18,   23,   30,   37,
-        44,   48,   55,   62,   68,   72,   78,   84,   90 ],
-
-    [    0,  115,  237,  289,  311,  422,  547,  608,
-       635,  680,  737,  771,  788,  807,  832,  849,  859 ],
-
-    [    0,  107,  221,  272,  293,  399,  521,  582,
-       610,  656,  714,  749,  767,  787,  813,  831,  842 ],
-
-    [    0,    6,   16,   26,   35,   45,   60,   75,
-        89,   98,  112,  125,  137,  145,  157,  168,  178 ],
-
-    [    0,   72,  160,  210,  236,  320,  422,  482,
-       514,  555,  608,  644,  665,  685,  712,  732,  745 ],
-
-    [    0,   45,  108,  153,  183,  244,  327,  385,
-       421,  455,  502,  536,  559,  578,  605,  626,  641 ],
-
-    [    0,    1,    2,    9,   16,   17,   18,   26,
-        34,   40,   48,   55,   62,   68,   75,   82,   88 ],
-
-    [    0,   29,   73,  108,  132,  174,  236,  284,
-       318,  348,  391,  426,  452,  471,  500,  524,  543 ],
-
-    [    0,   20,   51,   76,   93,  123,  166,  200,
-       225,  247,  279,  305,  326,  342,  365,  385,  401 ],
-
-    [    0,  742,  845,  850,  851,  959,  997, 1001,
-      1002, 1009, 1014, 1016, 1017, 1019, 1020, 1021, 1022 ],
-
-    [    0,   42,   94,  121,  137,  186,  244,  280,
-       303,  330,  366,  392,  410,  427,  451,  470,  484 ],
-
-    [    0,   13,   33,   51,   66,   85,  114,  140,
-       161,  178,  203,  225,  243,  256,  275,  292,  307 ],
-
-    [    0,  501,  670,  689,  693,  848,  936,  952,
-       956,  975,  991,  997,  999, 1004, 1008, 1010, 1011 ],
-
-    [    0,  445,  581,  603,  609,  767,  865,  888,
-       895,  926,  954,  964,  968,  977,  986,  991,  993 ],
-
-    [    0,  285,  442,  479,  489,  650,  779,  818,
-       830,  870,  912,  930,  937,  949,  963,  971,  975 ],
-
-    [    0,  349,  528,  561,  569,  731,  852,  883,
-       892,  923,  953,  965,  970,  978,  987,  992,  994 ],
-
-    [    0,  199,  355,  402,  417,  563,  700,  750,
-       767,  811,  860,  884,  894,  909,  926,  936,  942 ],
-
-    [    0,  141,  275,  325,  343,  471,  606,  664,
-       686,  734,  791,  822,  836,  854,  877,  891,  899 ],
-
-    [    0,  243,  437,  493,  510,  649,  775,  820,
-       836,  869,  905,  923,  931,  941,  953,  960,  964 ],
-
-    [    0,   91,  197,  248,  271,  370,  487,  550,
-       580,  625,  684,  721,  741,  761,  788,  807,  819 ],
-
-    [    0,  107,  201,  242,  262,  354,  451,  503,
-       531,  573,  626,  660,  680,  701,  730,  751,  765 ],
-
-    [    0,  168,  339,  407,  432,  553,  676,  731,
-       755,  789,  830,  854,  866,  879,  895,  906,  912 ],
-
-    [    0,   67,  147,  191,  214,  290,  384,  441,
-       472,  513,  567,  604,  627,  648,  678,  700,  715 ],
-
-    [    0,   46,  109,  148,  171,  229,  307,  359,
-       391,  427,  476,  513,  537,  558,  588,  612,  629 ],
-
-    [    0,  848,  918,  920,  921,  996, 1012, 1013,
-      1014, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023 ],
-
-    [    0,   36,   88,  123,  145,  193,  260,  308,
-       340,  372,  417,  452,  476,  496,  525,  548,  565 ],
-
-    [    0,   24,   61,   90,  110,  145,  196,  237,
-       266,  292,  330,  361,  385,  403,  430,  453,  471 ],
-
-    [    0,   85,  182,  230,  253,  344,  454,  515,
-       545,  590,  648,  685,  706,  727,  756,  776,  789 ],
-
-    [    0,   22,   55,   82,  102,  135,  183,  222,
-       252,  278,  315,  345,  368,  385,  410,  431,  448 ],
-
-    [    0,    1,    2,   56,   89,   90,   91,  140,
-       172,  221,  268,  303,  328,  358,  388,  412,  430 ],
-
-    [    0,   45,  109,  152,  177,  239,  320,  376,
-       411,  448,  499,  537,  563,  585,  616,  640,  658 ],
-
-    [    0,  247,  395,  433,  445,  599,  729,  771,
-       785,  829,  875,  896,  905,  920,  937,  946,  951 ],
-
-    [    0,  231,  367,  408,  423,  557,  676,  723,
-       742,  786,  835,  860,  872,  889,  909,  921,  928 ]
-
-])
-
-AC_SPEC_FREQ = np.array([
-
-    [    1,    1,  175,   48,    1,    1,  109,   36,
-       171,  109,   47,   20,   49,   36,   20,   10,  190 ],
-
-    [   18,   26,   17,   10,   27,   37,   24,   16,
-        22,   32,   22,   14,   17,   26,   20,   13,  683 ],
-
-    [   71,   92,   49,   25,   81,  102,   61,   33,
-        42,   57,   39,   23,   22,   30,   22,   15,  260 ],
-
-    [  160,  130,   46,   18,  121,  123,   55,   24,
-        45,   55,   31,   15,   19,   24,   15,    9,  134 ],
-
-    [   71,   73,   33,   18,   71,   76,   43,   26,
-        34,   44,   30,   20,   20,   27,   21,   15,  402 ],
-
-    [   48,   60,   32,   19,   58,   68,   42,   27,
-        31,   42,   30,   21,   19,   27,   21,   16,  463 ],
-
-    [  138,  109,   43,   18,  111,  112,   53,   25,
-        46,   55,   32,   17,   21,   27,   18,   11,  188 ],
-
-    [   16,   24,   22,   17,   24,   36,   31,   25,
-        20,   30,   25,   20,   15,   22,   19,   16,  662 ],
-
-    [  579,  150,   12,    2,  154,   73,   10,    2,
-        14,   11,    3,    1,    3,    3,    1,    1,    5 ],
-
-    [  398,  184,   25,    5,  176,  114,   23,    6,
-        25,   23,    8,    3,    6,    6,    3,    2,   17 ],
-
-    [   13,   21,   18,   11,   20,   29,   22,   15,
-        14,   20,   16,   12,   10,   14,   12,   10,  767 ],
-
-    [  281,  183,   37,    9,  171,  139,   37,   10,
-        35,   36,   15,    6,    9,   10,    6,    3,   37 ],
-
-    [  198,  164,   46,   13,  154,  147,   51,   16,
-        43,   49,   24,   10,   13,   16,   10,    5,   65 ],
-
-    [    1,    1,   93,   44,    1,    1,   72,   38,
-        86,   70,   43,   25,   40,   36,   25,   16,  432 ],
-
-    [  133,  141,   64,   28,  117,  122,   59,   27,
-        39,   48,   29,   15,   15,   20,   13,    8,  146 ],
-
-    [  128,  125,   49,   18,  123,  134,   59,   23,
-        49,   59,   32,   15,   19,   24,   15,    9,  143 ],
-
-    [    1,    1,   23,   17,    1,    1,   23,   18,
-        20,   21,   18,   15,   15,   17,   14,   12,  807 ],
-
-    [   70,   96,   63,   38,   89,  112,   65,   36,
-        37,   47,   32,   20,   17,   23,   17,   12,  250 ],
-
-    [   55,   75,   45,   25,   68,   90,   58,   33,
-        39,   54,   39,   25,   22,   31,   24,   16,  325 ],
-
-    [    1,    2,    2,    2,    2,    2,    2,    2,
-         2,    2,    2,    2,    2,    2,    2,    2,  993 ],
-
-    [   34,   51,   38,   24,   49,   69,   52,   35,
-        34,   47,   37,   27,   21,   31,   25,   19,  431 ],
-
-    [   30,   43,   32,   22,   43,   59,   45,   31,
-        30,   42,   34,   25,   19,   28,   23,   18,  500 ],
-
-    [    9,   15,   14,   13,   14,   22,   21,   18,
-        13,   20,   18,   16,   11,   17,   15,   14,  774 ],
-
-    [   30,   44,   31,   20,   41,   58,   42,   28,
-        28,   39,   30,   22,   18,   26,   21,   16,  530 ],
-
-    [   15,   23,   20,   15,   22,   33,   28,   22,
-        18,   26,   23,   18,   13,   20,   18,   15,  695 ],
-
-    [   11,   17,   16,   13,   17,   26,   23,   19,
-        15,   22,   20,   17,   12,   18,   16,   14,  748 ],
-
-    [  448,  171,   20,    4,  178,  105,   18,    4,
-        23,   20,    7,    2,    5,    5,    2,    1,   11 ],
-
-    [  332,  188,   29,    6,  186,  133,   29,    7,
-        30,   30,   11,    4,    6,    7,    4,    2,   20 ],
-
-    [    8,   13,   13,   11,   13,   20,   18,   16,
-        12,   17,   16,   13,   10,   14,   13,   12,  805 ],
-
-    [  239,  176,   42,   11,  163,  145,   44,   13,
-        39,   42,   19,    7,   11,   13,    7,    4,   49 ],
-
-    [  165,  145,   49,   16,  138,  139,   55,   20,
-        47,   54,   28,   12,   16,   20,   12,    7,  101 ],
-
-    [    3,    5,    5,    5,    5,    7,    7,    7,
-         4,    7,    7,    6,    4,    6,    6,    6,  934 ],
-
-    [  115,  122,   52,   22,  111,  125,   61,   27,
-        45,   57,   34,   17,   19,   25,   17,   10,  165 ],
-
-    [  107,  114,   51,   21,  106,  122,   61,   28,
-        46,   58,   35,   18,   20,   26,   18,   11,  182 ],
-
-    [    6,   10,   10,    9,   10,   15,   15,   14,
-         9,   14,   13,   12,    8,   12,   11,   10,  846 ],
-
-    [   72,   88,   50,   26,   84,  102,   60,   32,
-        41,   53,   36,   21,   20,   27,   20,   13,  279 ],
-
-    [   45,   63,   45,   30,   61,   83,   58,   36,
-        34,   47,   34,   23,   19,   27,   21,   15,  383 ],
-
-    [    1,    1,    7,    7,    1,    1,    8,    8,
-         6,    8,    7,    7,    6,    7,    7,    6,  936 ],
-
-    [   29,   44,   35,   24,   42,   62,   48,   34,
-        30,   43,   35,   26,   19,   29,   24,   19,  481 ],
-
-    [   20,   31,   25,   17,   30,   43,   34,   25,
-        22,   32,   26,   21,   16,   23,   20,   16,  623 ],
-
-    [  742,  103,    5,    1,  108,   38,    4,    1,
-         7,    5,    2,    1,    2,    1,    1,    1,    2 ],
-
-    [   42,   52,   27,   16,   49,   58,   36,   23,
-        27,   36,   26,   18,   17,   24,   19,   14,  540 ],
-
-    [   13,   20,   18,   15,   19,   29,   26,   21,
-        17,   25,   22,   18,   13,   19,   17,   15,  717 ],
-
-    [  501,  169,   19,    4,  155,   88,   16,    4,
-        19,   16,    6,    2,    5,    4,    2,    1,   13 ],
-
-    [  445,  136,   22,    6,  158,   98,   23,    7,
-        31,   28,   10,    4,    9,    9,    5,    2,   31 ],
-
-    [  285,  157,   37,   10,  161,  129,   39,   12,
-        40,   42,   18,    7,   12,   14,    8,    4,   49 ],
-
-    [  349,  179,   33,    8,  162,  121,   31,    9,
-        31,   30,   12,    5,    8,    9,    5,    2,   30 ],
-
-    [  199,  156,   47,   15,  146,  137,   50,   17,
-        44,   49,   24,   10,   15,   17,   10,    6,   82 ],
-
-    [  141,  134,   50,   18,  128,  135,   58,   22,
-        48,   57,   31,   14,   18,   23,   14,    8,  125 ],
-
-    [  243,  194,   56,   17,  139,  126,   45,   16,
-        33,   36,   18,    8,   10,   12,    7,    4,   60 ],
-
-    [   91,  106,   51,   23,   99,  117,   63,   30,
-        45,   59,   37,   20,   20,   27,   19,   12,  205 ],
-
-    [  107,   94,   41,   20,   92,   97,   52,   28,
-        42,   53,   34,   20,   21,   29,   21,   14,  259 ],
-
-    [  168,  171,   68,   25,  121,  123,   55,   24,
-        34,   41,   24,   12,   13,   16,   11,    6,  112 ],
-
-    [   67,   80,   44,   23,   76,   94,   57,   31,
-        41,   54,   37,   23,   21,   30,   22,   15,  309 ],
-
-    [   46,   63,   39,   23,   58,   78,   52,   32,
-        36,   49,   37,   24,   21,   30,   24,   17,  395 ],
-
-    [  848,   70,    2,    1,   75,   16,    1,    1,
-         2,    1,    1,    1,    1,    1,    1,    1,    1 ],
-
-    [   36,   52,   35,   22,   48,   67,   48,   32,
-        32,   45,   35,   24,   20,   29,   23,   17,  459 ],
-
-    [   24,   37,   29,   20,   35,   51,   41,   29,
-        26,   38,   31,   24,   18,   27,   23,   18,  553 ],
-
-    [   85,   97,   48,   23,   91,  110,   61,   30,
-        45,   58,   37,   21,   21,   29,   20,   13,  235 ],
-
-    [   22,   33,   27,   20,   33,   48,   39,   30,
-        26,   37,   30,   23,   17,   25,   21,   17,  576 ],
-
-    [    1,    1,   54,   33,    1,    1,   49,   32,
-        49,   47,   35,   25,   30,   30,   24,   18,  594 ],
-
-    [   45,   64,   43,   25,   62,   81,   56,   35,
-        37,   51,   38,   26,   22,   31,   24,   18,  366 ],
-
-    [  247,  148,   38,   12,  154,  130,   42,   14,
-        44,   46,   21,    9,   15,   17,    9,    5,   73 ],
-
-    [  231,  136,   41,   15,  134,  119,   47,   19,
-        44,   49,   25,   12,   17,   20,   12,    7,   96 ]
-
-])
diff --git a/system/embdrv/lc3/test/tns.py b/system/embdrv/lc3/test/tns.py
deleted file mode 100644
index 188093f..0000000
--- a/system/embdrv/lc3/test/tns.py
+++ /dev/null
@@ -1,440 +0,0 @@
-#
-# Copyright 2022 Google LLC
-#
-# 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 numpy as np
-
-import build.lc3 as lc3
-import tables as T, appendix_c as C
-
-### ------------------------------------------------------------------------ ###
-
-class Tns:
-
-    SUB_LIM_10M_NB   = [ [  12,  34,  57,  80 ] ]
-    SUB_LIM_10M_WB   = [ [  12,  61, 110, 160 ] ]
-    SUB_LIM_10M_SSWB = [ [  12,  88, 164, 240 ] ]
-    SUB_LIM_10M_SWB  = [ [  12,  61, 110, 160 ], [ 160, 213, 266, 320 ] ]
-    SUB_LIM_10M_FB   = [ [  12,  74, 137, 200 ], [ 200, 266, 333, 400 ] ]
-
-    SUB_LIM_10M = [ SUB_LIM_10M_NB, SUB_LIM_10M_WB,
-        SUB_LIM_10M_SSWB, SUB_LIM_10M_SWB, SUB_LIM_10M_FB ]
-
-    SUB_LIM_7M5_NB   = [ [   9,  26,  43,  60 ] ]
-    SUB_LIM_7M5_WB   = [ [   9,  46,  83, 120 ] ]
-    SUB_LIM_7M5_SSWB = [ [   9,  66, 123, 180 ] ]
-    SUB_LIM_7M5_SWB  = [ [   9,  46,  82, 120 ], [ 120, 159, 200, 240 ] ]
-    SUB_LIM_7M5_FB   = [ [   9,  56, 103, 150 ], [ 150, 200, 250, 300 ] ]
-
-    SUB_LIM_7M5 = [ SUB_LIM_7M5_NB, SUB_LIM_7M5_WB,
-        SUB_LIM_7M5_SSWB, SUB_LIM_7M5_SWB, SUB_LIM_7M5_FB ]
-
-    SUB_LIM = [ SUB_LIM_7M5, SUB_LIM_10M ]
-
-    FREQ_LIM_10M_NB   = [  12,  80 ]
-    FREQ_LIM_10M_WB   = [  12, 160 ]
-    FREQ_LIM_10M_SSWB = [  12, 240 ]
-    FREQ_LIM_10M_SWB  = [  12, 160, 320 ]
-    FREQ_LIM_10M_FB   = [  12, 200, 400 ]
-
-    FREQ_LIM_10M = [ FREQ_LIM_10M_NB, FREQ_LIM_10M_WB,
-        FREQ_LIM_10M_SSWB, FREQ_LIM_10M_SWB, FREQ_LIM_10M_FB ]
-
-    FREQ_LIM_7M5_NB   = [   9,  60 ]
-    FREQ_LIM_7M5_WB   = [   9, 120 ]
-    FREQ_LIM_7M5_SSWB = [   9, 180 ]
-    FREQ_LIM_7M5_SWB  = [   9, 120, 240 ]
-    FREQ_LIM_7M5_FB   = [   9, 150, 300 ]
-
-    FREQ_LIM_7M5 = [ FREQ_LIM_7M5_NB, FREQ_LIM_7M5_WB,
-        FREQ_LIM_7M5_SSWB, FREQ_LIM_7M5_SWB, FREQ_LIM_7M5_FB ]
-
-    FREQ_LIM = [ FREQ_LIM_7M5, FREQ_LIM_10M ]
-
-    def __init__(self, dt):
-
-        self.dt = dt
-
-        (self.nfilters, self.lpc_weighting, self.rc_order, self.rc) = \
-            (None, None, None, None)
-
-    def get_data(self):
-
-        return { 'nfilters' : self.nfilters,
-                 'lpc_weighting' : self.lpc_weighting,
-                 'rc_order' : self.rc_order, 'rc' : self.rc - 8 }
-
-    def get_nbits(self):
-
-        lpc_weighting = self.lpc_weighting
-        nbits = 0
-
-        for f in range(self.nfilters):
-            rc_order = self.rc_order[f]
-            rc = self.rc[f]
-
-            nbits_order = T.TNS_ORDER_BITS[int(lpc_weighting)][rc_order]
-            nbits_coef = sum([ T.TNS_COEF_BITS[k][rc[k]]
-                                  for k in range(rc_order) ])
-
-            nbits += ((2048 + nbits_order + nbits_coef) + 2047) >> 11
-
-        return nbits
-
-
-class TnsAnalysis(Tns):
-
-    def __init__(self, dt):
-
-        super().__init__(dt)
-
-    def compute_lpc_coeffs(self, bw, f, x):
-
-        ### Normalized autocorrelation function
-
-        S = Tns.SUB_LIM[self.dt][bw][f]
-
-        r = np.append([ 3 ], np.zeros(8))
-        e = [ sum(x[S[s]:S[s+1]] ** 2) for s in range(3) ]
-
-        for k in range(len(r) if sum(e) > 0 else 0):
-            c = [ np.dot(x[S[s]:S[s+1]-k], x[S[s]+k:S[s+1]])
-                      for s in range(3) ]
-
-            r[k] = np.sum( np.array(c) / np.array(e) )
-
-        r *= np.exp(-0.5 * (0.02 * np.pi * np.arange(9)) ** 2)
-
-        ### Levinson-Durbin recursion
-
-        err = r[0]
-        a = np.ones(len(r))
-
-        for k in range(1, len(a)):
-
-            rc = -sum(a[:k] * r[k:0:-1]) / err
-
-            a[1:k] += rc * a[k-1:0:-1]
-            a[k] = rc
-
-            err *= 1 - rc ** 2
-
-        return (r[0] / err, a)
-
-    def lpc_weighting(self, pred_gain, a):
-
-        gamma = 1 - (1 - 0.85) * (2 - pred_gain) / (2 - 1.5)
-        return a * np.power(gamma, np.arange(len(a)))
-
-    def coeffs_reflexion(self, a):
-
-        rc = np.zeros(8)
-        b  = a.copy()
-
-        for k in range(8, 0, -1):
-            rc[k-1] = b[k]
-            e = 1 - rc[k-1] ** 2
-            b[1:k] = (b[1:k] - rc[k-1] * b[k-1:0:-1]) / e
-
-        return rc
-
-    def quantization(self, rc, lpc_weighting):
-
-        delta = np.pi / 17
-        rc_i = np.rint(np.arcsin(rc) / delta).astype(int) + 8
-        rc_q = np.sin(delta * (rc_i - 8))
-
-        rc_order = len(rc_i) - np.argmin(rc_i[::-1] == 8)
-
-        return (rc_order, rc_q, rc_i)
-
-    def filtering(self, st, x, rc_order, rc):
-
-        y = np.empty(len(x))
-
-        for i in range(len(x)):
-
-            xi = x[i]
-            s1 = xi
-
-            for k in range(rc_order):
-                s0 = st[k]
-                st[k] = s1
-
-                s1  = rc[k] * xi + s0
-                xi += rc[k] * s0
-
-            y[i] = xi
-
-        return y
-
-    def run(self, x, bw, nn_flag, nbytes):
-
-        fstate = np.zeros(8)
-        y = x.copy()
-
-        self.nfilters = len(Tns.SUB_LIM[self.dt][bw])
-        self.lpc_weighting = nbytes * 8 < 48 * T.DT_MS[self.dt]
-        self.rc_order = np.zeros(2, dtype=np.int)
-        self.rc = np.zeros((2, 8), dtype=np.int)
-
-        for f in range(self.nfilters):
-
-            (pred_gain, a) = self.compute_lpc_coeffs(bw, f, x)
-
-            tns_off = pred_gain <= 1.5 or nn_flag
-            if tns_off:
-                continue
-
-            if self.lpc_weighting and pred_gain < 2:
-                a = self.lpc_weighting(pred_gain, a)
-
-            rc = self.coeffs_reflexion(a)
-
-            (rc_order, rc_q, rc_i) = \
-                self.quantization(rc, self.lpc_weighting)
-
-            self.rc_order[f] = rc_order
-            self.rc[f] = rc_i
-
-            if rc_order > 0:
-                i0 = Tns.FREQ_LIM[self.dt][bw][f]
-                i1 = Tns.FREQ_LIM[self.dt][bw][f+1]
-
-                y[i0:i1] = self.filtering(
-                    fstate, x[i0:i1], rc_order, rc_q)
-
-        return y
-
-    def store(self, b):
-
-        for f in range(self.nfilters):
-            lpc_weighting = self.lpc_weighting[f]
-            rc_order = self.rc_order[f]
-            rc = self.rc[f]
-
-            b.write_bit(min(rc_order, 1))
-
-            if rc_order > 0:
-                b.ac_encode(
-                    T.TNS_ORDER_CUMFREQ[int(lpc_weighting)][rc_order-1],
-                    T.TNS_ORDER_FREQ[int(lpc_weighting)][rc_order-1]    )
-
-            for k in range(rc_order):
-                b.ac_encode(T.TNS_COEF_CUMFREQ[k][rc[k]],
-                            T.TNS_COEF_FREQ[k][rc[k]]    )
-
-
-class TnsSynthesis(Tns):
-
-    def filtering(self, st, x, rc_order, rc):
-
-        y = x.copy()
-
-        for i in range(len(x)):
-
-            xi = x[i] - rc[rc_order-1] * st[rc_order-1]
-            for k in range(rc_order-2, -1, -1):
-                xi -= rc[k] * st[k]
-                st[k+1] = xi * rc[k] + st[k];
-            st[0] = xi;
-
-            y[i] = xi
-
-        return y
-
-    def load(self, b, bw, nbytes):
-
-        self.nfilters = len(Tns.SUB_LIM[self.dt][bw])
-        self.lpc_weighting = nbytes * 8 < 48 * T.DT_MS[self.dt]
-        self.rc_order = np.zeros(2, dtype=np.int)
-        self.rc = 8 * np.ones((2, 8), dtype=np.int)
-
-        for f in range(self.nfilters):
-
-            if not b.read_bit():
-                continue
-
-            rc_order = 1 + b.ac_decode(
-                T.TNS_ORDER_CUMFREQ[int(self.lpc_weighting)],
-                T.TNS_ORDER_FREQ[int(self.lpc_weighting)])
-
-            self.rc_order[f] = rc_order
-
-            for k in range(rc_order):
-                rc = b.ac_decode(T.TNS_COEF_CUMFREQ[k], T.TNS_COEF_FREQ[k])
-                self.rc[f][k] = rc
-
-    def run(self, x, bw):
-
-        fstate = np.zeros(8)
-        y = x.copy()
-
-        for f in range(self.nfilters):
-
-            rc_order = self.rc_order[f]
-            rc = np.sin((np.pi / 17) * (self.rc[f] - 8))
-
-            if rc_order > 0:
-                i0 = Tns.FREQ_LIM[self.dt][bw][f]
-                i1 = Tns.FREQ_LIM[self.dt][bw][f+1]
-
-                y[i0:i1] = self.filtering(
-                    fstate, x[i0:i1], rc_order, rc)
-
-        return y
-
-
-### ------------------------------------------------------------------------ ###
-
-def check_analysis(rng, dt, bw):
-
-    ok = True
-
-    analysis = TnsAnalysis(dt)
-    nbytes_lim = int((48 * T.DT_MS[dt]) // 8)
-
-    for i in range(10):
-        x = rng.random(T.NE[dt][bw]) * 1e2
-        x = pow(x, .5 + i/5)
-
-        for nn_flag in (True, False):
-            for nbytes in (nbytes_lim, nbytes_lim + 1):
-
-                y = analysis.run(x, bw, nn_flag, nbytes)
-                (y_c, data_c) = lc3.tns_analyze(dt, bw, nn_flag, nbytes, x)
-
-                ok = ok and data_c['nfilters'] == analysis.nfilters
-                ok = ok and data_c['lpc_weighting'] == analysis.lpc_weighting
-                for f in range(analysis.nfilters):
-                    rc_order = analysis.rc_order[f]
-                    rc_order_c = data_c['rc_order'][f]
-                    rc_c = 8 + data_c['rc'][f]
-                    ok = ok and rc_order_c == rc_order
-                    ok = ok and not np.any((rc_c - analysis.rc[f])[:rc_order])
-
-                ok = ok and lc3.tns_get_nbits(data_c) == analysis.get_nbits()
-                ok = ok and np.amax(np.abs(y_c - y)) < 1e-2
-
-    return ok
-
-def check_synthesis(rng, dt, bw):
-
-    ok = True
-    synthesis = TnsSynthesis(dt)
-
-    for i in range(100):
-
-        x = rng.random(T.NE[dt][bw]) * 1e2
-
-        synthesis.nfilters = 1 + int(bw >= T.SRATE_32K)
-        synthesis.rc_order = rng.integers(0, 9, 2)
-        synthesis.rc = rng.integers(0, 17, 16).reshape(2, 8)
-
-        y = synthesis.run(x, bw)
-        y_c = lc3.tns_synthesize(dt, bw, synthesis.get_data(), x)
-
-        ok = ok and np.amax(np.abs(y_c - y) < 1e-6)
-
-    return ok
-
-def check_analysis_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ok = True
-
-    fs = Tns.FREQ_LIM[dt][sr][0]
-    fe = Tns.FREQ_LIM[dt][sr][1]
-    st = np.zeros(8)
-
-    for i in range(len(C.X_S[dt])):
-
-        (_, a) = lc3.tns_compute_lpc_coeffs(dt, sr, C.X_S[dt][i])
-        ok = ok and np.amax(np.abs(a[0] - C.TNS_LEV_A[dt][i])) < 1e-5
-
-        rc = lc3.tns_lpc_reflection(a[0])
-        ok = ok and np.amax(np.abs(rc - C.TNS_LEV_RC[dt][i])) < 1e-5
-
-        (rc_order, rc_i) = lc3.tns_quantize_rc(C.TNS_LEV_RC[dt][i])
-        ok = ok and rc_order == C.RC_ORDER[dt][i][0]
-        ok = ok and np.any((rc_i + 8) - C.RC_I_1[dt][i] == 0)
-
-        rc_q = lc3.tns_unquantize_rc(rc_i, rc_order)
-        ok = ok and np.amax(np.abs(rc_q - C.RC_Q_1[dt][i])) < 1e-6
-
-        (x, side) = lc3.tns_analyze(dt, sr, False, C.NBYTES[dt], C.X_S[dt][i])
-        ok = ok and side['nfilters'] == 1
-        ok = ok and side['rc_order'][0] == C.RC_ORDER[dt][i][0]
-        ok = ok and not np.any((side['rc'][0] + 8) - C.RC_I_1[dt][i])
-        ok = ok and lc3.tns_get_nbits(side) == C.NBITS_TNS[dt][i]
-        ok = ok and np.amax(np.abs(x - C.X_F[dt][i])) < 1e-3
-
-    return ok
-
-def check_synthesis_appendix_c(dt):
-
-    sr = T.SRATE_16K
-    ok = True
-
-    for i in range(len(C.X_HAT_Q[dt])):
-
-        side = {
-            'nfilters' : 1,
-            'lpc_weighting' : C.NBYTES[dt] * 8 < 48 * T.DT_MS[dt],
-            'rc_order': C.RC_ORDER[dt][i],
-            'rc': [ C.RC_I_1[dt][i] - 8, C.RC_I_2[dt][i] - 8 ]
-        }
-
-        g_int = C.GG_IND_ADJ[dt][i] + C.GG_OFF[dt][i]
-        x = C.X_HAT_Q[dt][i] * (10 ** (g_int / 28))
-
-        x = lc3.tns_synthesize(dt, sr, side, x)
-        ok = ok and np.amax(np.abs(x - C.X_HAT_TNS[dt][i])) < 1e-3
-
-    if dt != T.DT_10M:
-        return ok
-
-    sr = T.SRATE_48K
-
-    side = {
-        'nfilters' : 2,
-        'lpc_weighting' : False,
-        'rc_order': C.RC_ORDER_48K_10M,
-        'rc': [ C.RC_I_1_48K_10M - 8, C.RC_I_2_48K_10M - 8 ]
-    }
-
-    x = C.X_HAT_F_48K_10M
-    x = lc3.tns_synthesize(dt, sr, side, x)
-    ok = ok and np.amax(np.abs(x - C.X_HAT_TNS_48K_10M)) < 1e-3
-
-    return ok
-
-def check():
-
-    rng = np.random.default_rng(1234)
-    ok = True
-
-    for dt in range(T.NUM_DT):
-        for sr in range(T.NUM_SRATE):
-            ok = ok and check_analysis(rng, dt, sr)
-            ok = ok and check_synthesis(rng, dt, sr)
-
-    for dt in range(T.NUM_DT):
-        ok = ok and check_analysis_appendix_c(dt)
-        ok = ok and check_synthesis_appendix_c(dt)
-
-    return ok
-
-### ------------------------------------------------------------------------ ###
diff --git a/system/embdrv/lc3/test/tns_py.c b/system/embdrv/lc3/test/tns_py.c
deleted file mode 100644
index cb96d02..0000000
--- a/system/embdrv/lc3/test/tns_py.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 "lc3.h"
-#include <Python.h>
-#include <numpy/ndarrayobject.h>
-
-#include <tns.c>
-#include "ctypes.h"
-
-static PyObject *compute_lpc_coeffs_py(PyObject *m, PyObject *args)
-{
-    PyObject *x_obj, *a_obj, *g_obj;
-    unsigned dt, bw;
-    float *x, *g, (*a)[9];
-
-    if (!PyArg_ParseTuple(args, "IIO", &dt, &bw, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("sr", (unsigned)bw < LC3_NUM_BANDWIDTH);
-
-    int ne = LC3_NE(dt, bw);
-
-    CTYPES_CHECK("x", to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
-
-    g_obj = new_1d_ptr(NPY_FLOAT, 2, &g);
-    a_obj = new_2d_ptr(NPY_FLOAT, 2, 9, &a);
-
-    compute_lpc_coeffs(dt, bw, x, g, a);
-
-    return Py_BuildValue("NN", g_obj, a_obj);
-}
-
-static PyObject *lpc_reflection_py(PyObject *m, PyObject *args)
-{
-    PyObject *a_obj, *rc_obj;
-    float *a, *rc;
-
-    if (!PyArg_ParseTuple(args, "O", &a_obj))
-        return NULL;
-
-    CTYPES_CHECK("a", to_1d_ptr(a_obj, NPY_FLOAT, 9, &a));
-    rc_obj = new_1d_ptr(NPY_FLOAT, 8, &rc);
-
-    lpc_reflection(a, rc);
-
-    return Py_BuildValue("N", rc_obj);
-}
-
-static PyObject *quantize_rc_py(PyObject *m, PyObject *args)
-{
-    PyObject *rc_obj, *rc_q_obj;
-    float *rc;
-    int rc_order, *rc_q;
-
-    if (!PyArg_ParseTuple(args, "O", &rc_obj))
-        return NULL;
-
-    CTYPES_CHECK("rc", to_1d_ptr(rc_obj, NPY_FLOAT, 8, &rc));
-
-    rc_q_obj = new_1d_ptr(NPY_INT, 8, &rc_q);
-
-    quantize_rc(rc, &rc_order, rc_q);
-
-    return Py_BuildValue("iN", rc_order, rc_q_obj);
-}
-
-static PyObject *unquantize_rc_py(PyObject *m, PyObject *args)
-{
-    PyObject *rc_q_obj, *rc_obj;
-    int rc_order, *rc_q;
-    float *rc;
-
-    if (!PyArg_ParseTuple(args, "OI", &rc_q_obj, &rc_order))
-        return NULL;
-
-    CTYPES_CHECK("rc_q", to_1d_ptr(rc_q_obj, NPY_INT, 8, &rc_q));
-    CTYPES_CHECK("rc_order", (unsigned)rc_order <= 8);
-
-    rc_obj = new_1d_ptr(NPY_FLOAT, 8, &rc);
-
-    unquantize_rc(rc_q, rc_order, rc);
-
-    return Py_BuildValue("N", rc_obj);
-}
-
-static PyObject *analyze_py(PyObject *m, PyObject *args)
-{
-    PyObject *x_obj;
-    struct lc3_tns_data data = { 0 };
-    unsigned dt, bw;
-    int nn_flag;
-    unsigned nbytes;
-    float *x;
-
-    if (!PyArg_ParseTuple(args, "IIpIO", &dt, &bw, &nn_flag, &nbytes, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("bw", (unsigned)bw < LC3_NUM_BANDWIDTH);
-
-    int ne = LC3_NE(dt, bw);
-
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
-
-    lc3_tns_analyze(dt, bw, nn_flag, nbytes, &data, x);
-
-    return Py_BuildValue("ON", x_obj, new_tns_data(&data));
-}
-
-static PyObject *synthesize_py(PyObject *m, PyObject *args)
-{
-    PyObject *data_obj, *x_obj;
-    unsigned dt, bw;
-    struct lc3_tns_data data;
-    float *x;
-
-    if (!PyArg_ParseTuple(args, "IIOO", &dt, &bw, &data_obj, &x_obj))
-        return NULL;
-
-    CTYPES_CHECK("dt", (unsigned)dt < LC3_NUM_DT);
-    CTYPES_CHECK("bw", (unsigned)bw < LC3_NUM_BANDWIDTH);
-    CTYPES_CHECK(NULL, data_obj = to_tns_data(data_obj, &data));
-
-    int ne = LC3_NE(dt, bw);
-
-    CTYPES_CHECK("x", x_obj = to_1d_ptr(x_obj, NPY_FLOAT, ne, &x));
-
-    lc3_tns_synthesize(dt, bw, &data, x);
-
-    return Py_BuildValue("O", x_obj);
-}
-
-static PyObject *get_nbits_py(PyObject *m, PyObject *args)
-{
-    PyObject *data_obj;
-    struct lc3_tns_data data = { 0 };
-
-    if (!PyArg_ParseTuple(args, "O", &data_obj))
-        return NULL;
-
-    CTYPES_CHECK("data", to_tns_data(data_obj, &data));
-
-    int nbits = lc3_tns_get_nbits(&data);
-
-    return Py_BuildValue("i", nbits);
-}
-
-static PyMethodDef methods[] = {
-    { "tns_compute_lpc_coeffs", compute_lpc_coeffs_py, METH_VARARGS },
-    { "tns_lpc_reflection"    , lpc_reflection_py    , METH_VARARGS },
-    { "tns_quantize_rc"       , quantize_rc_py       , METH_VARARGS },
-    { "tns_unquantize_rc"     , unquantize_rc_py     , METH_VARARGS },
-    { "tns_analyze"           , analyze_py           , METH_VARARGS },
-    { "tns_synthesize"        , synthesize_py        , METH_VARARGS },
-    { "tns_get_nbits"         , get_nbits_py         , METH_VARARGS },
-    { NULL },
-};
-
-PyMODINIT_FUNC lc3_tns_py_init(PyObject *m)
-{
-    import_array();
-
-    PyModule_AddFunctions(m, methods);
-
-    return m;
-}
diff --git a/system/embdrv/lc3/tools/dlc3.c b/system/embdrv/lc3/tools/dlc3.c
deleted file mode 100644
index b334898..0000000
--- a/system/embdrv/lc3/tools/dlc3.c
+++ /dev/null
@@ -1,257 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#define _POSIX_C_SOURCE 199309L
-
-#include <stdalign.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <time.h>
-#include <errno.h>
-
-#include <lc3.h>
-#include "lc3bin.h"
-#include "wave.h"
-
-#ifndef MIN
-#define MIN(a, b)  ( (a) < (b) ? (a) : (b) )
-#endif
-
-#ifndef MAX
-#define MAX(a, b)  ( (a) > (b) ? (a) : (b) )
-#endif
-
-
-/**
- * Error handling
- */
-
-static void error(int status, const char *format, ...)
-{
-    va_list args;
-
-    fflush(stdout);
-
-    va_start(args, format);
-    vfprintf(stderr, format, args);
-    va_end(args);
-
-    fprintf(stderr, status ? ": %s\n" : "\n", strerror(status));
-    exit(status);
-}
-
-
-/**
- * Parameters
- */
-
-struct parameters {
-    const char *fname_in;
-    const char *fname_out;
-    int bitdepth;
-    int srate_hz;
-};
-
-static struct parameters parse_args(int argc, char *argv[])
-{
-    static const char *usage =
-        "Usage: %s [in_file] [wav_file]\n"
-        "\n"
-        "wav_file\t"  "Input wave file, stdin if omitted\n"
-        "out_file\t"  "Output bitstream file, stdout if omitted\n"
-        "\n"
-        "Options:\n"
-        "\t-h\t"     "Display help\n"
-        "\t-b\t"     "Output bitdepth, 16 bits (default) or 24 bits\n"
-        "\t-r\t"     "Output samplerate, default is LC3 stream samplerate\n"
-        "\n";
-
-    struct parameters p = { .bitdepth = 16 };
-
-    for (int iarg = 1; iarg < argc; ) {
-        const char *arg = argv[iarg++];
-
-        if (arg[0] == '-') {
-            if (arg[2] != '\0')
-                error(EINVAL, "Option %s", arg);
-
-            char opt = arg[1];
-            const char *optarg;
-
-            switch (opt) {
-                case 'b': case 'r':
-                    if (iarg >= argc)
-                        error(EINVAL, "Argument %s", arg);
-                    optarg = argv[iarg++];
-            }
-
-            switch (opt) {
-                case 'h': fprintf(stderr, usage, argv[0]); exit(0);
-                case 'b': p.bitdepth = atoi(optarg); break;
-                case 'r': p.srate_hz = atoi(optarg); break;
-                default:
-                    error(EINVAL, "Option %s", arg);
-            }
-
-        } else {
-
-            if (!p.fname_in)
-                p.fname_in = arg;
-            else if (!p.fname_out)
-                p.fname_out = arg;
-            else
-                error(EINVAL, "Argument %s", arg);
-        }
-    }
-
-    return p;
-}
-
-/**
- * Return time in (us) from unspecified point in the past
- */
-static unsigned clock_us(void)
-{
-    struct timespec ts;
-
-    clock_gettime(CLOCK_REALTIME, &ts);
-
-    return (unsigned)(ts.tv_sec * 1000*1000) + (unsigned)(ts.tv_nsec / 1000);
-}
-
-/**
- * Entry point
- */
-int main(int argc, char *argv[])
-{
-    /* --- Read parameters --- */
-
-    struct parameters p = parse_args(argc, argv);
-    FILE *fp_in = stdin, *fp_out = stdout;
-
-    if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL)
-        error(errno, "%s", p.fname_in);
-
-    if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL)
-        error(errno, "%s", p.fname_out);
-
-    if (p.srate_hz && !LC3_CHECK_SR_HZ(p.srate_hz))
-        error(EINVAL, "Samplerate %d Hz", p.srate_hz);
-
-    if (p.bitdepth && p.bitdepth != 16 && p.bitdepth != 24)
-        error(EINVAL, "Bitdepth %d", p.bitdepth);
-
-    /* --- Check parameters --- */
-
-    int frame_us, srate_hz, nch, nsamples;
-
-    if (lc3bin_read_header(fp_in, &frame_us, &srate_hz, &nch, &nsamples) < 0)
-        error(EINVAL, "LC3 binary input file");
-
-    if (nch  < 1 || nch  > 2)
-        error(EINVAL, "Number of channels %d", nch);
-
-    if (!LC3_CHECK_DT_US(frame_us))
-        error(EINVAL, "Frame duration");
-
-    if (!LC3_CHECK_SR_HZ(srate_hz) || (p.srate_hz && p.srate_hz < srate_hz))
-         error(EINVAL, "Samplerate %d Hz", srate_hz);
-
-    int pcm_sbits = p.bitdepth;
-    int pcm_sbytes = 2 + 2*(pcm_sbits > 16);
-
-    int pcm_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz;
-    int pcm_samples = !p.srate_hz ? nsamples :
-        ((int64_t)nsamples * pcm_srate_hz) / srate_hz;
-
-    wave_write_header(fp_out,
-          pcm_sbits, pcm_sbytes, pcm_srate_hz, nch, pcm_samples);
-
-    /* --- Setup decoding --- */
-
-    int frame_samples = lc3_frame_samples(frame_us, pcm_srate_hz);
-    int encode_samples = pcm_samples +
-        lc3_delay_samples(frame_us, pcm_srate_hz);
-
-    lc3_decoder_t dec[nch];
-    uint8_t in[nch * LC3_MAX_FRAME_BYTES];
-    int8_t alignas(int32_t) pcm[nch * frame_samples * pcm_sbytes];
-    enum lc3_pcm_format pcm_fmt =
-        pcm_sbits == 24 ? LC3_PCM_FORMAT_S24 : LC3_PCM_FORMAT_S16;
-
-    for (int ich = 0; ich < nch; ich++)
-        dec[ich] = lc3_setup_decoder(frame_us, srate_hz, p.srate_hz,
-            malloc(lc3_decoder_size(frame_us, pcm_srate_hz)));
-
-    /* --- Decoding loop --- */
-
-    static const char *dash_line = "========================================";
-
-    int nsec = 0;
-    unsigned t0 = clock_us();
-
-    for (int i = 0; i * frame_samples < encode_samples; i++) {
-
-        int frame_bytes = lc3bin_read_data(fp_in, nch, in);
-
-        if (floorf(i * frame_us * 1e-6) > nsec) {
-
-            float progress = fminf((float)i * frame_samples / pcm_samples, 1);
-
-            fprintf(stderr, "%02d:%02d [%-40s]\r",
-                    nsec / 60, nsec % 60,
-                    dash_line + (int)floorf((1 - progress) * 40));
-
-            nsec = rint(i * frame_us * 1e-6);
-        }
-
-        if (frame_bytes <= 0)
-            memset(pcm, 0, nch * frame_samples * pcm_sbytes);
-        else
-            for (int ich = 0; ich < nch; ich++)
-                lc3_decode(dec[ich],
-                    in + ich * frame_bytes, frame_bytes,
-                    pcm_fmt, pcm + ich * pcm_sbytes, nch);
-
-        int pcm_offset = i > 0 ? 0 : encode_samples - pcm_samples;
-        int pcm_nwrite = MIN(frame_samples - pcm_offset,
-            encode_samples - i*frame_samples);
-
-        wave_write_pcm(fp_out, pcm_sbytes, pcm, nch, pcm_offset, pcm_nwrite);
-    }
-
-    unsigned t = (clock_us() - t0) / 1000;
-    nsec = nsamples / srate_hz;
-
-    fprintf(stderr, "%02d:%02d Decoded in %d.%03d seconds %20s\n",
-        nsec / 60, nsec % 60, t / 1000, t % 1000, "");
-
-    /* --- Cleanup --- */
-
-    for (int ich = 0; ich < nch; ich++)
-        free(dec[ich]);
-
-    if (fp_in != stdin)
-        fclose(fp_in);
-
-    if (fp_out != stdout)
-        fclose(fp_out);
-}
diff --git a/system/embdrv/lc3/tools/elc3.c b/system/embdrv/lc3/tools/elc3.c
deleted file mode 100644
index b81c656..0000000
--- a/system/embdrv/lc3/tools/elc3.c
+++ /dev/null
@@ -1,258 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#define _POSIX_C_SOURCE 199309L
-
-#include <stdalign.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <time.h>
-#include <errno.h>
-
-#include <lc3.h>
-#include "lc3bin.h"
-#include "wave.h"
-
-
-/**
- * Error handling
- */
-
-static void error(int status, const char *format, ...)
-{
-    va_list args;
-
-    fflush(stdout);
-
-    va_start(args, format);
-    vfprintf(stderr, format, args);
-    va_end(args);
-
-    fprintf(stderr, status ? ": %s\n" : "\n", strerror(status));
-    exit(status);
-}
-
-
-/**
- * Parameters
- */
-
-struct parameters {
-    const char *fname_in;
-    const char *fname_out;
-    float frame_ms;
-    int srate_hz;
-    int bitrate;
-};
-
-static struct parameters parse_args(int argc, char *argv[])
-{
-    static const char *usage =
-        "Usage: %s [options] [wav_file] [out_file]\n"
-        "\n"
-        "wav_file\t"  "Input wave file, stdin if omitted\n"
-        "out_file\t"  "Output bitstream file, stdout if omitted\n"
-        "\n"
-        "Options:\n"
-        "\t-h\t"     "Display help\n"
-        "\t-b\t"     "Bitrate in bps (mandatory)\n"
-        "\t-m\t"     "Frame duration in ms (default 10)\n"
-        "\t-r\t"     "Encoder samplerate (default is input samplerate)\n"
-        "\n";
-
-    struct parameters p = { .frame_ms = 10 };
-
-    for (int iarg = 1; iarg < argc; ) {
-        const char *arg = argv[iarg++];
-
-        if (arg[0] == '-') {
-            if (arg[2] != '\0')
-                error(EINVAL, "Option %s", arg);
-
-            char opt = arg[1];
-            const char *optarg;
-
-            switch (opt) {
-                case 'b': case 'm': case 'r':
-                    if (iarg >= argc)
-                        error(EINVAL, "Argument %s", arg);
-                    optarg = argv[iarg++];
-            }
-
-            switch (opt) {
-                case 'h': fprintf(stderr, usage, argv[0]); exit(0);
-                case 'b': p.bitrate = atoi(optarg); break;
-                case 'm': p.frame_ms = atof(optarg); break;
-                case 'r': p.srate_hz = atoi(optarg); break;
-                default:
-                    error(EINVAL, "Option %s", arg);
-            }
-
-        } else {
-
-            if (!p.fname_in)
-                p.fname_in = arg;
-            else if (!p.fname_out)
-                p.fname_out = arg;
-            else
-                error(EINVAL, "Argument %s", arg);
-        }
-    }
-
-    return p;
-}
-
-
-/**
- * Return time in (us) from unspecified point in the past
- */
-
-static unsigned clock_us(void)
-{
-    struct timespec ts;
-
-    clock_gettime(CLOCK_MONOTONIC, &ts);
-
-    return (unsigned)(ts.tv_sec * 1000*1000) + (unsigned)(ts.tv_nsec / 1000);
-}
-
-
-/**
- * Entry point
- */
-
-int main(int argc, char *argv[])
-{
-    /* --- Read parameters --- */
-
-    struct parameters p = parse_args(argc, argv);
-    FILE *fp_in = stdin, *fp_out = stdout;
-
-    if (p.fname_in && (fp_in = fopen(p.fname_in, "rb")) == NULL)
-        error(errno, "%s", p.fname_in);
-
-    if (p.fname_out && (fp_out = fopen(p.fname_out, "wb")) == NULL)
-        error(errno, "%s", p.fname_out);
-
-    if (p.srate_hz && !LC3_CHECK_SR_HZ(p.srate_hz))
-        error(EINVAL, "Samplerate %d Hz", p.srate_hz);
-
-    /* --- Check parameters --- */
-
-    int frame_us = p.frame_ms * 1000;
-    int srate_hz, nch, nsamples;
-    int pcm_sbits, pcm_sbytes;
-
-    if (wave_read_header(fp_in,
-            &pcm_sbits, &pcm_sbytes, &srate_hz, &nch, &nsamples) < 0)
-        error(EINVAL, "Bad or unsupported WAVE input file");
-
-    if (p.bitrate <= 0)
-        error(EINVAL, "Bitrate");
-
-    if (!LC3_CHECK_DT_US(frame_us))
-        error(EINVAL, "Frame duration");
-
-    if (!LC3_CHECK_SR_HZ(srate_hz) || (p.srate_hz && p.srate_hz > srate_hz))
-        error(EINVAL, "Samplerate %d Hz", srate_hz);
-
-    if (pcm_sbits != 16 && pcm_sbits != 24)
-        error(EINVAL, "Bitdepth %d", pcm_sbits);
-
-    if (pcm_sbytes != (pcm_sbits == 16 ? 2 : 4))
-        error(EINVAL, "Sample storage on %d bytes", pcm_sbytes);
-
-    if (nch  < 1 || nch  > 2)
-        error(EINVAL, "Number of channels %d", nch);
-
-    int enc_srate_hz = !p.srate_hz ? srate_hz : p.srate_hz;
-    int enc_samples = !p.srate_hz ? nsamples :
-        ((int64_t)nsamples * enc_srate_hz) / srate_hz;
-
-    lc3bin_write_header(fp_out,
-        frame_us, enc_srate_hz, p.bitrate, nch, enc_samples);
-
-    /* --- Setup encoding --- */
-
-    int frame_bytes = lc3_frame_bytes(frame_us, p.bitrate / nch);
-    int frame_samples = lc3_frame_samples(frame_us, srate_hz);
-    int encode_samples = nsamples + lc3_delay_samples(frame_us, srate_hz);
-
-    lc3_encoder_t enc[nch];
-    int8_t alignas(int32_t) pcm[nch * frame_samples * pcm_sbytes];
-    enum lc3_pcm_format pcm_fmt =
-        pcm_sbits == 24 ? LC3_PCM_FORMAT_S24 : LC3_PCM_FORMAT_S16;
-    uint8_t out[nch][frame_bytes];
-
-    for (int ich = 0; ich < nch; ich++)
-        enc[ich] = lc3_setup_encoder(frame_us, enc_srate_hz, srate_hz,
-            malloc(lc3_encoder_size(frame_us, srate_hz)));
-
-    /* --- Encoding loop --- */
-
-    static const char *dash_line = "========================================";
-
-    int nsec = 0;
-    unsigned t0 = clock_us();
-
-    for (int i = 0; i * frame_samples < encode_samples; i++) {
-
-        int nread = wave_read_pcm(fp_in, pcm_sbytes, nch, frame_samples, pcm);
-
-        memset(pcm + nread * nch, 0,
-            nch * (frame_samples - nread) * pcm_sbytes);
-
-        if (floorf(i * frame_us * 1e-6) > nsec) {
-            float progress = fminf(
-                (float)i * frame_samples / encode_samples, 1);
-
-            fprintf(stderr, "%02d:%02d [%-40s]\r",
-                    nsec / 60, nsec % 60,
-                    dash_line + (int)floorf((1 - progress) * 40));
-
-            nsec = (int)(i * frame_us * 1e-6);
-        }
-
-        for (int ich = 0; ich < nch; ich++)
-            lc3_encode(enc[ich],
-                pcm_fmt, pcm + ich * pcm_sbytes, nch,
-                frame_bytes, out[ich]);
-
-        lc3bin_write_data(fp_out, out, nch, frame_bytes);
-    }
-
-    unsigned t = (clock_us() - t0) / 1000;
-    nsec = encode_samples / srate_hz;
-
-    fprintf(stderr, "%02d:%02d Encoded in %d.%d seconds %20s\n",
-        nsec / 60, nsec % 60, t / 1000, t % 1000, "");
-
-    /* --- Cleanup --- */
-
-    for (int ich = 0; ich < nch; ich++)
-        free(enc[ich]);
-
-    if (fp_in != stdin)
-        fclose(fp_in);
-
-    if (fp_out != stdout)
-        fclose(fp_out);
-}
diff --git a/system/embdrv/lc3/tools/lc3bin.c b/system/embdrv/lc3/tools/lc3bin.c
deleted file mode 100644
index 4187808..0000000
--- a/system/embdrv/lc3/tools/lc3bin.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <stdint.h>
-#include "lc3bin.h"
-
-
-/**
- * LC3 binary header
- */
-
-#define LC3_FILE_ID (0x1C | (0xCC << 8))
-
-struct lc3bin_header {
-    uint16_t file_id;
-    uint16_t header_size;
-    uint16_t srate_100hz;
-    uint16_t bitrate_100bps;
-    uint16_t channels;
-    uint16_t frame_10us;
-    uint16_t rfu;
-    uint16_t nsamples_low;
-    uint16_t nsamples_high;
-};
-
-
-/**
- * Read LC3 binary header
- */
-int lc3bin_read_header(FILE *fp,
-    int *frame_us, int *srate_hz, int *nchannels, int *nsamples)
-{
-    struct lc3bin_header hdr;
-
-    if (fread(&hdr, sizeof(hdr), 1, fp) != 1
-            || hdr.file_id != LC3_FILE_ID)
-        return -1;
-
-    *nchannels = hdr.channels;
-    *frame_us = hdr.frame_10us * 10;
-    *srate_hz = hdr.srate_100hz * 100;
-    *nsamples = hdr.nsamples_low | (hdr.nsamples_high << 16);
-
-    fseek(fp, SEEK_SET, hdr.header_size);
-
-    return 0;
-}
-
-/**
- * Read LC3 block of data
- */
-int lc3bin_read_data(FILE *fp, int nchannels, void *buffer)
-{
-    uint16_t nbytes;
-
-    if (fread(&nbytes, sizeof(nbytes), 1, fp) < 1
-            || nbytes > nchannels * LC3_MAX_FRAME_BYTES
-            || nbytes % nchannels
-            || fread(buffer, nbytes, 1, fp) < 1)
-        return -1;
-
-    return nbytes / nchannels;
-}
-
-/**
- * Write LC3 binary header
- */
-void lc3bin_write_header(FILE *fp,
-    int frame_us, int srate_hz, int bitrate, int nchannels, int nsamples)
-{
-    struct lc3bin_header hdr = {
-        .file_id = LC3_FILE_ID,
-        .header_size = sizeof(struct lc3bin_header),
-        .srate_100hz = srate_hz / 100,
-        .bitrate_100bps = bitrate / 100,
-        .channels = nchannels,
-        .frame_10us = frame_us / 10,
-        .nsamples_low = nsamples & 0xffff,
-        .nsamples_high = nsamples >> 16,
-    };
-
-    fwrite(&hdr, sizeof(hdr), 1, fp);
-}
-
-/**
- * Write LC3 block of data
- */
-void lc3bin_write_data(FILE *fp,
-    const void *data, int nchannels, int frame_bytes)
-{
-    uint16_t nbytes = nchannels * frame_bytes;
-    fwrite(&nbytes, sizeof(nbytes), 1, fp);
-
-    fwrite(data, 1, nbytes, fp);
-}
diff --git a/system/embdrv/lc3/tools/lc3bin.h b/system/embdrv/lc3/tools/lc3bin.h
deleted file mode 100644
index b2d70e6..0000000
--- a/system/embdrv/lc3/tools/lc3bin.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#ifndef __LC3BIN_H
-#define __LC3BIN_H
-
-#include <stdio.h>
-#include <stdint.h>
-#include <lc3.h>
-
-
-/**
- * Read LC3 binary header
- * fp              Opened file, moved after header on return
- * frame_us        Return frame duration, in us
- * srate_hz        Return samplerate, in Hz
- * nchannels       Return number of channels
- * nsamples        Return count of source samples by channels
- * return          0: Ok  -1: Bad LC3 File
- */
-int lc3bin_read_header(FILE *fp,
-    int *frame_us, int *srate_hz, int *nchannels, int *nsamples);
-
-/**
- * Read LC3 block of data
- * fp              Opened file
- * nchannels       Number of channels
- * buffer          Output buffer of `nchannels * LC3_MAX_FRAME_BYTES`
- * return          Size of each 'nchannels` frames, -1 on error
- */
-int lc3bin_read_data(FILE *fp, int nchannels, void *buffer);
-
-/**
- * Write LC3 binary header
- * fp              Opened file, moved after header on return
- * frame_us        Frame duration, in us
- * srate_hz        Samplerate, in Hz
- * bitrate         Bitrate indication of the stream, in bps
- * nchannels       Number of channels
- * nsamples        Count of source samples by channels
- */
-void lc3bin_write_header(FILE *fp,
-    int frame_us, int srate_hz, int bitrate, int nchannels, int nsamples);
-
-/**
- * Write LC3 block of data
- * fp              Opened file
- * data            The frames data
- * nchannels       Number of channels
- * frame_bytes     Size of each `nchannels` frames
- */
-void lc3bin_write_data(FILE *fp,
-    const void *data, int nchannels, int frame_bytes);
-
-
-#endif /* __LC3BIN_H */
diff --git a/system/embdrv/lc3/tools/wave.c b/system/embdrv/lc3/tools/wave.c
deleted file mode 100644
index ca68e25..0000000
--- a/system/embdrv/lc3/tools/wave.c
+++ /dev/null
@@ -1,179 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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 <stdint.h>
-#include "wave.h"
-
-
-/**
- * Id formatting
- */
-
-#define __WAVE_ID(s) \
-    (uint32_t)( s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24) )
-
-
-/**
- * File format statement
- * | type_id     WAVE_FILE_TYPE_ID
- * | size        File size - 8 bytes
- * | type_id     WAVE_FILE_FMT_ID
- */
-
-#define WAVE_FILE_TYPE_ID  __WAVE_ID("RIFF")
-#define WAVE_FILE_FMT_ID   __WAVE_ID("WAVE")
-
-struct wave_file {
-    uint32_t type_id;
-    uint32_t size;
-    uint32_t fmt_id;
-};
-
-
-/**
- * Audio format statement
- * | id          WAVE_FORMAT_ID
- * | size        Size of the block - 8 bytes (= 16 bytes)
- * | format      WAVE_FORMAT_PCM
- * | channels    Number of channels
- * | samplerate  Sampling rate
- * | byterate    Bytes per secondes = `samplerate * framesize`
- * | framesize   Bytes per sampling time = `channels * bitdepth / 8`
- * | bitdepth    Number of bits per sample
- */
-
-#define WAVE_FORMAT_ID   __WAVE_ID("fmt ")
-#define WAVE_FORMAT_PCM  1
-
-struct wave_format {
-    uint32_t id;
-    uint32_t size;
-    uint16_t fmt;
-    uint16_t channels;
-    uint32_t samplerate;
-    uint32_t byterate;
-    uint16_t framesize;
-    uint16_t bitdepth;
-};
-
-
-/**
- * Audio data statement
- * | id          WAV_DATA_ID
- * | size        Size of the data following
- */
-
-#define WAVE_DATA_ID  __WAVE_ID("data")
-
-struct wave_data {
-    uint32_t id;
-    uint32_t size;
-};
-
-
-/**
- * Read WAVE file header
- */
-int wave_read_header(FILE *fp, int *bitdepth, int *samplesize,
-    int *samplerate, int *nchannels, int *nframes)
-{
-    struct wave_file file;
-    struct wave_format format;
-    struct wave_data data;
-
-    if (fread(&file, sizeof(file), 1, fp) != 1
-            || file.type_id != WAVE_FILE_TYPE_ID
-            || file.fmt_id  != WAVE_FILE_FMT_ID)
-        return -1;
-
-    if (fread(&format, sizeof(format), 1, fp) != 1
-            || format.id       != WAVE_FORMAT_ID
-            || format.fmt      != WAVE_FORMAT_PCM
-            || format.byterate != format.samplerate * format.framesize)
-        return -1;
-
-    fseek(fp, sizeof(format) - (8 + format.size), SEEK_CUR);
-
-    if (fread(&data, sizeof(data), 1, fp) != 1
-            || data.id != WAVE_DATA_ID)
-        return -1;
-
-    *bitdepth = format.bitdepth;
-    *samplesize = format.framesize / format.channels;
-    *samplerate = format.samplerate;
-    *nchannels = format.channels;
-    *nframes = data.size / format.framesize;
-
-    return 0;
-}
-
-/**
- * Read PCM samples from wave file
- */
-int wave_read_pcm(FILE *fp, int samplesize,
-    int nch, int count, void *buffer)
-{
-    return fread(buffer, nch * samplesize, count, fp);
-}
-
-/**
- * Write WAVE file header
- */
-void wave_write_header(FILE *fp, int bitdepth, int samplesize,
-    int samplerate, int nchannels, int nframes)
-{
-    struct {
-        struct wave_file file;
-        struct wave_format format;
-        struct wave_data data;
-    } header;
-
-    long data_size = nchannels * nframes * samplesize;
-    long file_size = sizeof(header) + data_size;
-
-    header.file = (struct wave_file){
-        WAVE_FILE_TYPE_ID, file_size - 8,
-        .fmt_id = WAVE_FILE_FMT_ID
-    };
-
-    header.format = (struct wave_format){
-        WAVE_FORMAT_ID, sizeof(header.format) - 8,
-        .fmt = WAVE_FORMAT_PCM,
-        .channels = nchannels,
-        .samplerate = samplerate,
-        .byterate = samplerate * nchannels * samplesize,
-        .framesize = nchannels * samplesize,
-        .bitdepth = bitdepth,
-    };
-
-    header.data = (struct wave_data){
-        WAVE_DATA_ID, data_size
-    };
-
-    fwrite(&header, sizeof(header), 1, fp);
-}
-
-/**
- * Write PCM samples to wave file
- */
-void wave_write_pcm(FILE *fp, int samplesize,
-    const void *_pcm, int nch, int off, int count)
-{
-    const int8_t *pcm = _pcm;
-    fwrite(pcm + nch * off * samplesize, nch * samplesize, count, fp);
-}
diff --git a/system/embdrv/lc3/tools/wave.h b/system/embdrv/lc3/tools/wave.h
deleted file mode 100644
index 87e8186..0000000
--- a/system/embdrv/lc3/tools/wave.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2022 Google LLC
- *
- *  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.
- *
- ******************************************************************************/
-
-#ifndef __WAVE_H
-#define __WAVE_H
-
-#include <stdio.h>
-#include <stdint.h>
-
-
-/**
- * Read WAVE file header
- * fp              Opened file, moved after header on return
- * bitdepth        Return bitdepth
- * samplesize      Return size of samples, in bytes
- * samplerate      Return samplerate, in Hz
- * nchannels       Return number of channels
- * nframes         Return count of frames
- * return          0: Ok  -1: Bad or unsupported WAVE File
- */
-int wave_read_header(FILE *fp, int *bitdepth, int *samplesize,
-    int *samplerate, int *nchannels, int *nframes);
-
-/**
- * Read PCM samples from wave file
- * fp              Opened file
- * samplesize      Size of samples, in bytes
- * nch, count      Number of channels and count of frames to read
- * buffer          Output buffer of `nchannels * count` interleaved samples
- * return          Number of frames read
- */
-int wave_read_pcm(FILE *fp, int samplesize,
-    int nch, int count, void *_buffer);
-
-/**
- * Write WAVE file header
- * fp              Opened file, moved after header on return
- * bitdepth        Bitdepth
- * samplesize      Size of samples
- * samplerate      Samplerate, in Hz
- * nchannels       Number of channels
- * nframes         Count of frames
- */
-void wave_write_header(FILE *fp, int bitdepth, int samplesize,
-    int samplerate, int nchannels, int nframes);
-
-/**
- * Write PCM samples to wave file
- * fp              Opened file
- * samplesize      Size of samples, in bytes
- * pcm, nch        PCM frames, as 'nch' interleaved samples
- * off, count      Offset and count of frames
- */
-void wave_write_pcm(FILE *fp, int samplesize,
-    const void *pcm, int nch, int off, int count);
-
-
-#endif /* __WAVE_H */
diff --git a/system/gd/Android.bp b/system/gd/Android.bp
index 95af513..4cf595c 100644
--- a/system/gd/Android.bp
+++ b/system/gd/Android.bp
@@ -99,20 +99,17 @@
     target: {
         linux: {
             srcs: [
-                ":BluetoothBtaaSources_linux_generic",
                 ":BluetoothOsSources_linux_generic",
             ],
         },
         host: {
             srcs: [
-                ":BluetoothBtaaSources_host",
                 ":BluetoothHalSources_hci_host",
                 ":BluetoothOsSources_host",
             ],
         },
         android: {
             srcs: [
-                ":BluetoothBtaaSources_android",
                 ":BluetoothHalSources_hci_android_hidl",
                 ":BluetoothOsSources_android",
             ],
@@ -156,7 +153,6 @@
     generated_headers: [
         "BluetoothGeneratedBundlerSchema_h_bfbs",
         "BluetoothGeneratedDumpsysDataSchema_h",
-        "BluetoothGeneratedPackets_h",
     ],
     shared_libs: [
         "libcrypto",
@@ -166,6 +162,9 @@
         "libflatbuffers-cpp",
     ],
     whole_static_libs: [
+        "libbluetooth_hci_pdl",
+        "libbluetooth_l2cap_pdl",
+        "libbluetooth_smp_pdl",
         "libc++fs",
     ],
     static_libs: [
@@ -221,7 +220,9 @@
     cflags: [
         "-DUSE_FAKE_TIMERS",
     ],
-    static_libs: ["libchrome"],
+    static_libs: [
+        "libchrome",
+    ],
 }
 
 cc_binary {
@@ -229,6 +230,10 @@
     defaults: [
         "gd_defaults",
     ],
+    include_dirs: [
+        "packages/modules/Bluetooth/system/include",
+        "packages/modules/Bluetooth/system/types",
+    ],
     host_supported: true,
     srcs: [
         ":BluetoothFacade_hci_hal",
@@ -310,6 +315,10 @@
         "gd_defaults",
         "mts_defaults",
     ],
+    include_dirs: [
+        "packages/modules/Bluetooth/system/include",
+        "packages/modules/Bluetooth/system/types",
+    ],
     host_supported: true,
     // TODO(b/231993739): Reenable isolated:true by deleting the explicit disable below
     isolated: false,
@@ -351,7 +360,6 @@
         },
     },
     srcs: [
-        ":BluetoothBtaaSources_linux_generic_tests",
         ":BluetoothCommonTestSources",
         ":BluetoothCryptoToolboxTestSources",
         ":BluetoothDumpsysTestSources",
@@ -555,32 +563,6 @@
     ],
 }
 
-genrule {
-    name: "BluetoothGeneratedPackets_h",
-    tools: [
-        "bluetooth_packetgen",
-    ],
-    cmd: "$(location bluetooth_packetgen) --fuzzing --testing --include=packages/modules/Bluetooth/system/gd --out=$(genDir) $(in)",
-    srcs: [
-        "hci/hci_packets.pdl",
-        "l2cap/l2cap_packets.pdl",
-        "security/smp_packets.pdl",
-    ],
-    out: [
-        "hci/hci_packets.h",
-        "l2cap/l2cap_packets.h",
-        "security/smp_packets.h",
-    ],
-    visibility: ["//visibility:public"],
-}
-
-genrule {
-    name: "BluetoothGeneratedPackets_rust",
-    defaults: ["pdl_rust_generator_defaults"],
-    srcs: ["hci/hci_packets.pdl"],
-    out: ["hci/hci_packets.rs"],
-}
-
 rust_library {
     name: "libbt_packets",
     defaults: ["gd_rust_defaults"],
@@ -636,7 +618,6 @@
     ],
     cmd: "$(location flatc) -I packages/modules/Bluetooth/system/gd -b --schema -o $(genDir) $(in) ",
     srcs: [
-        "btaa/activity_attribution.fbs",
         "common/init_flags.fbs",
         "dumpsys_data.fbs",
         "hci/hci_acl_manager.fbs",
@@ -646,7 +627,6 @@
         "shim/dumpsys.fbs",
     ],
     out: [
-        "activity_attribution.bfbs",
         "dumpsys.bfbs",
         "dumpsys_data.bfbs",
         "hci_acl_manager.bfbs",
@@ -664,7 +644,6 @@
     ],
     cmd: "$(location flatc) -I packages/modules/Bluetooth/system/gd -o $(genDir) --cpp $(in) ",
     srcs: [
-        "btaa/activity_attribution.fbs",
         "common/init_flags.fbs",
         "dumpsys_data.fbs",
         "hci/hci_acl_manager.fbs",
@@ -674,7 +653,6 @@
         "shim/dumpsys.fbs",
     ],
     out: [
-        "activity_attribution_generated.h",
         "dumpsys_data_generated.h",
         "dumpsys_generated.h",
         "hci_acl_manager_generated.h",
@@ -685,42 +663,6 @@
     ],
 }
 
-genrule {
-    name: "BluetoothGeneratedPackets_python3_cc",
-    tools: [
-        "bluetooth_packetgen",
-    ],
-    cmd: "$(location bluetooth_packetgen) --include=packages/modules/Bluetooth/system/gd --out=$(genDir) --num_shards=10 $(in)",
-    srcs: [
-        "l2cap/l2cap_packets.pdl",
-        "security/smp_packets.pdl",
-    ],
-    out: [
-        "l2cap/l2cap_packets_python3.cc",
-        "l2cap/l2cap_packets_python3_shard_0.cc",
-        "l2cap/l2cap_packets_python3_shard_1.cc",
-        "l2cap/l2cap_packets_python3_shard_2.cc",
-        "l2cap/l2cap_packets_python3_shard_3.cc",
-        "l2cap/l2cap_packets_python3_shard_4.cc",
-        "l2cap/l2cap_packets_python3_shard_5.cc",
-        "l2cap/l2cap_packets_python3_shard_6.cc",
-        "l2cap/l2cap_packets_python3_shard_7.cc",
-        "l2cap/l2cap_packets_python3_shard_8.cc",
-        "l2cap/l2cap_packets_python3_shard_9.cc",
-        "security/smp_packets_python3.cc",
-        "security/smp_packets_python3_shard_0.cc",
-        "security/smp_packets_python3_shard_1.cc",
-        "security/smp_packets_python3_shard_2.cc",
-        "security/smp_packets_python3_shard_3.cc",
-        "security/smp_packets_python3_shard_4.cc",
-        "security/smp_packets_python3_shard_5.cc",
-        "security/smp_packets_python3_shard_6.cc",
-        "security/smp_packets_python3_shard_7.cc",
-        "security/smp_packets_python3_shard_8.cc",
-        "security/smp_packets_python3_shard_9.cc",
-    ],
-}
-
 cc_defaults {
     name: "bluetooth_py3_native_extension_defaults",
     include_dirs: [
@@ -823,19 +765,3 @@
     ],
     rtti: true,
 }
-
-// Generate the python parser+serializer backend for
-// hci_packets.pdl.
-genrule {
-    name: "gd_hci_packets_python3_gen",
-    defaults: ["pdl_python_generator_defaults"],
-    cmd: "$(location :pdlc) $(in) |" +
-        " $(location :pdl_python_generator)" +
-        " --output $(out) --custom-type-location blueberry.utils.bluetooth",
-    srcs: [
-        ":BluetoothHciPackets",
-    ],
-    out: [
-        "hci_packets.py",
-    ],
-}
diff --git a/system/gd/BUILD.gn b/system/gd/BUILD.gn
index c570819..3697efa 100644
--- a/system/gd/BUILD.gn
+++ b/system/gd/BUILD.gn
@@ -46,9 +46,9 @@
   deps = [
     "//bt/system:libbt-platform-protos-lite",
     "//bt/system/gd:BluetoothGeneratedDumpsysDataSchema_h",
-    "//bt/system/gd:BluetoothGeneratedPackets_h",
     "//bt/system/gd/dumpsys:libbluetooth-dumpsys",
     "//bt/system/gd/rust/shim:init_flags_bridge_header",
+    "//bt/system/pdl:BluetoothGeneratedPackets_h",
   ]
 }
 
@@ -64,12 +64,10 @@
     "//bt/system/gd/rust/common:libbt_keystore_cc",
     "//bt/system/gd/rust/topshim:libbluetooth_topshim",
     "//bt/system/gd/rust/shim:libbluetooth_rust_interop",
-    "//bt/system/gd:BluetoothGeneratedPackets_h",
     "//bt/system/gd/att:BluetoothAttSources",
     "//bt/system/gd/common:BluetoothCommonSources",
     "//bt/system/gd/crypto_toolbox:BluetoothCryptoToolboxSources",
     "//bt/system/gd/dumpsys:BluetoothDumpsysSources",
-    "//bt/system/gd/btaa:BluetoothBtaaSources_linux",
     "//bt/system/gd/hal:BluetoothHalSources",
     "//bt/system/gd/hal:BluetoothHalSources_hci_host",
     "//bt/system/gd/l2cap:BluetoothL2capSources",
@@ -79,12 +77,12 @@
     "//bt/system/gd/shim:BluetoothShimSources",
     "//bt/system/gd/storage:BluetoothStorageSources",
     "//bt/system/gd/sysprops:BluetoothSyspropsSources",
+    "//bt/system/pdl:BluetoothGeneratedPackets_h",
   ]
 }
 
 flatbuffer("BluetoothGeneratedDumpsysDataSchema_h") {
   sources = [
-    "btaa/activity_attribution.fbs",
     "common/init_flags.fbs",
     "dumpsys_data.fbs",
     "hci/hci_acl_manager.fbs",
@@ -97,7 +95,6 @@
 
 bt_flatc_binary_schema("BluetoothGeneratedDumpsysBinarySchema_bfbs") {
   sources = [
-    "btaa/activity_attribution.fbs",
     "common/init_flags.fbs",
     "dumpsys_data.fbs",
     "hci/hci_acl_manager.fbs",
@@ -109,26 +106,3 @@
 
   include_dir = "system/gd"
 }
-
-packetgen_py("BluetoothGeneratedPackets_python3_cc") {
-  sources = [
-    "hci/hci_packets.pdl",
-    "l2cap/l2cap_packets.pdl",
-    "security/smp_packets.pdl",
-  ]
-
-  include = "system/gd"
-  source_root = "../.."
-  shards = 10
-}
-
-packetgen_headers("BluetoothGeneratedPackets_h") {
-  sources = [
-    "hci/hci_packets.pdl",
-    "l2cap/l2cap_packets.pdl",
-    "security/smp_packets.pdl",
-  ]
-
-  include = "system/gd"
-  source_root = "../.."
-}
diff --git a/system/gd/btaa/Android.bp b/system/gd/btaa/Android.bp
deleted file mode 100644
index 6b4c269..0000000
--- a/system/gd/btaa/Android.bp
+++ /dev/null
@@ -1,39 +0,0 @@
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "system_bt_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["system_bt_license"],
-}
-
-filegroup {
-    name: "BluetoothBtaaSources_android",
-    srcs: [
-        "android/activity_attribution.cc",
-    ],
-}
-
-filegroup {
-    name: "BluetoothBtaaSources_host",
-    srcs: [
-        "host/activity_attribution.cc",
-    ],
-}
-
-filegroup {
-    name: "BluetoothBtaaSources_linux_generic",
-    srcs: [
-        "linux_generic/attribution_processor.cc",
-        "linux_generic/cmd_evt_classification.cc",
-        "linux_generic/hci_processor.cc",
-        "linux_generic/wakelock_processor.cc",
-    ],
-}
-
-filegroup {
-    name: "BluetoothBtaaSources_linux_generic_tests",
-    srcs: [
-        "linux_generic/attribution_processor_tests.cc",
-    ],
-}
diff --git a/system/gd/btaa/activity_attribution.fbs b/system/gd/btaa/activity_attribution.fbs
deleted file mode 100644
index b3ba851..0000000
--- a/system/gd/btaa/activity_attribution.fbs
+++ /dev/null
@@ -1,37 +0,0 @@
-namespace bluetooth.activity_attribution;
-
-attribute "privacy";
-
-table WakeupEntry {
-    wakeup_time:string;
-    activity:string;
-    address:string;
-    package_info:string;
-}
-
-table ActivityAggregationEntry {
-    package_info:string;
-    address:string;
-    activity:string;
-    wakeup_count:int;
-    byte_count:int;
-    wakelock_duration_ms:int;
-    creation_time:string;
-}
-
-table ActivityAttributionData {
-    title_device_wakeup:string;
-    num_device_wakeup:int;
-    device_wakeup_attribution:[WakeupEntry];
-    title_device_activity:string;
-    num_device_activity:int;
-    device_activity_aggregation:[ActivityAggregationEntry];
-    title_app_wakeup:string;
-    num_app_wakeup:int;
-    app_wakeup_attribution:[WakeupEntry];
-    title_app_activity:string;
-    num_app_activity:int;
-    app_activity_aggregation:[ActivityAggregationEntry];
-}
-
-root_type ActivityAttributionData;
\ No newline at end of file
diff --git a/system/gd/btaa/activity_attribution.h b/system/gd/btaa/activity_attribution.h
deleted file mode 100644
index 7ad4ad4..0000000
--- a/system/gd/btaa/activity_attribution.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2020 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 "hal/snoop_logger.h"
-#include "hci/address.h"
-#include "module.h"
-
-namespace bluetooth {
-namespace activity_attribution {
-
-enum class Activity : uint8_t { UNKNOWN = 0, ACL, ADVERTISE, CONNECT, CONTROL, HFP, ISO, SCAN, VENDOR };
-
-using CreationTime = std::chrono::time_point<std::chrono::system_clock>;
-
-struct BtaaAggregationEntry {
-  hci::Address address;
-  Activity activity;
-  uint16_t wakeup_count;
-  uint32_t byte_count;
-  uint32_t wakelock_duration_ms;
-  CreationTime creation_time;
-};
-
-class ActivityAttribution : public bluetooth::Module {
- public:
-  ActivityAttribution() = default;
-  ActivityAttribution(const ActivityAttribution&) = delete;
-  ActivityAttribution& operator=(const ActivityAttribution&) = delete;
-
-  ~ActivityAttribution() = default;
-
-  void Capture(const hal::HciPacket& packet, hal::SnoopLogger::PacketType type);
-  void OnWakelockAcquired();
-  void OnWakelockReleased();
-  void OnWakeup();
-  void NotifyActivityAttributionInfo(int uid, const std::string& package_name, const std::string& device_address);
-
-  static const ModuleFactory Factory;
-
- protected:
-  std::string ToString() const override;
-  void ListDependencies(ModuleList* list) const override;
-  void Start() override;
-  void Stop() override;
-  DumpsysDataFinisher GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const override;  // Module
-
- private:
-  struct impl;
-  std::unique_ptr<impl> pimpl_;
-};
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/android/activity_attribution.cc b/system/gd/btaa/android/activity_attribution.cc
deleted file mode 100644
index e7a15bd..0000000
--- a/system/gd/btaa/android/activity_attribution.cc
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#define LOG_TAG "btaa"
-
-#include "btaa/activity_attribution.h"
-#include "activity_attribution_generated.h"
-
-#include <aidl/android/system/suspend/BnSuspendCallback.h>
-#include <aidl/android/system/suspend/BnWakelockCallback.h>
-#include <aidl/android/system/suspend/ISuspendControlService.h>
-#include <android/binder_manager.h>
-
-#include "btaa/attribution_processor.h"
-#include "btaa/hci_processor.h"
-#include "btaa/wakelock_processor.h"
-#include "module.h"
-#include "os/log.h"
-
-using aidl::android::system::suspend::BnSuspendCallback;
-using aidl::android::system::suspend::BnWakelockCallback;
-using aidl::android::system::suspend::ISuspendCallback;
-using aidl::android::system::suspend::ISuspendControlService;
-using Status = ::ndk::ScopedAStatus;
-using namespace ndk;
-
-namespace bluetooth {
-namespace activity_attribution {
-
-const ModuleFactory ActivityAttribution::Factory = ModuleFactory([]() { return new ActivityAttribution(); });
-
-static const std::string kBtWakelockName("hal_bluetooth_lock");
-static const std::string kBtWakeupReason("hs_uart_wakeup");
-static const std::string kSuspendService("suspend_control");
-static const size_t kHciAclHeaderSize = 4;
-
-static std::mutex g_module_mutex;
-static ActivityAttribution* g_module = nullptr;
-static bool is_wakeup_callback_registered = false;
-static bool is_wakelock_callback_registered = false;
-
-struct wakelock_callback : public BnWakelockCallback {
-  wakelock_callback() {}
-
-  Status notifyAcquired() override {
-    std::lock_guard<std::mutex> guard(g_module_mutex);
-    if (g_module != nullptr) {
-      g_module->OnWakelockAcquired();
-    }
-    return Status::ok();
-  }
-  Status notifyReleased() override {
-    std::lock_guard<std::mutex> guard(g_module_mutex);
-    if (g_module != nullptr) {
-      g_module->OnWakelockReleased();
-    }
-    return Status::ok();
-  }
-};
-
-static std::shared_ptr<wakelock_callback> g_wakelock_callback = nullptr;
-
-struct wakeup_callback : public BnSuspendCallback {
-  wakeup_callback() {}
-
-  Status notifyWakeup(bool success, const std::vector<std::string>& wakeup_reasons) override {
-    for (auto& wakeup_reason : wakeup_reasons) {
-      if (wakeup_reason.find(kBtWakeupReason) != std::string::npos) {
-        std::lock_guard<std::mutex> guard(g_module_mutex);
-        if (g_module != nullptr) {
-          g_module->OnWakeup();
-        }
-        break;
-      }
-    }
-    return Status::ok();
-  }
-};
-
-static std::shared_ptr<wakeup_callback> g_wakeup_callback = nullptr;
-
-struct ActivityAttribution::impl {
-  impl(ActivityAttribution* module) {
-    std::lock_guard<std::mutex> guard(g_module_mutex);
-    g_module = module;
-    if (is_wakeup_callback_registered && is_wakelock_callback_registered) {
-      LOG_ERROR("Wakeup and wakelock callbacks are already registered");
-      return;
-    }
-
-    Status register_callback_status;
-    bool is_register_successful = false;
-    if (!AServiceManager_isDeclared(kSuspendService.c_str())) {
-      LOG_ERROR("No suspend control service available.");
-      return;
-    }
-    auto control_service = ISuspendControlService::fromBinder(
-        SpAIBinder(AServiceManager_waitForService(kSuspendService.c_str())));
-
-    if (!is_wakeup_callback_registered) {
-      g_wakeup_callback = SharedRefBase::make<wakeup_callback>();
-      register_callback_status = control_service->registerCallback(g_wakeup_callback, &is_register_successful);
-      if (!is_register_successful || !register_callback_status.isOk()) {
-        LOG_ERROR("Fail to register wakeup callback");
-        return;
-      }
-      is_wakeup_callback_registered = true;
-    }
-
-    if (!is_wakelock_callback_registered) {
-      g_wakelock_callback = SharedRefBase::make<wakelock_callback>();
-      register_callback_status =
-          control_service->registerWakelockCallback(g_wakelock_callback, kBtWakelockName, &is_register_successful);
-      if (!is_register_successful || !register_callback_status.isOk()) {
-        LOG_ERROR("Fail to register wakelock callback");
-        return;
-      }
-      is_wakelock_callback_registered = true;
-    }
-  }
-
-  ~impl() {
-    std::lock_guard<std::mutex> guard(g_module_mutex);
-    g_module = nullptr;
-  }
-
-  void on_hci_packet(hal::HciPacket packet, hal::SnoopLogger::PacketType type, uint16_t length) {
-    attribution_processor_.OnBtaaPackets(std::move(hci_processor_.OnHciPacket(std::move(packet), type, length)));
-  }
-
-  void on_wakelock_acquired() {
-    wakelock_processor_.OnWakelockAcquired();
-  }
-
-  void on_wakelock_released() {
-    uint32_t wakelock_duration_ms = 0;
-
-    wakelock_duration_ms = wakelock_processor_.OnWakelockReleased();
-    if (wakelock_duration_ms != 0) {
-      attribution_processor_.OnWakelockReleased(wakelock_duration_ms);
-    }
-  }
-
-  void on_wakeup() {
-    attribution_processor_.OnWakeup();
-  }
-
-  void notify_activity_attribution_info(int uid, const std::string& package_name, const std::string& device_address) {
-    attribution_processor_.NotifyActivityAttributionInfo(uid, package_name, device_address);
-  }
-
-  void Dump(
-      std::promise<flatbuffers::Offset<ActivityAttributionData>> promise, flatbuffers::FlatBufferBuilder* fb_builder) {
-    attribution_processor_.Dump(std::move(promise), fb_builder);
-  }
-
-  AttributionProcessor attribution_processor_;
-  HciProcessor hci_processor_;
-  WakelockProcessor wakelock_processor_;
-};
-
-void ActivityAttribution::Capture(const hal::HciPacket& packet, hal::SnoopLogger::PacketType type) {
-  uint16_t original_length = packet.size();
-  uint16_t truncate_length;
-
-  switch (type) {
-    case hal::SnoopLogger::PacketType::CMD:
-    case hal::SnoopLogger::PacketType::EVT:
-      truncate_length = packet.size();
-      break;
-    case hal::SnoopLogger::PacketType::ACL:
-    case hal::SnoopLogger::PacketType::SCO:
-    case hal::SnoopLogger::PacketType::ISO:
-      truncate_length = kHciAclHeaderSize;
-      break;
-  }
-
-  if (!truncate_length) {
-    return;
-  }
-
-  hal::HciPacket truncate_packet(packet.begin(), packet.begin() + truncate_length);
-  CallOn(pimpl_.get(), &impl::on_hci_packet, truncate_packet, type, original_length);
-}
-
-void ActivityAttribution::OnWakelockAcquired() {
-  CallOn(pimpl_.get(), &impl::on_wakelock_acquired);
-}
-
-void ActivityAttribution::OnWakelockReleased() {
-  CallOn(pimpl_.get(), &impl::on_wakelock_released);
-}
-
-void ActivityAttribution::OnWakeup() {
-  CallOn(pimpl_.get(), &impl::on_wakeup);
-}
-
-void ActivityAttribution::NotifyActivityAttributionInfo(
-    int uid, const std::string& package_name, const std::string& device_address) {
-  CallOn(pimpl_.get(), &impl::notify_activity_attribution_info, uid, package_name, device_address);
-}
-
-std::string ActivityAttribution::ToString() const {
-  return "Btaa Module";
-}
-
-void ActivityAttribution::ListDependencies(ModuleList* list) const {}
-
-void ActivityAttribution::Start() {
-  pimpl_ = std::make_unique<impl>(this);
-}
-
-void ActivityAttribution::Stop() {
-  pimpl_.reset();
-}
-
-DumpsysDataFinisher ActivityAttribution::GetDumpsysData(flatbuffers::FlatBufferBuilder* fb_builder) const {
-  ASSERT(fb_builder != nullptr);
-
-  std::promise<flatbuffers::Offset<ActivityAttributionData>> promise;
-  auto future = promise.get_future();
-  pimpl_->Dump(std::move(promise), fb_builder);
-
-  auto dumpsys_data = future.get();
-
-  return [dumpsys_data](DumpsysDataBuilder* dumpsys_builder) {
-    dumpsys_builder->add_activity_attribution_dumpsys_data(dumpsys_data);
-  };
-}
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/attribution_processor.h b/system/gd/btaa/attribution_processor.h
deleted file mode 100644
index e28e6cb..0000000
--- a/system/gd/btaa/attribution_processor.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright 2020 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 <cstdint>
-#include <unordered_map>
-
-#include "hci_processor.h"
-
-namespace bluetooth {
-namespace activity_attribution {
-
-static constexpr size_t kWakeupAggregatorSize = 200;
-
-struct AddressActivityKey {
-  hci::Address address;
-  Activity activity;
-
-  bool operator==(const AddressActivityKey& other) const {
-    return (address == other.address && activity == other.activity);
-  }
-};
-
-struct AddressActivityKeyHasher {
-  std::size_t operator()(const AddressActivityKey& key) const {
-    return (
-        (std::hash<std::string>()(key.address.ToString()) ^
-         (std::hash<unsigned char>()(static_cast<unsigned char>(key.activity)))));
-  }
-};
-
-struct AppActivityKey {
-  std::string app;
-  Activity activity;
-
-  bool operator==(const AppActivityKey& other) const {
-    return (app == other.app && activity == other.activity);
-  }
-};
-
-struct AppActivityKeyHasher {
-  std::size_t operator()(const AppActivityKey& key) const {
-    return (
-        (std::hash<std::string>()(key.app) ^ (std::hash<unsigned char>()(static_cast<unsigned char>(key.activity)))));
-  }
-};
-
-struct DeviceWakeupDescriptor {
-  Activity activity_;
-  const hci::Address address_;
-  DeviceWakeupDescriptor(Activity activity, const hci::Address address) : activity_(activity), address_(address) {}
-  virtual ~DeviceWakeupDescriptor() {}
-};
-
-struct AppWakeupDescriptor {
-  Activity activity_;
-  std::string package_info_;
-  AppWakeupDescriptor(Activity activity, std::string package_info) : activity_(activity), package_info_(package_info) {}
-  virtual ~AppWakeupDescriptor() {}
-};
-
-class AttributionProcessor {
- public:
-  void OnBtaaPackets(std::vector<BtaaHciPacket> btaa_packets);
-  void OnWakelockReleased(uint32_t duration_ms);
-  void OnWakeup();
-  void NotifyActivityAttributionInfo(int uid, const std::string& package_name, const std::string& device_address);
-  void Dump(
-      std::promise<flatbuffers::Offset<ActivityAttributionData>> promise, flatbuffers::FlatBufferBuilder* fb_builder);
-
-  using ClockType = std::chrono::time_point<std::chrono::system_clock>;
-  using NowFunc = ClockType (*)();
-
-  // by default, we use the std::chrono::system_clock::now implementation to
-  // get the current timestamp
-  AttributionProcessor() : now_func_(std::chrono::system_clock::now) {}
-  // in other cases, we may need to use different implementation
-  // e.g., for testing purposes
-  AttributionProcessor(NowFunc func) : now_func_(func) {}
-
- private:
-  // this function is added for testing support in
-  // OnWakelockReleased
-  NowFunc now_func_ = std::chrono::system_clock::now;
-  bool wakeup_ = false;
-  std::unordered_map<AddressActivityKey, BtaaAggregationEntry, AddressActivityKeyHasher> btaa_aggregator_;
-  std::unordered_map<AddressActivityKey, BtaaAggregationEntry, AddressActivityKeyHasher> wakelock_duration_aggregator_;
-  std::unordered_map<std::string, std::string> address_app_map_;
-  std::unordered_map<AppActivityKey, BtaaAggregationEntry, AppActivityKeyHasher> app_activity_aggregator_;
-  common::TimestampedCircularBuffer<DeviceWakeupDescriptor> device_wakeup_aggregator_ =
-      common::TimestampedCircularBuffer<DeviceWakeupDescriptor>(kWakeupAggregatorSize);
-  common::TimestampedCircularBuffer<AppWakeupDescriptor> app_wakeup_aggregator_ =
-      common::TimestampedCircularBuffer<AppWakeupDescriptor>(kWakeupAggregatorSize);
-  const char* ActivityToString(Activity activity);
-};
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/cmd_evt_classification.h b/system/gd/btaa/cmd_evt_classification.h
deleted file mode 100644
index 1aae6c2..0000000
--- a/system/gd/btaa/cmd_evt_classification.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2021 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 "btaa/activity_attribution.h"
-#include "hci/hci_packets.h"
-
-namespace bluetooth {
-namespace activity_attribution {
-
-struct CmdEvtActivityClassification {
-  Activity activity;
-  uint16_t connection_handle_pos;
-  uint16_t address_pos;
-};
-
-CmdEvtActivityClassification lookup_cmd(hci::OpCode opcode);
-CmdEvtActivityClassification lookup_event(hci::EventCode event_code);
-CmdEvtActivityClassification lookup_le_event(hci::SubeventCode subevent_code);
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/hci_processor.h b/system/gd/btaa/hci_processor.h
deleted file mode 100644
index 47fbc68..0000000
--- a/system/gd/btaa/hci_processor.h
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright 2021 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 "btaa/activity_attribution.h"
-#include "btaa/cmd_evt_classification.h"
-#include "hal/snoop_logger.h"
-#include "hci/address.h"
-
-namespace bluetooth {
-namespace activity_attribution {
-
-struct BtaaHciPacket {
-  Activity activity;
-  hci::Address address;
-  uint16_t byte_count;
-
-  BtaaHciPacket() {}
-  BtaaHciPacket(Activity activity, hci::Address address, uint16_t byte_count)
-      : activity(activity), address(address), byte_count(byte_count) {}
-};
-
-class DeviceParser {
- public:
-  void match_handle_with_address(uint16_t connection_handle, hci::Address& address);
-
- private:
-  std::map<uint16_t, hci::Address> connection_lookup_table_;
-};
-
-struct PendingCommand {
-  hci::OpCode opcode;
-  BtaaHciPacket btaa_hci_packet;
-};
-
-class HciProcessor {
- public:
-  std::vector<BtaaHciPacket> OnHciPacket(hal::HciPacket packet, hal::SnoopLogger::PacketType type, uint16_t length);
-
- private:
-  void process_le_event(std::vector<BtaaHciPacket>& btaa_hci_packets, int16_t byte_count, hci::EventView& event);
-  void process_special_event(
-      std::vector<BtaaHciPacket>& btaa_hci_packets,
-      hci::EventCode event_code,
-      uint16_t byte_count,
-      hci::EventView& event);
-  void process_command(
-      std::vector<BtaaHciPacket>& btaa_hci_packets,
-      packet::PacketView<packet::kLittleEndian>& packet_view,
-      uint16_t byte_count);
-  void process_event(
-      std::vector<BtaaHciPacket>& btaa_hci_packets,
-      packet::PacketView<packet::kLittleEndian>& packet_view,
-      uint16_t byte_count);
-  void process_acl(
-      std::vector<BtaaHciPacket>& btaa_hci_packets,
-      packet::PacketView<packet::kLittleEndian>& packet_view,
-      uint16_t byte_count);
-  void process_sco(
-      std::vector<BtaaHciPacket>& btaa_hci_packets,
-      packet::PacketView<packet::kLittleEndian>& packet_view,
-      uint16_t byte_count);
-  void process_iso(
-      std::vector<BtaaHciPacket>& btaa_hci_packets,
-      packet::PacketView<packet::kLittleEndian>& packet_view,
-      uint16_t byte_count);
-
-  DeviceParser device_parser_;
-  PendingCommand pending_command_;
-};
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/host/activity_attribution.cc b/system/gd/btaa/host/activity_attribution.cc
deleted file mode 100644
index 20c7ac7..0000000
--- a/system/gd/btaa/host/activity_attribution.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2020 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 "btaa/activity_attribution.h"
-
-#include "module.h"
-
-namespace bluetooth {
-namespace activity_attribution {
-
-struct ActivityAttribution::impl {};
-
-void ActivityAttribution::OnWakelockAcquired() {}
-
-void ActivityAttribution::OnWakelockReleased() {}
-
-void ActivityAttribution::OnWakeup() {}
-
-void ActivityAttribution::NotifyActivityAttributionInfo(
-    int uid, const std::string& package_name, const std::string& device_address) {}
-
-std::string ActivityAttribution::ToString() const {
-  return "Btaa Module";
-}
-
-void ActivityAttribution::ListDependencies(ModuleList* list) const {}
-
-void ActivityAttribution::Start() {}
-
-void ActivityAttribution::Stop() {}
-
-const ModuleFactory ActivityAttribution::Factory = ModuleFactory([]() { return new ActivityAttribution(); });
-
-DumpsysDataFinisher EmptyDumpsysDataFinisher = [](DumpsysDataBuilder* dumpsys_data_builder) {};
-DumpsysDataFinisher ActivityAttribution::GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const {
-  return EmptyDumpsysDataFinisher;
-}
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/linux/activity_attribution.cc b/system/gd/btaa/linux/activity_attribution.cc
deleted file mode 100644
index c391867..0000000
--- a/system/gd/btaa/linux/activity_attribution.cc
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2020 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 "btaa/activity_attribution.h"
-
-// TODO: Implement for Linux.
-namespace bluetooth {
-namespace activity_attribution {
-
-const ModuleFactory ActivityAttribution::Factory = ModuleFactory([]() { return new ActivityAttribution(); });
-
-struct ActivityAttribution::impl {
-  impl(ActivityAttribution* module) {}
-
-  void on_hci_packet(hal::HciPacket packet, hal::SnoopLogger::PacketType type, uint16_t length) {}
-};
-
-void ActivityAttribution::Capture(const hal::HciPacket& packet, hal::SnoopLogger::PacketType type) {}
-
-void ActivityAttribution::NotifyActivityAttributionInfo(
-    int uid, const std::string& package_name, const std::string& device_address) {}
-
-std::string ActivityAttribution::ToString() const {
-  return "Btaa Module";
-}
-
-void ActivityAttribution::ListDependencies(ModuleList* list) const {}
-
-void ActivityAttribution::Start() {}
-
-void ActivityAttribution::Stop() {}
-
-DumpsysDataFinisher EmptyDumpsysDataFinisher = [](DumpsysDataBuilder* dumpsys_data_builder) {};
-DumpsysDataFinisher ActivityAttribution::GetDumpsysData(flatbuffers::FlatBufferBuilder* builder) const {
-  return EmptyDumpsysDataFinisher;
-}
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/linux_generic/attribution_processor.cc b/system/gd/btaa/linux_generic/attribution_processor.cc
deleted file mode 100644
index eadad9d..0000000
--- a/system/gd/btaa/linux_generic/attribution_processor.cc
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * Copyright 2020 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 "btaa/attribution_processor.h"
-#include "common/strings.h"
-
-#include "os/log.h"
-
-namespace bluetooth {
-namespace activity_attribution {
-
-constexpr char kActivityAttributionTimeFormat[] = "%Y-%m-%d %H:%M:%S";
-static const std::string kUnknownPackageInfo = "UNKNOWN";
-// A device-activity aggregation entry expires after two days (172800 seconds)
-static const int kDurationToKeepDeviceActivityEntrySecs = 172800;
-// A transient device-activity aggregation entry is defined as an entry with very few Byte count
-// (200 Bytes, this is about the size of 5 advertising packets) over a period of time (15 minutes)
-static const int kByteCountTransientDeviceActivityEntry = 200;
-static const int kDurationTransientDeviceActivityEntrySecs = 900;
-static const int kMapSizeTrimDownAggregationEntry = 200;
-
-void AttributionProcessor::OnBtaaPackets(std::vector<BtaaHciPacket> btaa_packets) {
-  AddressActivityKey key;
-
-  for (auto& btaa_packet : btaa_packets) {
-    key.address = btaa_packet.address;
-    key.activity = btaa_packet.activity;
-
-    if (wakelock_duration_aggregator_.find(key) == wakelock_duration_aggregator_.end()) {
-      wakelock_duration_aggregator_[key] = {};
-    }
-    wakelock_duration_aggregator_[key].byte_count += btaa_packet.byte_count;
-
-    if (wakeup_) {
-      wakelock_duration_aggregator_[key].wakeup_count += 1;
-      device_wakeup_aggregator_.Push(std::move(DeviceWakeupDescriptor(btaa_packet.activity, btaa_packet.address)));
-      std::string package_info = kUnknownPackageInfo;
-      std::string address = btaa_packet.address.ToString();
-      if (address_app_map_.find(address) != address_app_map_.end()) {
-        package_info = address_app_map_[address];
-      }
-      app_wakeup_aggregator_.Push(std::move(AppWakeupDescriptor(btaa_packet.activity, package_info)));
-    }
-  }
-  wakeup_ = false;
-}
-
-void AttributionProcessor::OnWakelockReleased(uint32_t duration_ms) {
-  uint32_t total_byte_count = 0;
-
-  for (auto& it : wakelock_duration_aggregator_) {
-    total_byte_count += it.second.byte_count;
-  }
-
-  if (total_byte_count == 0) {
-    return;
-  }
-
-  auto cur_time = now_func_();
-  for (auto& it : wakelock_duration_aggregator_) {
-    it.second.wakelock_duration_ms = (uint64_t)duration_ms * it.second.byte_count / total_byte_count;
-    if (btaa_aggregator_.find(it.first) == btaa_aggregator_.end()) {
-      btaa_aggregator_[it.first] = {};
-      btaa_aggregator_[it.first].creation_time = cur_time;
-    }
-
-    auto elapsed_time_sec =
-        std::chrono::duration_cast<std::chrono::seconds>(cur_time - btaa_aggregator_[it.first].creation_time).count();
-    if (elapsed_time_sec > kDurationToKeepDeviceActivityEntrySecs) {
-      btaa_aggregator_[it.first].wakeup_count = 0;
-      btaa_aggregator_[it.first].byte_count = 0;
-      btaa_aggregator_[it.first].wakelock_duration_ms = 0;
-      btaa_aggregator_[it.first].creation_time = cur_time;
-    }
-
-    btaa_aggregator_[it.first].wakeup_count += it.second.wakeup_count;
-    btaa_aggregator_[it.first].byte_count += it.second.byte_count;
-    btaa_aggregator_[it.first].wakelock_duration_ms += it.second.wakelock_duration_ms;
-
-    std::string address = it.first.address.ToString();
-    std::string package_info = kUnknownPackageInfo;
-    if (address_app_map_.find(address) != address_app_map_.end()) {
-      package_info = address_app_map_[address];
-    }
-    AppActivityKey key;
-    key.app = package_info;
-    key.activity = it.first.activity;
-
-    if (app_activity_aggregator_.find(key) == app_activity_aggregator_.end()) {
-      app_activity_aggregator_[key] = {};
-      app_activity_aggregator_[key].creation_time = cur_time;
-    }
-
-    elapsed_time_sec =
-        std::chrono::duration_cast<std::chrono::seconds>(cur_time - app_activity_aggregator_[key].creation_time)
-            .count();
-    if (elapsed_time_sec > kDurationToKeepDeviceActivityEntrySecs) {
-      app_activity_aggregator_[key].wakeup_count = 0;
-      app_activity_aggregator_[key].byte_count = 0;
-      app_activity_aggregator_[key].wakelock_duration_ms = 0;
-      app_activity_aggregator_[key].creation_time = cur_time;
-    }
-
-    app_activity_aggregator_[key].wakeup_count += it.second.wakeup_count;
-    app_activity_aggregator_[key].byte_count += it.second.byte_count;
-    app_activity_aggregator_[key].wakelock_duration_ms += it.second.wakelock_duration_ms;
-  }
-  wakelock_duration_aggregator_.clear();
-
-  if (btaa_aggregator_.size() <= kMapSizeTrimDownAggregationEntry &&
-      app_activity_aggregator_.size() <= kMapSizeTrimDownAggregationEntry) {
-    return;
-  }
-  // Trim down the transient entries in the aggregator to avoid that it overgrows
-  if (btaa_aggregator_.size() > kMapSizeTrimDownAggregationEntry) {
-    auto it = btaa_aggregator_.begin();
-    while (it != btaa_aggregator_.end()) {
-      auto elapsed_time_sec =
-          std::chrono::duration_cast<std::chrono::seconds>(cur_time - it->second.creation_time).count();
-      if (elapsed_time_sec > kDurationTransientDeviceActivityEntrySecs &&
-          it->second.byte_count < kByteCountTransientDeviceActivityEntry) {
-        it = btaa_aggregator_.erase(it);
-      } else {
-        it++;
-      }
-    }
-  }
-
-  if (app_activity_aggregator_.size() > kMapSizeTrimDownAggregationEntry) {
-    auto it = app_activity_aggregator_.begin();
-    while (it != app_activity_aggregator_.end()) {
-      auto elapsed_time_sec =
-          std::chrono::duration_cast<std::chrono::seconds>(cur_time - it->second.creation_time).count();
-      if (elapsed_time_sec > kDurationTransientDeviceActivityEntrySecs &&
-          it->second.byte_count < kByteCountTransientDeviceActivityEntry) {
-        it = app_activity_aggregator_.erase(it);
-      } else {
-        it++;
-      }
-    }
-  }
-}
-
-void AttributionProcessor::OnWakeup() {
-  if (wakeup_) {
-    LOG_INFO("Previous wakeup notification is not consumed.");
-  }
-  wakeup_ = true;
-}
-
-void AttributionProcessor::NotifyActivityAttributionInfo(
-    int uid, const std::string& package_name, const std::string& device_address) {
-  if (address_app_map_.size() > kMapSizeTrimDownAggregationEntry) {
-    LOG_INFO("The map from device address and app info overflows.");
-    return;
-  }
-  address_app_map_[device_address] = package_name + "/" + std::to_string(uid);
-}
-
-void AttributionProcessor::Dump(
-    std::promise<flatbuffers::Offset<ActivityAttributionData>> promise, flatbuffers::FlatBufferBuilder* fb_builder) {
-  // Dump device-based wakeup attribution data
-  auto title_device_wakeup = fb_builder->CreateString("----- Device-based Wakeup Attribution Dumpsys -----");
-  std::vector<common::TimestampedEntry<DeviceWakeupDescriptor>> device_wakeup_aggregator =
-      device_wakeup_aggregator_.Pull();
-  std::vector<flatbuffers::Offset<WakeupEntry>> device_wakeup_entry_offsets;
-  for (auto& it : device_wakeup_aggregator) {
-    WakeupEntryBuilder wakeup_entry_builder(*fb_builder);
-    std::chrono::milliseconds duration(it.timestamp);
-    std::chrono::time_point<std::chrono::system_clock> wakeup_time(duration);
-    wakeup_entry_builder.add_wakeup_time(fb_builder->CreateString(
-        bluetooth::common::StringFormatTimeWithMilliseconds(kActivityAttributionTimeFormat, wakeup_time).c_str()));
-    wakeup_entry_builder.add_activity(fb_builder->CreateString((ActivityToString(it.entry.activity_))));
-    wakeup_entry_builder.add_address(fb_builder->CreateString(it.entry.address_.ToString()));
-    device_wakeup_entry_offsets.push_back(wakeup_entry_builder.Finish());
-  }
-  auto device_wakeup_entries = fb_builder->CreateVector(device_wakeup_entry_offsets);
-
-  // Dump device-based activity aggregation data
-  auto title_device_activity = fb_builder->CreateString("----- Device-based Activity Attribution Dumpsys -----");
-  std::vector<flatbuffers::Offset<ActivityAggregationEntry>> device_aggregation_entry_offsets;
-  for (auto& it : btaa_aggregator_) {
-    ActivityAggregationEntryBuilder device_entry_builder(*fb_builder);
-    device_entry_builder.add_address(fb_builder->CreateString(it.first.address.ToString()));
-    device_entry_builder.add_activity(fb_builder->CreateString((ActivityToString(it.first.activity))));
-    device_entry_builder.add_wakeup_count(it.second.wakeup_count);
-    device_entry_builder.add_byte_count(it.second.byte_count);
-    device_entry_builder.add_wakelock_duration_ms(it.second.wakelock_duration_ms);
-    device_entry_builder.add_creation_time(fb_builder->CreateString(
-        bluetooth::common::StringFormatTimeWithMilliseconds(kActivityAttributionTimeFormat, it.second.creation_time)
-            .c_str()));
-    device_aggregation_entry_offsets.push_back(device_entry_builder.Finish());
-  }
-  auto device_aggregation_entries = fb_builder->CreateVector(device_aggregation_entry_offsets);
-
-  // Dump App-based wakeup attribution data
-  auto title_app_wakeup = fb_builder->CreateString("----- App-based Wakeup Attribution Dumpsys -----");
-  std::vector<common::TimestampedEntry<AppWakeupDescriptor>> app_wakeup_aggregator = app_wakeup_aggregator_.Pull();
-  std::vector<flatbuffers::Offset<WakeupEntry>> app_wakeup_entry_offsets;
-  for (auto& it : app_wakeup_aggregator) {
-    WakeupEntryBuilder wakeup_entry_builder(*fb_builder);
-    std::chrono::milliseconds duration(it.timestamp);
-    std::chrono::time_point<std::chrono::system_clock> wakeup_time(duration);
-    wakeup_entry_builder.add_wakeup_time(fb_builder->CreateString(
-        bluetooth::common::StringFormatTimeWithMilliseconds(kActivityAttributionTimeFormat, wakeup_time).c_str()));
-    wakeup_entry_builder.add_activity(fb_builder->CreateString((ActivityToString(it.entry.activity_))));
-    wakeup_entry_builder.add_package_info(fb_builder->CreateString(it.entry.package_info_));
-    app_wakeup_entry_offsets.push_back(wakeup_entry_builder.Finish());
-  }
-  auto app_wakeup_entries = fb_builder->CreateVector(app_wakeup_entry_offsets);
-
-  // Dump app-based activity aggregation data
-  auto title_app_activity = fb_builder->CreateString("----- App-based Activity Attribution Dumpsys -----");
-  std::vector<flatbuffers::Offset<ActivityAggregationEntry>> app_aggregation_entry_offsets;
-  for (auto& it : app_activity_aggregator_) {
-    ActivityAggregationEntryBuilder app_entry_builder(*fb_builder);
-    app_entry_builder.add_package_info(fb_builder->CreateString(it.first.app));
-    app_entry_builder.add_activity(fb_builder->CreateString((ActivityToString(it.first.activity))));
-    app_entry_builder.add_wakeup_count(it.second.wakeup_count);
-    app_entry_builder.add_byte_count(it.second.byte_count);
-    app_entry_builder.add_wakelock_duration_ms(it.second.wakelock_duration_ms);
-    app_entry_builder.add_creation_time(fb_builder->CreateString(
-        bluetooth::common::StringFormatTimeWithMilliseconds(kActivityAttributionTimeFormat, it.second.creation_time)
-            .c_str()));
-    app_aggregation_entry_offsets.push_back(app_entry_builder.Finish());
-  }
-  auto app_aggregation_entries = fb_builder->CreateVector(app_aggregation_entry_offsets);
-
-  ActivityAttributionDataBuilder builder(*fb_builder);
-  builder.add_title_device_wakeup(title_device_wakeup);
-  builder.add_num_device_wakeup(device_wakeup_aggregator.size());
-  builder.add_device_wakeup_attribution(device_wakeup_entries);
-  builder.add_title_device_activity(title_device_activity);
-  builder.add_num_device_activity(btaa_aggregator_.size());
-  builder.add_device_activity_aggregation(device_aggregation_entries);
-  btaa_aggregator_.clear();
-
-  builder.add_title_app_wakeup(title_app_wakeup);
-  builder.add_num_app_wakeup(app_wakeup_aggregator.size());
-  builder.add_app_wakeup_attribution(app_wakeup_entries);
-  builder.add_title_app_activity(title_app_activity);
-  builder.add_num_app_activity(app_activity_aggregator_.size());
-  builder.add_app_activity_aggregation(app_aggregation_entries);
-  app_activity_aggregator_.clear();
-
-  flatbuffers::Offset<ActivityAttributionData> dumpsys_data = builder.Finish();
-  promise.set_value(dumpsys_data);
-}
-
-#ifndef CASE_RETURN_TEXT
-#define CASE_RETURN_TEXT(code) \
-  case code:                   \
-    return #code
-#endif
-
-const char* AttributionProcessor::ActivityToString(Activity activity) {
-  switch (activity) {
-    CASE_RETURN_TEXT(Activity::ACL);
-    CASE_RETURN_TEXT(Activity::ADVERTISE);
-    CASE_RETURN_TEXT(Activity::CONNECT);
-    CASE_RETURN_TEXT(Activity::CONTROL);
-    CASE_RETURN_TEXT(Activity::HFP);
-    CASE_RETURN_TEXT(Activity::ISO);
-    CASE_RETURN_TEXT(Activity::SCAN);
-    CASE_RETURN_TEXT(Activity::VENDOR);
-    default:
-      return "UNKNOWN";
-  }
-}
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/linux_generic/attribution_processor_tests.cc b/system/gd/btaa/linux_generic/attribution_processor_tests.cc
deleted file mode 100644
index 1a27206..0000000
--- a/system/gd/btaa/linux_generic/attribution_processor_tests.cc
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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 <base/strings/stringprintf.h>
-#include <gtest/gtest.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <functional>
-#include <memory>
-#include <vector>
-
-#include "btaa/activity_attribution.h"
-#include "btaa/attribution_processor.h"
-
-using bluetooth::hci::Address;
-using namespace bluetooth::activity_attribution;
-using namespace std::chrono;
-
-// mock for std::chrono::system_clock::now
-static AttributionProcessor::ClockType now_ret_val;
-static AttributionProcessor::ClockType fake_now() {
-  return now_ret_val;
-}
-
-class AttributionProcessorTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    pAttProc = std::make_unique<AttributionProcessor>(fake_now);
-  }
-  void TearDown() override {
-    pAttProc.reset();
-  }
-
-  std::unique_ptr<AttributionProcessor> pAttProc;
-};
-
-static void fake_now_set_current() {
-  now_ret_val = system_clock::now();
-}
-
-static void fake_now_advance_1000sec() {
-  now_ret_val += seconds(1000s);
-}
-
-TEST_F(AttributionProcessorTest, UAFInOnWakelockReleasedRegressionTest) {
-  std::vector<BtaaHciPacket> btaaPackets;
-  Address addr;
-
-  fake_now_set_current();
-
-  // setup the condition 1 for triggering erase operation
-  // add 220 entries in app_activity_aggregator_
-  // and btaa_aggregator_
-  for (int i = 0; i < 220; i++) {
-    std::string addrStr = base::StringPrintf("21:43:65:87:a9:%02x", i + 10);
-    ASSERT_TRUE(Address::FromString(addrStr, addr));
-    BtaaHciPacket packet(Activity::ACL, addr, 30 * i);
-    btaaPackets.push_back(packet);
-    pAttProc->NotifyActivityAttributionInfo(i + 1000, "com.test.app" + std::to_string(i), addrStr);
-  }
-
-  pAttProc->OnBtaaPackets(btaaPackets);
-  pAttProc->OnWakelockReleased(100);
-
-  // setup the condition 2 for triggering erase operation
-  // make elapsed_time_sec > 900s
-  fake_now_advance_1000sec();
-
-  pAttProc->OnBtaaPackets(btaaPackets);
-  pAttProc->OnWakelockReleased(100);
-}
diff --git a/system/gd/btaa/linux_generic/cmd_evt_classification.cc b/system/gd/btaa/linux_generic/cmd_evt_classification.cc
deleted file mode 100644
index 60fc566..0000000
--- a/system/gd/btaa/linux_generic/cmd_evt_classification.cc
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Copyright 2021 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 "btaa/cmd_evt_classification.h"
-
-namespace bluetooth {
-namespace activity_attribution {
-
-CmdEvtActivityClassification lookup_cmd(hci::OpCode opcode) {
-  CmdEvtActivityClassification classification = {};
-  switch (opcode) {
-    case hci::OpCode::INQUIRY:
-    case hci::OpCode::INQUIRY_CANCEL:
-    case hci::OpCode::PERIODIC_INQUIRY_MODE:
-    case hci::OpCode::EXIT_PERIODIC_INQUIRY_MODE:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 0, .address_pos = 0};
-      break;
-
-    case hci::OpCode::CREATE_CONNECTION:
-    case hci::OpCode::CREATE_CONNECTION_CANCEL:
-    case hci::OpCode::ACCEPT_CONNECTION_REQUEST:
-    case hci::OpCode::LINK_KEY_REQUEST_REPLY:
-    case hci::OpCode::LINK_KEY_REQUEST_NEGATIVE_REPLY:
-    case hci::OpCode::PIN_CODE_REQUEST_REPLY:
-    case hci::OpCode::PIN_CODE_REQUEST_NEGATIVE_REPLY:
-    case hci::OpCode::REJECT_CONNECTION_REQUEST:
-    case hci::OpCode::REMOTE_NAME_REQUEST:
-    case hci::OpCode::REMOTE_NAME_REQUEST_CANCEL:
-    case hci::OpCode::ACCEPT_SYNCHRONOUS_CONNECTION:
-    case hci::OpCode::REJECT_SYNCHRONOUS_CONNECTION:
-    case hci::OpCode::IO_CAPABILITY_REQUEST_REPLY:
-    case hci::OpCode::USER_CONFIRMATION_REQUEST_REPLY:
-    case hci::OpCode::USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY:
-    case hci::OpCode::USER_PASSKEY_REQUEST_REPLY:
-    case hci::OpCode::USER_PASSKEY_REQUEST_NEGATIVE_REPLY:
-    case hci::OpCode::REMOTE_OOB_DATA_REQUEST_REPLY:
-    case hci::OpCode::REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY:
-    case hci::OpCode::IO_CAPABILITY_REQUEST_NEGATIVE_REPLY:
-    case hci::OpCode::ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION:
-    case hci::OpCode::REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY:
-    case hci::OpCode::SWITCH_ROLE:
-    case hci::OpCode::READ_STORED_LINK_KEY:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 0, .address_pos = 3};
-      break;
-
-    case hci::OpCode::CENTRAL_LINK_KEY:
-    case hci::OpCode::READ_DEFAULT_LINK_POLICY_SETTINGS:
-    case hci::OpCode::WRITE_DEFAULT_LINK_POLICY_SETTINGS:
-    case hci::OpCode::WRITE_SCAN_ENABLE:
-    case hci::OpCode::READ_PAGE_SCAN_ACTIVITY:
-    case hci::OpCode::WRITE_PAGE_SCAN_ACTIVITY:
-    case hci::OpCode::READ_PAGE_SCAN_TYPE:
-    case hci::OpCode::WRITE_PAGE_SCAN_TYPE:
-    case hci::OpCode::READ_SIMPLE_PAIRING_MODE:
-    case hci::OpCode::WRITE_SIMPLE_PAIRING_MODE:
-    case hci::OpCode::READ_SCAN_ENABLE:
-    case hci::OpCode::LE_CREATE_CONNECTION_CANCEL:
-    case hci::OpCode::LE_READ_FILTER_ACCEPT_LIST_SIZE:
-    case hci::OpCode::LE_CLEAR_FILTER_ACCEPT_LIST:
-    case hci::OpCode::SEND_KEYPRESS_NOTIFICATION:
-    case hci::OpCode::LE_CLEAR_RESOLVING_LIST:
-    case hci::OpCode::LE_READ_RESOLVING_LIST_SIZE:
-    case hci::OpCode::LE_SET_HOST_CHANNEL_CLASSIFICATION:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 0, .address_pos = 0};
-      break;
-
-    case hci::OpCode::DISCONNECT:
-    case hci::OpCode::CHANGE_CONNECTION_PACKET_TYPE:
-    case hci::OpCode::AUTHENTICATION_REQUESTED:
-    case hci::OpCode::SET_CONNECTION_ENCRYPTION:
-    case hci::OpCode::CHANGE_CONNECTION_LINK_KEY:
-    case hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES:
-    case hci::OpCode::READ_REMOTE_EXTENDED_FEATURES:
-    case hci::OpCode::READ_REMOTE_VERSION_INFORMATION:
-    case hci::OpCode::READ_CLOCK_OFFSET:
-    case hci::OpCode::READ_LMP_HANDLE:
-    case hci::OpCode::SETUP_SYNCHRONOUS_CONNECTION:
-    case hci::OpCode::ENHANCED_SETUP_SYNCHRONOUS_CONNECTION:
-    case hci::OpCode::HOLD_MODE:
-    case hci::OpCode::SNIFF_MODE:
-    case hci::OpCode::EXIT_SNIFF_MODE:
-    case hci::OpCode::QOS_SETUP:
-    case hci::OpCode::ROLE_DISCOVERY:
-    case hci::OpCode::READ_LINK_POLICY_SETTINGS:
-    case hci::OpCode::WRITE_LINK_POLICY_SETTINGS:
-    case hci::OpCode::FLOW_SPECIFICATION:
-    case hci::OpCode::SNIFF_SUBRATING:
-    case hci::OpCode::FLUSH:
-    case hci::OpCode::READ_AUTOMATIC_FLUSH_TIMEOUT:
-    case hci::OpCode::WRITE_AUTOMATIC_FLUSH_TIMEOUT:
-    case hci::OpCode::READ_LINK_SUPERVISION_TIMEOUT:
-    case hci::OpCode::WRITE_LINK_SUPERVISION_TIMEOUT:
-    case hci::OpCode::REFRESH_ENCRYPTION_KEY:
-    case hci::OpCode::READ_FAILED_CONTACT_COUNTER:
-    case hci::OpCode::RESET_FAILED_CONTACT_COUNTER:
-    case hci::OpCode::READ_LINK_QUALITY:
-    case hci::OpCode::READ_RSSI:
-    case hci::OpCode::READ_AFH_CHANNEL_MAP:
-    case hci::OpCode::READ_CLOCK:
-    case hci::OpCode::READ_ENCRYPTION_KEY_SIZE:
-    // READ_LOOPBACK_MODE = 0x1801,
-    // WRITE_LOOPBACK_MODE = 0x1802,
-    // ENABLE_DEVICE_UNDER_TEST_MODE = 0x1803,
-    // WRITE_SIMPLE_PAIRING_DEBUG_MODE = 0x1804,
-    // WRITE_SECURE_CONNECTIONS_TEST_MODE = 0x180a,
-    case hci::OpCode::ENHANCED_FLUSH:
-    case hci::OpCode::LE_CONNECTION_UPDATE:
-    case hci::OpCode::LE_START_ENCRYPTION:
-    case hci::OpCode::LE_LONG_TERM_KEY_REQUEST_REPLY:
-    case hci::OpCode::LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY:
-    case hci::OpCode::LE_READ_PHY:
-    case hci::OpCode::LE_SET_PHY:
-    case hci::OpCode::LE_READ_REMOTE_FEATURES:
-    case hci::OpCode::LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY:
-    case hci::OpCode::LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY:
-    case hci::OpCode::LE_SET_DATA_LENGTH:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 3, .address_pos = 0};
-      break;
-
-    case hci::OpCode::SET_EVENT_MASK:
-    case hci::OpCode::RESET:
-    case hci::OpCode::SET_EVENT_FILTER:
-    case hci::OpCode::READ_PIN_TYPE:
-    case hci::OpCode::WRITE_PIN_TYPE:
-    case hci::OpCode::WRITE_LOCAL_NAME:
-    case hci::OpCode::READ_LOCAL_NAME:
-    case hci::OpCode::READ_CONNECTION_ACCEPT_TIMEOUT:
-    case hci::OpCode::WRITE_CONNECTION_ACCEPT_TIMEOUT:
-    case hci::OpCode::READ_PAGE_TIMEOUT:
-    case hci::OpCode::WRITE_PAGE_TIMEOUT:
-    case hci::OpCode::READ_AUTHENTICATION_ENABLE:
-    case hci::OpCode::WRITE_AUTHENTICATION_ENABLE:
-    case hci::OpCode::READ_CLASS_OF_DEVICE:
-    case hci::OpCode::WRITE_CLASS_OF_DEVICE:
-    case hci::OpCode::READ_VOICE_SETTING:
-    case hci::OpCode::WRITE_VOICE_SETTING:
-    case hci::OpCode::READ_NUM_BROADCAST_RETRANSMITS:
-    case hci::OpCode::WRITE_NUM_BROADCAST_RETRANSMITS:
-    case hci::OpCode::READ_HOLD_MODE_ACTIVITY:
-    case hci::OpCode::WRITE_HOLD_MODE_ACTIVITY:
-    case hci::OpCode::READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE:
-    case hci::OpCode::WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE:
-    case hci::OpCode::SET_CONTROLLER_TO_HOST_FLOW_CONTROL:
-    case hci::OpCode::HOST_BUFFER_SIZE:
-    case hci::OpCode::HOST_NUMBER_OF_COMPLETED_PACKETS:
-    case hci::OpCode::READ_NUMBER_OF_SUPPORTED_IAC:
-    case hci::OpCode::READ_CURRENT_IAC_LAP:
-    case hci::OpCode::WRITE_CURRENT_IAC_LAP:
-    case hci::OpCode::SET_AFH_HOST_CHANNEL_CLASSIFICATION:
-    case hci::OpCode::READ_AFH_CHANNEL_ASSESSMENT_MODE:
-    case hci::OpCode::WRITE_AFH_CHANNEL_ASSESSMENT_MODE:
-    case hci::OpCode::READ_LE_HOST_SUPPORT:
-    case hci::OpCode::WRITE_LE_HOST_SUPPORT:
-    case hci::OpCode::READ_SECURE_CONNECTIONS_HOST_SUPPORT:
-    case hci::OpCode::WRITE_SECURE_CONNECTIONS_HOST_SUPPORT:
-    case hci::OpCode::READ_LOCAL_OOB_EXTENDED_DATA:
-    case hci::OpCode::SET_ECOSYSTEM_BASE_INTERVAL:
-    case hci::OpCode::CONFIGURE_DATA_PATH:
-    case hci::OpCode::READ_LOCAL_VERSION_INFORMATION:
-    case hci::OpCode::READ_LOCAL_SUPPORTED_COMMANDS:
-    case hci::OpCode::READ_LOCAL_SUPPORTED_FEATURES:
-    case hci::OpCode::READ_LOCAL_EXTENDED_FEATURES:
-    case hci::OpCode::READ_BUFFER_SIZE:
-    case hci::OpCode::READ_BD_ADDR:
-    case hci::OpCode::READ_DATA_BLOCK_SIZE:
-    case hci::OpCode::READ_LOCAL_SUPPORTED_CODECS_V1:
-    case hci::OpCode::READ_LOCAL_SUPPORTED_CODECS_V2:
-    case hci::OpCode::READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES:
-    case hci::OpCode::READ_LOCAL_SUPPORTED_CONTROLLER_DELAY:
-    case hci::OpCode::READ_LOCAL_OOB_DATA:
-    case hci::OpCode::LE_GENERATE_DHKEY_V2:
-    case hci::OpCode::LE_MODIFY_SLEEP_CLOCK_ACCURACY:
-    case hci::OpCode::LE_READ_BUFFER_SIZE_V2:
-    case hci::OpCode::LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH:
-    case hci::OpCode::LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH:
-    case hci::OpCode::LE_READ_LOCAL_P_256_PUBLIC_KEY:
-    case hci::OpCode::LE_GENERATE_DHKEY_V1:
-    case hci::OpCode::LE_SET_EVENT_MASK:
-    case hci::OpCode::LE_READ_BUFFER_SIZE_V1:
-    case hci::OpCode::LE_READ_LOCAL_SUPPORTED_FEATURES:
-    case hci::OpCode::LE_SET_RANDOM_ADDRESS:
-    case hci::OpCode::LE_READ_TRANSMIT_POWER:
-    case hci::OpCode::LE_READ_RF_PATH_COMPENSATION_POWER:
-    case hci::OpCode::LE_WRITE_RF_PATH_COMPENSATION_POWER:
-    case hci::OpCode::LE_SET_DEFAULT_PHY:
-    case hci::OpCode::LE_ENCRYPT:
-    case hci::OpCode::LE_RAND:
-    case hci::OpCode::LE_SET_ADDRESS_RESOLUTION_ENABLE:
-    case hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT:
-    case hci::OpCode::LE_READ_MAXIMUM_DATA_LENGTH:
-    case hci::OpCode::LE_READ_SUPPORTED_STATES:
-      classification = {.activity = Activity::CONTROL, .connection_handle_pos = 0, .address_pos = 0};
-      break;
-
-    case hci::OpCode::DELETE_STORED_LINK_KEY:
-      classification = {.activity = Activity::CONTROL, .connection_handle_pos = 0, .address_pos = 3};
-      break;
-    case hci::OpCode::READ_TRANSMIT_POWER_LEVEL:
-      classification = {.activity = Activity::CONTROL, .connection_handle_pos = 3, .address_pos = 0};
-      break;
-
-    case hci::OpCode::READ_INQUIRY_SCAN_ACTIVITY:
-    case hci::OpCode::WRITE_INQUIRY_SCAN_ACTIVITY:
-    case hci::OpCode::READ_INQUIRY_SCAN_TYPE:
-    case hci::OpCode::WRITE_INQUIRY_SCAN_TYPE:
-    case hci::OpCode::READ_INQUIRY_MODE:
-    case hci::OpCode::WRITE_INQUIRY_MODE:
-    case hci::OpCode::READ_EXTENDED_INQUIRY_RESPONSE:
-    case hci::OpCode::WRITE_EXTENDED_INQUIRY_RESPONSE:
-    case hci::OpCode::LE_SET_CIG_PARAMETERS:
-    case hci::OpCode::LE_CREATE_CIS:
-    case hci::OpCode::LE_REMOVE_CIG:
-    case hci::OpCode::LE_ACCEPT_CIS_REQUEST:
-    case hci::OpCode::LE_REJECT_CIS_REQUEST:
-    case hci::OpCode::LE_CREATE_BIG:
-    case hci::OpCode::LE_TERMINATE_BIG:
-    case hci::OpCode::LE_BIG_CREATE_SYNC:
-    case hci::OpCode::LE_BIG_TERMINATE_SYNC:
-    case hci::OpCode::LE_REQUEST_PEER_SCA:
-    case hci::OpCode::LE_SETUP_ISO_DATA_PATH:
-    case hci::OpCode::LE_REMOVE_ISO_DATA_PATH:
-    case hci::OpCode::LE_SET_HOST_FEATURE:
-    case hci::OpCode::LE_READ_ISO_LINK_QUALITY:
-    case hci::OpCode::LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL:
-    case hci::OpCode::LE_READ_REMOTE_TRANSMIT_POWER_LEVEL:
-    case hci::OpCode::LE_SET_PATH_LOSS_REPORTING_PARAMETERS:
-    case hci::OpCode::LE_SET_PATH_LOSS_REPORTING_ENABLE:
-    case hci::OpCode::LE_SET_TRANSMIT_POWER_REPORTING_ENABLE:
-    case hci::OpCode::LE_GET_VENDOR_CAPABILITIES:
-    case hci::OpCode::LE_MULTI_ADVT:
-    case hci::OpCode::LE_BATCH_SCAN:
-    case hci::OpCode::LE_ADV_FILTER:
-    case hci::OpCode::LE_ENERGY_INFO:
-    case hci::OpCode::LE_EXTENDED_SCAN_PARAMS:
-    case hci::OpCode::CONTROLLER_DEBUG_INFO:
-    case hci::OpCode::CONTROLLER_A2DP_OPCODE:
-    case hci::OpCode::CONTROLLER_BQR:
-    case hci::OpCode::READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL:
-    case hci::OpCode::WRITE_INQUIRY_TRANSMIT_POWER_LEVEL:
-    case hci::OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS:
-    case hci::OpCode::LE_SET_EXTENDED_SCAN_ENABLE:
-    case hci::OpCode::LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL:
-    case hci::OpCode::LE_SET_SCAN_PARAMETERS:
-    case hci::OpCode::LE_SET_SCAN_ENABLE:
-    case hci::OpCode::LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS:
-    case hci::OpCode::LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE:
-    case hci::OpCode::LE_CLEAR_PERIODIC_ADVERTISER_LIST:
-    case hci::OpCode::LE_READ_PERIODIC_ADVERTISER_LIST_SIZE:
-    case hci::OpCode::LE_PERIODIC_ADVERTISING_TERMINATE_SYNC:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 0, .address_pos = 0};
-      break;
-
-    case hci::OpCode::LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER:
-    case hci::OpCode::LE_SET_ADVERTISING_DATA:
-    case hci::OpCode::LE_SET_SCAN_RESPONSE_DATA:
-    case hci::OpCode::LE_SET_ADVERTISING_ENABLE:
-    case hci::OpCode::LE_SET_EXTENDED_ADVERTISING_DATA:
-    case hci::OpCode::LE_SET_EXTENDED_SCAN_RESPONSE_DATA:
-    case hci::OpCode::LE_SET_EXTENDED_ADVERTISING_ENABLE:
-    case hci::OpCode::LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH:
-    case hci::OpCode::LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS:
-    case hci::OpCode::LE_REMOVE_ADVERTISING_SET:
-    case hci::OpCode::LE_CLEAR_ADVERTISING_SETS:
-    case hci::OpCode::LE_SET_PERIODIC_ADVERTISING_PARAMETERS:
-    case hci::OpCode::LE_SET_PERIODIC_ADVERTISING_DATA:
-    case hci::OpCode::LE_SET_PERIODIC_ADVERTISING_ENABLE:
-    case hci::OpCode::LE_SET_ADVERTISING_SET_RANDOM_ADDRESS:
-      classification = {.activity = Activity::ADVERTISE, .connection_handle_pos = 0, .address_pos = 0};
-      break;
-
-    case hci::OpCode::LE_SET_ADVERTISING_PARAMETERS:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 0, .address_pos = 10};
-      break;
-    case hci::OpCode::LE_CREATE_CONNECTION:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 0, .address_pos = 9};
-      break;
-    case hci::OpCode::LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST:
-    case hci::OpCode::LE_READ_CHANNEL_MAP:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 4, .address_pos = 0};
-      break;
-
-    case hci::OpCode::LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST:
-    case hci::OpCode::LE_ADD_DEVICE_TO_RESOLVING_LIST:
-    case hci::OpCode::LE_REMOVE_DEVICE_FROM_RESOLVING_LIST:
-    case hci::OpCode::LE_READ_PEER_RESOLVABLE_ADDRESS:
-    case hci::OpCode::LE_READ_LOCAL_RESOLVABLE_ADDRESS:
-    case hci::OpCode::LE_SET_PRIVACY_MODE:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 0, .address_pos = 4};
-      break;
-
-    case hci::OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS:
-      classification = {.activity = Activity::ADVERTISE, .connection_handle_pos = 0, .address_pos = 15};
-      break;
-    case hci::OpCode::LE_EXTENDED_CREATE_CONNECTION:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 0, .address_pos = 6};
-      break;
-    case hci::OpCode::LE_PERIODIC_ADVERTISING_CREATE_SYNC:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 0, .address_pos = 6};
-      break;
-    case hci::OpCode::LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST:
-    case hci::OpCode::LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 0, .address_pos = 4};
-      break;
-    case hci::OpCode::LE_PERIODIC_ADVERTISING_SYNC_TRANSFER:
-    case hci::OpCode::LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER:
-    case hci::OpCode::LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 3, .address_pos = 0};
-      break;
-
-    default:
-      classification = {.activity = Activity::UNKNOWN, .connection_handle_pos = 0, .address_pos = 0};
-      break;
-  }
-  return classification;
-}
-
-CmdEvtActivityClassification lookup_event(hci::EventCode event_code) {
-  CmdEvtActivityClassification classification = {};
-  switch (event_code) {
-    case hci::EventCode::INQUIRY_COMPLETE:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 0, .address_pos = 0};
-      break;
-    case hci::EventCode::CONNECTION_COMPLETE:
-    case hci::EventCode::SYNCHRONOUS_CONNECTION_COMPLETE:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 3, .address_pos = 5};
-      break;
-
-    case hci::EventCode::CONNECTION_REQUEST:
-    case hci::EventCode::PIN_CODE_REQUEST:
-    case hci::EventCode::LINK_KEY_REQUEST:
-    case hci::EventCode::LINK_KEY_NOTIFICATION:
-    case hci::EventCode::USER_PASSKEY_NOTIFICATION:
-    case hci::EventCode::KEYPRESS_NOTIFICATION:
-    case hci::EventCode::REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION:
-    case hci::EventCode::IO_CAPABILITY_REQUEST:
-    case hci::EventCode::IO_CAPABILITY_RESPONSE:
-    case hci::EventCode::USER_CONFIRMATION_REQUEST:
-    case hci::EventCode::USER_PASSKEY_REQUEST:
-    case hci::EventCode::REMOTE_OOB_DATA_REQUEST:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 0, .address_pos = 2};
-      break;
-
-    case hci::EventCode::DISCONNECTION_COMPLETE:
-    case hci::EventCode::AUTHENTICATION_COMPLETE:
-    case hci::EventCode::ENCRYPTION_CHANGE:
-    case hci::EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE:
-    case hci::EventCode::LINK_SUPERVISION_TIMEOUT_CHANGED:
-    case hci::EventCode::CHANGE_CONNECTION_LINK_KEY_COMPLETE:
-    case hci::EventCode::CENTRAL_LINK_KEY_COMPLETE:
-    case hci::EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE:
-    case hci::EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE:
-    case hci::EventCode::QOS_SETUP_COMPLETE:
-    case hci::EventCode::MODE_CHANGE:
-    case hci::EventCode::READ_CLOCK_OFFSET_COMPLETE:
-    case hci::EventCode::CONNECTION_PACKET_TYPE_CHANGED:
-    case hci::EventCode::FLOW_SPECIFICATION_COMPLETE:
-    case hci::EventCode::READ_REMOTE_EXTENDED_FEATURES_COMPLETE:
-    case hci::EventCode::SYNCHRONOUS_CONNECTION_CHANGED:
-    case hci::EventCode::SNIFF_SUBRATING:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 3, .address_pos = 0};
-      break;
-
-    case hci::EventCode::REMOTE_NAME_REQUEST_COMPLETE:
-    case hci::EventCode::EXTENDED_INQUIRY_RESULT:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 0, .address_pos = 3};
-      break;
-    case hci::EventCode::FLUSH_OCCURRED:
-    case hci::EventCode::MAX_SLOTS_CHANGE:
-    case hci::EventCode::QOS_VIOLATION:
-    case hci::EventCode::ENHANCED_FLUSH_COMPLETE:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 2, .address_pos = 0};
-      break;
-    case hci::EventCode::ROLE_CHANGE:
-    case hci::EventCode::SIMPLE_PAIRING_COMPLETE:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 0, .address_pos = 3};
-      break;
-    case hci::EventCode::PAGE_SCAN_REPETITION_MODE_CHANGE:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 0, .address_pos = 2};
-      break;
-
-    default:
-      classification = {.activity = Activity::UNKNOWN, .connection_handle_pos = 0, .address_pos = 0};
-  }
-  return classification;
-}
-
-CmdEvtActivityClassification lookup_le_event(hci::SubeventCode subevent_code) {
-  CmdEvtActivityClassification classification = {};
-  switch (subevent_code) {
-    case hci::SubeventCode::CONNECTION_COMPLETE:
-    case hci::SubeventCode::ENHANCED_CONNECTION_COMPLETE:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 4, .address_pos = 7};
-      break;
-
-    case hci::SubeventCode::CONNECTION_UPDATE_COMPLETE:
-    case hci::SubeventCode::READ_REMOTE_FEATURES_COMPLETE:
-    case hci::SubeventCode::PHY_UPDATE_COMPLETE:
-    case hci::SubeventCode::CTE_REQUEST_FAILED:
-    case hci::SubeventCode::TRANSMIT_POWER_REPORTING:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 4, .address_pos = 0};
-      break;
-
-    case hci::SubeventCode::LONG_TERM_KEY_REQUEST:
-    case hci::SubeventCode::REMOTE_CONNECTION_PARAMETER_REQUEST:
-    case hci::SubeventCode::DATA_LENGTH_CHANGE:
-    case hci::SubeventCode::CHANNEL_SELECTION_ALGORITHM:
-    case hci::SubeventCode::CONNECTION_IQ_REPORT:
-    case hci::SubeventCode::PATH_LOSS_THRESHOLD:
-      classification = {.activity = Activity::CONNECT, .connection_handle_pos = 3, .address_pos = 0};
-      break;
-
-    case hci::SubeventCode::READ_LOCAL_P256_PUBLIC_KEY_COMPLETE:
-    case hci::SubeventCode::GENERATE_DHKEY_COMPLETE:
-      classification = {.activity = Activity::CONTROL, .connection_handle_pos = 0, .address_pos = 0};
-      break;
-
-    case hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED:
-    case hci::SubeventCode::PERIODIC_ADVERTISING_REPORT:
-    case hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_LOST:
-    case hci::SubeventCode::ADVERTISING_SET_TERMINATED:
-      classification = {.activity = Activity::ADVERTISE, .connection_handle_pos = 0, .address_pos = 0};
-      break;
-
-    case hci::SubeventCode::SCAN_TIMEOUT:
-    case hci::SubeventCode::BIG_INFO_ADVERTISING_REPORT:
-    case hci::SubeventCode::CONNECTIONLESS_IQ_REPORT:
-    case hci::SubeventCode::CREATE_BIG_COMPLETE:
-    case hci::SubeventCode::TERMINATE_BIG_COMPLETE:
-    case hci::SubeventCode::BIG_SYNC_ESTABLISHED:
-    case hci::SubeventCode::BIG_SYNC_LOST:
-    case hci::SubeventCode::REQUEST_PEER_SCA_COMPLETE:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 0, .address_pos = 0};
-      break;
-
-    case hci::SubeventCode::SCAN_REQUEST_RECEIVED:
-      classification = {.activity = Activity::ADVERTISE, .connection_handle_pos = 0, .address_pos = 5};
-      break;
-
-    case hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED:
-    case hci::SubeventCode::CIS_ESTABLISHED:
-    case hci::SubeventCode::CIS_REQUEST:
-      classification = {.activity = Activity::SCAN, .connection_handle_pos = 4, .address_pos = 0};
-      break;
-
-    default:
-      classification = {.activity = Activity::UNKNOWN, .connection_handle_pos = 0, .address_pos = 0};
-  }
-  return classification;
-}
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/linux_generic/hci_processor.cc b/system/gd/btaa/linux_generic/hci_processor.cc
deleted file mode 100644
index 28c1e70..0000000
--- a/system/gd/btaa/linux_generic/hci_processor.cc
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright 2021 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 "btaa/hci_processor.h"
-
-#include "os/log.h"
-
-namespace bluetooth {
-namespace activity_attribution {
-
-void DeviceParser::match_handle_with_address(uint16_t connection_handle, hci::Address& address) {
-  if (connection_handle && !address.IsEmpty()) {
-    connection_lookup_table_[connection_handle] = address;
-  } else if (connection_handle) {
-    if (connection_lookup_table_.find(connection_handle) != connection_lookup_table_.end()) {
-      address = connection_lookup_table_[connection_handle];
-    }
-  }
-}
-
-void HciProcessor::process_le_event(
-    std::vector<BtaaHciPacket>& btaa_hci_packets, int16_t byte_count, hci::EventView& event) {
-  uint16_t connection_handle_value = 0;
-  hci::Address address_value;
-
-  auto le_packet_view = hci::LeMetaEventView::Create(event);
-  if (!le_packet_view.IsValid()) {
-    return;
-  }
-
-  auto subevent_code = le_packet_view.GetSubeventCode();
-  auto le_event_info = lookup_le_event(subevent_code);
-
-  if (le_event_info.activity != Activity::UNKNOWN) {
-    // lookup_le_event returns all simple classic event which does not require additional processing.
-    if (le_event_info.connection_handle_pos) {
-      auto connection_handle_it = event.begin() + le_event_info.connection_handle_pos;
-      connection_handle_value = connection_handle_it.extract<uint16_t>();
-    }
-    if (le_event_info.address_pos) {
-      auto address_value_it = event.begin() + le_event_info.address_pos;
-      address_value = address_value_it.extract<hci::Address>();
-    }
-    device_parser_.match_handle_with_address(connection_handle_value, address_value);
-    btaa_hci_packets.push_back(BtaaHciPacket(le_event_info.activity, address_value, byte_count));
-  }
-}
-
-void HciProcessor::process_special_event(
-    std::vector<BtaaHciPacket>& btaa_hci_packets,
-    hci::EventCode event_code,
-    uint16_t byte_count,
-    hci::EventView& event) {
-  uint16_t avg_byte_count;
-  hci::Address address_value;
-
-  switch (event_code) {
-    case hci::EventCode::INQUIRY_RESULT:
-    case hci::EventCode::INQUIRY_RESULT_WITH_RSSI: {
-      auto packet_view = hci::InquiryResultView::Create(event);
-      if (!packet_view.IsValid()) {
-        return;
-      }
-      auto inquiry_results = packet_view.GetResponses();
-      avg_byte_count = byte_count / inquiry_results.size();
-      for (auto& inquiry_result : inquiry_results) {
-        btaa_hci_packets.push_back(BtaaHciPacket(Activity::SCAN, inquiry_result.bd_addr_, avg_byte_count));
-      }
-    } break;
-
-    case hci::EventCode::NUMBER_OF_COMPLETED_PACKETS: {
-      auto packet_view = hci::NumberOfCompletedPacketsView::Create(event);
-      if (!packet_view.IsValid()) {
-        return;
-      }
-      auto completed_packets = packet_view.GetCompletedPackets();
-      avg_byte_count = byte_count / completed_packets.size();
-      for (auto& completed_packet : completed_packets) {
-        device_parser_.match_handle_with_address(completed_packet.connection_handle_, address_value);
-        btaa_hci_packets.push_back(BtaaHciPacket(Activity::CONNECT, address_value, avg_byte_count));
-      }
-    } break;
-
-    case hci::EventCode::RETURN_LINK_KEYS: {
-      auto packet_view = hci::ReturnLinkKeysView::Create(event);
-      if (!packet_view.IsValid()) {
-        return;
-      }
-      auto keys_and_addresses = packet_view.GetKeys();
-      avg_byte_count = byte_count / keys_and_addresses.size();
-      for (auto& key_and_address : keys_and_addresses) {
-        btaa_hci_packets.push_back(BtaaHciPacket(Activity::CONNECT, key_and_address.address_, avg_byte_count));
-      }
-    } break;
-
-    default: {
-      btaa_hci_packets.push_back(BtaaHciPacket(Activity::UNKNOWN, address_value, byte_count));
-    } break;
-  }
-}
-
-void HciProcessor::process_command(
-    std::vector<BtaaHciPacket>& btaa_hci_packets,
-    packet::PacketView<packet::kLittleEndian>& packet_view,
-    uint16_t byte_count) {
-  hci::CommandView command = hci::CommandView::Create(packet_view);
-  if (!command.IsValid()) {
-    return;
-  }
-
-  uint16_t connection_handle_value = 0;
-  hci::Address address_value;
-  auto opcode = command.GetOpCode();
-  auto cmd_info = lookup_cmd(opcode);
-
-  if (cmd_info.connection_handle_pos) {
-    auto connection_handle_it = command.begin() + cmd_info.connection_handle_pos;
-    connection_handle_value = connection_handle_it.extract<uint16_t>();
-  }
-  if (cmd_info.address_pos) {
-    auto address_value_it = command.begin() + cmd_info.address_pos;
-    address_value = address_value_it.extract<hci::Address>();
-  }
-  device_parser_.match_handle_with_address(connection_handle_value, address_value);
-  pending_command_.btaa_hci_packet = BtaaHciPacket(cmd_info.activity, address_value, byte_count);
-
-  pending_command_.opcode = opcode;
-}
-
-void HciProcessor::process_event(
-    std::vector<BtaaHciPacket>& btaa_hci_packets,
-    packet::PacketView<packet::kLittleEndian>& packet_view,
-    uint16_t byte_count) {
-  hci::EventView event = hci::EventView::Create(packet_view);
-  if (!event.IsValid()) {
-    return;
-  }
-
-  uint16_t connection_handle_value = 0;
-  hci::Address address_value;
-  auto event_code = event.GetEventCode();
-  auto event_info = lookup_event(event_code);
-
-  if (event_info.activity != Activity::UNKNOWN) {
-    // lookup_event returns all simple classic event which does not require additional processing.
-    if (event_info.connection_handle_pos) {
-      auto connection_handle_it = event.begin() + event_info.connection_handle_pos;
-      connection_handle_value = connection_handle_it.extract<uint16_t>();
-    }
-    if (event_info.address_pos) {
-      auto address_value_it = event.begin() + event_info.address_pos;
-      address_value = address_value_it.extract<hci::Address>();
-    }
-    device_parser_.match_handle_with_address(connection_handle_value, address_value);
-    btaa_hci_packets.push_back(BtaaHciPacket(event_info.activity, address_value, byte_count));
-  } else {
-    // The event requires additional processing.
-    switch (event_code) {
-      case hci::EventCode::COMMAND_COMPLETE: {
-        auto packet_view = hci::CommandCompleteView::Create(event);
-        if (packet_view.IsValid() && packet_view.GetCommandOpCode() == pending_command_.opcode) {
-          pending_command_.btaa_hci_packet.byte_count += byte_count;
-          btaa_hci_packets.push_back(std::move(pending_command_.btaa_hci_packet));
-        } else {
-          btaa_hci_packets.push_back(BtaaHciPacket(Activity::UNKNOWN, address_value, byte_count));
-        }
-      } break;
-      case hci::EventCode::COMMAND_STATUS: {
-        auto packet_view = hci::CommandStatusView::Create(event);
-        if (packet_view.IsValid() && packet_view.GetCommandOpCode() == pending_command_.opcode) {
-          pending_command_.btaa_hci_packet.byte_count += byte_count;
-          btaa_hci_packets.push_back(std::move(pending_command_.btaa_hci_packet));
-        } else {
-          btaa_hci_packets.push_back(BtaaHciPacket(Activity::UNKNOWN, address_value, byte_count));
-        }
-        break;
-      }
-      case hci::EventCode::LE_META_EVENT:
-        process_le_event(btaa_hci_packets, byte_count, event);
-        break;
-      case hci::EventCode::VENDOR_SPECIFIC:
-        btaa_hci_packets.push_back(BtaaHciPacket(Activity::VENDOR, address_value, byte_count));
-        break;
-      default:
-        process_special_event(btaa_hci_packets, event_code, byte_count, event);
-        break;
-    }
-  }
-}
-
-void HciProcessor::process_acl(
-    std::vector<BtaaHciPacket>& btaa_hci_packets,
-    packet::PacketView<packet::kLittleEndian>& packet_view,
-    uint16_t byte_count) {
-  hci::AclView acl = hci::AclView::Create(packet_view);
-  auto connection_handle = acl.begin();
-  // Connection handle is extracted from the 12 least significant bit.
-  uint16_t connection_handle_value = connection_handle.extract<uint16_t>() & 0xfff;
-  hci::Address address_value;
-  device_parser_.match_handle_with_address(connection_handle_value, address_value);
-  btaa_hci_packets.push_back(BtaaHciPacket(Activity::ACL, address_value, byte_count));
-}
-
-void HciProcessor::process_sco(
-    std::vector<BtaaHciPacket>& btaa_hci_packets,
-    packet::PacketView<packet::kLittleEndian>& packet_view,
-    uint16_t byte_count) {
-  hci::ScoView sco = hci::ScoView::Create(packet_view);
-  auto connection_handle = sco.begin();
-  // Connection handle is extracted from the 12 least significant bit.
-  uint16_t connection_handle_value = connection_handle.extract<uint16_t>() & 0xfff;
-  hci::Address address_value;
-  device_parser_.match_handle_with_address(connection_handle_value, address_value);
-  btaa_hci_packets.push_back(BtaaHciPacket(Activity::HFP, address_value, byte_count));
-}
-
-void HciProcessor::process_iso(
-    std::vector<BtaaHciPacket>& btaa_hci_packets,
-    packet::PacketView<packet::kLittleEndian>& packet_view,
-    uint16_t byte_count) {
-  hci::IsoView iso = hci::IsoView::Create(packet_view);
-  auto connection_handle = iso.begin();
-  // Connection handle is extracted from the 12 least significant bit.
-  uint16_t connection_handle_value = connection_handle.extract<uint16_t>() & 0xfff;
-  hci::Address address_value;
-  device_parser_.match_handle_with_address(connection_handle_value, address_value);
-  btaa_hci_packets.push_back(BtaaHciPacket(Activity::ISO, address_value, byte_count));
-}
-
-std::vector<BtaaHciPacket> HciProcessor::OnHciPacket(
-    hal::HciPacket packet, hal::SnoopLogger::PacketType type, uint16_t length) {
-  std::vector<BtaaHciPacket> btaa_hci_packets;
-  auto packet_view = packet::PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>(packet));
-  switch (type) {
-    case hal::SnoopLogger::PacketType::CMD:
-      process_command(btaa_hci_packets, packet_view, length);
-      break;
-    case hal::SnoopLogger::PacketType::EVT:
-      process_event(btaa_hci_packets, packet_view, length);
-      break;
-    case hal::SnoopLogger::PacketType::ACL:
-      process_acl(btaa_hci_packets, packet_view, length);
-      break;
-    case hal::SnoopLogger::PacketType::SCO:
-      process_sco(btaa_hci_packets, packet_view, length);
-      break;
-    case hal::SnoopLogger::PacketType::ISO:
-      process_iso(btaa_hci_packets, packet_view, length);
-      break;
-  }
-  return btaa_hci_packets;
-}
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/linux_generic/wakelock_processor.cc b/system/gd/btaa/linux_generic/wakelock_processor.cc
deleted file mode 100644
index bd3b3ed..0000000
--- a/system/gd/btaa/linux_generic/wakelock_processor.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2020 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 "btaa/wakelock_processor.h"
-
-#include "os/log.h"
-
-namespace bluetooth {
-namespace activity_attribution {
-
-static const int kWakelockMaxDurationMs(10000);
-
-WakelockProcessor::WakelockProcessor() {
-  wakelock_net_count_ = 0;
-  wakelock_acquired_time_ = {};
-}
-
-uint32_t WakelockProcessor::OnWakelockReleased() {
-  auto cur_time = std::chrono::system_clock::now();
-  uint32_t wakelock_duration_ms = 0;
-
-  if (wakelock_net_count_ == 0) {
-    LOG_INFO("Release a never acquired wakelock, ignored.");
-  } else {
-    wakelock_net_count_--;
-    if (wakelock_net_count_ == 0) {
-      wakelock_duration_ms = static_cast<uint32_t>(
-          std::chrono::duration_cast<std::chrono::milliseconds>(cur_time - wakelock_acquired_time_).count());
-      wakelock_acquired_time_ = {};
-    }
-  }
-
-  return wakelock_duration_ms;
-}
-
-void WakelockProcessor::OnWakelockAcquired() {
-  auto cur_time = std::chrono::system_clock::now();
-
-  if (wakelock_net_count_ == 0) {
-    if (wakelock_acquired_time_.time_since_epoch().count()) {
-      LOG_INFO("Previous wakelock acquired time is not consumed, dropped.");
-    }
-    wakelock_acquired_time_ = cur_time;
-  } else if (cur_time - wakelock_acquired_time_ > std::chrono::milliseconds(kWakelockMaxDurationMs)) {
-    LOG_INFO("Wakelock held for too long, likely we missed a release notification. Resetting wakelock stats.");
-    wakelock_net_count_ = 0;
-    wakelock_acquired_time_ = cur_time;
-  }
-
-  wakelock_net_count_++;
-}
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/btaa/wakelock_processor.h b/system/gd/btaa/wakelock_processor.h
deleted file mode 100644
index 8b700d2..0000000
--- a/system/gd/btaa/wakelock_processor.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2020 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 <chrono>
-#include <cstdint>
-
-namespace bluetooth {
-namespace activity_attribution {
-
-class WakelockProcessor {
- public:
-  WakelockProcessor();
-
-  uint32_t OnWakelockReleased();
-  void OnWakelockAcquired();
-
- private:
-  std::chrono::time_point<std::chrono::system_clock> wakelock_acquired_time_;
-  uint8_t wakelock_net_count_;
-};
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
diff --git a/system/gd/cert/bluetooth_packets_python3_setup.py b/system/gd/cert/bluetooth_packets_python3_setup.py
index 6e3657b..482b00b 100644
--- a/system/gd/cert/bluetooth_packets_python3_setup.py
+++ b/system/gd/cert/bluetooth_packets_python3_setup.py
@@ -32,7 +32,8 @@
 PYBIND11_INCLUDE_DIR = os.path.join(ANDROID_BUILD_TOP, "external/python/pybind11/include")
 GD_DIR = os.path.join(ANDROID_BUILD_TOP, "packages/modules/Bluetooth/system/gd")
 BT_PACKETS_GEN_DIR = os.path.join(
-    ANDROID_BUILD_TOP, "out/soong/.intermediates/packages/modules/Bluetooth/system/gd/BluetoothGeneratedPackets_h/gen")
+    ANDROID_BUILD_TOP,
+    "out/soong/.intermediates/packages/modules/Bluetooth/system/gd/BluetoothGeneratedPackets_h/gen")
 BT_PACKETS_PY3_GEN_DIR = os.path.join(
     ANDROID_BUILD_TOP,
     "out/soong/.intermediates/packages/modules/Bluetooth/system/gd/BluetoothGeneratedPackets_python3_cc/gen")
@@ -52,7 +53,6 @@
 BT_PACKETS_PY3_SRCs = \
   [os.path.join(GD_DIR, "packet/python3_module.cc")] \
   + glob.glob(os.path.join(BT_PACKETS_PY3_GEN_DIR, "l2cap", "*.cc")) \
-  + glob.glob(os.path.join(BT_PACKETS_PY3_GEN_DIR, "security", "*.cc"))
 
 bluetooth_packets_python3_module = Extension(
     'bluetooth_packets_python3',
diff --git a/system/gd/cert/run b/system/gd/cert/run
index 8fcf22b..51b1007 100755
--- a/system/gd/cert/run
+++ b/system/gd/cert/run
@@ -82,8 +82,8 @@
 TEST_CONFIG="${ANDROID_BUILD_TOP}/packages/modules/Bluetooth/system/blueberry/tests/gd/host_config.yaml"
 TEST_FILTER="--presubmit"
 TEST_RUNNER="blueberry/tests/gd/gd_test_runner.py"
-CPP_BUILD_TARGET="bluetooth_stack_with_facade root-canal bluetooth_packets_python3 gd_hci_packets_python3_gen"
-RUST_BUILD_TARGET="bluetooth_with_facades root-canal bluetooth_packets_python3 bt_topshim_facade gd_hci_packets_python3_gen"
+CPP_BUILD_TARGET="bluetooth_stack_with_facade root-canal bluetooth_packets_python3 gd_hci_packets_python3_gen gd_smp_packets_python3_gen"
+RUST_BUILD_TARGET="bluetooth_with_facades root-canal bluetooth_packets_python3 bt_topshim_facade gd_hci_packets_python3_gen gd_smp_packets_python3_gen"
 BUILD_TARGET=$CPP_BUILD_TARGET
 
 CLEAN_VENV=false
diff --git a/system/gd/common/Android.bp b/system/gd/common/Android.bp
index ad6d036..36da276 100644
--- a/system/gd/common/Android.bp
+++ b/system/gd/common/Android.bp
@@ -30,7 +30,6 @@
         "metric_id_manager_unittest.cc",
         "multi_priority_queue_test.cc",
         "numbers_test.cc",
-        "observer_registry_test.cc",
         "strings_test.cc",
         "sync_map_count_test.cc",
     ],
diff --git a/system/gd/common/callback_list.h b/system/gd/common/callback_list.h
deleted file mode 100644
index 441a5fa..0000000
--- a/system/gd/common/callback_list.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2019 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 <utility>
-
-#include "base/callback_list.h"
-#include "os/handler.h"
-
-/* This file contains CallbackList implementation that will execute callback on provided Handler thread
-
-Example usage inside your class:
-
-private:
-  common::CallbackList<void(int)> callbacks_list_;
-public:
-  std::unique_ptr<common::CallbackList<void(int)>::Subscription> RegisterCallback(
-      const base::RepeatingCallback<void(int)>& cb, os::Handler* handler) {
-    return callbacks_list_.Add({cb, handler});
-  }
-
-  void NotifyAllCallbacks(int value) {
-    callbacks_list_.Notify(value);
-  }
-*/
-
-namespace bluetooth {
-namespace common {
-
-namespace {
-template <typename CallbackType>
-struct CallbackWithHandler {
-  CallbackWithHandler(base::RepeatingCallback<CallbackType> callback, os::Handler* handler)
-      : callback(callback), handler(handler) {}
-
-  bool is_null() const {
-    return callback.is_null();
-  }
-
-  void Reset() {
-    callback.Reset();
-  }
-
-  base::RepeatingCallback<CallbackType> callback;
-  os::Handler* handler;
-};
-
-}  // namespace
-
-template <typename Sig>
-class CallbackList;
-template <typename... Args>
-class CallbackList<void(Args...)> : public base::internal::CallbackListBase<CallbackWithHandler<void(Args...)>> {
- public:
-  using CallbackType = CallbackWithHandler<void(Args...)>;
-  CallbackList() = default;
-  CallbackList(const CallbackList&) = delete;
-  CallbackList& operator=(const CallbackList&) = delete;
-
-  template <typename... RunArgs>
-  void Notify(RunArgs&&... args) {
-    auto it = this->GetIterator();
-    CallbackType* cb;
-    while ((cb = it.GetNext()) != nullptr) {
-      cb->handler->Post(base::Bind(cb->callback, args...));
-    }
-  }
-};
-
-}  // namespace common
-}  // namespace bluetooth
diff --git a/system/gd/common/observer_registry.h b/system/gd/common/observer_registry.h
deleted file mode 100644
index 99de57f..0000000
--- a/system/gd/common/observer_registry.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2019 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 <array>
-
-#include "common/bind.h"
-#include "common/callback.h"
-#include "os/log.h"
-
-namespace bluetooth {
-namespace common {
-
-// Tracks an observer registration on client (observer) code. Register() returns a wrapped callback object which can
-// be passed to server's register API. Unregister() invalidates the wrapped callback so all callbacks that are posted
-// to the client handler after the client called Unregister() call and before the server processed the Unregister()
-// call on its handler, are dropped.
-// Note: Register() invalidates the previous registration.
-class SingleObserverRegistry {
- public:
-  template <typename R, typename... T>
-  decltype(auto) Register(Callback<R(T...)> callback) {
-    session_++;
-    return Bind(&SingleObserverRegistry::callback_wrapper<R, T...>, Unretained(this), session_, callback);
-  }
-
-  void Unregister() {
-    session_++;
-  }
-
- private:
-  template <typename R, typename... T>
-  void callback_wrapper(int session, Callback<R(T...)> callback, T... t) {
-    if (session == session_) {
-      callback.Run(std::forward<T>(t)...);
-    }
-  }
-
-  uint8_t session_ = 0;
-};
-
-// Tracks observer registration for multiple event type. Each event type is represented as an integer in [0, Capacity).
-template <int Capacity = 10>
-class MultipleObserverRegistry {
- public:
-  template <typename R, typename... T>
-  decltype(auto) Register(int event_type, Callback<R(T...)> callback) {
-    ASSERT(event_type < Capacity);
-    return registry_[event_type].Register(callback);
-  }
-
-  void Unregister(int event_type) {
-    ASSERT(event_type < Capacity);
-    registry_[event_type].Unregister();
-  }
-
-  std::array<SingleObserverRegistry, Capacity> registry_;
-};
-
-}  // namespace common
-}  // namespace bluetooth
diff --git a/system/gd/common/observer_registry_test.cc b/system/gd/common/observer_registry_test.cc
deleted file mode 100644
index f1233a8..0000000
--- a/system/gd/common/observer_registry_test.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2019 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 "common/observer_registry.h"
-
-#include "common/bind.h"
-#include "gtest/gtest.h"
-
-namespace bluetooth {
-namespace common {
-
-class SingleObserverRegistryTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    registry_ = new SingleObserverRegistry;
-  }
-
-  void TearDown() override {
-    delete registry_;
-  }
-
-  SingleObserverRegistry* registry_;
-};
-
-void Increment(int* count) {
-  (*count)++;
-}
-
-void IncrementBy(int* count, int n) {
-  (*count) += n;
-}
-
-TEST_F(SingleObserverRegistryTest, wrapped_callback) {
-  int count = 0;
-  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
-  wrapped_callback.Run();
-  EXPECT_EQ(count, 1);
-  wrapped_callback.Run();
-  EXPECT_EQ(count, 2);
-  wrapped_callback.Run();
-  EXPECT_EQ(count, 3);
-  registry_->Unregister();
-}
-
-TEST_F(SingleObserverRegistryTest, unregister) {
-  int count = 0;
-  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
-  registry_->Unregister();
-  wrapped_callback.Run();
-  EXPECT_EQ(count, 0);
-}
-
-TEST_F(SingleObserverRegistryTest, second_register) {
-  int count = 0;
-  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
-  registry_->Unregister();
-  auto wrapped_callback2 = registry_->Register(Bind(&Increment, Unretained(&count)));
-  wrapped_callback.Run();
-  EXPECT_EQ(count, 0);
-  wrapped_callback2.Run();
-  EXPECT_EQ(count, 1);
-}
-
-class MultipleObserverRegistryTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    registry_ = new MultipleObserverRegistry<2>;
-  }
-
-  void TearDown() override {
-    delete registry_;
-  }
-
-  MultipleObserverRegistry<2>* registry_;
-};
-
-TEST_F(MultipleObserverRegistryTest, single_wrapped_callback) {
-  int count = 0;
-  auto wrapped_callback = registry_->Register(0, Bind(&Increment, Unretained(&count)));
-  wrapped_callback.Run();
-  EXPECT_EQ(count, 1);
-  wrapped_callback.Run();
-  EXPECT_EQ(count, 2);
-  wrapped_callback.Run();
-  EXPECT_EQ(count, 3);
-  registry_->Unregister(0);
-}
-
-TEST_F(MultipleObserverRegistryTest, multiple_wrapped_callback) {
-  int count = 0;
-  auto wrapped_callback0 = registry_->Register(0, Bind(&Increment, Unretained(&count)));
-  auto wrapped_callback1 = registry_->Register(1, Bind(&IncrementBy, Unretained(&count), 10));
-  wrapped_callback0.Run();
-  EXPECT_EQ(count, 1);
-  wrapped_callback1.Run();
-  EXPECT_EQ(count, 11);
-  registry_->Unregister(0);
-  wrapped_callback0.Run();
-  EXPECT_EQ(count, 11);
-  wrapped_callback1.Run();
-  EXPECT_EQ(count, 21);
-  registry_->Unregister(1);
-  EXPECT_EQ(count, 21);
-}
-
-}  // namespace common
-}  // namespace bluetooth
diff --git a/system/gd/dumpsys_data.fbs b/system/gd/dumpsys_data.fbs
index 326d211..e2e4454 100644
--- a/system/gd/dumpsys_data.fbs
+++ b/system/gd/dumpsys_data.fbs
@@ -9,7 +9,6 @@
 // privacy:"Any"
 
 
-include "btaa/activity_attribution.fbs";
 include "common/init_flags.fbs";
 include "hci/hci_acl_manager.fbs";
 include "hci/hci_controller.fbs";
@@ -31,7 +30,6 @@
     hci_acl_manager_dumpsys_data:bluetooth.hci.AclManagerData (privacy:"Any");
     hci_controller_dumpsys_data:bluetooth.hci.ControllerData (privacy:"Any");
     module_unittest_data:bluetooth.ModuleUnitTestData; // private
-    activity_attribution_dumpsys_data:bluetooth.activity_attribution.ActivityAttributionData (privacy:"Any");
 }
 
 root_type DumpsysData;
diff --git a/system/gd/hal/hci_hal_android_hidl.cc b/system/gd/hal/hci_hal_android_hidl.cc
index e15de74..300f294 100644
--- a/system/gd/hal/hci_hal_android_hidl.cc
+++ b/system/gd/hal/hci_hal_android_hidl.cc
@@ -34,7 +34,6 @@
 #include <mutex>
 #include <vector>
 
-#include "btaa/activity_attribution.h"
 #include "common/init_flags.h"
 #include "common/stop_watch.h"
 #include "common/strings.h"
@@ -81,8 +80,7 @@
 
 class InternalHciCallbacks : public IBluetoothHciCallbacks_1_1 {
  public:
-  InternalHciCallbacks(activity_attribution::ActivityAttribution* btaa_logger_, SnoopLogger* btsnoop_logger)
-      : btaa_logger_(btaa_logger_), btsnoop_logger_(btsnoop_logger) {
+  InternalHciCallbacks(SnoopLogger* btsnoop_logger) : btsnoop_logger_(btsnoop_logger) {
     init_promise_ = new std::promise<void>();
   }
 
@@ -113,10 +111,8 @@
   Return<void> hciEventReceived(const hidl_vec<uint8_t>& event) override {
     common::StopWatch stop_watch(GetTimerText(__func__, event));
     std::vector<uint8_t> received_hci_packet(event.begin(), event.end());
-    btsnoop_logger_->Capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      btaa_logger_->Capture(received_hci_packet, SnoopLogger::PacketType::EVT);
-    }
+    btsnoop_logger_->Capture(
+        received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
     {
       std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
       if (callback_ != nullptr) {
@@ -129,10 +125,8 @@
   Return<void> aclDataReceived(const hidl_vec<uint8_t>& data) override {
     common::StopWatch stop_watch(GetTimerText(__func__, data));
     std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
-    btsnoop_logger_->Capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      btaa_logger_->Capture(received_hci_packet, SnoopLogger::PacketType::ACL);
-    }
+    btsnoop_logger_->Capture(
+        received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
     {
       std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
       if (callback_ != nullptr) {
@@ -145,11 +139,8 @@
   Return<void> scoDataReceived(const hidl_vec<uint8_t>& data) override {
     common::StopWatch stop_watch(GetTimerText(__func__, data));
     std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
-    btsnoop_logger_->Capture(received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::SCO);
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      btaa_logger_->Capture(received_hci_packet, SnoopLogger::PacketType::SCO);
-    }
-
+    btsnoop_logger_->Capture(
+        received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::SCO);
     {
       std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
       if (callback_ != nullptr) {
@@ -177,7 +168,6 @@
   std::mutex incoming_packet_callback_mutex_;
   std::promise<void>* init_promise_ = nullptr;
   HciHalCallbacks* callback_ = nullptr;
-  activity_attribution::ActivityAttribution* btaa_logger_ = nullptr;
   SnoopLogger* btsnoop_logger_ = nullptr;
 };
 
@@ -186,9 +176,7 @@
 
 class AidlHciCallbacks : public ::aidl::android::hardware::bluetooth::BnBluetoothHciCallbacks {
  public:
-  AidlHciCallbacks(
-      activity_attribution::ActivityAttribution* btaa_logger_, SnoopLogger* btsnoop_logger)
-      : btaa_logger_(btaa_logger_), btsnoop_logger_(btsnoop_logger) {
+  AidlHciCallbacks(SnoopLogger* btsnoop_logger) : btsnoop_logger_(btsnoop_logger) {
     init_promise_ = new std::promise<void>();
   }
 
@@ -219,9 +207,6 @@
     std::vector<uint8_t> received_hci_packet(event.begin(), event.end());
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::EVT);
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      btaa_logger_->Capture(received_hci_packet, SnoopLogger::PacketType::EVT);
-    }
     bool sent = false;
     {
       std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
@@ -241,9 +226,6 @@
     std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::ACL);
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      btaa_logger_->Capture(received_hci_packet, SnoopLogger::PacketType::ACL);
-    }
     bool sent = false;
     {
       std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
@@ -263,9 +245,6 @@
     std::vector<uint8_t> received_hci_packet(data.begin(), data.end());
     btsnoop_logger_->Capture(
         received_hci_packet, SnoopLogger::Direction::INCOMING, SnoopLogger::PacketType::SCO);
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      btaa_logger_->Capture(received_hci_packet, SnoopLogger::PacketType::SCO);
-    }
     bool sent = false;
     {
       std::lock_guard<std::mutex> incoming_packet_callback_lock(incoming_packet_callback_mutex_);
@@ -303,7 +282,6 @@
   std::mutex incoming_packet_callback_mutex_;
   std::promise<void>* init_promise_ = nullptr;
   HciHalCallbacks* callback_ = nullptr;
-  activity_attribution::ActivityAttribution* btaa_logger_ = nullptr;
   SnoopLogger* btsnoop_logger_ = nullptr;
 };
 
@@ -330,10 +308,8 @@
   }
 
   void sendHciCommand(HciPacket command) override {
-    btsnoop_logger_->Capture(command, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      btaa_logger_->Capture(command, SnoopLogger::PacketType::CMD);
-    }
+    btsnoop_logger_->Capture(
+        command, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::CMD);
     if (aidl_hci_) {
       aidl_hci_->sendHciCommand(command);
     } else {
@@ -342,10 +318,8 @@
   }
 
   void sendAclData(HciPacket packet) override {
-    btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ACL);
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      btaa_logger_->Capture(packet, SnoopLogger::PacketType::ACL);
-    }
+    btsnoop_logger_->Capture(
+        packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::ACL);
     if (aidl_hci_) {
       aidl_hci_->sendAclData(packet);
     } else {
@@ -354,10 +328,8 @@
   }
 
   void sendScoData(HciPacket packet) override {
-    btsnoop_logger_->Capture(packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::SCO);
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      btaa_logger_->Capture(packet, SnoopLogger::PacketType::SCO);
-    }
+    btsnoop_logger_->Capture(
+        packet, SnoopLogger::Direction::OUTGOING, SnoopLogger::PacketType::SCO);
     if (aidl_hci_) {
       aidl_hci_->sendScoData(packet);
     } else {
@@ -382,9 +354,6 @@
  protected:
   void ListDependencies(ModuleList* list) const {
     list->add<SnoopLogger>();
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      list->add<activity_attribution::ActivityAttribution>();
-    }
   }
 
   void Start() override {
@@ -395,9 +364,6 @@
     ASSERT(bt_hci_1_1_ == nullptr);
     ASSERT(aidl_hci_ == nullptr);
 
-    if (common::init_flags::btaa_hci_is_enabled()) {
-      btaa_logger_ = GetDependency<activity_attribution::ActivityAttribution>();
-    }
     btsnoop_logger_ = GetDependency<SnoopLogger>();
 
     if (AServiceManager_isDeclared(kBluetoothAidlHalServiceName)) {
@@ -430,7 +396,7 @@
       ASSERT_LOG(
           death_link == STATUS_OK, "Unable to set the death recipient for the Bluetooth HAL");
 
-      aidl_callbacks_ = ::ndk::SharedRefBase::make<AidlHciCallbacks>(btaa_logger_, btsnoop_logger_);
+      aidl_callbacks_ = ::ndk::SharedRefBase::make<AidlHciCallbacks>(btsnoop_logger_);
       aidl_hci_->initialize(aidl_callbacks_);
     }
   }
@@ -470,7 +436,7 @@
     ASSERT(bt_hci_ != nullptr);
     auto death_link = bt_hci_->linkToDeath(hci_death_recipient_, 0);
     ASSERT_LOG(death_link.isOk(), "Unable to set the death recipient for the Bluetooth HAL");
-    hidl_callbacks_ = new InternalHciCallbacks(btaa_logger_, btsnoop_logger_);
+    hidl_callbacks_ = new InternalHciCallbacks(btsnoop_logger_);
 
     if (bt_hci_1_1_ != nullptr) {
       bt_hci_1_1_->initialize_1_1(hidl_callbacks_);
@@ -528,7 +494,6 @@
   std::shared_ptr<IBluetoothHci> aidl_hci_;
   std::shared_ptr<AidlHciCallbacks> aidl_callbacks_;
   ::ndk::ScopedAIBinder_DeathRecipient aidl_death_recipient_;
-  activity_attribution::ActivityAttribution* btaa_logger_;
   SnoopLogger* btsnoop_logger_;
 };
 
diff --git a/system/gd/hal/hci_hal_host.cc b/system/gd/hal/hci_hal_host.cc
index 5e7fde7..e707f88 100644
--- a/system/gd/hal/hci_hal_host.cc
+++ b/system/gd/hal/hci_hal_host.cc
@@ -162,6 +162,9 @@
               break;
             }
           }
+
+          // Chipset might be lost. Wait for index added event.
+          LOG_ERROR("HCI interface(%d) not found in the MGMT lndex list", hci_interface);
         } else {
           // Unlikely event (probably developer error or driver shut down).
           LOG_ERROR("Failed to read index list: status(%d)", cc->status);
@@ -289,7 +292,14 @@
     std::lock_guard<std::mutex> lock(api_mutex_);
     ASSERT(sock_fd_ == INVALID_FD);
     sock_fd_ = ConnectToSocket();
-    ASSERT(sock_fd_ != INVALID_FD);
+
+    // We don't want to crash when the chipset is broken.
+    if (sock_fd_ == INVALID_FD) {
+      LOG_ERROR("Failed to connect to HCI socket. Aborting HAL initialization process.");
+      raise(SIGINT);
+      return;
+    }
+
     reactable_ = hci_incoming_thread_.GetReactor()->Register(
         sock_fd_,
         common::Bind(&HciHalHost::incoming_packet_received, common::Unretained(this)),
@@ -370,7 +380,15 @@
 
     ssize_t received_size;
     RUN_NO_INTR(received_size = read(sock_fd_, buf, kBufSize));
-    ASSERT_LOG(received_size != -1, "Can't receive from socket: %s", strerror(errno));
+
+    // we don't want crash when the chipset is broken.
+    if (received_size == -1) {
+      LOG_ERROR("Can't receive from socket: %s", strerror(errno));
+      close(sock_fd_);
+      raise(SIGINT);
+      return;
+    }
+
     if (received_size == 0) {
       LOG_WARN("Can't read H4 header. EOF received");
       // First close sock fd before raising sigint
diff --git a/system/gd/hci/Android.bp b/system/gd/hci/Android.bp
index e1a6f28..530cb7b 100644
--- a/system/gd/hci/Android.bp
+++ b/system/gd/hci/Android.bp
@@ -8,18 +8,8 @@
 }
 
 filegroup {
-    name: "BluetoothHciPacketSources",
-    srcs: [
-        "address.cc",
-        "class_of_device.cc",
-    ],
-    visibility: ["//visibility:public"],
-}
-
-filegroup {
     name: "BluetoothHciSources",
     srcs: [
-        ":BluetoothHciPacketSources",
         "acl_manager.cc",
         "acl_manager/acl_connection.cc",
         "acl_manager/acl_fragmenter.cc",
@@ -103,10 +93,3 @@
         "fuzz/status_vs_complete_commands.cc",
     ],
 }
-
-filegroup {
-    name: "BluetoothHciPackets",
-    srcs: [
-        "hci_packets.pdl",
-    ],
-}
diff --git a/system/gd/hci/acl_manager/le_acl_connection.h b/system/gd/hci/acl_manager/le_acl_connection.h
index 58a8b77..b42da54 100644
--- a/system/gd/hci/acl_manager/le_acl_connection.h
+++ b/system/gd/hci/acl_manager/le_acl_connection.h
@@ -83,8 +83,7 @@
 
   // The peer address and type returned from the Connection Complete Event
   AddressWithType peer_address_with_type_;
-  Address remote_initiator_address_;
-  Address local_initiator_address_;
+
   // 5.2::7.7.65.10 Connection interval used on this connection.
   // Range: 0x0006 to 0x0C80
   // Time = N * 1.25 ms
@@ -108,6 +107,27 @@
   Address local_resolvable_private_address_ = Address::kEmpty;
   Address peer_resolvable_private_address_ = Address::kEmpty;
 
+  virtual AddressWithType GetPeerAddress() const {
+    return peer_address_with_type_;
+  }
+
+  // This function return actual peer address which was used for the connection over the air.
+  virtual AddressWithType GetPeerOtaAddress() const {
+    if (peer_resolvable_private_address_ == Address::kEmpty) {
+      return GetPeerAddress();
+    }
+    return AddressWithType(peer_resolvable_private_address_, AddressType::RANDOM_DEVICE_ADDRESS);
+  }
+
+  // This function return actual local address which was used for the connection over the air.
+  virtual AddressWithType GetLocalOtaAddress() const {
+    if (local_resolvable_private_address_ == Address::kEmpty) {
+      return GetLocalAddress();
+    }
+
+    return AddressWithType(local_resolvable_private_address_, AddressType::RANDOM_DEVICE_ADDRESS);
+  }
+
   virtual void RegisterCallbacks(LeConnectionManagementCallbacks* callbacks, os::Handler* handler);
   virtual void Disconnect(DisconnectReason reason);
 
diff --git a/system/gd/hci/controller.cc b/system/gd/hci/controller.cc
index 8c25637..78fd6fb 100644
--- a/system/gd/hci/controller.cc
+++ b/system/gd/hci/controller.cc
@@ -205,9 +205,6 @@
   }
 
   void Stop() {
-    if (bluetooth::common::init_flags::gd_core_is_enabled()) {
-      hci_->UnregisterEventHandler(EventCode::NUMBER_OF_COMPLETED_PACKETS);
-    }
     hci_ = nullptr;
   }
 
diff --git a/system/gd/hci/facade/le_advertising_manager_facade.cc b/system/gd/hci/facade/le_advertising_manager_facade.cc
index 93e40ae..40cde66 100644
--- a/system/gd/hci/facade/le_advertising_manager_facade.cc
+++ b/system/gd/hci/facade/le_advertising_manager_facade.cc
@@ -25,6 +25,7 @@
 #include "common/bidi_queue.h"
 #include "common/bind.h"
 #include "grpc/grpc_event_queue.h"
+#include "hardware/ble_advertiser.h"
 #include "hci/address.h"
 #include "hci/address_with_type.h"
 #include "hci/le_advertising_manager.h"
@@ -234,6 +235,7 @@
     pending_advertiser_id_ = std::promise<AdvertiserId>();
     auto future = pending_advertiser_id_->get_future();
     le_advertising_manager_->ExtendedCreateAdvertiser(
+        kAdvertiserClientIdJni,
         0,
         config,
         common::Bind(&LeAdvertiser::ScanCallback, common::Unretained(&le_advertiser)),
@@ -266,6 +268,7 @@
     pending_advertiser_id_ = std::promise<AdvertiserId>();
     auto future = pending_advertiser_id_->get_future();
     le_advertising_manager_->ExtendedCreateAdvertiser(
+        kAdvertiserClientIdJni,
         0,
         config,
         common::Bind(&LeAdvertiser::ScanCallback, common::Unretained(&le_advertiser)),
diff --git a/system/gd/hci/hci_layer.cc b/system/gd/hci/hci_layer.cc
index c3312a5..70d3583 100644
--- a/system/gd/hci/hci_layer.cc
+++ b/system/gd/hci/hci_layer.cc
@@ -188,6 +188,7 @@
         OpCodeText(op_code).c_str());
     if (waiting_command_ == OpCode::CONTROLLER_DEBUG_INFO && op_code != OpCode::CONTROLLER_DEBUG_INFO) {
       LOG_ERROR("Discarding event that came after timeout 0x%02hx (%s)", op_code, OpCodeText(op_code).c_str());
+      common::StopWatch::DumpStopWatchLog();
       return;
     }
     ASSERT_LOG(
diff --git a/system/gd/hci/hci_layer_unittest.cc b/system/gd/hci/hci_layer_unittest.cc
index ef1d25e..f141047 100644
--- a/system/gd/hci/hci_layer_unittest.cc
+++ b/system/gd/hci/hci_layer_unittest.cc
@@ -118,6 +118,8 @@
   TestModuleRegistry fake_registry_;
 };
 
+class HciLayerDeathTest : public HciLayerTest {};
+
 TEST_F(HciLayerTest, setup_teardown) {}
 
 TEST_F(HciLayerTest, reset_command_sent_on_start) {
@@ -136,7 +138,7 @@
   ASSERT_TRUE(debug_info_view.IsValid());
 }
 
-TEST_F(HciLayerTest, abort_after_hci_restart_timeout) {
+TEST_F(HciLayerDeathTest, abort_after_hci_restart_timeout) {
   FailIfResetNotSent();
   FakeTimerAdvance(HciLayer::kHciTimeoutMs.count());
 
@@ -154,7 +156,28 @@
       "");
 }
 
-TEST_F(HciLayerTest, abort_on_root_inflammation_event) {
+TEST_F(HciLayerDeathTest, discard_event_after_hci_timeout) {
+  FailIfResetNotSent();
+  FakeTimerAdvance(HciLayer::kHciTimeoutMs.count());
+
+  auto sent_command = hal_->GetSentCommand();
+  ASSERT_TRUE(sent_command.has_value());
+  auto debug_info_view = ControllerDebugInfoView::Create(VendorCommandView::Create(*sent_command));
+  ASSERT_TRUE(debug_info_view.IsValid());
+
+  // This event should be discarded, not cause an abort.
+  hal_->InjectEvent(ResetCompleteBuilder::Create(1, ErrorCode::SUCCESS));
+  sync_handler();
+
+  ASSERT_DEATH(
+      {
+        FakeTimerAdvance(HciLayer::kHciTimeoutRestartMs.count());
+        sync_handler();
+      },
+      "");
+}
+
+TEST_F(HciLayerDeathTest, abort_on_root_inflammation_event) {
   FailIfResetNotSent();
 
   ASSERT_DEATH(
@@ -174,7 +197,7 @@
   sync_handler();
 }
 
-TEST_F(HciLayerTest, abort_if_reset_complete_returns_error) {
+TEST_F(HciLayerDeathTest, abort_if_reset_complete_returns_error) {
   FailIfResetNotSent();
   ASSERT_DEATH(
       {
@@ -213,7 +236,7 @@
       ClockAccuracy::PPM_30));
 }
 
-TEST_F(HciLayerTest, abort_on_second_register_event_handler) {
+TEST_F(HciLayerDeathTest, abort_on_second_register_event_handler) {
   FailIfResetNotSent();
   ASSERT_DEATH(
       {
@@ -226,7 +249,7 @@
       "");
 }
 
-TEST_F(HciLayerTest, abort_on_second_register_le_event_handler) {
+TEST_F(HciLayerDeathTest, abort_on_second_register_le_event_handler) {
   ASSERT_DEATH(
       {
         FailIfResetNotSent();
@@ -378,7 +401,7 @@
 }
 
 TEST_F(
-    HciLayerTest,
+    HciLayerDeathTest,
     command_complete_callback_is_invoked_with_an_opcode_that_does_not_match_command_queue) {
   ASSERT_DEATH(
       {
@@ -392,7 +415,7 @@
 }
 
 TEST_F(
-    HciLayerTest,
+    HciLayerDeathTest,
     command_status_callback_is_invoked_with_an_opcode_that_does_not_match_command_queue) {
   ASSERT_DEATH(
       {
@@ -405,7 +428,7 @@
       "");
 }
 
-TEST_F(HciLayerTest, command_complete_callback_is_invoked_but_command_queue_empty) {
+TEST_F(HciLayerDeathTest, command_complete_callback_is_invoked_but_command_queue_empty) {
   ASSERT_DEATH(
       {
         FailIfResetNotSent();
@@ -416,7 +439,7 @@
       "");
 }
 
-TEST_F(HciLayerTest, command_status_callback_is_invoked_but_command_queue_empty) {
+TEST_F(HciLayerDeathTest, command_status_callback_is_invoked_but_command_queue_empty) {
   ASSERT_DEATH(
       {
         FailIfResetNotSent();
diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc
index 98c5441..bae063c 100644
--- a/system/gd/hci/le_advertising_manager.cc
+++ b/system/gd/hci/le_advertising_manager.cc
@@ -20,6 +20,7 @@
 
 #include "common/init_flags.h"
 #include "common/strings.h"
+#include "hardware/ble_advertiser.h"
 #include "hci/acl_manager.h"
 #include "hci/controller.h"
 #include "hci/hci_layer.h"
@@ -488,11 +489,21 @@
     advertising_sets_[id].status_callback = std::move(status_callback);
     advertising_sets_[id].timeout_callback = std::move(timeout_callback);
 
+    // legacy start_advertising use default jni client id
     create_extended_advertiser_with_id(
-        kIdLocal, id, config, scan_callback, set_terminated_callback, duration, 0, handler);
+        kAdvertiserClientIdJni,
+        kIdLocal,
+        id,
+        config,
+        scan_callback,
+        set_terminated_callback,
+        duration,
+        0,
+        handler);
   }
 
   void create_extended_advertiser(
+      uint8_t client_id,
       int reg_id,
       const AdvertisingConfig config,
       common::Callback<void(Address, AddressType)> scan_callback,
@@ -507,6 +518,7 @@
       return;
     }
     create_extended_advertiser_with_id(
+        client_id,
         reg_id,
         id,
         config,
@@ -518,6 +530,7 @@
   }
 
   void create_extended_advertiser_with_id(
+      uint8_t client_id,
       int reg_id,
       AdvertiserId id,
       const AdvertisingConfig config,
@@ -573,9 +586,19 @@
               id,
               advertising_sets_[id].current_address));
 
-      // but we only rotate if the AdvertiserAddressType is non-public (since static random
-      // addresses don't rotate)
-      if (advertising_sets_[id].address_type != AdvertiserAddressType::PUBLIC) {
+      bool leaudio_requested_nrpa = false;
+      if (client_id == kAdvertiserClientIdLeAudio &&
+          advertising_sets_[id].address_type == AdvertiserAddressType::NONRESOLVABLE_RANDOM) {
+        LOG_INFO(
+            "Advertiser started by le audio client with address type: %d",
+            advertising_sets_[id].address_type);
+        leaudio_requested_nrpa = true;
+      }
+
+      // but we only rotate if the AdvertiserAddressType is non-public
+      // or non-rpa requested by leaudio(since static random addresses don't rotate)
+      if (advertising_sets_[id].address_type != AdvertiserAddressType::PUBLIC &&
+          !leaudio_requested_nrpa) {
         // start timer for random address
         advertising_sets_[id].address_rotation_alarm = std::make_unique<os::Alarm>(module_handler_);
         advertising_sets_[id].address_rotation_alarm->Schedule(
@@ -1647,6 +1670,7 @@
 }
 
 void LeAdvertisingManager::ExtendedCreateAdvertiser(
+    uint8_t client_id,
     int reg_id,
     const AdvertisingConfig config,
     common::Callback<void(Address, AddressType)> scan_callback,
@@ -1715,6 +1739,7 @@
   CallOn(
       pimpl_.get(),
       &impl::create_extended_advertiser,
+      client_id,
       reg_id,
       config,
       scan_callback,
diff --git a/system/gd/hci/le_advertising_manager.h b/system/gd/hci/le_advertising_manager.h
index 326f4a4..57bf99e 100644
--- a/system/gd/hci/le_advertising_manager.h
+++ b/system/gd/hci/le_advertising_manager.h
@@ -114,6 +114,7 @@
   int GetAdvertiserRegId(AdvertiserId advertiser_id);
 
   void ExtendedCreateAdvertiser(
+      uint8_t client_id,
       int reg_id,
       const AdvertisingConfig config,
       common::Callback<void(Address, AddressType)> scan_callback,
diff --git a/system/gd/hci/le_advertising_manager_mock.h b/system/gd/hci/le_advertising_manager_mock.h
index a6198ca..d56e91d 100644
--- a/system/gd/hci/le_advertising_manager_mock.h
+++ b/system/gd/hci/le_advertising_manager_mock.h
@@ -36,7 +36,8 @@
   MOCK_METHOD(
       AdvertiserId,
       ExtendedCreateAdvertiser,
-      (int regId,
+      (uint8_t client_id,
+       int regId,
        const AdvertisingConfig,
        const common::Callback<void(Address, AddressType)>&,
        const common::Callback<void(ErrorCode, uint8_t, uint8_t)>&,
diff --git a/system/gd/hci/le_advertising_manager_test.cc b/system/gd/hci/le_advertising_manager_test.cc
index 4a7c420..9387ea0 100644
--- a/system/gd/hci/le_advertising_manager_test.cc
+++ b/system/gd/hci/le_advertising_manager_test.cc
@@ -25,6 +25,7 @@
 #include <map>
 
 #include "common/bind.h"
+#include "hardware/ble_advertiser.h"
 #include "hci/acl_manager.h"
 #include "hci/address.h"
 #include "hci/controller.h"
@@ -270,7 +271,14 @@
         .WillOnce(SaveArg<1>(&advertiser_id_));
 
     le_advertising_manager_->ExtendedCreateAdvertiser(
-        0x00, advertising_config, scan_callback, set_terminated_callback, 0, 0, client_handler_);
+        kAdvertiserClientIdJni,
+        0x00,
+        advertising_config,
+        scan_callback,
+        set_terminated_callback,
+        0,
+        0,
+        client_handler_);
 
     std::vector<OpCode> adv_opcodes = {
         OpCode::LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER,
@@ -334,7 +342,14 @@
         .WillOnce(SaveArg<1>(&advertiser_id_));
 
     le_advertising_manager_->ExtendedCreateAdvertiser(
-        0x00, advertising_config, scan_callback, set_terminated_callback, 0, 0, client_handler_);
+        kAdvertiserClientIdJni,
+        0x00,
+        advertising_config,
+        scan_callback,
+        set_terminated_callback,
+        0,
+        0,
+        client_handler_);
 
     std::vector<SubOcf> sub_ocf = {
         SubOcf::SET_PARAM,
@@ -385,7 +400,14 @@
         .WillOnce(SaveArg<1>(&advertiser_id_));
 
     le_advertising_manager_->ExtendedCreateAdvertiser(
-        0x00, advertising_config, scan_callback, set_terminated_callback, 0, 0, client_handler_);
+        kAdvertiserClientIdJni,
+        0x00,
+        advertising_config,
+        scan_callback,
+        set_terminated_callback,
+        0,
+        0,
+        client_handler_);
 
     std::vector<SubOcf> sub_ocf = {
         SubOcf::SET_PARAM,
@@ -445,7 +467,14 @@
         .WillOnce(SaveArg<1>(&advertiser_id_));
 
     le_advertising_manager_->ExtendedCreateAdvertiser(
-        0x00, advertising_config, scan_callback, set_terminated_callback, 0, 0, client_handler_);
+        kAdvertiserClientIdJni,
+        0x00,
+        advertising_config,
+        scan_callback,
+        set_terminated_callback,
+        0,
+        0,
+        client_handler_);
 
     std::vector<OpCode> adv_opcodes = {
         OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS,
@@ -495,7 +524,14 @@
   advertising_config.channel_map = 1;
 
   le_advertising_manager_->ExtendedCreateAdvertiser(
-      0x00, advertising_config, scan_callback, set_terminated_callback, 0, 0, client_handler_);
+      kAdvertiserClientIdJni,
+      0x00,
+      advertising_config,
+      scan_callback,
+      set_terminated_callback,
+      0,
+      0,
+      client_handler_);
   std::vector<OpCode> adv_opcodes = {
       OpCode::LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER,
       OpCode::LE_SET_ADVERTISING_PARAMETERS,
@@ -552,7 +588,14 @@
       .WillOnce(SaveArg<1>(&id));
 
   le_advertising_manager_->ExtendedCreateAdvertiser(
-      0x00, advertising_config, scan_callback, set_terminated_callback, 0, 0, client_handler_);
+      kAdvertiserClientIdJni,
+      0x00,
+      advertising_config,
+      scan_callback,
+      set_terminated_callback,
+      0,
+      0,
+      client_handler_);
 
   std::vector<SubOcf> sub_ocf = {
       SubOcf::SET_PARAM,
@@ -589,7 +632,14 @@
       .WillOnce(SaveArg<1>(&id));
 
   le_advertising_manager_->ExtendedCreateAdvertiser(
-      0x00, advertising_config, scan_callback, set_terminated_callback, 0, 0, client_handler_);
+      kAdvertiserClientIdJni,
+      0x00,
+      advertising_config,
+      scan_callback,
+      set_terminated_callback,
+      0,
+      0,
+      client_handler_);
   std::vector<SubOcf> sub_ocf = {
       SubOcf::SET_PARAM,
       SubOcf::SET_SCAN_RESP,
@@ -635,7 +685,14 @@
       .WillOnce(SaveArg<1>(&id));
 
   le_advertising_manager_->ExtendedCreateAdvertiser(
-      0x00, advertising_config, scan_callback, set_terminated_callback, 0, 0, client_handler_);
+      kAdvertiserClientIdJni,
+      0x00,
+      advertising_config,
+      scan_callback,
+      set_terminated_callback,
+      0,
+      0,
+      client_handler_);
 
   std::vector<OpCode> adv_opcodes = {
       OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS,
@@ -692,7 +749,14 @@
       .WillOnce(SaveArg<1>(&id));
 
   le_advertising_manager_->ExtendedCreateAdvertiser(
-      0x00, advertising_config, scan_callback, set_terminated_callback, 0, 0, client_handler_);
+      kAdvertiserClientIdJni,
+      0x00,
+      advertising_config,
+      scan_callback,
+      set_terminated_callback,
+      0,
+      0,
+      client_handler_);
 
   std::vector<OpCode> adv_opcodes = {
       OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS,
@@ -1332,6 +1396,7 @@
 
   // act: start advertising set with RPA
   le_advertising_manager_->ExtendedCreateAdvertiser(
+      kAdvertiserClientIdJni,
       0x00,
       AdvertisingConfig{
           .requested_advertiser_address_type = AdvertiserAddressType::RESOLVABLE_RANDOM,
@@ -1359,6 +1424,7 @@
 
   // start advertising set with NRPA
   le_advertising_manager_->ExtendedCreateAdvertiser(
+      kAdvertiserClientIdJni,
       0x00,
       AdvertisingConfig{
           .requested_advertiser_address_type = AdvertiserAddressType::NONRESOLVABLE_RANDOM,
@@ -1395,6 +1461,7 @@
 
   // act: start advertising set with RPA
   le_advertising_manager_->ExtendedCreateAdvertiser(
+      kAdvertiserClientIdJni,
       0x00,
       AdvertisingConfig{
           .requested_advertiser_address_type = AdvertiserAddressType::RESOLVABLE_RANDOM,
diff --git a/system/gd/iso/internal/iso_manager_impl.cc b/system/gd/iso/internal/iso_manager_impl.cc
index f4eef2d..d1ead43 100644
--- a/system/gd/iso/internal/iso_manager_impl.cc
+++ b/system/gd/iso/internal/iso_manager_impl.cc
@@ -131,9 +131,9 @@
     std::vector<uint16_t> handles;
     while (cis_it != cis_configs.end()) {
       iso_connections_.push_back({
+          .connection_handle = *handle_it,
           .cig_id = cig_id,
           .cis_id = cis_it->cis_id_,
-          .connection_handle = *handle_it,
       });
 
       handles.push_back(*handle_it);
@@ -204,9 +204,9 @@
     std::vector<uint16_t> handles;
     while (cis_it != cis_configs.end()) {
       iso_connections_.push_back({
+          .connection_handle = *handle_it,
           .cig_id = cig_id,
           .cis_id = cis_it->cis_id_,
-          .connection_handle = *handle_it,
       });
 
       handles.push_back(*handle_it);
diff --git a/system/gd/l2cap/fuzz/Android.bp b/system/gd/l2cap/fuzz/Android.bp
index 6127a98..9912147 100644
--- a/system/gd/l2cap/fuzz/Android.bp
+++ b/system/gd/l2cap/fuzz/Android.bp
@@ -28,7 +28,6 @@
         "packages/modules/Bluetooth/system/stack/include",
         "packages/modules/Bluetooth/system/stack/l2cap",
         "packages/modules/Bluetooth/system/udrv/include",
-        "packages/modules/Bluetooth/system/vnd/include",
         "system/security/keystore/include",
     ],
     srcs: [
diff --git a/system/gd/l2cap/le/internal/signalling_manager.cc b/system/gd/l2cap/le/internal/signalling_manager.cc
index 084f900..65e7925 100644
--- a/system/gd/l2cap/le/internal/signalling_manager.cc
+++ b/system/gd/l2cap/le/internal/signalling_manager.cc
@@ -200,10 +200,10 @@
 
   PendingConnection pending{
       .remote_cid = remote_cid,
-      .incoming_signal_id = signal_id,
-      .initial_credits = initial_credits,
-      .max_pdu_size = mps,
       .mtu = mtu,
+      .max_pdu_size = mps,
+      .initial_credits = initial_credits,
+      .incoming_signal_id = signal_id,
   };
   dynamic_service_manager_->GetSecurityEnforcementInterface()->Enforce(
       link_->GetDevice(),
diff --git a/system/gd/packet/parser/packet_def.cc b/system/gd/packet/parser/packet_def.cc
index 9e35e3b..c0f62f1 100644
--- a/system/gd/packet/parser/packet_def.cc
+++ b/system/gd/packet/parser/packet_def.cc
@@ -592,9 +592,7 @@
   s << ".def(py::init([](";
   auto params = GetParamList();
   std::vector<std::string> constructor_args;
-  int i = 1;
   for (const auto& param : params) {
-    i++;
     std::stringstream ss;
     auto param_type = param->GetBuilderParameterType();
     if (param_type.empty()) {
diff --git a/system/gd/packet/python3_module.cc b/system/gd/packet/python3_module.cc
index 743d754..3059114 100644
--- a/system/gd/packet/python3_module.cc
+++ b/system/gd/packet/python3_module.cc
@@ -19,8 +19,6 @@
 #include <cstring>
 #include <memory>
 
-#include "hci/address.h"
-#include "hci/class_of_device.h"
 #include "packet/base_packet_builder.h"
 #include "packet/bit_inserter.h"
 #include "packet/checksum_type_checker.h"
@@ -38,14 +36,9 @@
 namespace l2cap {
 void define_l2cap_packets_submodule(py::module&);
 }
-namespace security {
-void define_smp_packets_submodule(py::module&);
-}
 
 namespace packet {
 
-using ::bluetooth::hci::Address;
-using ::bluetooth::hci::ClassOfDevice;
 using ::bluetooth::packet::BasePacketBuilder;
 using ::bluetooth::packet::BaseStruct;
 using ::bluetooth::packet::BitInserter;
@@ -104,8 +97,6 @@
 
   py::module l2cap_m = m.def_submodule("l2cap_packets", "A submodule of l2cap_packets");
   bluetooth::l2cap::define_l2cap_packets_submodule(l2cap_m);
-  py::module security_m = m.def_submodule("security_packets", "A submodule of security_packets");
-  bluetooth::security::define_smp_packets_submodule(security_m);
 }
 
 }  // namespace packet
diff --git a/system/gd/rust/common/src/init_flags.rs b/system/gd/rust/common/src/init_flags.rs
index 5d6ea21..1cdb295 100644
--- a/system/gd/rust/common/src/init_flags.rs
+++ b/system/gd/rust/common/src/init_flags.rs
@@ -362,8 +362,6 @@
         asha_phy_update_retry_limit: i32 = 5,
         always_send_services_if_gatt_disc_done = true,
         always_use_private_gatt_for_debugging,
-        asynchronously_start_l2cap_coc = true,
-        btaa_hci = true,
         bta_dm_clear_conn_id_on_client_close = true,
         btm_dm_flush_discovery_queue_on_search_cancel,
         bta_dm_stop_discovery_on_search_cancel,
@@ -372,12 +370,9 @@
         delay_hidh_cleanup_until_hidh_ready_start = true,
         device_iot_config_logging,
         dynamic_avrcp_version_enhancement = true,
-        finite_att_timeout = true,
         gatt_robust_caching_client = true,
         gatt_robust_caching_server,
-        gd_core,
         gd_l2cap,
-        gd_link_policy,
         hci_adapter: i32,
         hfp_dynamic_version = true,
         irk_rotation,
@@ -387,10 +382,9 @@
         pbap_pse_dynamic_version_upgrade = false,
         periodic_advertising_adi = true,
         private_gatt = true,
-        queue_l2cap_coc_while_encrypting = true,
         redact_log = true,
         rust_event_loop = true,
-        sco_codec_select_lc3,
+        sco_codec_select_lc3 = true,
         sco_codec_timeout_clear,
         sdp_serialization = true,
         sdp_skip_rnr_if_known = true,
@@ -470,10 +464,10 @@
     fn simple_flag() {
         let _guard = ASYNC_LOCK.lock().unwrap();
         test_load(vec![
-            "INIT_btaa_hci=false", //override a default flag
+            "INIT_private_gatt=false", //override a default flag
             "INIT_gatt_robust_caching_server=true",
         ]);
-        assert!(!btaa_hci_is_enabled());
+        assert!(!private_gatt_is_enabled());
         assert!(gatt_robust_caching_server_is_enabled());
     }
     #[test]
@@ -482,10 +476,10 @@
         test_load(vec![
             "foo=bar=?",                                // vec length
             "foo=bar",                                  // flag not save
-            "INIT_btaa_hci=not_false",                  // parse error but has default value
+            "INIT_private_gatt=not_false",              // parse error but has default value
             "INIT_gatt_robust_caching_server=not_true", // parse error
         ]);
-        assert!(btaa_hci_is_enabled());
+        assert!(private_gatt_is_enabled());
         assert!(!gatt_robust_caching_server_is_enabled());
     }
     #[test]
@@ -531,8 +525,8 @@
     #[test]
     fn test_runtime_update() {
         let _guard = ASYNC_LOCK.lock().unwrap();
-        test_load(vec!["INIT_btaa_hci=true", "INIT_default_log_level_str=LOG_WARN"]);
-        assert!(btaa_hci_is_enabled());
+        test_load(vec!["INIT_private_gatt=true", "INIT_default_log_level_str=LOG_WARN"]);
+        assert!(private_gatt_is_enabled());
         assert!(get_default_log_level() == LOG_TAG_WARN);
 
         update_default_log_level(LOG_TAG_DEBUG);
diff --git a/system/gd/rust/linux/client/src/command_handler.rs b/system/gd/rust/linux/client/src/command_handler.rs
index ccf3464..17a2900 100644
--- a/system/gd/rust/linux/client/src/command_handler.rs
+++ b/system/gd/rust/linux/client/src/command_handler.rs
@@ -251,7 +251,7 @@
                 String::from(
                     "socket connect <address> <l2cap|rfcomm> <psm|uuid> <auth-required> <Bredr|LE>",
                 ),
-                String::from("socket disconnect <socket_id>"),
+                String::from("socket close <socket_id>"),
                 String::from("socket set-on-connect-schedule <send|resend|dump>"),
             ],
             description: String::from("Socket manager utilities."),
@@ -681,7 +681,7 @@
                     name: String::from("Classic Device"),
                 };
 
-                self.lock_context().adapter_dbus.as_ref().unwrap().remove_bond(device);
+                self.lock_context().adapter_dbus.as_mut().unwrap().remove_bond(device);
             }
             "cancel" => {
                 let device = BluetoothDevice {
@@ -689,7 +689,7 @@
                     name: String::from("Classic Device"),
                 };
 
-                self.lock_context().adapter_dbus.as_ref().unwrap().cancel_bond_process(device);
+                self.lock_context().adapter_dbus.as_mut().unwrap().cancel_bond_process(device);
             }
             other => {
                 println!("Invalid argument '{}'", other);
@@ -1626,6 +1626,26 @@
                     status, id, addr, sock_type, psm_or_uuid);
                 }
             }
+            "close" => {
+                let sockid = String::from(get_arg(args, 1)?)
+                    .parse::<u64>()
+                    .or(Err("Failed parsing socket ID"))?;
+                let status = self
+                    .context
+                    .lock()
+                    .unwrap()
+                    .socket_manager_dbus
+                    .as_mut()
+                    .unwrap()
+                    .close(callback_id, sockid);
+                if status != BtStatus::Success {
+                    return Err(format!(
+                        "Failed to close the listening socket, status = {:?}",
+                        status,
+                    )
+                    .into());
+                }
+            }
 
             _ => return Err(CommandError::InvalidArgs),
         };
diff --git a/system/gd/rust/linux/client/src/dbus_iface.rs b/system/gd/rust/linux/client/src/dbus_iface.rs
index 98e2e0c..af201ca 100644
--- a/system/gd/rust/linux/client/src/dbus_iface.rs
+++ b/system/gd/rust/linux/client/src/dbus_iface.rs
@@ -831,12 +831,12 @@
     }
 
     #[dbus_method("CancelBondProcess")]
-    fn cancel_bond_process(&self, device: BluetoothDevice) -> bool {
+    fn cancel_bond_process(&mut self, device: BluetoothDevice) -> bool {
         dbus_generated!()
     }
 
     #[dbus_method("RemoveBond")]
-    fn remove_bond(&self, device: BluetoothDevice) -> bool {
+    fn remove_bond(&mut self, device: BluetoothDevice) -> bool {
         dbus_generated!()
     }
 
diff --git a/system/gd/rust/linux/mgmt/src/bluetooth_experimental_dbus.rs b/system/gd/rust/linux/mgmt/src/bluetooth_experimental_dbus.rs
index b725b50..842b7ae 100644
--- a/system/gd/rust/linux/mgmt/src/bluetooth_experimental_dbus.rs
+++ b/system/gd/rust/linux/mgmt/src/bluetooth_experimental_dbus.rs
@@ -17,7 +17,7 @@
 )]
 impl IBluetoothExperimental for BluetoothExperimentalDBus {
     #[dbus_method("SetLLPrivacy")]
-    fn set_ll_privacy(&mut self, enabled: bool) {
+    fn set_ll_privacy(&mut self, enabled: bool) -> bool {
         dbus_generated!()
     }
 
diff --git a/system/gd/rust/linux/mgmt/src/bluetooth_manager.rs b/system/gd/rust/linux/mgmt/src/bluetooth_manager.rs
index 535423a..1941766 100644
--- a/system/gd/rust/linux/mgmt/src/bluetooth_manager.rs
+++ b/system/gd/rust/linux/mgmt/src/bluetooth_manager.rs
@@ -41,9 +41,9 @@
 
     pub(crate) fn callback_hci_enabled_change(&mut self, hci_device: i32, enabled: bool) {
         if enabled {
-            info!("Started {}", hci_device);
+            warn!("Started {}", hci_device);
         } else {
-            info!("Stopped {}", hci_device);
+            warn!("Stopped {}", hci_device);
         }
 
         for (_, callback) in &mut self.callbacks {
@@ -60,11 +60,19 @@
     pub(crate) fn callback_disconnected(&mut self, id: u32) {
         self.callbacks.remove(&id);
     }
+
+    pub(crate) fn restart_available_adapters(&mut self) {
+        self.get_available_adapters()
+            .into_iter()
+            .filter(|adapter| adapter.enabled)
+            .map(|adapter| VirtualHciIndex(adapter.hci_interface))
+            .for_each(|virt_hci| self.proxy.restart_bluetooth(virt_hci));
+    }
 }
 
 impl IBluetoothManager for BluetoothManager {
     fn start(&mut self, hci_interface: i32) {
-        info!("Starting {}", hci_interface);
+        warn!("Starting {}", hci_interface);
 
         if !config_util::modify_hci_n_enabled(hci_interface, true) {
             error!("Config is not successfully modified");
@@ -76,7 +84,13 @@
         self.proxy.modify_state(virt_hci, move |a: &mut AdapterState| a.config_enabled = true);
 
         // Ignore the request if adapter is already enabled or not present.
-        if self.is_adapter_enabled(virt_hci) || !self.is_adapter_present(virt_hci) {
+        if self.is_adapter_enabled(virt_hci) {
+            warn!("Adapter {} is already enabled.", hci_interface);
+            return;
+        }
+
+        if !self.is_adapter_present(virt_hci) {
+            warn!("Adapter {} is not present.", hci_interface);
             return;
         }
 
@@ -84,7 +98,7 @@
     }
 
     fn stop(&mut self, hci_interface: i32) {
-        info!("Stopping {}", hci_interface);
+        warn!("Stopping {}", hci_interface);
         if !config_util::modify_hci_n_enabled(hci_interface, false) {
             error!("Config is not successfully modified");
         }
@@ -96,6 +110,7 @@
 
         // Ignore the request if adapter is already disabled.
         if !self.is_adapter_enabled(virt_hci) {
+            warn!("Adapter {} is already stopped", hci_interface);
             return;
         }
 
@@ -172,20 +187,25 @@
 
 /// Implementation of IBluetoothExperimental
 impl IBluetoothExperimental for BluetoothManager {
-    fn set_ll_privacy(&mut self, enabled: bool) {
+    fn set_ll_privacy(&mut self, enabled: bool) -> bool {
         let current_status = match config_util::read_floss_ll_privacy_enabled() {
             Ok(true) => true,
             _ => false,
         };
 
         if current_status == enabled {
-            return;
+            return true;
         }
 
+        info!("Set floss ll privacy to {}", enabled);
         if let Err(e) = config_util::write_floss_ll_privacy_enabled(enabled) {
             error!("Failed to write ll privacy status: {}", e);
-            return;
+            return false;
         }
+
+        self.restart_available_adapters();
+
+        return true;
     }
 
     fn set_devcoredump(&mut self, enabled: bool) -> bool {
diff --git a/system/gd/rust/linux/mgmt/src/iface_bluetooth_experimental.rs b/system/gd/rust/linux/mgmt/src/iface_bluetooth_experimental.rs
index 5af8d5f..4a38e9e 100644
--- a/system/gd/rust/linux/mgmt/src/iface_bluetooth_experimental.rs
+++ b/system/gd/rust/linux/mgmt/src/iface_bluetooth_experimental.rs
@@ -1,7 +1,7 @@
 /// Bluetooth experimental feature API
 pub trait IBluetoothExperimental {
     /// Set LL privacy status
-    fn set_ll_privacy(&mut self, enabled: bool);
+    fn set_ll_privacy(&mut self, enabled: bool) -> bool;
 
     /// Set devcoredump status
     fn set_devcoredump(&mut self, enabled: bool) -> bool;
diff --git a/system/gd/rust/linux/mgmt/src/state_machine.rs b/system/gd/rust/linux/mgmt/src/state_machine.rs
index d136575..569ad7c 100644
--- a/system/gd/rust/linux/mgmt/src/state_machine.rs
+++ b/system/gd/rust/linux/mgmt/src/state_machine.rs
@@ -38,6 +38,7 @@
     TurningOn = 1,  // We are not notified that the Bluetooth is running
     On = 2,         // Bluetooth is running
     TurningOff = 3, // We are not notified that the Bluetooth is stopped
+    Restarting = 4, // Pending restart and not notified that the Bluetooth is stopped
 }
 
 /// Check whether adapter is enabled by checking internal state.
@@ -90,6 +91,7 @@
 pub enum AdapterStateActions {
     StartBluetooth(VirtualHciIndex),
     StopBluetooth(VirtualHciIndex),
+    RestartBluetooth(VirtualHciIndex),
     BluetoothStarted(i32, RealHciIndex), // PID and HCI
     BluetoothStopped(RealHciIndex),
     HciDevicePresence(DevPath, RealHciIndex, bool),
@@ -186,6 +188,15 @@
         });
     }
 
+    pub fn restart_bluetooth(&self, hci: VirtualHciIndex) {
+        let tx = self.tx.clone();
+        tokio::spawn(async move {
+            let _ = tx
+                .send(Message::AdapterStateChange(AdapterStateActions::RestartBluetooth(hci)))
+                .await;
+        });
+    }
+
     /// Read state for an hci device.
     pub fn get_state<T, F>(&self, hci: VirtualHciIndex, call: F) -> Option<T>
     where
@@ -623,7 +634,7 @@
         let m = context.rx.recv().await;
 
         if m.is_none() {
-            info!("Exiting manager mainloop");
+            warn!("Exiting manager mainloop");
             break;
         }
 
@@ -631,13 +642,13 @@
 
         match m.unwrap() {
             // Adapter action has changed
-            Message::AdapterStateChange(action) => {
+            Message::AdapterStateChange(adapter_action) => {
                 // Grab previous state from lock and release
                 let hci: VirtualHciIndex;
                 let next_state;
                 let prev_state;
 
-                match &action {
+                match &adapter_action {
                     AdapterStateActions::StartBluetooth(i) => {
                         hci = *i;
                         prev_state = context.state_machine.get_process_state(hci);
@@ -654,6 +665,14 @@
                         let action = context.state_machine.action_stop_bluetooth(hci);
                         cmd_timeout.lock().unwrap().handle_timeout_action(hci, action);
                     }
+                    AdapterStateActions::RestartBluetooth(i) => {
+                        hci = *i;
+                        prev_state = context.state_machine.get_process_state(hci);
+                        next_state = ProcessState::Restarting;
+
+                        let action = context.state_machine.action_restart_bluetooth(hci);
+                        cmd_timeout.lock().unwrap().handle_timeout_action(hci, action);
+                    }
                     AdapterStateActions::BluetoothStarted(pid, real_hci) => {
                         hci = match context.state_machine.get_virtual_id_by_real_id(*real_hci) {
                             Some(v) => v,
@@ -735,9 +754,10 @@
                     }
                 };
 
-                debug!(
-                    "[hci{}]: Took action {:?} with prev_state({:?}) and next_state({:?})",
-                    hci, action, prev_state, next_state
+                // All actions and the resulting state changes should be logged for debugging.
+                info!(
+                    "[hci{}]: Action={:?}, Previous State({:?}), Next State({:?})",
+                    hci, adapter_action, prev_state, next_state
                 );
 
                 // Only emit enabled event for certain transitions
@@ -1243,8 +1263,9 @@
             .next()
     }
 
-    /// Set the desired default adapter. Returns true if the default adapter was changed as result
-    /// (meaning the newly desired adapter is either present or enabled).
+    /// Set the desired default adapter. Returns a NewDefaultAdapter action if the default
+    /// adapter was changed as a result (meaning the newly desired adapter is either present or
+    /// enabled).
     pub fn set_desired_default_adapter(&mut self, adapter: VirtualHciIndex) -> AdapterChangeAction {
         self.desired_adapter = adapter;
 
@@ -1261,7 +1282,7 @@
         return AdapterChangeAction::DoNothing;
     }
 
-    /// Returns true if we are starting bluetooth process.
+    /// Returns an action to reset timer if we are starting bluetooth process.
     pub fn action_start_bluetooth(&mut self, hci: VirtualHciIndex) -> CommandTimeoutAction {
         let state = self.get_process_state(hci);
         let present = self.get_state(hci, move |a: &AdapterState| Some(a.present)).unwrap_or(false);
@@ -1284,7 +1305,7 @@
         }
     }
 
-    /// Returns true if we are stopping bluetooth process.
+    /// Returns an action to reset or cancel timer if we are stopping bluetooth process.
     pub fn action_stop_bluetooth(&mut self, hci: VirtualHciIndex) -> CommandTimeoutAction {
         if !self.is_known(hci) {
             warn!("Attempting to stop unknown hci{}", hci.to_i32());
@@ -1310,7 +1331,34 @@
         }
     }
 
-    /// Handles a bluetooth started event. Always returns true even with unknown interfaces.
+    /// Returns an action to reset timer if we are restarting bluetooth process
+    pub fn action_restart_bluetooth(&mut self, hci: VirtualHciIndex) -> CommandTimeoutAction {
+        if !self.is_known(hci) {
+            warn!("Attempting to restart unknown hci{}", hci);
+            return CommandTimeoutAction::DoNothing;
+        }
+
+        let state = self.get_process_state(hci);
+        let present = self.get_state(hci, move |a: &AdapterState| Some(a.present)).unwrap_or(false);
+        let floss_enabled = self.get_floss_enabled();
+
+        match state {
+            ProcessState::On if present && floss_enabled => {
+                self.modify_state(hci, |s: &mut AdapterState| s.state = ProcessState::Restarting);
+                self.process_manager
+                    .stop(hci.to_string(), self.get_real_hci_by_virtual_id(hci).to_string());
+                CommandTimeoutAction::ResetTimer
+            }
+            ProcessState::TurningOn => {
+                debug!("hci{} is already starting.", hci);
+                CommandTimeoutAction::DoNothing
+            }
+            _ => CommandTimeoutAction::DoNothing,
+        }
+    }
+
+    /// Handles a bluetooth started event. Always return the acction to cancel timer even with
+    /// unknown interfaces.
     pub fn action_on_bluetooth_started(
         &mut self,
         pid: i32,
@@ -1330,8 +1378,9 @@
         CommandTimeoutAction::CancelTimer
     }
 
-    /// Returns true if the event is expected.
-    /// If unexpected, Bluetooth probably crashed, returning false and starting the timer for restart timeout.
+    /// Returns an action to cancel timer if the event is expected.
+    /// If unexpected, Bluetooth probably crashed, returns an action to reset the timer to restart
+    /// timeout.
     pub fn action_on_bluetooth_stopped(&mut self, hci: VirtualHciIndex) -> CommandTimeoutAction {
         let state = self.get_process_state(hci);
         let (present, config_enabled) = self
@@ -1345,6 +1394,13 @@
                 self.modify_state(hci, |s: &mut AdapterState| s.state = ProcessState::Off);
                 CommandTimeoutAction::CancelTimer
             }
+            ProcessState::Restarting => {
+                debug!("hci{} restarting", hci.to_i32());
+                self.modify_state(hci, |s: &mut AdapterState| s.state = ProcessState::TurningOn);
+                self.process_manager
+                    .start(hci.to_string(), self.get_real_hci_by_virtual_id(hci).to_string());
+                CommandTimeoutAction::ResetTimer
+            }
             // Running bluetooth stopped unexpectedly.
             ProcessState::On if floss_enabled && config_enabled => {
                 let restart_count =
@@ -1412,7 +1468,7 @@
             // If Floss is not enabled, just send |Stop| to process manager and end the state
             // machine actions.
             ProcessState::TurningOn if !floss_enabled => {
-                info!("Timed out turning on but floss is disabled: {}", hci);
+                warn!("Timed out turning on but floss is disabled: {}", hci);
                 self.modify_state(hci, |s: &mut AdapterState| s.state = ProcessState::Off);
                 self.process_manager
                     .stop(hci.to_string(), self.get_real_hci_by_virtual_id(hci).to_string());
diff --git a/system/gd/rust/linux/service/build.rs b/system/gd/rust/linux/service/build.rs
index d505501..959da60 100644
--- a/system/gd/rust/linux/service/build.rs
+++ b/system/gd/rust/linux/service/build.rs
@@ -25,6 +25,7 @@
     Config::new().probe("libchrome").unwrap();
     Config::new().probe("libmodp_b64").unwrap();
     Config::new().probe("tinyxml2").unwrap();
+    Config::new().probe("lc3").unwrap();
 
     // Include ChromeOS-specific dependencies.
     if option_env!("TARGET_OS_VARIANT").unwrap_or("None").to_string() == "chromeos" {
diff --git a/system/gd/rust/linux/service/src/iface_battery_provider_manager.rs b/system/gd/rust/linux/service/src/iface_battery_provider_manager.rs
index f2ce28b..dac6457 100644
--- a/system/gd/rust/linux/service/src/iface_battery_provider_manager.rs
+++ b/system/gd/rust/linux/service/src/iface_battery_provider_manager.rs
@@ -41,4 +41,9 @@
     fn set_battery_info(&mut self, battery_provider_id: u32, battery_set: BatterySet) {
         dbus_generated!()
     }
+
+    #[dbus_method("RemoveBatteryInfo")]
+    fn remove_battery_info(&mut self, battery_provider_id: u32, address: String, uuid: String) {
+        dbus_generated!()
+    }
 }
diff --git a/system/gd/rust/linux/service/src/iface_bluetooth.rs b/system/gd/rust/linux/service/src/iface_bluetooth.rs
index ea651d5..2d45662 100644
--- a/system/gd/rust/linux/service/src/iface_bluetooth.rs
+++ b/system/gd/rust/linux/service/src/iface_bluetooth.rs
@@ -541,12 +541,12 @@
     }
 
     #[dbus_method("CancelBondProcess")]
-    fn cancel_bond_process(&self, device: BluetoothDevice) -> bool {
+    fn cancel_bond_process(&mut self, device: BluetoothDevice) -> bool {
         dbus_generated!()
     }
 
     #[dbus_method("RemoveBond")]
-    fn remove_bond(&self, device: BluetoothDevice) -> bool {
+    fn remove_bond(&mut self, device: BluetoothDevice) -> bool {
         dbus_generated!()
     }
 
diff --git a/system/gd/rust/linux/service/src/main.rs b/system/gd/rust/linux/service/src/main.rs
index 1f9a586..229cc3b 100644
--- a/system/gd/rust/linux/service/src/main.rs
+++ b/system/gd/rust/linux/service/src/main.rs
@@ -412,14 +412,21 @@
             // Install SIGTERM handler so that we can properly shutdown
             *SIG_DATA.lock().unwrap() = Some((tx.clone(), sig_notifier.clone()));
 
-            let sig_action = signal::SigAction::new(
+            let sig_action_term = signal::SigAction::new(
                 signal::SigHandler::Handler(handle_sigterm),
                 signal::SaFlags::empty(),
                 signal::SigSet::empty(),
             );
 
+            let sig_action_int = signal::SigAction::new(
+                signal::SigHandler::Handler(handle_sigint),
+                signal::SaFlags::empty(),
+                signal::SigSet::empty(),
+            );
+
             unsafe {
-                signal::sigaction(signal::SIGTERM, &sig_action).unwrap();
+                signal::sigaction(signal::SIGTERM, &sig_action_term).unwrap();
+                signal::sigaction(signal::SIGINT, &sig_action_int).unwrap();
             }
         }
 
@@ -467,3 +474,10 @@
     log::debug!("Sigterm completed");
     std::process::exit(0);
 }
+
+extern "C" fn handle_sigint(_signum: i32) {
+    // Assumed this is from HAL Host, which is likely caused by chipset error.
+    // In this case, don't crash the daemon and don't try to power off the adapter.
+    log::debug!("Sigint completed");
+    std::process::exit(0);
+}
diff --git a/system/gd/rust/linux/stack/Cargo.toml b/system/gd/rust/linux/stack/Cargo.toml
index ba6cc99..0941450 100644
--- a/system/gd/rust/linux/stack/Cargo.toml
+++ b/system/gd/rust/linux/stack/Cargo.toml
@@ -23,6 +23,7 @@
 serde_json = "1.0"
 syslog = "6"
 tokio = { version = "1", features = ['bytes', 'fs', 'io-util', 'libc', 'macros', 'mio', 'net', 'num_cpus', 'rt', 'rt-multi-thread', 'sync', 'time', 'tokio-macros'] }
+log-panics = "2.1.0"
 
 [lib]
 path = "src/lib.rs"
diff --git a/system/gd/rust/linux/stack/src/battery_manager.rs b/system/gd/rust/linux/stack/src/battery_manager.rs
index 5723b68..393761c 100644
--- a/system/gd/rust/linux/stack/src/battery_manager.rs
+++ b/system/gd/rust/linux/stack/src/battery_manager.rs
@@ -3,6 +3,7 @@
 use crate::uuid;
 use crate::Message;
 use crate::RPCProxy;
+use itertools::Itertools;
 use std::sync::{Arc, Mutex};
 use tokio::sync::mpsc::Sender;
 
@@ -137,11 +138,22 @@
         }
     }
 
+    pub fn remove_battery_set(&mut self, uuid: &String) {
+        self.0.retain(|battery_set| &battery_set.source_uuid != uuid);
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
+
     /// Returns the best BatterySet from among reported battery data.
     pub fn pick_best(&self) -> Option<BatterySet> {
         self.0
             .iter()
-            .find(|battery_set| battery_set.source_uuid == uuid::BAS)
+            .filter(|battery_set| !battery_set.batteries.is_empty())
+            // Now we prefer BAS, but we might need to prioritize other sources first
+            // TODO (b/295577710): Make a preference list
+            .find_or_first(|battery_set| battery_set.source_uuid == uuid::BAS)
             .or_else(|| self.0.first())
             .cloned()
     }
diff --git a/system/gd/rust/linux/stack/src/battery_provider_manager.rs b/system/gd/rust/linux/stack/src/battery_provider_manager.rs
index e9a0b94..a0138e2 100644
--- a/system/gd/rust/linux/stack/src/battery_provider_manager.rs
+++ b/system/gd/rust/linux/stack/src/battery_provider_manager.rs
@@ -1,6 +1,7 @@
 use crate::battery_manager::{Batteries, BatterySet};
 use crate::callbacks::Callbacks;
 use crate::{Message, RPCProxy};
+use log::debug;
 use std::collections::HashMap;
 use tokio::sync::mpsc::Sender;
 
@@ -24,6 +25,9 @@
 
     /// Updates the battery information for the battery associated with battery_id.
     fn set_battery_info(&mut self, battery_provider_id: u32, battery_set: BatterySet);
+
+    /// Removes the battery information for the battery associated with battery_id.
+    fn remove_battery_info(&mut self, battery_provider_id: u32, address: String, uuid: String);
 }
 
 /// Represents the BatteryProviderManager, a central point for collecting battery information from
@@ -74,12 +78,34 @@
         self.remove_battery_provider_callback(battery_provider_id);
     }
 
+    fn remove_battery_info(&mut self, _battery_provider_id: u32, address: String, uuid: String) {
+        if let Some(batteries) = self.battery_info.get_mut(&address) {
+            batteries.remove_battery_set(&uuid);
+
+            if batteries.is_empty() {
+                self.battery_info.remove(&address);
+            }
+        }
+    }
+
     fn set_battery_info(&mut self, _battery_provider_id: u32, battery_set: BatterySet) {
+        debug!(
+            "BatteryProviderManager received BatterySet for [{}] from \"{}\": {:?}",
+            battery_set.address.clone(),
+            battery_set.source_info.clone(),
+            battery_set.clone()
+        );
+
+        if battery_set.batteries.is_empty() {
+            return;
+        }
+
         let batteries = self
             .battery_info
             .entry(battery_set.address.clone())
             .or_insert_with(|| Batteries::new());
         batteries.add_or_update_battery_set(battery_set);
+
         if let Some(best_battery_set) = batteries.pick_best() {
             let tx = self.tx.clone();
             tokio::spawn(async move {
diff --git a/system/gd/rust/linux/stack/src/battery_service.rs b/system/gd/rust/linux/stack/src/battery_service.rs
index a3bbf80..a8f8ae4 100644
--- a/system/gd/rust/linux/stack/src/battery_service.rs
+++ b/system/gd/rust/linux/stack/src/battery_service.rs
@@ -243,7 +243,7 @@
     fn set_battery_info(&mut self, remote_address: &String, value: &Vec<u8>) -> BatterySet {
         let level: Vec<_> = value.iter().cloned().chain(iter::repeat(0 as u8)).take(4).collect();
         let level = u32::from_le_bytes(level.try_into().unwrap());
-        debug!("Received battery level for {}: {}", remote_address.clone(), level);
+        debug!("BAS received battery level for {}: {}", remote_address.clone(), level);
         let battery_set = self.battery_sets.entry(remote_address.clone()).or_insert_with(|| {
             BatterySet::new(
                 remote_address.clone(),
@@ -284,14 +284,10 @@
             None => return,
         }
         // Let BatteryProviderManager know that BAS no longer has a battery for this device.
-        self.battery_provider_manager.lock().unwrap().set_battery_info(
+        self.battery_provider_manager.lock().unwrap().remove_battery_info(
             self.battery_provider_id,
-            BatterySet::new(
-                remote_address.clone(),
-                uuid::BAS.to_string(),
-                "BAS".to_string(),
-                vec![],
-            ),
+            remote_address.clone(),
+            uuid::BAS.to_string(),
         );
         self.battery_sets.remove(&remote_address);
     }
diff --git a/system/gd/rust/linux/stack/src/bluetooth.rs b/system/gd/rust/linux/stack/src/bluetooth.rs
index a62ec4c..0567b26 100644
--- a/system/gd/rust/linux/stack/src/bluetooth.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth.rs
@@ -26,7 +26,7 @@
 use log::{debug, warn};
 use num_traits::cast::ToPrimitive;
 use num_traits::pow;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 use std::convert::TryInto;
 use std::fs::File;
 use std::hash::Hash;
@@ -44,6 +44,7 @@
 use crate::bluetooth_gatt::{BluetoothGatt, IBluetoothGatt, IScannerCallback, ScanResult};
 use crate::bluetooth_media::{BluetoothMedia, IBluetoothMedia, MediaActions};
 use crate::callbacks::Callbacks;
+use crate::socket_manager::SocketActions;
 use crate::uuid::{Profile, UuidHelper, HOGP};
 use crate::{Message, RPCProxy, SuspendMode};
 
@@ -145,10 +146,10 @@
     fn create_bond(&mut self, device: BluetoothDevice, transport: BtTransport) -> bool;
 
     /// Cancels any pending bond attempt on given device.
-    fn cancel_bond_process(&self, device: BluetoothDevice) -> bool;
+    fn cancel_bond_process(&mut self, device: BluetoothDevice) -> bool;
 
     /// Removes pairing for given device.
-    fn remove_bond(&self, device: BluetoothDevice) -> bool;
+    fn remove_bond(&mut self, device: BluetoothDevice) -> bool;
 
     /// Returns a list of known bonded devices.
     fn get_bonded_devices(&self) -> Vec<BluetoothDevice>;
@@ -507,6 +508,7 @@
     tx: Sender<Message>,
     // Internal API members
     discoverable_timeout: Option<JoinHandle<()>>,
+    cancelling_devices: HashSet<RawAddress>,
 
     /// Used to notify signal handler that we have turned off the stack.
     sig_notifier: Arc<SigData>,
@@ -557,6 +559,7 @@
             tx,
             // Internal API members
             discoverable_timeout: None,
+            cancelling_devices: HashSet::new(),
             sig_notifier,
         }
     }
@@ -1555,7 +1558,13 @@
             );
         });
 
-        metrics::bond_state_changed(addr, device_type, status, bond_state, fail_reason);
+        // Don't emit the metrics event if we were cancelling the bond.
+        // It is ok to not send the pairing complete event as the server should ignore the dangling
+        // pairing attempt event.
+        // This behavior aligns with BlueZ.
+        if !self.cancelling_devices.remove(&addr) {
+            metrics::bond_state_changed(addr, device_type, status, bond_state, fail_reason);
+        }
     }
 
     fn remote_device_properties_changed(
@@ -2062,6 +2071,12 @@
             _ => self.get_remote_type(device.clone()),
         };
 
+        // There could be a race between bond complete and bond cancel, which makes
+        // |cancelling_devices| in a wrong state. Remove the device just in case.
+        if self.cancelling_devices.remove(&address) {
+            warn!("Device {} is also cancelling the bond.", DisplayAddress(&address));
+        }
+
         // We explicitly log the attempt to start the bonding separate from logging the bond state.
         // The start of the attempt is critical to help identify a bonding/pairing session.
         metrics::bond_create_attempt(address, device_type.clone());
@@ -2093,7 +2108,7 @@
         return true;
     }
 
-    fn cancel_bond_process(&self, device: BluetoothDevice) -> bool {
+    fn cancel_bond_process(&mut self, device: BluetoothDevice) -> bool {
         let addr = RawAddress::from_string(device.address.clone());
 
         if addr.is_none() {
@@ -2102,10 +2117,14 @@
         }
 
         let address = addr.unwrap();
+        if !self.cancelling_devices.insert(address.clone()) {
+            warn!("Device {} has been added to cancelling_device.", DisplayAddress(&address));
+        }
+
         self.intf.lock().unwrap().cancel_bond(&address) == 0
     }
 
-    fn remove_bond(&self, device: BluetoothDevice) -> bool {
+    fn remove_bond(&mut self, device: BluetoothDevice) -> bool {
         let addr = RawAddress::from_string(device.address.clone());
 
         if addr.is_none() {
@@ -2115,6 +2134,13 @@
 
         let address = addr.unwrap();
         debug!("Removing bond for {}", DisplayAddress(&address));
+
+        // There could be a race between bond complete and bond cancel, which makes
+        // |cancelling_devices| in a wrong state. Remove the device just in case.
+        if self.cancelling_devices.remove(&address) {
+            warn!("Device {} is also cancelling the bond.", DisplayAddress(&address));
+        }
+
         let status = self.intf.lock().unwrap().remove_bond(&address);
 
         if status != 0 {
@@ -2602,6 +2628,16 @@
             }
         }
 
+        // Disconnect all socket connections
+        if let Some(raw_addr) = RawAddress::from_string(device.address.clone()) {
+            let txl = self.tx.clone();
+            topstack::get_runtime().spawn(async move {
+                let _ = txl
+                    .send(Message::SocketManagerActions(SocketActions::DisconnectAll(raw_addr)))
+                    .await;
+            });
+        }
+
         return true;
     }
 
diff --git a/system/gd/rust/linux/stack/src/bluetooth_gatt.rs b/system/gd/rust/linux/stack/src/bluetooth_gatt.rs
index 20b995b..a058c9c 100644
--- a/system/gd/rust/linux/stack/src/bluetooth_gatt.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth_gatt.rs
@@ -4095,12 +4095,6 @@
             status
         );
 
-        if status != GattStatus::Success {
-            log::error!("Error registering scanner UUID {}", uuid);
-            self.scanners.lock().unwrap().remove(&uuid);
-            return;
-        }
-
         let mut scanners_lock = self.scanners.lock().unwrap();
         let scanner_info = scanners_lock.get_mut(&uuid);
 
@@ -4117,6 +4111,11 @@
                 uuid
             );
         }
+
+        if status != GattStatus::Success {
+            log::error!("Error registering scanner UUID {}", uuid);
+            scanners_lock.remove(&uuid);
+        }
     }
 
     fn on_scan_result(
diff --git a/system/gd/rust/linux/stack/src/bluetooth_logging.rs b/system/gd/rust/linux/stack/src/bluetooth_logging.rs
index fdfd245..de62256 100644
--- a/system/gd/rust/linux/stack/src/bluetooth_logging.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth_logging.rs
@@ -2,6 +2,8 @@
 use log::LevelFilter;
 use syslog::{BasicLogger, Facility, Formatter3164};
 
+use log_panics;
+
 /// API to modify log levels.
 pub trait IBluetoothLogging {
     /// Check whether debug logging is enabled.
@@ -33,6 +35,7 @@
             let logger = syslog::unix(formatter).expect("could not connect to syslog");
             let _ = log::set_boxed_logger(Box::new(BasicLogger::new(logger)))
                 .map(|()| log::set_max_level(level));
+            log_panics::init();
         }
 
         Self { is_debug }
diff --git a/system/gd/rust/linux/stack/src/socket_manager.rs b/system/gd/rust/linux/stack/src/socket_manager.rs
index a2b15d0..7916aa1 100644
--- a/system/gd/rust/linux/stack/src/socket_manager.rs
+++ b/system/gd/rust/linux/stack/src/socket_manager.rs
@@ -403,6 +403,9 @@
 
     /// Used by admin
     uuid: Option<Uuid>,
+
+    /// Used for tracing task status
+    joinhandle: JoinHandle<()>,
 }
 
 impl InternalListeningSocket {
@@ -411,22 +414,24 @@
         socket_id: SocketId,
         tx: Sender<SocketRunnerActions>,
         uuid: Option<Uuid>,
+        joinhandle: JoinHandle<()>,
     ) -> Self {
-        InternalListeningSocket { _callback_id, socket_id, tx, uuid }
+        InternalListeningSocket { _callback_id, socket_id, tx, uuid, joinhandle }
     }
 }
 
 /// Internal connecting socket data.
 struct InternalConnectingSocket {
     _callback_id: CallbackId,
-    socket_info: BluetoothSocket,
-    stream: Option<UnixStream>,
+    socket_id: SocketId,
+
+    /// Used for cleaning up
+    joinhandle: JoinHandle<()>,
 }
 
 impl InternalConnectingSocket {
-    fn new(_callback_id: CallbackId, socket_info: BluetoothSocket, fd: std::fs::File) -> Self {
-        let stream = file_to_unixstream(fd);
-        InternalConnectingSocket { _callback_id, socket_info, stream }
+    fn new(_callback_id: CallbackId, socket_id: SocketId, joinhandle: JoinHandle<()>) -> Self {
+        InternalConnectingSocket { _callback_id, socket_id, joinhandle }
     }
 }
 
@@ -476,8 +481,11 @@
     OnIncomingSocketClosed(CallbackId, SocketId, BtStatus),
     OnHandleIncomingConnection(CallbackId, SocketId, BluetoothSocket),
 
-    // Last event is for connecting socket.
+    // This event is for connecting socket.
     OnOutgoingConnectionResult(CallbackId, SocketId, BtStatus, Option<BluetoothSocket>),
+
+    // Request to disconnect all sockets, e.g. when user disconnects the peer device.
+    DisconnectAll(RawAddress),
 }
 
 /// Implementation of the `IBluetoothSocketManager` api.
@@ -488,8 +496,8 @@
     /// List of listening sockets.
     listening: HashMap<CallbackId, Vec<InternalListeningSocket>>,
 
-    /// Current futures mapped by callback id (so we can drop if callback disconnects).
-    futures: HashMap<CallbackId, Vec<JoinHandle<()>>>,
+    /// List of connecting sockets with futures (so we can drop if callback disconnects).
+    connecting: HashMap<CallbackId, Vec<InternalConnectingSocket>>,
 
     /// Separate runtime for socket listeners (so they're not dependent on the
     /// same runtime as RPC).
@@ -514,7 +522,7 @@
     pub fn new(tx: Sender<Message>, admin: Arc<Mutex<Box<BluetoothAdmin>>>) -> Self {
         let callbacks = Callbacks::new(tx.clone(), Message::SocketManagerCallbackDisconnected);
         let socket_counter: u64 = 1000;
-        let futures = HashMap::new();
+        let connecting = HashMap::new();
         let listening = HashMap::new();
         let runtime = Arc::new(
             Builder::new_multi_thread()
@@ -527,7 +535,7 @@
 
         BluetoothSocketManager {
             callbacks,
-            futures,
+            connecting,
             listening,
             runtime,
             sock: None,
@@ -570,6 +578,14 @@
                 log::debug!("service {} is blocked by admin policy", uuid);
                 return SocketResult::new(BtStatus::AuthRejected, INVALID_SOCKET_ID);
             }
+            if self
+                .listening
+                .iter()
+                .any(|(_, v)| v.iter().any(|s| s.uuid.map_or(false, |u| u == uuid)))
+            {
+                log::warn!("Service {} already exists", uuid);
+                return SocketResult::new(BtStatus::Fail, INVALID_SOCKET_ID);
+            }
         }
 
         // Create listener socket pair
@@ -599,10 +615,7 @@
                 let id = self.next_socket_id();
                 socket_info.id = id;
                 let (runner_tx, runner_rx) = channel::<SocketRunnerActions>(10);
-
-                // Keep track of active listener sockets.
-                let listener = InternalListeningSocket::new(cbid, id, runner_tx, socket_info.uuid);
-                self.listening.entry(cbid).or_default().push(listener);
+                let uuid = socket_info.uuid.clone();
 
                 // Push a listening task to local runtime to wait for device to
                 // start accepting or get closed.
@@ -631,7 +644,11 @@
                     .await;
                 });
 
-                self.futures.entry(cbid).or_default().push(joinhandle);
+                // Keep track of active listener sockets.
+                self.listening
+                    .entry(cbid)
+                    .or_default()
+                    .push(InternalListeningSocket::new(cbid, id, runner_tx, uuid, joinhandle));
 
                 SocketResult::new(status, id)
             }
@@ -697,7 +714,6 @@
                 // callbacks.
                 let id = self.next_socket_id();
                 socket_info.id = id;
-                let connector = InternalConnectingSocket::new(cbid, socket_info, file);
 
                 // Push a connecting task to local runtime to wait for connection
                 // completion.
@@ -707,7 +723,8 @@
                         cbid,
                         id,
                         tx,
-                        connector,
+                        socket_info,
+                        file_to_unixstream(file),
                         Duration::from_millis(CONNECT_COMPLETE_TIMEOUT_MS),
                     )
                     .await;
@@ -715,7 +732,10 @@
 
                 // Keep track of these futures in case they need to be cancelled due to callback
                 // disconnecting.
-                self.futures.entry(cbid).or_default().push(joinhandle);
+                self.connecting
+                    .entry(cbid)
+                    .or_default()
+                    .push(InternalConnectingSocket::new(cbid, id, joinhandle));
 
                 SocketResult::new(status, id)
             }
@@ -1030,12 +1050,13 @@
         cbid: CallbackId,
         socket_id: SocketId,
         tx: Sender<Message>,
-        connector: InternalConnectingSocket,
+        socket_info: BluetoothSocket,
+        stream: Option<UnixStream>,
         connection_timeout: Duration,
     ) {
         // If the unixstream isn't available for this connection, immediately return
         // a failure.
-        let stream = match connector.stream {
+        let stream = match stream {
             Some(s) => s,
             None => {
                 let _ = tx
@@ -1060,7 +1081,7 @@
         if status != BtStatus::Success {
             log::info!(
                 "Connecting socket to {} failed while trying to read channel from stream",
-                connector.socket_info
+                socket_info
             );
             let _ = tx
                 .send(Message::SocketManagerActions(SocketActions::OnOutgoingConnectionResult(
@@ -1076,7 +1097,7 @@
         if status != BtStatus::Success {
             log::info!(
                 "Connecting socket to {} failed while trying to read connect complete from stream",
-                connector.socket_info
+                socket_info
             );
             let _ = tx
                 .send(Message::SocketManagerActions(SocketActions::OnOutgoingConnectionResult(
@@ -1100,7 +1121,7 @@
                         ))
                         .await;
                 } else {
-                    let mut sock = connector.socket_info;
+                    let mut sock = socket_info;
                     sock.fd = Some(unixstream_to_file(stream));
                     sock.port = cc.channel;
                     sock.max_rx_size = cc.max_rx_packet_size.into();
@@ -1160,8 +1181,17 @@
             SocketActions::OnOutgoingConnectionResult(cbid, socket_id, status, socket) => {
                 if let Some(callback) = self.callbacks.get_by_id_mut(cbid) {
                     callback.on_outgoing_connection_result(socket_id, status, socket);
+
+                    // Also make sure to remove the socket from connecting list.
+                    self.connecting
+                        .entry(cbid)
+                        .and_modify(|v| v.retain(|s| s.socket_id != socket_id));
                 }
             }
+
+            SocketActions::DisconnectAll(addr) => {
+                self.sock.as_ref().expect("Socket Manager not initialized").disconnect_all(addr);
+            }
         }
     }
 
@@ -1196,8 +1226,23 @@
 
     pub fn remove_callback(&mut self, callback: CallbackId) {
         // Remove any associated futures and sockets waiting to accept.
-        self.futures.remove(&callback);
-        self.listening.remove(&callback);
+        self.connecting.remove(&callback).map(|sockets| {
+            for s in sockets {
+                s.joinhandle.abort();
+            }
+        });
+        self.listening.remove(&callback).map(|sockets| {
+            for s in sockets {
+                if s.joinhandle.is_finished() {
+                    continue;
+                }
+                let tx = s.tx.clone();
+                let id = s.socket_id;
+                self.runtime.spawn(async move {
+                    let _ = tx.send(SocketRunnerActions::Close(id)).await;
+                });
+            }
+        });
         self.callbacks.remove_callback(callback);
     }
 
diff --git a/system/gd/rust/packets/build.rs b/system/gd/rust/packets/build.rs
index 455b865..3011305 100644
--- a/system/gd/rust/packets/build.rs
+++ b/system/gd/rust/packets/build.rs
@@ -40,13 +40,15 @@
 fn generate_packets() {
     let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
 
-    let gd_root = match env::var("PLATFORM_SUBDIR") {
-        Ok(dir) => PathBuf::from(dir).join("bt/gd"),
+    let pdl_root = match env::var("PLATFORM_SUBDIR") {
+        Ok(dir) => PathBuf::from(dir).join("bt/pdl"),
         // Currently at //platform2/gd/rust/rust/packets
-        Err(_) => PathBuf::from(env::current_dir().unwrap()).join("../..").canonicalize().unwrap(),
+        Err(_) => {
+            PathBuf::from(env::current_dir().unwrap()).join("../../../pdl").canonicalize().unwrap()
+        }
     };
 
-    let in_file = gd_root.join("hci/hci_packets.pdl");
+    let in_file = pdl_root.join("hci/hci_packets.pdl");
     let out_file = File::create(out_dir.join("hci_packets.rs")).unwrap();
 
     // Expect pdlc to be in the PATH
diff --git a/system/gd/rust/shim/src/init_flags.rs b/system/gd/rust/shim/src/init_flags.rs
index e40fdd5..6c31ad8 100644
--- a/system/gd/rust/shim/src/init_flags.rs
+++ b/system/gd/rust/shim/src/init_flags.rs
@@ -14,8 +14,6 @@
 
         fn always_send_services_if_gatt_disc_done_is_enabled() -> bool;
         fn always_use_private_gatt_for_debugging_is_enabled() -> bool;
-        fn asynchronously_start_l2cap_coc_is_enabled() -> bool;
-        fn btaa_hci_is_enabled() -> bool;
         fn bta_dm_clear_conn_id_on_client_close_is_enabled() -> bool;
         fn delay_hidh_cleanup_until_hidh_ready_start_is_enabled() -> bool;
         fn btm_dm_flush_discovery_queue_on_search_cancel_is_enabled() -> bool;
@@ -24,12 +22,9 @@
         fn clear_hidd_interrupt_cid_on_disconnect_is_enabled() -> bool;
         fn device_iot_config_logging_is_enabled() -> bool;
         fn dynamic_avrcp_version_enhancement_is_enabled() -> bool;
-        fn finite_att_timeout_is_enabled() -> bool;
         fn gatt_robust_caching_client_is_enabled() -> bool;
         fn gatt_robust_caching_server_is_enabled() -> bool;
-        fn gd_core_is_enabled() -> bool;
         fn gd_l2cap_is_enabled() -> bool;
-        fn gd_link_policy_is_enabled() -> bool;
         fn get_default_log_level() -> i32;
         fn get_hci_adapter() -> i32;
         fn get_log_level_for_tag(tag: &str) -> i32;
@@ -43,7 +38,6 @@
         fn pbap_pse_dynamic_version_upgrade_is_enabled() -> bool;
         fn periodic_advertising_adi_is_enabled() -> bool;
         fn private_gatt_is_enabled() -> bool;
-        fn queue_l2cap_coc_while_encrypting_is_enabled() -> bool;
         fn redact_log_is_enabled() -> bool;
         fn rust_event_loop_is_enabled() -> bool;
         fn sco_codec_select_lc3_is_enabled() -> bool;
diff --git a/system/gd/rust/topshim/BUILD.gn b/system/gd/rust/topshim/BUILD.gn
index a0a71f0..cde636e 100644
--- a/system/gd/rust/topshim/BUILD.gn
+++ b/system/gd/rust/topshim/BUILD.gn
@@ -46,7 +46,7 @@
   ]
   deps = [
     ":btif_bridge_header",
-    "//bt/system/gd:BluetoothGeneratedPackets_h",
+    "//bt/system/pdl:BluetoothGeneratedPackets_h",
   ]
   configs = [ "//bt/system/gd:gd_defaults" ]
 }
@@ -66,8 +66,8 @@
 
   deps = [
     ":btif_bridge_header",
-    "//bt/system/gd:BluetoothGeneratedPackets_h",
     "//bt/system/gd/metrics:BluetoothMetricsSources",
+    "//bt/system/pdl:BluetoothGeneratedPackets_h",
   ]
   configs += [ "//bt/system/gd:gd_defaults" ]
 }
diff --git a/system/gd/rust/topshim/src/btif.rs b/system/gd/rust/topshim/src/btif.rs
index d67be44..1782bba 100644
--- a/system/gd/rust/topshim/src/btif.rs
+++ b/system/gd/rust/topshim/src/btif.rs
@@ -1124,7 +1124,6 @@
             // gd/rust/linux/stack.
             let mut callouts = Box::new(bindings::bt_os_callouts_t {
                 size: std::mem::size_of::<bindings::bt_os_callouts_t>(),
-                set_wake_alarm: None, // Not used
                 acquire_wake_lock: Some(wake_lock_noop),
                 release_wake_lock: Some(wake_lock_noop),
             });
diff --git a/system/gd/rust/topshim/src/profiles/socket.rs b/system/gd/rust/topshim/src/profiles/socket.rs
index 243b9de..eddbd0d 100644
--- a/system/gd/rust/topshim/src/profiles/socket.rs
+++ b/system/gd/rust/topshim/src/profiles/socket.rs
@@ -248,6 +248,10 @@
         )
         .into()
     }
+
+    pub fn disconnect_all(&self, addr: RawAddress) -> BtStatus {
+        ccall!(self, disconnect_all, &addr).into()
+    }
 }
 
 #[cfg(test)]
diff --git a/system/gd/storage/storage_module.cc b/system/gd/storage/storage_module.cc
index f9eab5f..6914360 100644
--- a/system/gd/storage/storage_module.cc
+++ b/system/gd/storage/storage_module.cc
@@ -36,8 +36,6 @@
 namespace bluetooth {
 namespace storage {
 
-using common::ListMap;
-using common::LruCache;
 using os::Alarm;
 using os::Handler;
 
diff --git a/system/hci/Android.bp b/system/hci/Android.bp
index 48bd6fb..d693301 100644
--- a/system/hci/Android.bp
+++ b/system/hci/Android.bp
@@ -36,6 +36,8 @@
     ],
     host_supported: true,
     min_sdk_version: "Tiramisu",
+    header_libs: ["libbluetooth_headers"],
+    static_libs: ["libbt_shim_bridge"],
 }
 
 // HCI unit tests for target
@@ -69,6 +71,7 @@
     ],
     static_libs: [
         "libbluetooth-for-tests",
+        "libbluetooth-types",
         "libbt-hci",
         "libbtcore",
         "libchrome",
diff --git a/system/hci/include/btsnoop_mem.h b/system/hci/include/btsnoop_mem.h
index c3372da..660678e 100644
--- a/system/hci/include/btsnoop_mem.h
+++ b/system/hci/include/btsnoop_mem.h
@@ -27,17 +27,10 @@
 typedef void (*btsnoop_data_cb)(const uint16_t type, const uint8_t* p_data,
                                 const size_t len, const uint64_t timestamp_us);
 
-// Callback invoked for each HCI packet when activity attribution is enabled.
-typedef void (*activity_attribution_cb)(const uint16_t type,
-                                        const uint8_t* p_data, const size_t len,
-                                        const uint64_t timestamp_us);
-
 // This call sets the (one and only) callback that will
 // be invoked once for each HCI packet/event.
 void btsnoop_mem_set_callback(btsnoop_data_cb cb);
 
-void activity_attribution_set_callback(activity_attribution_cb cb);
-
 // This function is invoked every time an HCI packet
 // is sent/received. Packets will be filtered  and then
 // forwarded to the |btsnoop_data_cb|.
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index efc91db..08080c0 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -54,7 +54,6 @@
 #define BT_PROFILE_HAP_CLIENT_ID "has_client"
 #define BT_PROFILE_LE_AUDIO_ID "le_audio"
 #define BT_KEYSTORE_ID "bluetooth_keystore"
-#define BT_ACTIVITY_ATTRIBUTION_ID "activity_attribution"
 #define BT_PROFILE_VC_ID "volume_control"
 #define BT_PROFILE_CSIS_CLIENT_ID "csis_client"
 #define BT_PROFILE_LE_AUDIO_ID "le_audio"
@@ -616,9 +615,6 @@
   le_rand_callback le_rand_cb;
 } bt_callbacks_t;
 
-typedef void (*alarm_cb)(void* data);
-typedef bool (*set_wake_alarm_callout)(uint64_t delay_millis, bool should_wake,
-                                       alarm_cb cb, void* data);
 typedef int (*acquire_wake_lock_callout)(const char* lock_name);
 typedef int (*release_wake_lock_callout)(const char* lock_name);
 
@@ -630,7 +626,6 @@
   /* set to sizeof(bt_os_callouts_t) */
   size_t size;
 
-  set_wake_alarm_callout set_wake_alarm;
   acquire_wake_lock_callout acquire_wake_lock;
   release_wake_lock_callout release_wake_lock;
 } bt_os_callouts_t;
diff --git a/system/include/hardware/bt_activity_attribution.h b/system/include/hardware/bt_activity_attribution.h
deleted file mode 100644
index c8f56bc..0000000
--- a/system/include/hardware/bt_activity_attribution.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#ifndef ANDROID_INCLUDE_BT_ACTIVITY_ATTRIBUTION_H
-#define ANDROID_INCLUDE_BT_ACTIVITY_ATTRIBUTION_H
-
-#include <vector>
-
-#include "raw_address.h"
-
-namespace bluetooth {
-namespace activity_attribution {
-
-class ActivityAttributionInterface {
- public:
-  virtual ~ActivityAttributionInterface() = default;
-
-  /** Notify the UID and package name of the app, and the address of associated
-   * active device */
-  virtual void NotifyActivityAttributionInfo(
-      int uid, const std::string& package_name,
-      const std::string& device_address) = 0;
-};
-
-}  // namespace activity_attribution
-}  // namespace bluetooth
-
-#endif /* ANDROID_INCLUDE_BT_ACTIVITY_ATTRIBUTION_H */
diff --git a/system/include/hardware/bt_sock.h b/system/include/hardware/bt_sock.h
index a60571f..7d5060e 100644
--- a/system/include/hardware/bt_sock.h
+++ b/system/include/hardware/bt_sock.h
@@ -101,6 +101,12 @@
                              uint8_t discard_buffers, uint8_t break_signal_seq,
                              bool fc);
 
+  /**
+   * Disconnect all RFCOMM and L2CAP socket connections with the associated
+   * device address.
+   */
+  bt_status_t (*disconnect_all)(const RawAddress* bd_addr);
+
 } btsock_interface_t;
 
 __END_DECLS
diff --git a/system/main/Android.bp b/system/main/Android.bp
index 8f246cb..aa89cf8 100644
--- a/system/main/Android.bp
+++ b/system/main/Android.bp
@@ -43,7 +43,6 @@
         "packages/modules/Bluetooth/system/stack/include",
         "packages/modules/Bluetooth/system/stack/l2cap",
         "packages/modules/Bluetooth/system/udrv/include",
-        "packages/modules/Bluetooth/system/vnd/include",
         "system/security/keystore/include",
     ],
     generated_headers: [
@@ -56,7 +55,12 @@
     ],
     host_supported: true,
     min_sdk_version: "Tiramisu",
-    static_libs: ["libbluetooth_gd"],
+    static_libs: [
+        "libbluetooth_gd",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+    ],
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_library {
@@ -86,7 +90,6 @@
         "packages/modules/Bluetooth/system/stack/include",
         "packages/modules/Bluetooth/system/stack/l2cap",
         "packages/modules/Bluetooth/system/udrv/include",
-        "packages/modules/Bluetooth/system/vnd/include",
         "system/security/keystore/include",
     ],
     shared_libs: [
@@ -149,6 +152,11 @@
     whole_static_libs: [
         "libbluetooth_gd", // Gabeldorsche
     ],
+    header_libs: ["libbluetooth_headers"],
+    static_libs: [
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+    ],
 }
 
 cc_test {
@@ -197,14 +205,19 @@
         "shim/metrics_api.cc",
         "shim/shim.cc",
         "shim/stack.cc",
+        "shim/utils.cc",
         "test/common_stack_test.cc",
         "test/main_shim_dumpsys_test.cc",
         "test/main_shim_test.cc",
     ],
     static_libs: [
         "libbluetooth-dumpsys",
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtdevice",
         "libchrome",
         "libevent",
@@ -232,4 +245,5 @@
         "BluetoothGeneratedPackets_h",
     ],
     min_sdk_version: "Tiramisu",
+    header_libs: ["libbluetooth_headers"],
 }
diff --git a/system/main/shim/Android.bp b/system/main/shim/Android.bp
index 173ed49..ae14e4f 100644
--- a/system/main/shim/Android.bp
+++ b/system/main/shim/Android.bp
@@ -13,7 +13,6 @@
         "acl.cc",
         "acl_api.cc",
         "acl_legacy_interface.cc",
-        "activity_attribution.cc",
         "btm.cc",
         "btm_api.cc",
         "config.cc",
@@ -29,5 +28,6 @@
         "metrics_api.cc",
         "shim.cc",
         "stack.cc",
+        "utils.cc",
     ],
 }
diff --git a/system/main/shim/BUILD.gn b/system/main/shim/BUILD.gn
index 727609e..fb6ba62 100644
--- a/system/main/shim/BUILD.gn
+++ b/system/main/shim/BUILD.gn
@@ -19,7 +19,6 @@
     "acl.cc",
     "acl_api.cc",
     "acl_legacy_interface.cc",
-    "activity_attribution.cc",
     "btm.cc",
     "btm_api.cc",
     "config.cc",
@@ -35,6 +34,7 @@
     "metrics_api.cc",
     "shim.cc",
     "stack.cc",
+    "utils.cc",
   ]
 
   include_dirs = [
@@ -49,7 +49,6 @@
 
   deps = [
     "//bt/system/gd:BluetoothGeneratedDumpsysDataSchema_h",
-    "//bt/system/gd:BluetoothGeneratedPackets_h",
     "//bt/system/gd/common:BluetoothCommonSources",
     "//bt/system/gd/dumpsys/bundler:BluetoothGeneratedBundlerSchema_h_bfbs",
     "//bt/system/gd/hci:BluetoothHciSources",
@@ -58,6 +57,7 @@
     "//bt/system/gd/rust/shim:libbluetooth_rust_interop",
     "//bt/system/gd/rust/topshim:libbluetooth_topshim",
     "//bt/system/osi",
+    "//bt/system/pdl:BluetoothGeneratedPackets_h",
     "//bt/system/stack",
     "//bt/system/types",
   ]
diff --git a/system/main/shim/acl.cc b/system/main/shim/acl.cc
index f7548ce..5cfb881 100644
--- a/system/main/shim/acl.cc
+++ b/system/main/shim/acl.cc
@@ -763,6 +763,18 @@
     return connection_->GetLocalAddress();
   }
 
+  bluetooth::hci::AddressWithType GetLocalOtaAddressWithType() {
+    return connection_->GetLocalOtaAddress();
+  }
+
+  bluetooth::hci::AddressWithType GetPeerAddressWithType() {
+    return connection_->GetPeerAddress();
+  }
+
+  bluetooth::hci::AddressWithType GetPeerOtaAddressWithType() {
+    return connection_->GetPeerOtaAddress();
+  }
+
   std::optional<uint8_t> GetAdvertisingSetConnectedTo() {
     return std::visit(
         [](auto&& data) {
@@ -1529,13 +1541,35 @@
 }
 
 bluetooth::hci::AddressWithType shim::legacy::Acl::GetConnectionLocalAddress(
-    const RawAddress& remote_bda) {
+    uint16_t handle, bool ota_address) {
   bluetooth::hci::AddressWithType address_with_type;
-  auto remote_address = ToGdAddress(remote_bda);
-  for (auto& [handle, connection] : pimpl_->handle_to_le_connection_map_) {
-    if (connection->GetRemoteAddressWithType().GetAddress() == remote_address) {
-      return connection->GetLocalAddressWithType();
+
+  for (auto& [acl_handle, connection] : pimpl_->handle_to_le_connection_map_) {
+    if (acl_handle != handle) {
+      continue;
     }
+
+    if (ota_address) {
+      return connection->GetLocalOtaAddressWithType();
+    }
+    return connection->GetLocalAddressWithType();
+  }
+  LOG_WARN("address not found!");
+  return address_with_type;
+}
+
+bluetooth::hci::AddressWithType shim::legacy::Acl::GetConnectionPeerAddress(
+    uint16_t handle, bool ota_address) {
+  bluetooth::hci::AddressWithType address_with_type;
+  for (auto& [acl_handle, connection] : pimpl_->handle_to_le_connection_map_) {
+    if (acl_handle != handle) {
+      continue;
+    }
+
+    if (ota_address) {
+      return connection->GetPeerOtaAddressWithType();
+    }
+    return connection->GetPeerAddressWithType();
   }
   LOG_WARN("address not found!");
   return address_with_type;
diff --git a/system/main/shim/acl.h b/system/main/shim/acl.h
index 8a8c626..335947a 100644
--- a/system/main/shim/acl.h
+++ b/system/main/shim/acl.h
@@ -69,8 +69,10 @@
       std::unique_ptr<hci::acl_manager::LeAclConnection>) override;
   void OnLeConnectFail(hci::AddressWithType, hci::ErrorCode reason) override;
   void OnLeLinkDisconnected(uint16_t handle, hci::ErrorCode reason);
-  bluetooth::hci::AddressWithType GetConnectionLocalAddress(
-      const RawAddress& remote_bda);
+  bluetooth::hci::AddressWithType GetConnectionLocalAddress(uint16_t handle,
+                                                            bool ota_address);
+  bluetooth::hci::AddressWithType GetConnectionPeerAddress(uint16_t handle,
+                                                           bool ota_address);
   std::optional<uint8_t> GetAdvertisingSetConnectedTo(
       const RawAddress& remote_bda);
 
diff --git a/system/main/shim/acl_api.cc b/system/main/shim/acl_api.cc
index 1f75546..f57062b 100644
--- a/system/main/shim/acl_api.cc
+++ b/system/main/shim/acl_api.cc
@@ -107,15 +107,31 @@
   return Stack::GetInstance()->GetAcl()->ClearFilterAcceptList();
 }
 
-void bluetooth::shim::ACL_ReadConnectionAddress(const RawAddress& pseudo_addr,
+void bluetooth::shim::ACL_ReadConnectionAddress(uint16_t handle,
                                                 RawAddress& conn_addr,
-                                                tBLE_ADDR_TYPE* p_addr_type) {
+                                                tBLE_ADDR_TYPE* p_addr_type,
+                                                bool ota_address) {
   auto local_address =
-      Stack::GetInstance()->GetAcl()->GetConnectionLocalAddress(pseudo_addr);
+      Stack::GetInstance()->GetAcl()->GetConnectionLocalAddress(handle,
+                                                                ota_address);
+
   conn_addr = ToRawAddress(local_address.GetAddress());
   *p_addr_type = static_cast<tBLE_ADDR_TYPE>(local_address.GetAddressType());
 }
 
+void bluetooth::shim::ACL_ReadPeerConnectionAddress(uint16_t handle,
+                                                    RawAddress& conn_addr,
+                                                    tBLE_ADDR_TYPE* p_addr_type,
+                                                    bool ota_address) {
+  auto remote_ota_address =
+      Stack::GetInstance()->GetAcl()->GetConnectionPeerAddress(handle,
+                                                               ota_address);
+
+  conn_addr = ToRawAddress(remote_ota_address.GetAddress());
+  *p_addr_type =
+      static_cast<tBLE_ADDR_TYPE>(remote_ota_address.GetAddressType());
+}
+
 std::optional<uint8_t> bluetooth::shim::ACL_GetAdvertisingSetConnectedTo(
     const RawAddress& addr) {
   return Stack::GetInstance()->GetAcl()->GetAdvertisingSetConnectedTo(addr);
diff --git a/system/main/shim/acl_api.h b/system/main/shim/acl_api.h
index fd5de15..4054e26 100644
--- a/system/main/shim/acl_api.h
+++ b/system/main/shim/acl_api.h
@@ -40,9 +40,12 @@
 void ACL_Shutdown();
 void ACL_IgnoreAllLeConnections();
 
-void ACL_ReadConnectionAddress(const RawAddress& pseudo_addr,
-                               RawAddress& conn_addr,
-                               tBLE_ADDR_TYPE* p_addr_type);
+void ACL_ReadConnectionAddress(uint16_t handle, RawAddress& conn_addr,
+                               tBLE_ADDR_TYPE* p_addr_type, bool ota_address);
+
+void ACL_ReadPeerConnectionAddress(uint16_t handle, RawAddress& conn_addr,
+                                   tBLE_ADDR_TYPE* p_addr_type,
+                                   bool ota_address);
 
 std::optional<uint8_t> ACL_GetAdvertisingSetConnectedTo(const RawAddress& addr);
 
diff --git a/system/main/shim/activity_attribution.cc b/system/main/shim/activity_attribution.cc
deleted file mode 100644
index 12eed9d..0000000
--- a/system/main/shim/activity_attribution.cc
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2020 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.
- */
-
-#define LOG_TAG "bt_shim_activity_attribution"
-#include "activity_attribution.h"
-
-#include "btif/include/btif_common.h"
-#include "gd/btaa/activity_attribution.h"
-#include "helpers.h"
-#include "main/shim/entry.h"
-#include "types/raw_address.h"
-
-class ActivityAttributionInterfaceImpl : public ActivityAttributionInterface {
- public:
-  ~ActivityAttributionInterfaceImpl() override = default;
-
-  static ActivityAttributionInterfaceImpl* GetInstance() {
-    static ActivityAttributionInterfaceImpl* instance =
-        new ActivityAttributionInterfaceImpl();
-    return instance;
-  }
-
-  void NotifyActivityAttributionInfo(
-      int uid, const std::string& package_name,
-      const std::string& device_address) override {
-    bluetooth::shim::GetActivityAttribution()->NotifyActivityAttributionInfo(
-        uid, package_name, device_address);
-  }
-
- private:
-  // Private constructor to prevent construction.
-  ActivityAttributionInterfaceImpl() {}
-};
-
-ActivityAttributionInterface*
-bluetooth::shim::get_activity_attribution_instance() {
-  return ActivityAttributionInterfaceImpl::GetInstance();
-}
diff --git a/system/main/shim/btm_api.h b/system/main/shim/btm_api.h
index 3884aec..30665ec 100644
--- a/system/main/shim/btm_api.h
+++ b/system/main/shim/btm_api.h
@@ -396,27 +396,41 @@
  *
  * Function         BTM_ReadConnectionAddr
  *
- * Description      Read the local device random address.
+ * Description      Read the local device address.
  *
+ *                  pseudo_addr - pseudo address used by the stack
+ *                  conn_addr   - returned addresss
+ *                  p_addr_type - returned address type
+ *                  ota_address - if set to true, function will provide RPA address
+ *                                if it was used during connection. e.g. It should
+ *                                be set to true by SMP module.
  * Returns          void
  *
  ******************************************************************************/
-void BTM_ReadConnectionAddr(const RawAddress& remote_bda,
+void BTM_ReadConnectionAddr(const RawAddress& pseudo_addr,
                             RawAddress& local_conn_addr,
-                            tBLE_ADDR_TYPE* p_addr_type);
+                            tBLE_ADDR_TYPE* p_addr_type,
+                            bool ota_address = false);
 
 /*******************************************************************************
  *
  * Function         BTM_ReadRemoteConnectionAddr
  *
- * Description      Read the remote device address currently used.
+ * Description      Read the remote device address.
+ *                  pseudo_addr - pseudo address used by the stack
+ *                  conn_addr   - returned addresss
+ *                  p_addr_type - returned address type
+ *                  ota_address - if set to true, function will provide RPA address
+ *                                if it was used during connection. It should be set
+ *                                to true by SMP module.
  *
- * Returns          void
+ * Returns          true if remote address found, false otherwise.
  *
  ******************************************************************************/
 bool BTM_ReadRemoteConnectionAddr(const RawAddress& pseudo_addr,
                                   RawAddress& conn_addr,
-                                  tBLE_ADDR_TYPE* p_addr_type);
+                                  tBLE_ADDR_TYPE* p_addr_type,
+                                  bool ota_address = false);
 
 /*******************************************************************************
  *
diff --git a/system/main/shim/entry.cc b/system/main/shim/entry.cc
index 61ac4ba..032c7a5 100644
--- a/system/main/shim/entry.cc
+++ b/system/main/shim/entry.cc
@@ -16,7 +16,6 @@
 
 #include "main/shim/entry.h"
 
-#include "gd/btaa/activity_attribution.h"
 #include "gd/hal/snoop_logger.h"
 #include "gd/hci/controller.h"
 #include "gd/hci/distance_measurement_manager.h"
@@ -147,12 +146,6 @@
       ->GetInstance<hci::VendorSpecificEventManager>();
 }
 
-activity_attribution::ActivityAttribution* GetActivityAttribution() {
-  return Stack::GetInstance()
-      ->GetStackManager()
-      ->GetInstance<activity_attribution::ActivityAttribution>();
-}
-
 metrics::CounterMetrics* GetCounterMetrics() {
   return Stack::GetInstance()
       ->GetStackManager()
diff --git a/system/main/shim/entry.h b/system/main/shim/entry.h
index 86815f2..6ae93f0 100644
--- a/system/main/shim/entry.h
+++ b/system/main/shim/entry.h
@@ -35,9 +35,6 @@
 namespace os {
 class Handler;
 }
-namespace activity_attribution {
-class ActivityAttribution;
-}
 namespace neighbor {
 class ConnectabilityModule;
 class DiscoverabilityModule;
@@ -104,7 +101,6 @@
 storage::StorageModule* GetStorage();
 hci::AclManager* GetAclManager();
 hci::VendorSpecificEventManager* GetVendorSpecificEventManager();
-activity_attribution::ActivityAttribution* GetActivityAttribution();
 metrics::CounterMetrics* GetCounterMetrics();
 hci::MsftExtensionManager* GetMsftExtensionManager();
 
diff --git a/system/main/shim/le_advertising_manager.cc b/system/main/shim/le_advertising_manager.cc
index e9c21e6..d36915c 100644
--- a/system/main/shim/le_advertising_manager.cc
+++ b/system/main/shim/le_advertising_manager.cc
@@ -17,6 +17,7 @@
 #define LOG_TAG "bt_shim_advertiser"
 
 #include "le_advertising_manager.h"
+#include "utils.h"
 
 #include <base/logging.h>
 #include <hardware/bluetooth.h>
@@ -45,6 +46,7 @@
 using bluetooth::hci::ErrorCode;
 using bluetooth::hci::GapData;
 using bluetooth::hci::OwnAddressType;
+using bluetooth::shim::parse_gap_data;
 using std::vector;
 
 namespace {
@@ -107,23 +109,8 @@
   void SetData(int advertiser_id, bool set_scan_rsp, vector<uint8_t> data,
                StatusCallback cb) override {
     LOG(INFO) << __func__ << " in shim layer";
-
-    size_t offset = 0;
     std::vector<GapData> advertising_data = {};
-
-    while (offset < data.size()) {
-      GapData gap_data;
-      uint8_t len = data[offset];
-      auto begin = data.begin() + offset;
-      auto end = begin + len + 1;  // 1 byte for len
-      auto data_copy = std::make_shared<std::vector<uint8_t>>(begin, end);
-      bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> packet(
-          data_copy);
-      GapData::Parse(&gap_data, packet.begin());
-      advertising_data.push_back(gap_data);
-      offset += len + 1;  // 1 byte for len
-    }
-
+    parse_gap_data(data, advertising_data);
     bluetooth::shim::GetAdvertising()->SetData(advertiser_id, set_scan_rsp,
                                                advertising_data);
   }
@@ -147,33 +134,8 @@
     bluetooth::hci::AdvertisingConfig config{};
     parse_parameter(config, params);
 
-    size_t offset = 0;
-    while (offset < advertise_data.size()) {
-      GapData gap_data;
-      uint8_t len = advertise_data[offset];
-      auto begin = advertise_data.begin() + offset;
-      auto end = begin + len + 1;  // 1 byte for len
-      auto data_copy = std::make_shared<std::vector<uint8_t>>(begin, end);
-      bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> packet(
-          data_copy);
-      GapData::Parse(&gap_data, packet.begin());
-      config.advertisement.push_back(gap_data);
-      offset += len + 1;  // 1 byte for len
-    }
-
-    offset = 0;
-    while (offset < scan_response_data.size()) {
-      GapData gap_data;
-      uint8_t len = scan_response_data[offset];
-      auto begin = scan_response_data.begin() + offset;
-      auto end = begin + len + 1;  // 1 byte for len
-      auto data_copy = std::make_shared<std::vector<uint8_t>>(begin, end);
-      bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> packet(
-          data_copy);
-      GapData::Parse(&gap_data, packet.begin());
-      config.scan_response.push_back(gap_data);
-      offset += len + 1;  // 1 byte for len
-    }
+    parse_gap_data(advertise_data, config.advertisement);
+    parse_gap_data(scan_response_data, config.scan_response);
 
     bluetooth::shim::GetAdvertising()->StartAdvertising(
         advertiser_id, config, timeout_s * 100, cb, timeout_cb, scan_callback,
@@ -196,47 +158,9 @@
     parse_periodic_advertising_parameter(config.periodic_advertising_parameters,
                                          periodic_params);
 
-    size_t offset = 0;
-    while (offset < advertise_data.size()) {
-      GapData gap_data;
-      uint8_t len = advertise_data[offset];
-      auto begin = advertise_data.begin() + offset;
-      auto end = begin + len + 1;  // 1 byte for len
-      auto data_copy = std::make_shared<std::vector<uint8_t>>(begin, end);
-      bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> packet(
-          data_copy);
-      GapData::Parse(&gap_data, packet.begin());
-      config.advertisement.push_back(gap_data);
-      offset += len + 1;  // 1 byte for len
-    }
-
-    offset = 0;
-    while (offset < scan_response_data.size()) {
-      GapData gap_data;
-      uint8_t len = scan_response_data[offset];
-      auto begin = scan_response_data.begin() + offset;
-      auto end = begin + len + 1;  // 1 byte for len
-      auto data_copy = std::make_shared<std::vector<uint8_t>>(begin, end);
-      bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> packet(
-          data_copy);
-      GapData::Parse(&gap_data, packet.begin());
-      config.scan_response.push_back(gap_data);
-      offset += len + 1;  // 1 byte for len
-    }
-
-    offset = 0;
-    while (offset < periodic_data.size()) {
-      GapData gap_data;
-      uint8_t len = periodic_data[offset];
-      auto begin = periodic_data.begin() + offset;
-      auto end = begin + len + 1;  // 1 byte for len
-      auto data_copy = std::make_shared<std::vector<uint8_t>>(begin, end);
-      bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> packet(
-          data_copy);
-      GapData::Parse(&gap_data, packet.begin());
-      config.periodic_data.push_back(gap_data);
-      offset += len + 1;  // 1 byte for len
-    }
+    parse_gap_data(advertise_data, config.advertisement);
+    parse_gap_data(scan_response_data, config.scan_response);
+    parse_gap_data(periodic_data, config.periodic_data);
 
     // if registered by native client, add the register id
     if (client_id != kAdvertiserClientIdJni) {
@@ -244,8 +168,8 @@
     }
 
     bluetooth::shim::GetAdvertising()->ExtendedCreateAdvertiser(
-        reg_id, config, scan_callback, set_terminated_callback, duration,
-        maxExtAdvEvents, bluetooth::shim::GetGdShimHandler());
+        client_id, reg_id, config, scan_callback, set_terminated_callback,
+        duration, maxExtAdvEvents, bluetooth::shim::GetGdShimHandler());
 
     LOG_INFO("create advertising set, client_id:%d, reg_id:%d", client_id,
              reg_id);
@@ -270,23 +194,8 @@
   void SetPeriodicAdvertisingData(int advertiser_id, std::vector<uint8_t> data,
                                   StatusCallback cb) override {
     LOG(INFO) << __func__ << " in shim layer";
-
-    size_t offset = 0;
     std::vector<GapData> advertising_data = {};
-
-    while (offset < data.size()) {
-      GapData gap_data;
-      uint8_t len = data[offset];
-      auto begin = data.begin() + offset;
-      auto end = begin + len + 1;  // 1 byte for len
-      auto data_copy = std::make_shared<std::vector<uint8_t>>(begin, end);
-      bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> packet(
-          data_copy);
-      GapData::Parse(&gap_data, packet.begin());
-      advertising_data.push_back(gap_data);
-      offset += len + 1;  // 1 byte for len
-    }
-
+    parse_gap_data(data, advertising_data);
     bluetooth::shim::GetAdvertising()->SetPeriodicData(advertiser_id,
                                                        advertising_data);
   }
diff --git a/system/main/shim/shim.cc b/system/main/shim/shim.cc
index 3d15a1f..972b269 100644
--- a/system/main/shim/shim.cc
+++ b/system/main/shim/shim.cc
@@ -61,18 +61,10 @@
     .clean_up = kUnusedModuleApi,
     .dependencies = {kUnusedModuleDependencies}};
 
-bool bluetooth::shim::is_gd_link_policy_enabled() {
-  return bluetooth::common::init_flags::gd_link_policy_is_enabled();
-}
-
 bool bluetooth::shim::is_gd_l2cap_enabled() {
   return bluetooth::common::init_flags::gd_l2cap_is_enabled();
 }
 
-bool bluetooth::shim::is_gd_shim_enabled() {
-  return bluetooth::common::init_flags::gd_core_is_enabled();
-}
-
 bool bluetooth::shim::is_gd_stack_started_up() {
   return bluetooth::shim::Stack::GetInstance()->IsRunning();
 }
@@ -81,10 +73,6 @@
   return bluetooth::shim::Stack::GetInstance()->IsDumpsysModuleStarted();
 }
 
-bool bluetooth::shim::is_gd_btaa_enabled() {
-  return bluetooth::common::init_flags::btaa_hci_is_enabled();
-}
-
 bool bluetooth::shim::is_classic_discovery_only_enabled() {
   return bluetooth::common::init_flags::classic_discovery_only_is_enabled();
 }
diff --git a/system/main/shim/shim.h b/system/main/shim/shim.h
index fb2c626..e12a54c 100644
--- a/system/main/shim/shim.h
+++ b/system/main/shim/shim.h
@@ -44,10 +44,7 @@
  *
  * @return true if using gd shim core, false if using legacy.
  */
-bool is_gd_link_policy_enabled();
 bool is_gd_l2cap_enabled();
-bool is_gd_shim_enabled();
-bool is_gd_btaa_enabled();
 
 /**
  * Checks if the bluetooth gd stack has been started up.
diff --git a/system/main/shim/stack.cc b/system/main/shim/stack.cc
index 017b954..86f95ec 100644
--- a/system/main/shim/stack.cc
+++ b/system/main/shim/stack.cc
@@ -26,7 +26,6 @@
 
 #include "device/include/controller.h"
 #include "gd/att/att_module.h"
-#include "gd/btaa/activity_attribution.h"
 #include "gd/common/init_flags.h"
 #include "gd/common/strings.h"
 #include "gd/hal/hci_hal.h"
@@ -55,7 +54,6 @@
 #include "gd/storage/storage_module.h"
 #include "gd/sysprops/sysprops_module.h"
 #include "main/shim/acl_legacy_interface.h"
-#include "main/shim/activity_attribution.h"
 #include "main/shim/distance_measurement_manager.h"
 #include "main/shim/hci_layer.h"
 #include "main/shim/helpers.h"
@@ -102,50 +100,26 @@
   modules.add<hci::MsftExtensionManager>();
   modules.add<hci::LeScanningManager>();
   modules.add<hci::DistanceMeasurementManager>();
-  if (common::init_flags::btaa_hci_is_enabled()) {
-    modules.add<activity_attribution::ActivityAttribution>();
-  }
-  if (common::init_flags::gd_core_is_enabled()) {
-    modules.add<att::AttModule>();
-    modules.add<neighbor::ConnectabilityModule>();
-    modules.add<neighbor::DiscoverabilityModule>();
-    modules.add<neighbor::InquiryModule>();
-    modules.add<neighbor::NameDbModule>();
-    modules.add<neighbor::PageModule>();
-    modules.add<neighbor::ScanModule>();
-    modules.add<storage::StorageModule>();
-  }
   Start(&modules);
   is_running_ = true;
   // Make sure the leaf modules are started
   ASSERT(stack_manager_.GetInstance<storage::StorageModule>() != nullptr);
   ASSERT(stack_manager_.GetInstance<shim::Dumpsys>() != nullptr);
-  if (common::init_flags::gd_core_is_enabled()) {
-    btm_ = new Btm(stack_handler_,
-                   stack_manager_.GetInstance<neighbor::InquiryModule>());
-  }
-  if (!common::init_flags::gd_core_is_enabled()) {
-    if (stack_manager_.IsStarted<hci::Controller>()) {
-      acl_ = new legacy::Acl(
-          stack_handler_, legacy::GetAclInterface(),
-          controller_get_interface()->get_ble_acceptlist_size(),
-          controller_get_interface()->get_ble_resolving_list_max_size());
-    } else {
-      LOG_ERROR(
-          "Unable to create shim ACL layer as Controller has not started");
-    }
+  if (stack_manager_.IsStarted<hci::Controller>()) {
+    acl_ = new legacy::Acl(
+        stack_handler_, legacy::GetAclInterface(),
+        controller_get_interface()->get_ble_acceptlist_size(),
+        controller_get_interface()->get_ble_resolving_list_max_size());
+  } else {
+    LOG_ERROR("Unable to create shim ACL layer as Controller has not started");
   }
 
-  if (!common::init_flags::gd_core_is_enabled()) {
-    bluetooth::shim::hci_on_reset_complete();
-  }
-
+  bluetooth::shim::hci_on_reset_complete();
   bluetooth::shim::init_advertising_manager();
   bluetooth::shim::init_scanning_manager();
   bluetooth::shim::init_distance_measurement_manager();
 
-  if (common::init_flags::gd_l2cap_is_enabled() &&
-      !common::init_flags::gd_core_is_enabled()) {
+  if (common::init_flags::gd_l2cap_is_enabled()) {
     L2CA_UseLegacySecurityModule();
   }
 }
@@ -165,9 +139,7 @@
 
 void Stack::Stop() {
   std::lock_guard<std::recursive_mutex> lock(mutex_);
-  if (!common::init_flags::gd_core_is_enabled()) {
-    bluetooth::shim::hci_on_shutting_down();
-  }
+  bluetooth::shim::hci_on_shutting_down();
 
   // Make sure gd acl flag is enabled and we started it up
   if (acl_ != nullptr) {
diff --git a/system/main/shim/utils.cc b/system/main/shim/utils.cc
new file mode 100644
index 0000000..9f18ddc
--- /dev/null
+++ b/system/main/shim/utils.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2023 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 "utils.h"
+
+namespace bluetooth {
+namespace shim {
+void parse_gap_data(const std::vector<uint8_t> &raw_data,
+                    std::vector<hci::GapData> &output) {
+    size_t offset = 0;
+    while (offset < raw_data.size()) {
+      hci::GapData gap_data;
+      uint8_t len = raw_data[offset];
+
+      if (offset + len + 1 > raw_data.size()) {
+        break;
+      }
+
+      auto begin = raw_data.begin() + offset;
+      auto end = begin + len + 1;  // 1 byte for len
+      auto data_copy = std::make_shared<std::vector<uint8_t>>(begin, end);
+      bluetooth::packet::PacketView<bluetooth::packet::kLittleEndian> packet(
+          data_copy);
+      hci::GapData::Parse(&gap_data, packet.begin());
+      output.push_back(gap_data);
+      offset += len + 1;  // 1 byte for len
+    }
+}
+
+}  // namespace shim
+}  // namespace bluetooth
diff --git a/system/main/shim/activity_attribution.h b/system/main/shim/utils.h
similarity index 67%
rename from system/main/shim/activity_attribution.h
rename to system/main/shim/utils.h
index 0f12534..56da2a0 100644
--- a/system/main/shim/activity_attribution.h
+++ b/system/main/shim/utils.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright 2023 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.
@@ -13,20 +13,20 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-/**
- * Gd shim layer to activity attribution
- */
 #pragma once
+#include <vector>
 
-#include "include/hardware/bt_activity_attribution.h"
-
-using namespace bluetooth::activity_attribution;
+#include "hci/hci_packets.h"
 
 namespace bluetooth {
 namespace shim {
-
-ActivityAttributionInterface* get_activity_attribution_instance();
-
+/**
+ * @brief Parsing gap data from raw bytes
+ *
+ * @param raw_data input, raw bytes
+ * @param output vector of GapData
+ */
+void parse_gap_data(const std::vector<uint8_t> &raw_data,
+                    std::vector<hci::GapData> &output);
 }  // namespace shim
 }  // namespace bluetooth
diff --git a/system/main/test/main_shim_test.cc b/system/main/test/main_shim_test.cc
index 83c6f43..27c4779 100644
--- a/system/main/test/main_shim_test.cc
+++ b/system/main/test/main_shim_test.cc
@@ -20,12 +20,13 @@
 #include <unistd.h>
 
 #include <cstddef>
+#include <cstdint>
 #include <cstdio>
 #include <future>
 #include <map>
 #include <optional>
+#include <vector>
 
-#include "btaa/activity_attribution.h"
 #include "btif/include/btif_hh.h"
 #include "device/include/controller.h"
 #include "hal/hci_hal.h"
@@ -44,12 +45,12 @@
 #include "hci/le_advertising_manager_mock.h"
 #include "hci/le_scanning_manager_mock.h"
 #include "include/hardware/ble_scanner.h"
-#include "include/hardware/bt_activity_attribution.h"
 #include "main/shim/acl.h"
 #include "main/shim/acl_legacy_interface.h"
 #include "main/shim/ble_scanner_interface_impl.h"
 #include "main/shim/helpers.h"
 #include "main/shim/le_advertising_manager.h"
+#include "main/shim/utils.h"
 #include "main/shim/le_scanning_manager.h"
 #include "os/handler.h"
 #include "os/mock_queue.h"
@@ -351,15 +352,6 @@
 }  // namespace testing
 }  // namespace shim
 
-namespace activity_attribution {
-ActivityAttributionInterface* get_activity_attribution_instance() {
-  return nullptr;
-}
-
-const ModuleFactory ActivityAttribution::Factory =
-    ModuleFactory([]() { return nullptr; });
-}  // namespace activity_attribution
-
 namespace hal {
 const ModuleFactory HciHal::Factory = ModuleFactory([]() { return nullptr; });
 }  // namespace hal
@@ -804,3 +796,15 @@
 
   DumpsysNeighbor(STDOUT_FILENO);
 }
+
+// test for b/277590580
+
+using bluetooth::hci::GapData;
+TEST(MainShimRegressionTest, OOB_In_StartAdvertisingSet) {
+  std::vector<uint8_t> raw_data = {10, 0, 0, 0, 0};
+  std::vector<GapData> res;
+
+  bluetooth::shim::parse_gap_data(raw_data, res);
+
+  ASSERT_EQ(res.size(), (size_t) 0);
+}
diff --git a/system/osi/Android.bp b/system/osi/Android.bp
index d131a39..2326dc3 100644
--- a/system/osi/Android.bp
+++ b/system/osi/Android.bp
@@ -87,6 +87,11 @@
     apex_available: [
         "com.android.btservices",
     ],
+    header_libs: ["libbluetooth_headers"],
+    static_libs: [
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+    ],
 }
 
 // libosi unit tests for target and host
@@ -129,6 +134,8 @@
     static_libs: [
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libc++fs",
         "libchrome",
         "libevent",
@@ -151,4 +158,5 @@
         address: true,
         cfi: false,
     },
+    header_libs: ["libbluetooth_headers"],
 }
diff --git a/system/osi/test/alarm_test.cc b/system/osi/test/alarm_test.cc
index 9a59166..c6e7527 100644
--- a/system/osi/test/alarm_test.cc
+++ b/system/osi/test/alarm_test.cc
@@ -59,7 +59,7 @@
 }
 
 static bt_os_callouts_t bt_wakelock_callouts = {
-    sizeof(bt_os_callouts_t), NULL, acquire_wake_lock_cb, release_wake_lock_cb};
+    sizeof(bt_os_callouts_t), acquire_wake_lock_cb, release_wake_lock_cb};
 
 class AlarmTest : public ::testing::Test {
  protected:
diff --git a/system/osi/test/fuzzers/alarm/Android.bp b/system/osi/test/fuzzers/alarm/Android.bp
index 4e09043..b28bcca 100644
--- a/system/osi/test/fuzzers/alarm/Android.bp
+++ b/system/osi/test/fuzzers/alarm/Android.bp
@@ -28,4 +28,5 @@
         "libosi",
     ],
     cflags: ["-Wno-unused-function"],
+    header_libs: ["libbluetooth_headers"],
 }
diff --git a/system/osi/test/wakelock_test.cc b/system/osi/test/wakelock_test.cc
index 4af764e..25fa4ce 100644
--- a/system/osi/test/wakelock_test.cc
+++ b/system/osi/test/wakelock_test.cc
@@ -39,7 +39,7 @@
 }
 
 static bt_os_callouts_t bt_wakelock_callouts = {
-    sizeof(bt_os_callouts_t), NULL, acquire_wake_lock_cb, release_wake_lock_cb};
+    sizeof(bt_os_callouts_t), acquire_wake_lock_cb, release_wake_lock_cb};
 
 class WakelockTest : public ::testing::Test {
  protected:
diff --git a/system/pdl/Android.bp b/system/pdl/Android.bp
new file mode 100644
index 0000000..9051a8d
--- /dev/null
+++ b/system/pdl/Android.bp
@@ -0,0 +1,51 @@
+genrule_defaults {
+    name: "BluetoothGeneratedPackets_default",
+    tools: ["bluetooth_packetgen"],
+    cmd: "$(location bluetooth_packetgen) --fuzzing --testing --include=packages/modules/Bluetooth/system/pdl --out=$(genDir) $(in)",
+    defaults_visibility: [":__subpackages__"],
+}
+
+// TODO get rid of this by converting the l2cap cert tests (or deprecating them)
+genrule {
+    name: "BluetoothGeneratedPackets_python3_cc",
+    tools: [
+        "bluetooth_packetgen",
+    ],
+    cmd: "$(location bluetooth_packetgen) --include=packages/modules/Bluetooth/system/pdl --out=$(genDir) --num_shards=10 $(in)",
+    srcs: [
+        "l2cap/l2cap_packets.pdl",
+    ],
+    out: [
+        "l2cap/l2cap_packets_python3.cc",
+        "l2cap/l2cap_packets_python3_shard_0.cc",
+        "l2cap/l2cap_packets_python3_shard_1.cc",
+        "l2cap/l2cap_packets_python3_shard_2.cc",
+        "l2cap/l2cap_packets_python3_shard_3.cc",
+        "l2cap/l2cap_packets_python3_shard_4.cc",
+        "l2cap/l2cap_packets_python3_shard_5.cc",
+        "l2cap/l2cap_packets_python3_shard_6.cc",
+        "l2cap/l2cap_packets_python3_shard_7.cc",
+        "l2cap/l2cap_packets_python3_shard_8.cc",
+        "l2cap/l2cap_packets_python3_shard_9.cc",
+    ],
+}
+
+// TODO replace progressivly by the appropriate static libs
+genrule {
+    name: "BluetoothGeneratedPackets_h",
+    tools: [
+        "bluetooth_packetgen",
+    ],
+    cmd: "$(location bluetooth_packetgen) --fuzzing --testing --include=packages/modules/Bluetooth/system/pdl --out=$(genDir) $(in)",
+    srcs: [
+        "hci/hci_packets.pdl",
+        "l2cap/l2cap_packets.pdl",
+        "security/smp_packets.pdl",
+    ],
+    out: [
+        "hci/hci_packets.h",
+        "l2cap/l2cap_packets.h",
+        "security/smp_packets.h",
+    ],
+    visibility: ["//visibility:public"],
+}
diff --git a/system/gd/btaa/BUILD.gn b/system/pdl/BUILD.gn
similarity index 65%
copy from system/gd/btaa/BUILD.gn
copy to system/pdl/BUILD.gn
index 9beaf1e..ac6dca4 100644
--- a/system/gd/btaa/BUILD.gn
+++ b/system/pdl/BUILD.gn
@@ -1,5 +1,5 @@
 #
-#  Copyright 2021 Google, Inc.
+#  Copyright 2023 Google, Inc.
 #
 #  Licensed under the Apache License, Version 2.0 (the "License");
 #  you may not use this file except in compliance with the License.
@@ -12,11 +12,16 @@
 #  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.
-#
 
-source_set("BluetoothBtaaSources_linux") {
-  sources = [ "linux/activity_attribution.cc" ]
+import("//bt/system/gd/packet/parser/packetgen.gni")
 
-  configs += [ "//bt/system/gd:gd_defaults" ]
-  deps = [ "//bt/system/gd:gd_default_deps" ]
+packetgen_headers("BluetoothGeneratedPackets_h") {
+  sources = [
+    "hci/hci_packets.pdl",
+    "l2cap/l2cap_packets.pdl",
+    "security/smp_packets.pdl",
+  ]
+
+  include = "system/pdl"
+  source_root = "../.."
 }
diff --git a/system/pdl/hci/Android.bp b/system/pdl/hci/Android.bp
new file mode 100644
index 0000000..d1c7a39
--- /dev/null
+++ b/system/pdl/hci/Android.bp
@@ -0,0 +1,77 @@
+genrule {
+    name: "BluetoothGeneratedPacketsHci_h",
+    defaults: ["BluetoothGeneratedPackets_default"],
+    srcs: ["hci_packets.pdl"],
+    out: ["hci/hci_packets.h"],
+    visibility: ["//visibility:private"],
+}
+
+genrule {
+    name: "BluetoothGeneratedPackets_rust",
+    defaults: ["pdl_rust_generator_defaults"],
+    srcs: ["hci_packets.pdl"],
+    out: ["hci/hci_packets.rs"],
+}
+
+cc_library_headers {
+    name: "libbluetooth_hci_pdl_header",
+    vendor_available: true,
+    export_include_dirs: [
+        "include",
+    ],
+    generated_headers: [
+        "BluetoothGeneratedPacketsHci_h",
+    ],
+    export_generated_headers: [
+        "BluetoothGeneratedPacketsHci_h",
+    ],
+    host_supported: true,
+    apex_available: [
+        "com.android.btservices",
+    ],
+    min_sdk_version: "33",
+}
+
+cc_library_static {
+    name: "libbluetooth_hci_pdl",
+    vendor_available: true,
+    srcs: [
+        "address.cc",
+        "class_of_device.cc",
+    ],
+    header_libs: [
+        "libbluetooth_hci_pdl_header",
+    ],
+    export_header_lib_headers: [
+        "libbluetooth_hci_pdl_header",
+    ],
+    // TODO remove this
+    // Directly add the common library + the os library as a dependency that export their header_libs
+    include_dirs: [
+        "packages/modules/Bluetooth/system/gd",
+    ],
+    host_supported: true,
+    visibility: [
+        "//hardware/interfaces/bluetooth/aidl/vts",
+        "//packages/modules/Bluetooth/system:__subpackages__",
+    ],
+    apex_available: [
+        "com.android.btservices",
+    ],
+    min_sdk_version: "33",
+}
+
+// Generate the python parser+serializer backend
+genrule {
+    name: "gd_hci_packets_python3_gen",
+    defaults: ["pdl_python_generator_defaults"],
+    cmd: "$(location :pdlc) $(in) |" +
+        " $(location :pdl_python_generator)" +
+        " --output $(out) --custom-type-location blueberry.utils.bluetooth",
+    srcs: [
+        "hci_packets.pdl",
+    ],
+    out: [
+        "hci_packets.py",
+    ],
+}
diff --git a/system/pdl/hci/address.cc b/system/pdl/hci/address.cc
new file mode 100644
index 0000000..8736c16
--- /dev/null
+++ b/system/pdl/hci/address.cc
@@ -0,0 +1,139 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 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 "hci/address.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstdio>
+#include <iomanip>
+#include <sstream>
+
+namespace bluetooth {
+namespace hci {
+
+const Address Address::kAny{{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}};
+const Address Address::kEmpty{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
+
+// Address cannot initialize member variables as it is a POD type
+// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
+Address::Address(const uint8_t (&addr)[6]) {
+  std::copy(addr, addr + kLength, data());
+}
+
+Address::Address(std::initializer_list<uint8_t> l) {
+  std::copy(l.begin(), std::min(l.begin() + kLength, l.end()), data());
+}
+
+std::string Address::_ToMaskedColonSepHexString(int bytes_to_mask) const {
+  std::stringstream ss;
+  int count = 0;
+  for (auto it = address.rbegin(); it != address.rend(); it++) {
+    if (count++ < bytes_to_mask) {
+      ss << "xx";
+    } else {
+      ss << std::nouppercase << std::hex << std::setw(2) << std::setfill('0')
+         << +*it;
+    }
+    if (std::next(it) != address.rend()) {
+      ss << ':';
+    }
+  }
+  return ss.str();
+}
+
+std::string Address::ToString() const { return _ToMaskedColonSepHexString(0); }
+
+std::string Address::ToColonSepHexString() const {
+  return _ToMaskedColonSepHexString(0);
+}
+
+std::string Address::ToStringForLogging() const {
+  return _ToMaskedColonSepHexString(0);
+}
+
+std::string Address::ToRedactedStringForLogging() const {
+  return _ToMaskedColonSepHexString(4);
+}
+
+std::string Address::ToLegacyConfigString() const { return ToString(); }
+
+std::optional<Address> Address::FromLegacyConfigString(const std::string& str) {
+  return FromString(str);
+}
+
+std::optional<Address> Address::FromString(const std::string& from) {
+  if (from.length() != 17) {
+    return std::nullopt;
+  }
+
+  Address addr{};
+  std::istringstream stream(from);
+  std::string token;
+  int index = 0;
+  while (getline(stream, token, ':')) {
+    if (index >= 6) {
+      return std::nullopt;
+    }
+
+    if (token.length() != 2) {
+      return std::nullopt;
+    }
+
+    char* temp = nullptr;
+    addr.address.at(5 - index) = std::strtol(token.c_str(), &temp, 16);
+    if (temp == token.c_str()) {
+      // string token is empty or has wrong format
+      return std::nullopt;
+    }
+    if (temp != (token.c_str() + token.size())) {
+      // cannot parse whole string
+      return std::nullopt;
+    }
+
+    index++;
+  }
+
+  if (index != 6) {
+    return std::nullopt;
+  }
+
+  return addr;
+}
+
+bool Address::FromString(const std::string& from, Address& to) {
+  auto addr = FromString(from);
+  if (!addr) {
+    to = {};
+    return false;
+  }
+  to = std::move(*addr);
+  return true;
+}
+
+size_t Address::FromOctets(const uint8_t* from) {
+  std::copy(from, from + kLength, data());
+  return kLength;
+};
+
+bool Address::IsValidAddress(const std::string& address) {
+  return Address::FromString(address).has_value();
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/system/pdl/hci/class_of_device.cc b/system/pdl/hci/class_of_device.cc
new file mode 100644
index 0000000..c4506f5d
--- /dev/null
+++ b/system/pdl/hci/class_of_device.cc
@@ -0,0 +1,137 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 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 "hci/class_of_device.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <sstream>
+#include <vector>
+
+namespace bluetooth {
+namespace hci {
+
+// ClassOfDevice cannot initialize member variables as it is a POD type
+// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
+ClassOfDevice::ClassOfDevice(const uint8_t (&class_of_device)[kLength]) {
+  std::copy(class_of_device, class_of_device + kLength, cod.data());
+};
+
+std::string ClassOfDevice::ToString() const {
+  char buffer[] = "000-0-00";
+  std::snprintf(&buffer[0], sizeof(buffer), "%03x-%01x-%02x",
+                (static_cast<uint16_t>(cod[2]) << 4) | cod[1] >> 4,
+                cod[1] & 0x0f, cod[0]);
+  std::string str(buffer);
+  return str;
+}
+
+std::string ClassOfDevice::ToLegacyConfigString() const {
+  return std::to_string(ToUint32Legacy());
+}
+
+std::optional<ClassOfDevice> ClassOfDevice::FromString(const std::string& str) {
+  if (str.length() != 8) {
+    return std::nullopt;
+  }
+
+  std::istringstream stream(str);
+  std::string token;
+  int index = 0;
+  uint16_t values[3];
+
+  ClassOfDevice new_cod{};
+  while (getline(stream, token, '-')) {
+    if (index >= 3) {
+      return std::nullopt;
+    }
+
+    if (index == 0 && token.length() != 3) {
+      return std::nullopt;
+    } else if (index == 1 && token.length() != 1) {
+      return std::nullopt;
+    } else if (index == 2 && token.length() != 2) {
+      return std::nullopt;
+    }
+    char* temp = nullptr;
+    values[index] = std::strtol(token.c_str(), &temp, 16);
+    if (*temp != '\0') {
+      return std::nullopt;
+    }
+
+    index++;
+  }
+
+  if (index != 3) {
+    return std::nullopt;
+  }
+
+  new_cod.cod[0] = values[2];
+  new_cod.cod[1] = values[1] | ((values[0] & 0xf) << 4);
+  new_cod.cod[2] = values[0] >> 4;
+
+  return new_cod;
+}
+
+bool ClassOfDevice::FromString(const std::string& from, ClassOfDevice& to) {
+  auto new_cod = FromString(from);
+  if (!new_cod) {
+    to = {};
+    return false;
+  }
+  to = std::move(*new_cod);
+  return true;
+}
+
+std::optional<ClassOfDevice> ClassOfDevice::FromUint32Legacy(uint32_t cod_int) {
+  if (cod_int != 0 && (cod_int >> 24) != 0) {
+    return std::nullopt;
+  }
+  ClassOfDevice result = {};
+  result.cod[2] = static_cast<uint8_t>(cod_int);
+  result.cod[1] = static_cast<uint8_t>(cod_int >> 8);
+  result.cod[0] = static_cast<uint8_t>(cod_int >> 16);
+  return result;
+}
+
+std::optional<ClassOfDevice> ClassOfDevice::FromLegacyConfigString(
+    const std::string& str) {
+  char* ptr = nullptr;
+  auto num = std::strtoull(str.data(), &ptr, 10);
+  if (num > 0xffffff) {
+    return std::nullopt;
+  }
+  return FromUint32Legacy(static_cast<uint32_t>(num));
+}
+
+uint32_t ClassOfDevice::ToUint32Legacy() const {
+  return (cod[2]) | (cod[1] << 8) | (cod[0] << 16);
+}
+
+size_t ClassOfDevice::FromOctets(const uint8_t* from) {
+  std::copy(from, from + kLength, data());
+  return kLength;
+};
+
+bool ClassOfDevice::IsValid(const std::string& cod) {
+  return ClassOfDevice::FromString(cod).has_value();
+}
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/system/gd/hci/hci_packets.pdl b/system/pdl/hci/hci_packets.pdl
similarity index 100%
rename from system/gd/hci/hci_packets.pdl
rename to system/pdl/hci/hci_packets.pdl
diff --git a/system/pdl/hci/include/hci/address.h b/system/pdl/hci/include/hci/address.h
new file mode 100644
index 0000000..283c9c3
--- /dev/null
+++ b/system/pdl/hci/include/hci/address.h
@@ -0,0 +1,112 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 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 <array>
+#include <cstring>
+#include <initializer_list>
+#include <optional>
+#include <ostream>
+#include <string>
+
+#include "common/interfaces/ILoggable.h"
+#include "packet/custom_field_fixed_size_interface.h"
+#include "storage/serializable.h"
+
+namespace bluetooth {
+namespace hci {
+
+class Address final : public packet::CustomFieldFixedSizeInterface<Address>,
+                      public storage::Serializable<Address>,
+                      public bluetooth::common::IRedactableLoggable {
+ public:
+  static constexpr size_t kLength = 6;
+
+  // Bluetooth MAC address bytes saved in little endian format.
+  // The address MSB is address[5], the address LSB is address[0].
+  // Note that the textual representation follows the big endian format,
+  // ie. Address{0, 1, 2, 3, 4, 5} is represented as 05:04:03:02:01:00.
+  std::array<uint8_t, kLength> address = {};
+
+  Address() = default;
+  Address(const uint8_t (&addr)[kLength]);
+  Address(std::initializer_list<uint8_t> l);
+
+  // CustomFieldFixedSizeInterface methods
+  inline uint8_t* data() override { return address.data(); }
+  inline const uint8_t* data() const override { return address.data(); }
+
+  // storage::Serializable methods
+  std::string ToString() const override;
+  std::string ToColonSepHexString() const;
+  std::string ToStringForLogging() const override;
+  std::string ToRedactedStringForLogging() const override;
+
+  static std::optional<Address> FromString(const std::string& from);
+  std::string ToLegacyConfigString() const override;
+  static std::optional<Address> FromLegacyConfigString(const std::string& str);
+
+  bool operator<(const Address& rhs) const { return address < rhs.address; }
+  bool operator==(const Address& rhs) const { return address == rhs.address; }
+  bool operator>(const Address& rhs) const { return (rhs < *this); }
+  bool operator<=(const Address& rhs) const { return !(*this > rhs); }
+  bool operator>=(const Address& rhs) const { return !(*this < rhs); }
+  bool operator!=(const Address& rhs) const { return !(*this == rhs); }
+
+  bool IsEmpty() const { return *this == kEmpty; }
+
+  // Converts |string| to Address and places it in |to|. If |from| does
+  // not represent a Bluetooth address, |to| is not modified and this function
+  // returns false. Otherwise, it returns true.
+  static bool FromString(const std::string& from, Address& to);
+
+  // Copies |from| raw Bluetooth address octets to the local object.
+  // Returns the number of copied octets - should be always Address::kLength
+  size_t FromOctets(const uint8_t* from);
+
+  static bool IsValidAddress(const std::string& address);
+
+  static const Address kEmpty;  // 00:00:00:00:00:00
+  static const Address kAny;    // FF:FF:FF:FF:FF:FF
+ private:
+  std::string _ToMaskedColonSepHexString(int bytes_to_mask) const;
+};
+
+// TODO: to fine-tune this.
+// we need an interface between the logger and ILoggable
+inline std::ostream& operator<<(std::ostream& os, const Address& a) {
+  os << a.ToString();
+  return os;
+}
+
+}  // namespace hci
+}  // namespace bluetooth
+
+namespace std {
+template <>
+struct hash<bluetooth::hci::Address> {
+  std::size_t operator()(const bluetooth::hci::Address& val) const {
+    static_assert(sizeof(uint64_t) >= bluetooth::hci::Address::kLength);
+    uint64_t int_addr = 0;
+    memcpy(reinterpret_cast<uint8_t*>(&int_addr), val.data(),
+           bluetooth::hci::Address::kLength);
+    return std::hash<uint64_t>{}(int_addr);
+  }
+};
+}  // namespace std
diff --git a/system/pdl/hci/include/hci/class_of_device.h b/system/pdl/hci/include/hci/class_of_device.h
new file mode 100644
index 0000000..a310ba9
--- /dev/null
+++ b/system/pdl/hci/include/hci/class_of_device.h
@@ -0,0 +1,85 @@
+/******************************************************************************
+ *
+ *  Copyright 2019 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 <array>
+#include <optional>
+#include <string>
+
+#include "packet/custom_field_fixed_size_interface.h"
+#include "storage/serializable.h"
+
+namespace bluetooth {
+namespace hci {
+
+class ClassOfDevice final
+    : public packet::CustomFieldFixedSizeInterface<ClassOfDevice>,
+      public storage::Serializable<ClassOfDevice> {
+ public:
+  static constexpr size_t kLength = 3;
+
+  std::array<uint8_t, kLength> cod = {};
+
+  ClassOfDevice() = default;
+  ClassOfDevice(const uint8_t (&class_of_device)[kLength]);
+
+  // packet::CustomFieldFixedSizeInterface methods
+  inline uint8_t* data() override { return cod.data(); }
+  inline const uint8_t* data() const override { return cod.data(); }
+
+  // storage::Serializable methods
+  std::string ToString() const;
+  static std::optional<ClassOfDevice> FromString(const std::string& str);
+  std::string ToLegacyConfigString() const override;
+  static std::optional<ClassOfDevice> FromLegacyConfigString(
+      const std::string& str);
+
+  bool operator<(const ClassOfDevice& rhs) const { return cod < rhs.cod; }
+  bool operator==(const ClassOfDevice& rhs) const { return cod == rhs.cod; }
+  bool operator>(const ClassOfDevice& rhs) const { return (rhs < *this); }
+  bool operator<=(const ClassOfDevice& rhs) const { return !(*this > rhs); }
+  bool operator>=(const ClassOfDevice& rhs) const { return !(*this < rhs); }
+  bool operator!=(const ClassOfDevice& rhs) const { return !(*this == rhs); }
+
+  // Converts |string| to ClassOfDevice and places it in |to|. If |from| does
+  // not represent a Class of Device, |to| is not modified and this function
+  // returns false. Otherwise, it returns true.
+  static bool FromString(const std::string& from, ClassOfDevice& to);
+
+  // Converts uint32_t encoded class of device to ClassOfDevice object
+  // uint32_t encoding:
+  //     <high> uint8_t(cod[0]) | uint8_t(cod[1]) | uint8_t(cod[2]) <low>
+  // Only used in legacy stack device config
+  static std::optional<ClassOfDevice> FromUint32Legacy(uint32_t cod_int);
+  uint32_t ToUint32Legacy() const;
+
+  // Copies |from| raw Class of Device octets to the local object.
+  // Returns the number of copied octets (always ClassOfDevice::kLength)
+  size_t FromOctets(const uint8_t* from);
+
+  static bool IsValid(const std::string& class_of_device);
+};
+
+inline std::ostream& operator<<(std::ostream& os, const ClassOfDevice& c) {
+  os << c.ToString();
+  return os;
+}
+
+}  // namespace hci
+}  // namespace bluetooth
diff --git a/system/pdl/l2cap/Android.bp b/system/pdl/l2cap/Android.bp
new file mode 100644
index 0000000..d559359
--- /dev/null
+++ b/system/pdl/l2cap/Android.bp
@@ -0,0 +1,37 @@
+genrule {
+    name: "BluetoothGeneratedPacketsL2cap_h",
+    defaults: ["BluetoothGeneratedPackets_default"],
+    srcs: ["l2cap_packets.pdl"],
+    out: ["l2cap/l2cap_packets.h"],
+    visibility: ["//visibility:private"],
+}
+
+cc_library_headers {
+    name: "libbluetooth_l2cap_pdl_header",
+    generated_headers: [
+        "BluetoothGeneratedPacketsL2cap_h",
+    ],
+    export_generated_headers: [
+        "BluetoothGeneratedPacketsL2cap_h",
+    ],
+    host_supported: true,
+    apex_available: [
+        "com.android.btservices",
+    ],
+    min_sdk_version: "33",
+}
+
+cc_library_static {
+    name: "libbluetooth_l2cap_pdl",
+    header_libs: [
+        "libbluetooth_l2cap_pdl_header",
+    ],
+    export_header_lib_headers: [
+        "libbluetooth_l2cap_pdl_header",
+    ],
+    host_supported: true,
+    apex_available: [
+        "com.android.btservices",
+    ],
+    min_sdk_version: "33",
+}
diff --git a/system/gd/l2cap/l2cap_packets.pdl b/system/pdl/l2cap/l2cap_packets.pdl
similarity index 100%
rename from system/gd/l2cap/l2cap_packets.pdl
rename to system/pdl/l2cap/l2cap_packets.pdl
diff --git a/system/pdl/security/Android.bp b/system/pdl/security/Android.bp
new file mode 100644
index 0000000..ebd5a3d
--- /dev/null
+++ b/system/pdl/security/Android.bp
@@ -0,0 +1,52 @@
+genrule {
+    name: "BluetoothGeneratedPacketsSmp_h",
+    defaults: ["BluetoothGeneratedPackets_default"],
+    srcs: ["smp_packets.pdl"],
+    out: ["security/smp_packets.h"],
+    visibility: ["//visibility:private"],
+}
+
+cc_library_headers {
+    name: "libbluetooth_smp_pdl_header",
+    generated_headers: [
+        "BluetoothGeneratedPacketsSmp_h",
+    ],
+    export_generated_headers: [
+        "BluetoothGeneratedPacketsSmp_h",
+    ],
+    host_supported: true,
+    apex_available: [
+        "com.android.btservices",
+    ],
+    min_sdk_version: "33",
+}
+
+cc_library_static {
+    name: "libbluetooth_smp_pdl",
+    header_libs: [
+        "libbluetooth_smp_pdl_header",
+    ],
+    export_header_lib_headers: [
+        "libbluetooth_smp_pdl_header",
+    ],
+    host_supported: true,
+    apex_available: [
+        "com.android.btservices",
+    ],
+    min_sdk_version: "33",
+}
+
+// Generate the python parser+serializer backend
+genrule {
+    name: "gd_smp_packets_python3_gen",
+    defaults: ["pdl_python_generator_defaults"],
+    cmd: "$(location :pdlc) $(in) |" +
+        " $(location :pdl_python_generator)" +
+        " --output $(out) --custom-type-location blueberry.utils.bluetooth",
+    srcs: [
+        "smp_packets.pdl",
+    ],
+    out: [
+        "smp_packets.py",
+    ],
+}
diff --git a/system/gd/security/smp_packets.pdl b/system/pdl/security/smp_packets.pdl
similarity index 100%
rename from system/gd/security/smp_packets.pdl
rename to system/pdl/security/smp_packets.pdl
diff --git a/system/profile/avrcp/Android.bp b/system/profile/avrcp/Android.bp
index 719697c..6b829e2 100644
--- a/system/profile/avrcp/Android.bp
+++ b/system/profile/avrcp/Android.bp
@@ -27,6 +27,7 @@
     static_libs: [
         "lib-bt-packets",
         "libbluetooth-types",
+        "libbt_shim_bridge",
         "libosi",
     ],
     whole_static_libs: [
@@ -39,6 +40,7 @@
         "com.android.btservices",
     ],
     min_sdk_version: "Tiramisu",
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -69,7 +71,10 @@
         "lib-bt-packets-avrcp",
         "lib-bt-packets-base",
         "libbase",
+        "libbluetooth-types",
         "libbluetooth_gd",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtdevice",
         "libchrome",
         "libcutils",
@@ -83,6 +88,7 @@
     },
 
     cflags: ["-DBUILDCFG"],
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_fuzz {
@@ -107,7 +113,10 @@
         "lib-bt-packets-avrcp",
         "lib-bt-packets-base",
         "libbase",
+        "libbluetooth-types",
         "libbluetooth_gd",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libcutils",
         "libevent",
diff --git a/system/profile/avrcp/connection_handler.cc b/system/profile/avrcp/connection_handler.cc
index d892c93..dfe0916 100644
--- a/system/profile/avrcp/connection_handler.cc
+++ b/system/profile/avrcp/connection_handler.cc
@@ -45,7 +45,10 @@
 
 ConnectionHandler* ConnectionHandler::instance_ = nullptr;
 
-std::mutex device_map_lock;
+// ConnectionHandler::CleanUp take the lock and calls
+// ConnectionHandler::AcceptorControlCB with AVRC_CLOSE_IND_EVT
+// which also takes the lock, so use a recursive_mutex.
+std::recursive_mutex device_map_lock;
 
 ConnectionHandler* ConnectionHandler::Get() {
   CHECK(instance_);
@@ -95,7 +98,7 @@
   CHECK(instance_ != nullptr);
 
   // TODO (apanicke): Cleanup the SDP Entries here
-  std::lock_guard<std::mutex> lock(device_map_lock);
+  std::lock_guard<std::recursive_mutex> lock(device_map_lock);
   for (auto entry = instance_->device_map_.begin();
        entry != instance_->device_map_.end();) {
     auto curr = entry;
@@ -175,7 +178,7 @@
 std::vector<std::shared_ptr<Device>> ConnectionHandler::GetListOfDevices()
     const {
   std::vector<std::shared_ptr<Device>> list;
-  std::lock_guard<std::mutex> lock(device_map_lock);
+  std::lock_guard<std::recursive_mutex> lock(device_map_lock);
   for (const auto& device : device_map_) {
     list.push_back(device.second);
   }
@@ -298,7 +301,7 @@
             << "Connection Close received from device that doesn't exist";
         return;
       }
-      std::lock_guard<std::mutex> lock(device_map_lock);
+      std::lock_guard<std::recursive_mutex> lock(device_map_lock);
       avrc_->Close(handle);
       feature_map_.erase(device_map_[handle]->GetAddress());
       device_map_[handle]->DeviceDisconnected();
@@ -344,7 +347,7 @@
           btif_av_peer_is_connected_source(*peer_addr)) {
         LOG(WARNING) << "peer is src, close new avrcp cback";
         if (device_map_.find(handle) != device_map_.end()) {
-          std::lock_guard<std::mutex> lock(device_map_lock);
+          std::lock_guard<std::recursive_mutex> lock(device_map_lock);
           feature_map_.erase(device_map_[handle]->GetAddress());
           device_map_[handle]->DeviceDisconnected();
           device_map_.erase(handle);
@@ -407,7 +410,7 @@
         return;
       }
       {
-        std::lock_guard<std::mutex> lock(device_map_lock);
+        std::lock_guard<std::recursive_mutex> lock(device_map_lock);
         feature_map_.erase(device_map_[handle]->GetAddress());
         device_map_[handle]->DeviceDisconnected();
         device_map_.erase(handle);
@@ -515,7 +518,9 @@
         VLOG(1) << __PRETTY_FUNCTION__ << " Get Supported categories";
         tSDP_DISC_ATTR* sdp_attribute =
             sdp_->FindAttributeInRec(sdp_record, ATTR_ID_SUPPORTED_FEATURES);
-        if (sdp_attribute != NULL) {
+        if (sdp_attribute != NULL &&
+            SDP_DISC_ATTR_TYPE(sdp_attribute->attr_len_type) == UINT_DESC_TYPE &&
+            SDP_DISC_ATTR_LEN(sdp_attribute->attr_len_type) >= 2) {
           VLOG(1) << __PRETTY_FUNCTION__
                   << "Get Supported categories SDP ATTRIBUTES != null";
           uint16_t categories = sdp_attribute->attr_value.v.u16;
@@ -562,7 +567,9 @@
         VLOG(1) << __PRETTY_FUNCTION__ << " Get Supported categories";
         tSDP_DISC_ATTR* sdp_attribute =
             sdp_->FindAttributeInRec(sdp_record, ATTR_ID_SUPPORTED_FEATURES);
-        if (sdp_attribute != NULL) {
+        if (sdp_attribute != NULL &&
+            SDP_DISC_ATTR_TYPE(sdp_attribute->attr_len_type) == UINT_DESC_TYPE &&
+            SDP_DISC_ATTR_LEN(sdp_attribute->attr_len_type) >= 2) {
           VLOG(1) << __PRETTY_FUNCTION__
                   << "Get Supported categories SDP ATTRIBUTES != null";
           uint16_t categories = sdp_attribute->attr_value.v.u16;
diff --git a/system/profile/avrcp/tests/avrcp_connection_handler_test.cc b/system/profile/avrcp/tests/avrcp_connection_handler_test.cc
index 47c3df1..32fc810 100644
--- a/system/profile/avrcp/tests/avrcp_connection_handler_test.cc
+++ b/system/profile/avrcp/tests/avrcp_connection_handler_test.cc
@@ -19,6 +19,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "sdpdefs.h"
 #include "avrcp_internal.h"
 #include "avrcp_test_helper.h"
 #include "connection_handler.h"
@@ -63,7 +64,7 @@
     fake_features = {
         .p_next_attr = nullptr,
         .attr_id = 0,
-        .attr_len_type = 0,
+        .attr_len_type = (UINT_DESC_TYPE<<12) | 2,
         .attr_value = {.v = {.u16 = 0}},
     };
 
diff --git a/system/profile/sdp/Android.bp b/system/profile/sdp/Android.bp
index d524ce7..8068972 100644
--- a/system/profile/sdp/Android.bp
+++ b/system/profile/sdp/Android.bp
@@ -24,6 +24,7 @@
         "lib-bt-packets",
         "libbluetooth-types",
     ],
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
diff --git a/system/rust/BUILD.gn b/system/rust/BUILD.gn
index f61804a..bc6e791 100644
--- a/system/rust/BUILD.gn
+++ b/system/rust/BUILD.gn
@@ -42,7 +42,7 @@
   deps = [
     ":cxxlibheader",
     "//bt/system/gd/rust/shim:init_flags_bridge_header",
-    "//bt/system/gd:BluetoothGeneratedPackets_h",
+    "//bt/system/pdl:BluetoothGeneratedPackets_h",
   ]
 
   defines = [
diff --git a/system/rust/src/connection/ffi.rs b/system/rust/src/connection/ffi.rs
index 08506d5..012045f 100644
--- a/system/rust/src/connection/ffi.rs
+++ b/system/rust/src/connection/ffi.rs
@@ -11,7 +11,7 @@
     task::spawn_local,
 };
 
-use crate::{core::address::AddressWithType, do_in_rust_thread};
+use crate::do_in_rust_thread;
 
 use super::{
     attempt_manager::ConnectionMode,
diff --git a/system/rust/src/gatt/callbacks.rs b/system/rust/src/gatt/callbacks.rs
index f8958d7..a90e55c 100644
--- a/system/rust/src/gatt/callbacks.rs
+++ b/system/rust/src/gatt/callbacks.rs
@@ -203,7 +203,7 @@
     /// Execute or cancel any prepared writes
     async fn execute(&self, _: TransportIndex, _: TransactionDecision) -> Result<(), AttErrorCode> {
         // we never do prepared writes, so who cares
-        return Ok(());
+        Ok(())
     }
 }
 
diff --git a/system/rust/src/gatt/server/gatt_database.rs b/system/rust/src/gatt/server/gatt_database.rs
index 00cda0d..2f7a3f4 100644
--- a/system/rust/src/gatt/server/gatt_database.rs
+++ b/system/rust/src/gatt/server/gatt_database.rs
@@ -264,7 +264,7 @@
         }
 
         // if we made it here, we successfully loaded the new service
-        static_data.attributes.extend(attributes.clone().into_iter());
+        static_data.attributes.extend(attributes.clone());
 
         // re-entrancy via the listeners is possible, so we prevent it by dropping here
         drop(static_data);
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 2800915..3481268 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -27,6 +27,7 @@
         "liblog",
     ],
     srcs: crypto_toolbox_srcs,
+    header_libs: ["libbluetooth_headers"],
 }
 
 // Bluetooth stack static library for target
@@ -70,8 +71,6 @@
         "packages/modules/Bluetooth/system/gd/hal",
         "packages/modules/Bluetooth/system/internal_include",
         "packages/modules/Bluetooth/system/udrv/include",
-        "packages/modules/Bluetooth/system/vnd/ble",
-        "packages/modules/Bluetooth/system/vnd/include",
     ],
     generated_headers: [
         "BluetoothGeneratedDumpsysDataSchema_h",
@@ -138,7 +137,10 @@
     ],
     static_libs: [
         "libbt-hci",
+        "libbt-platform-protos-lite",
         "libbt-stack-core",
+        "libbt_shim_bridge",
+        "liblc3",
     ],
     whole_static_libs: [
         "libaptx_enc",
@@ -198,8 +200,6 @@
         "packages/modules/Bluetooth/system/gd",
         "packages/modules/Bluetooth/system/internal_include",
         "packages/modules/Bluetooth/system/udrv/include",
-        "packages/modules/Bluetooth/system/vnd/ble",
-        "packages/modules/Bluetooth/system/vnd/include",
     ],
     srcs: crypto_toolbox_srcs + [
         ":LegacyStackSdp",
@@ -292,6 +292,8 @@
         "libbluetooth_core_rs",
         "libbluetooth_gd",
         "libbt-hci",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
     ],
     shared_libs: [
         "libPlatformProperties",
@@ -395,6 +397,12 @@
         ":TestMockStackMetrics",
         "fuzzers/sdp_fuzzer.cc",
     ],
+    static_libs: [
+        "libbluetooth-types",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
+    ],
 }
 
 cc_fuzz {
@@ -435,6 +443,10 @@
         "gatt/*.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libgmock",
     ],
 }
@@ -472,6 +484,10 @@
         "smp/*.cc", // add other sources files  (p256 related) under smp into this test
     ],
     static_libs: [
+        "libbluetooth-types",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libgmock",
     ],
 }
@@ -509,6 +525,10 @@
         "fuzzers/bnep_fuzzer.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libgmock",
     ],
 }
@@ -548,6 +568,10 @@
         "fuzzers/avrc_fuzzer.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libgmock",
         "libosi",
     ],
@@ -592,6 +616,10 @@
         "l2cap/*.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libgmock",
     ],
     target: {
@@ -653,6 +681,7 @@
         "android.system.suspend.control-V1-ndk",
         "libFraunhoferAAC",
         "libbluetooth-dumpsys",
+        "libbluetooth-types",
         "libbluetooth_core_rs",
         "libbt-audio-hal-interface",
         "libbt-bta",
@@ -663,6 +692,7 @@
         "libbt-sbc-encoder",
         "libbt-stack",
         "libbt-stack-core",
+        "libbt_shim_bridge",
         "libbtcore",
         "libbtdevice",
         "libbtif",
@@ -679,6 +709,7 @@
     whole_static_libs: [
         "libbluetooth-for-tests",
     ],
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -737,8 +768,12 @@
         "libcutils",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
         "libgmock",
@@ -757,6 +792,7 @@
     sanitize: {
         cfi: false,
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 // Bluetooth stack smp unit tests for target
@@ -815,13 +851,18 @@
         "libcutils",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
         "libgmock",
         "liblog",
         "libosi",
     ],
+    header_libs: ["libbluetooth_headers"],
 }
 
 // Bluetooth stack advertise data parsing unit tests for target
@@ -884,6 +925,8 @@
         "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
         "libgmock",
@@ -892,6 +935,7 @@
     sanitize: {
         cfi: false,
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -931,8 +975,11 @@
         "libcutils",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
         "libgmock",
@@ -952,6 +999,7 @@
         cfi: true,
         misc_undefined: ["bounds"],
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -996,8 +1044,11 @@
         "libcutils",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
         "liblog",
@@ -1015,6 +1066,7 @@
         cfi: true,
         misc_undefined: ["bounds"],
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -1105,10 +1157,13 @@
     ],
     static_libs: [
         "libFraunhoferAAC",
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
         "libbt-sbc-decoder",
         "libbt-sbc-encoder",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libgmock",
         "liblog",
@@ -1126,6 +1181,7 @@
         cfi: true,
         misc_undefined: ["bounds"],
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -1157,6 +1213,7 @@
     ],
     static_libs: [
         "libbt-common",
+        "libbt_shim_bridge",
         "libchrome",
         "liblog",
         "libosi",
@@ -1166,6 +1223,7 @@
         cfi: true,
         misc_undefined: ["bounds"],
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 // gatt sr hash test
@@ -1210,8 +1268,12 @@
         "libcutils",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
         "libgmock",
@@ -1224,6 +1286,7 @@
             shared_libs: ["libstatssocket"],
         },
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 // Iso manager unit tests
@@ -1257,7 +1320,9 @@
         "test/common/mock_hcic_layer.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbt-common",
+        "libbt_shim_bridge",
         "libchrome",
         "libgmock",
         "liblog",
@@ -1273,6 +1338,7 @@
             undefined: true,
         },
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 // EATT unit tests
@@ -1318,8 +1384,11 @@
         "libcutils",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libevent",
         "libgmock",
@@ -1336,6 +1405,7 @@
     sanitize: {
         cfi: false,
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -1359,14 +1429,12 @@
         "packages/modules/Bluetooth/system",
         "packages/modules/Bluetooth/system/device/include",
         "packages/modules/Bluetooth/system/gd",
-        "packages/modules/Bluetooth/system/vnd/ble",
     ],
     generated_headers: [
         "BluetoothGeneratedDumpsysDataSchema_h",
         "BluetoothGeneratedPackets_h",
     ],
     srcs: crypto_toolbox_srcs + [
-        ":BluetoothBtaaSources_host",
         ":BluetoothHalSources_hci_host",
         ":BluetoothOsSources_host",
         ":OsiCompatSources",
@@ -1428,16 +1496,21 @@
         "test/btm/peer_packet_types_test.cc",
         "test/btm/sco_hci_test.cc",
         "test/btm/sco_pkt_status_test.cc",
+        "test/btm/stack_btm_power_mode_test.cc",
         "test/btm/stack_btm_regression_tests.cc",
         "test/btm/stack_btm_test.cc",
         "test/common/mock_eatt.cc",
         "test/stack_include_test.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
         "libbt-sbc-decoder",
         "libbt-sbc-encoder",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtdevice",
         "libchrome",
         "libevent",
@@ -1460,6 +1533,7 @@
             undefined: true,
         },
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -1479,7 +1553,6 @@
     include_dirs: [
         "packages/modules/Bluetooth/system",
         "packages/modules/Bluetooth/system/gd",
-        "packages/modules/Bluetooth/system/vnd/ble",
     ],
     srcs: crypto_toolbox_srcs + [
         ":TestCommonMockFunctions",
@@ -1487,6 +1560,7 @@
     ],
     static_libs: [
         "libbt-common",
+        "libbt_shim_bridge",
         "libbtdevice",
         "libbte",
         "libchrome",
@@ -1507,6 +1581,7 @@
             undefined: true,
         },
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -1541,7 +1616,11 @@
         "test/hid/stack_hid_test.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtdevice",
         "libbte",
         "libchrome",
@@ -1569,6 +1648,7 @@
             undefined: true,
         },
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -1612,8 +1692,12 @@
         "test/stack_btu_test.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtdevice",
         "libchrome",
         "libevent",
@@ -1635,6 +1719,7 @@
             undefined: true,
         },
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -1693,8 +1778,12 @@
         "test/gatt/stack_gatt_test.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtdevice",
         "libchrome",
         "libevent",
@@ -1724,6 +1813,7 @@
             undefined: true,
         },
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -1775,8 +1865,12 @@
         "test/stack_l2cap_test.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtdevice",
         "libchrome",
         "libevent",
@@ -1809,6 +1903,7 @@
             undefined: true,
         },
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 cc_test {
@@ -1861,8 +1956,12 @@
         "test/stack_acl_test.cc",
     ],
     static_libs: [
+        "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libbtdevice",
         "libchrome",
         "libevent",
@@ -1890,6 +1989,7 @@
             undefined: true,
         },
     },
+    header_libs: ["libbluetooth_headers"],
 }
 
 // Bluetooth stack connection multiplexing
@@ -1932,8 +2032,12 @@
         "libbluetooth-types",
         "libbluetooth_gd",
         "libbt-common",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
+        "libbt_shim_ffi",
         "libchrome",
         "libgmock",
         "liblog",
     ],
+    header_libs: ["libbluetooth_headers"],
 }
diff --git a/system/stack/BUILD.gn b/system/stack/BUILD.gn
index 646cd1b..673355f 100644
--- a/system/stack/BUILD.gn
+++ b/system/stack/BUILD.gn
@@ -236,7 +236,6 @@
     "//bt/system/gd/rust/shim:init_flags_bridge_header",
     "//bt/system/types",
     "//bt/system/types",
-    "//bt/system/embdrv/lc3:liblc3",
   ]
 
   configs += [ "//bt/system:target_defaults" ]
diff --git a/system/stack/a2dp/a2dp_aac_encoder_linux.cc b/system/stack/a2dp/a2dp_aac_encoder_linux.cc
index 80b2de8..0488c14 100644
--- a/system/stack/a2dp/a2dp_aac_encoder_linux.cc
+++ b/system/stack/a2dp/a2dp_aac_encoder_linux.cc
@@ -314,9 +314,16 @@
   tA2DP_BITS_PER_SAMPLE bits_per_sample =
       a2dp_codec_config->getAudioBitsPerSample();
 
-  uint32_t pcm_samples_per_frame =
+  int pcm_samples_per_frame =
       codec_intf.prepare_context(sample_rate, channel_count, bit_rate);
 
+  if (pcm_samples_per_frame < 0) {
+    LOG_ERROR("%s: Failed to prepare context: %d", __func__,
+              pcm_samples_per_frame);
+    codec_intf.clear_context();
+    return;  // TODO(b/294165759): need to return an error
+  }
+
   uint32_t encoder_interval_ms = pcm_samples_per_frame * 1000 / sample_rate;
 
   a2dp_aac_encoder_cb = tA2DP_AAC_ENCODER_CB{
@@ -337,7 +344,7 @@
                                  channel_count * encoder_interval_ms) /
                                 1000,
           },
-      .pcm_samples_per_frame = pcm_samples_per_frame,
+      .pcm_samples_per_frame = static_cast<uint32_t>(pcm_samples_per_frame),
       .encoder_interval_ms = encoder_interval_ms,
       .stats =
           a2dp_aac_encoder_stats_t{
diff --git a/system/stack/a2dp/a2dp_api.cc b/system/stack/a2dp/a2dp_api.cc
index c3a0a1b..a8941c2 100644
--- a/system/stack/a2dp/a2dp_api.cc
+++ b/system/stack/a2dp/a2dp_api.cc
@@ -95,21 +95,40 @@
       /* get service name */
       if ((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
                p_rec, ATTR_ID_SERVICE_NAME)) != NULL) {
-        a2dp_svc.p_service_name = (char*)p_attr->attr_value.v.array;
-        a2dp_svc.service_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+        if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == TEXT_STR_DESC_TYPE) {
+          a2dp_svc.p_service_name = (char*)p_attr->attr_value.v.array;
+          a2dp_svc.service_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+        } else {
+          LOG_ERROR("ATTR_ID_SERVICE_NAME attr type not STR!!");
+        }
+      } else {
+        LOG_ERROR("ATTR_ID_SERVICE_NAME attr not found!!");
       }
 
       /* get provider name */
       if ((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
                p_rec, ATTR_ID_PROVIDER_NAME)) != NULL) {
-        a2dp_svc.p_provider_name = (char*)p_attr->attr_value.v.array;
-        a2dp_svc.provider_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+        if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == TEXT_STR_DESC_TYPE) {
+          a2dp_svc.p_provider_name = (char*)p_attr->attr_value.v.array;
+          a2dp_svc.provider_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+        } else {
+          LOG_ERROR("ATTR_ID_PROVIDER_NAME attr type not STR!!");
+        }
+      } else {
+        LOG_ERROR("ATTR_ID_PROVIDER_NAME attr not found!!");
       }
 
       /* get supported features */
       if ((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
                p_rec, ATTR_ID_SUPPORTED_FEATURES)) != NULL) {
-        a2dp_svc.features = p_attr->attr_value.v.u16;
+        if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+            SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
+          a2dp_svc.features = p_attr->attr_value.v.u16;
+        } else {
+          LOG_ERROR("ATTR_ID_SUPPORTED_FEATURES attr type not STR!!");
+        }
+      } else {
+        LOG_ERROR("ATTR_ID_SUPPORTED_FEATURES attr not found!!");
       }
 
       /* get AVDTP version */
diff --git a/system/stack/acl/acl.h b/system/stack/acl/acl.h
index d369716..71b03b1 100644
--- a/system/stack/acl/acl.h
+++ b/system/stack/acl/acl.h
@@ -311,62 +311,6 @@
   uint8_t sca; /* Sleep clock accuracy */
 
   void Reset();
-
-  struct tPolicy {
-    tBTM_PM_MODE Mode() const { return this->mode.mode_; }
-    struct {
-      bool IsPending() const { return pending_ != BTM_PM_MD_UNKNOWN; }
-      tBTM_PM_MODE Pending() const { return pending_; }
-      uint16_t Interval() const { return interval_; }
-
-     private:
-      tBTM_PM_MODE mode_{BTM_PM_MD_ACTIVE};
-      tBTM_PM_MODE pending_{BTM_PM_MD_UNKNOWN};
-      uint16_t interval_{0};
-      friend tBTM_STATUS bluetooth::shim::BTM_SetPowerMode(
-          uint16_t, const tBTM_PM_PWR_MD& new_mode);
-      friend void bluetooth::shim::btm_pm_on_mode_change(tHCI_STATUS status,
-                                                         uint16_t handle,
-                                                         tHCI_MODE hci_mode,
-                                                         uint16_t interval);
-      friend void tACL_CONN::Reset();
-      friend tBTM_PM_MODE tACL_CONN::tPolicy::Mode() const;
-    } mode;
-
-    hci_role_t Role() const { return this->role.role_; }
-    struct {
-      unsigned RoleSwitchFailedCount() const { return role_switch_failed_cnt_; }
-
-     private:
-      hci_role_t role_{HCI_ROLE_CENTRAL};
-      unsigned role_switch_failed_cnt_{0};
-      friend void tACL_CONN::Reset();
-      friend hci_role_t tACL_CONN::tPolicy::Role() const;
-    } role;
-
-    struct {
-      bool IsPending() const { return pending_; }
-
-     private:
-      bool pending_{false};
-      friend tBTM_STATUS bluetooth::shim::BTM_SetSsrParams(uint16_t handle,
-                                                           uint16_t max_lat,
-                                                           uint16_t min_rmt_to,
-                                                           uint16_t min_loc_to);
-      friend void bluetooth::shim::btm_pm_on_sniff_subrating(
-          tHCI_STATUS status, uint16_t handle,
-          uint16_t maximum_transmit_latency, uint16_t maximum_receive_latency,
-          uint16_t minimum_remote_timeout, uint16_t minimum_local_timeout);
-      friend void tACL_CONN::Reset();
-    } sniff_subrating;
-
-    tLINK_POLICY Settings() const { return settings_; }
-
-   private:
-    tLINK_POLICY settings_{kAllLinkPoliciesEnabled};
-    friend void btm_set_link_policy(tACL_CONN* conn, tLINK_POLICY policy);
-    friend void tACL_CONN::Reset();
-  } policy;
 };
 
 struct controller_t;
diff --git a/system/stack/acl/btm_acl.cc b/system/stack/acl/btm_acl.cc
index 3cc6cb0..eaf2829 100644
--- a/system/stack/acl/btm_acl.cc
+++ b/system/stack/acl/btm_acl.cc
@@ -214,8 +214,8 @@
       // link stays up due to the presence of other clients.
       bluetooth::connection::GetConnectionManager()
           .stop_all_connections_to_device(bluetooth::core::ToRustAddress(
-              tBLE_BD_ADDR{.bda = p_acl.active_remote_addr,
-                           .type = p_acl.active_remote_addr_type}));
+              tBLE_BD_ADDR{.type = p_acl.active_remote_addr_type,
+                           .bda = p_acl.active_remote_addr}));
     }
   }
 
@@ -2346,7 +2346,7 @@
 }
 
 bool acl_peer_supports_ble_connection_subrating(const RawAddress& remote_bda) {
-  tACL_CONN* p_acl = internal_.btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR);
+  tACL_CONN* p_acl = internal_.btm_bda_to_acl(remote_bda, BT_TRANSPORT_LE);
   if (p_acl == nullptr) {
     LOG_WARN("Unable to find active acl");
     return false;
@@ -2361,7 +2361,7 @@
 
 bool acl_peer_supports_ble_connection_subrating_host(
     const RawAddress& remote_bda) {
-  tACL_CONN* p_acl = internal_.btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR);
+  tACL_CONN* p_acl = internal_.btm_bda_to_acl(remote_bda, BT_TRANSPORT_LE);
   if (p_acl == nullptr) {
     LOG_WARN("Unable to find active acl");
     return false;
@@ -2378,7 +2378,7 @@
  *
  * Function         BTM_ReadConnectionAddr
  *
- * Description      This function is called to get the local device address
+ * Description      This function is called to get the local LE device address
  *                  information.
  *
  * Returns          void
@@ -2386,16 +2386,22 @@
  ******************************************************************************/
 void BTM_ReadConnectionAddr(const RawAddress& remote_bda,
                             RawAddress& local_conn_addr,
-                            tBLE_ADDR_TYPE* p_addr_type) {
+                            tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
   if (bluetooth::shim::is_gd_l2cap_enabled()) {
     bluetooth::shim::L2CA_ReadConnectionAddr(remote_bda, local_conn_addr,
                                              p_addr_type);
     return;
-  } else {
-    bluetooth::shim::ACL_ReadConnectionAddress(remote_bda, local_conn_addr,
-                                               p_addr_type);
+  }
+
+  tBTM_SEC_DEV_REC* p_sec_rec = btm_find_dev(remote_bda);
+  if (p_sec_rec == nullptr) {
+    LOG_WARN("No matching known device %s in record",
+             ADDRESS_TO_LOGGABLE_CSTR(remote_bda));
     return;
   }
+
+  bluetooth::shim::ACL_ReadConnectionAddress(
+      p_sec_rec->ble_hci_handle, local_conn_addr, p_addr_type, ota_address);
 }
 
 /*******************************************************************************
@@ -2409,10 +2415,6 @@
  *
  ******************************************************************************/
 bool BTM_IsBleConnection(uint16_t hci_handle) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    ASSERT_LOG(false, "This should not be invoked from code path");
-  }
-
   if (bluetooth::shim::is_gd_l2cap_enabled()) {
     return bluetooth::shim::L2CA_IsLeLink(hci_handle);
   }
@@ -2477,35 +2479,38 @@
  *
  * Function       BTM_ReadRemoteConnectionAddr
  *
- * Description    This function is read the remote device address currently used
+ * Description    This function is read the LE remote device address used in
+ *                connection establishment
  *
  * Parameters     pseudo_addr: pseudo random address available
  *                conn_addr:connection address used
  *                p_addr_type : BD Address type, Public or Random of the address
  *                              used
+ *                ota_address: When use if remote used RPA in OTA it will be
+ *returned.
  *
  * Returns        bool, true if connection to remote device exists, else false
  *
  ******************************************************************************/
 bool BTM_ReadRemoteConnectionAddr(const RawAddress& pseudo_addr,
                                   RawAddress& conn_addr,
-                                  tBLE_ADDR_TYPE* p_addr_type) {
+                                  tBLE_ADDR_TYPE* p_addr_type,
+                                  bool ota_address) {
   if (bluetooth::shim::is_gd_l2cap_enabled()) {
     return bluetooth::shim::L2CA_ReadRemoteConnectionAddr(
         pseudo_addr, conn_addr, p_addr_type);
   }
 
-  bool st = true;
-  tACL_CONN* p_acl = internal_.btm_bda_to_acl(pseudo_addr, BT_TRANSPORT_LE);
-
-  if (p_acl == NULL) {
-    LOG_WARN("Unable to find active acl");
+  tBTM_SEC_DEV_REC* p_sec_rec = btm_find_dev(pseudo_addr);
+  if (p_sec_rec == nullptr) {
+    LOG_WARN("No matching known device %s in record",
+             ADDRESS_TO_LOGGABLE_CSTR(pseudo_addr));
     return false;
   }
 
-  conn_addr = p_acl->active_remote_addr;
-  *p_addr_type = p_acl->active_remote_addr_type;
-  return st;
+  bluetooth::shim::ACL_ReadPeerConnectionAddress(
+      p_sec_rec->ble_hci_handle, conn_addr, p_addr_type, ota_address);
+  return true;
 }
 
 uint8_t acl_link_role_from_handle(uint16_t handle) {
@@ -2708,8 +2713,37 @@
   acl_disconnect_after_role_switch(handle, reason, comment);
 }
 
+// BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 4, Part E
+// 7.1.6 Disconnect command
+// Only a subset of reasons are valid and will be accepted
+// by the controller
+bool is_disconnect_reason_valid(const tHCI_REASON& reason) {
+  switch (reason) {
+    case HCI_ERR_AUTH_FAILURE:
+    case HCI_ERR_PEER_USER:
+    case HCI_ERR_REMOTE_LOW_RESOURCE:
+    case HCI_ERR_REMOTE_POWER_OFF:
+    case HCI_ERR_UNSUPPORTED_REM_FEATURE:
+    case HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED:
+    case HCI_ERR_UNACCEPT_CONN_INTERVAL:
+      return true;
+    default:
+      break;
+  }
+  return false;
+}
+
 void acl_disconnect_after_role_switch(uint16_t conn_handle, tHCI_STATUS reason,
                                       std::string comment) {
+  if (!is_disconnect_reason_valid(reason)) {
+    LOG_WARN(
+        "Controller will not accept invalid reason parameter:%s"
+        " instead sending:%s",
+        hci_error_code_text(reason).c_str(),
+        hci_error_code_text(HCI_ERR_PEER_USER).c_str());
+    reason = HCI_ERR_PEER_USER;
+  }
+
   tACL_CONN* p_acl = internal_.acl_get_connection_from_handle(conn_handle);
   if (p_acl == nullptr) {
     LOG_ERROR("Sending disconnect for unknown acl:%hu PLEASE FIX", conn_handle);
@@ -2775,8 +2809,8 @@
 bool acl_create_le_connection_with_id(uint8_t id, const RawAddress& bd_addr,
                                       tBLE_ADDR_TYPE addr_type) {
   tBLE_BD_ADDR address_with_type{
-      .bda = bd_addr,
       .type = addr_type,
+      .bda = bd_addr,
   };
 
   gatt_find_in_device_record(bd_addr, &address_with_type);
diff --git a/system/stack/acl/btm_pm.cc b/system/stack/acl/btm_pm.cc
index 7903f0d..381c6ac 100644
--- a/system/stack/acl/btm_pm.cc
+++ b/system/stack/acl/btm_pm.cc
@@ -94,6 +94,12 @@
 static void send_sniff_subrating(uint16_t handle, const RawAddress& addr,
                                  uint16_t max_lat, uint16_t min_rmt_to,
                                  uint16_t min_loc_to) {
+  uint16_t new_max_lat = 0;
+  if (interop_match_addr_get_max_lat(INTEROP_UPDATE_HID_SSR_MAX_LAT, &addr,
+                                     &new_max_lat)) {
+    max_lat = new_max_lat;
+  }
+
   btsnd_hcic_sniff_sub_rate(handle, max_lat, min_rmt_to, min_loc_to);
   BTM_LogHistory(kBtmLogTag, addr, "Sniff subrating",
                  base::StringPrintf(
@@ -122,12 +128,6 @@
  ******************************************************************************/
 tBTM_STATUS BTM_PmRegister(uint8_t mask, uint8_t* p_pm_id,
                            tBTM_PM_STATUS_CBACK* p_cb) {
-  if (bluetooth::shim::is_gd_link_policy_enabled()) {
-    ASSERT(p_pm_id != nullptr);
-    ASSERT(p_cb != nullptr);
-    return BTM_NO_RESOURCES;
-  }
-
   /* de-register */
   if (mask & BTM_PM_DEREG) {
     if (*p_pm_id >= BTM_MAX_PM_RECORDS) return BTM_ILLEGAL_VALUE;
diff --git a/system/stack/btm/btm_ble.cc b/system/stack/btm/btm_ble.cc
index 9923290..1979312 100644
--- a/system/stack/btm/btm_ble.cc
+++ b/system/stack/btm/btm_ble.cc
@@ -992,18 +992,12 @@
       break;
   }
 
-  if (ble_sec_act == BTM_BLE_SEC_NONE) {
-    if (bluetooth::common::init_flags::queue_l2cap_coc_while_encrypting_is_enabled()) {
-      if (sec_act != BTM_SEC_ENC_PENDING) {
-        return result;
-      }
-    } else {
-      return result;
-    }
-  } else {
-    l2cble_update_sec_act(bd_addr, sec_act);
+  if (ble_sec_act == BTM_BLE_SEC_NONE && sec_act != BTM_SEC_ENC_PENDING) {
+    return result;
   }
 
+  l2cble_update_sec_act(bd_addr, sec_act);
+
   BTM_SetEncryption(bd_addr, BT_TRANSPORT_LE, p_callback, p_ref_data,
                     ble_sec_act);
 
@@ -1698,11 +1692,11 @@
   BTM_TRACE_DEBUG("%s", __func__);
   *p_data = tBTM_LE_IO_REQ{
       .io_cap = BTM_IO_CAP_UNKNOWN,
+      .oob_data = false,
       .auth_req = BTM_LE_AUTH_REQ_SC_MITM_BOND,
+      .max_key_size = BTM_BLE_MAX_KEY_SIZE,
       .init_keys = SMP_BR_SEC_DEFAULT_KEY,
       .resp_keys = SMP_BR_SEC_DEFAULT_KEY,
-      .max_key_size = BTM_BLE_MAX_KEY_SIZE,
-      .oob_data = false,
   };
 
   if (osi_property_get_bool(kPropertyCtkdDisableCsrkDistribution, false)) {
@@ -2029,9 +2023,6 @@
  *
  ******************************************************************************/
 void BTM_BleSirkConfirmDeviceReply(const RawAddress& bd_addr, uint8_t res) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    ASSERT_LOG(false, "This should not be invoked from code path");
-  }
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
   tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_FAIL;
 
diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc
index 2ec7e70..eb2ae48 100644
--- a/system/stack/btm/btm_ble_gap.cc
+++ b/system/stack/btm/btm_ble_gap.cc
@@ -1613,8 +1613,8 @@
   if (evt_type == BTM_BLE_CONNECT_EVT) {
     CHECK(p_peer_addr_type != nullptr);
     const tBLE_BD_ADDR ble_bd_addr = {
-        .bda = p_peer_addr_ptr,
         .type = *p_peer_addr_type,
+        .bda = p_peer_addr_ptr,
     };
     LOG_DEBUG("Received BLE connect event %s", ADDRESS_TO_LOGGABLE_CSTR(ble_bd_addr));
 
diff --git a/system/stack/btm/btm_ble_privacy.cc b/system/stack/btm/btm_ble_privacy.cc
index 986c89e..781a701 100644
--- a/system/stack/btm/btm_ble_privacy.cc
+++ b/system/stack/btm/btm_ble_privacy.cc
@@ -31,11 +31,12 @@
 #include "stack/btm/btm_dev.h"
 #include "stack/include/bt_octets.h"
 #include "types/raw_address.h"
-#include "vendor_hcidefs.h"
 
 extern tBTM_CB btm_cb;
 
 /* RPA offload VSC specifics */
+#define HCI_VENDOR_BLE_RPA_VSC (0x0155 | HCI_GRP_VENDOR_SPECIFIC)
+
 #define BTM_BLE_META_IRK_ENABLE 0x01
 #define BTM_BLE_META_ADD_IRK_ENTRY 0x02
 #define BTM_BLE_META_REMOVE_IRK_ENTRY 0x03
@@ -566,8 +567,8 @@
 
   if (dev_rec.ble.identity_address_with_type.bda.IsEmpty()) {
     dev_rec.ble.identity_address_with_type = {
-        .bda = dev_rec.bd_addr,
         .type = dev_rec.ble.AddressType(),
+        .bda = dev_rec.bd_addr,
     };
   }
 
diff --git a/system/stack/btm/btm_client_interface.cc b/system/stack/btm/btm_client_interface.cc
index cf836da..2a3669b 100644
--- a/system/stack/btm/btm_client_interface.cc
+++ b/system/stack/btm/btm_client_interface.cc
@@ -32,8 +32,8 @@
 struct btm_client_interface_t btm_client_interface = {
     .lifecycle =
         {
-            .BTM_GetHCIConnHandle = BTM_GetHCIConnHandle,
             .BTM_PmRegister = BTM_PmRegister,
+            .BTM_GetHCIConnHandle = BTM_GetHCIConnHandle,
             .BTM_VendorSpecificCommand = BTM_VendorSpecificCommand,
             .ACL_RegisterClient = ACL_RegisterClient,
             .ACL_UnregisterClient = ACL_UnregisterClient,
@@ -51,20 +51,6 @@
             .BTM_FreeSCN = BTM_FreeSCN,
         },
 
-    .neighbor =
-        {
-            .BTM_CancelInquiry = BTM_CancelInquiry,
-            .BTM_ClearInqDb = BTM_ClearInqDb,
-            .BTM_InqDbNext = BTM_InqDbNext,
-            .BTM_SetConnectability = BTM_SetConnectability,
-            .BTM_SetDiscoverability = BTM_SetDiscoverability,
-            .BTM_StartInquiry = BTM_StartInquiry,
-            .BTM_IsInquiryActive = BTM_IsInquiryActive,
-            .BTM_SetInquiryMode = BTM_SetInquiryMode,
-            .BTM_EnableInterlacedInquiryScan = BTM_EnableInterlacedInquiryScan,
-            .BTM_EnableInterlacedPageScan = BTM_EnableInterlacedPageScan,
-        },
-
     // Acl peer and lifecycle
     .peer =
         {
@@ -74,13 +60,13 @@
                         ACL_SupportTransparentSynchronousData,
                 },
 
-            .BTM_CancelRemoteDeviceName = BTM_CancelRemoteDeviceName,
             .BTM_IsAclConnectionUp = BTM_IsAclConnectionUp,
             .BTM_ReadConnectedTransportAddress =
                 BTM_ReadConnectedTransportAddress,
-            .BTM_ReadDevInfo = BTM_ReadDevInfo,
+            .BTM_CancelRemoteDeviceName = BTM_CancelRemoteDeviceName,
             .BTM_ReadRemoteDeviceName = BTM_ReadRemoteDeviceName,
             .BTM_ReadRemoteFeatures = BTM_ReadRemoteFeatures,
+            .BTM_ReadDevInfo = BTM_ReadDevInfo,
             .BTM_GetMaxPacketSize = BTM_GetMaxPacketSize,
             .BTM_ReadRemoteVersion = BTM_ReadRemoteVersion,
         },
@@ -91,12 +77,12 @@
             .BTM_SetPowerMode = BTM_SetPowerMode,
             .BTM_SetSsrParams = BTM_SetSsrParams,
             .BTM_SwitchRoleToCentral = BTM_SwitchRoleToCentral,
-            .BTM_WritePageTimeout = BTM_WritePageTimeout,
             .BTM_block_role_switch_for = BTM_block_role_switch_for,
             .BTM_block_sniff_mode_for = BTM_block_sniff_mode_for,
             .BTM_default_unblock_role_switch = BTM_default_unblock_role_switch,
             .BTM_unblock_role_switch_for = BTM_unblock_role_switch_for,
             .BTM_unblock_sniff_mode_for = BTM_unblock_sniff_mode_for,
+            .BTM_WritePageTimeout = BTM_WritePageTimeout,
         },
 
     .link_controller =
@@ -107,23 +93,23 @@
 
     .security =
         {
-            .BTM_ConfirmReqReply = BTM_ConfirmReqReply,
-            .BTM_PINCodeReply = BTM_PINCodeReply,
-            .BTM_RemoteOobDataReply = BTM_RemoteOobDataReply,
-            .BTM_SecAddBleDevice = BTM_SecAddBleDevice,
-            .BTM_SecAddBleKey = BTM_SecAddBleKey,
             .BTM_SecAddDevice = BTM_SecAddDevice,
             .BTM_SecAddRmtNameNotifyCallback = BTM_SecAddRmtNameNotifyCallback,
+            .BTM_SecDeleteDevice = BTM_SecDeleteDevice,
+            .BTM_SecRegister = BTM_SecRegister,
+            .BTM_SecReadDevName = BTM_SecReadDevName,
             .BTM_SecBond = BTM_SecBond,
             .BTM_SecBondCancel = BTM_SecBondCancel,
+            .BTM_SecAddBleKey = BTM_SecAddBleKey,
+            .BTM_SecAddBleDevice = BTM_SecAddBleDevice,
             .BTM_SecClearSecurityFlags = BTM_SecClearSecurityFlags,
             .BTM_SecClrService = BTM_SecClrService,
             .BTM_SecClrServiceByPsm = BTM_SecClrServiceByPsm,
-            .BTM_SecDeleteDevice = BTM_SecDeleteDevice,
+            .BTM_RemoteOobDataReply = BTM_RemoteOobDataReply,
+            .BTM_PINCodeReply = BTM_PINCodeReply,
+            .BTM_ConfirmReqReply = BTM_ConfirmReqReply,
             .BTM_SecDeleteRmtNameNotifyCallback =
                 BTM_SecDeleteRmtNameNotifyCallback,
-            .BTM_SecReadDevName = BTM_SecReadDevName,
-            .BTM_SecRegister = BTM_SecRegister,
             .BTM_SetEncryption = BTM_SetEncryption,
             .BTM_IsEncrypted = BTM_IsEncrypted,
             .BTM_SecIsSecurityPending = BTM_SecIsSecurityPending,
@@ -134,46 +120,46 @@
 
     .ble =
         {
-            .BTM_BleConfirmReply = BTM_BleConfirmReply,
             .BTM_BleGetEnergyInfo = BTM_BleGetEnergyInfo,
-            .BTM_BleLoadLocalKeys = BTM_BleLoadLocalKeys,
             .BTM_BleObserve = BTM_BleObserve,
+            .BTM_SetBleDataLength = BTM_SetBleDataLength,
+            .BTM_BleConfirmReply = BTM_BleConfirmReply,
+            .BTM_BleLoadLocalKeys = BTM_BleLoadLocalKeys,
             .BTM_BlePasskeyReply = BTM_BlePasskeyReply,
             .BTM_BleReadControllerFeatures = BTM_BleReadControllerFeatures,
             .BTM_BleSetPhy = BTM_BleSetPhy,
             .BTM_BleSetPrefConnParams = BTM_BleSetPrefConnParams,
-            .BTM_SetBleDataLength = BTM_SetBleDataLength,
             .BTM_UseLeLink = BTM_UseLeLink,
         },
 
     .sco =
         {
             .BTM_CreateSco = BTM_CreateSco,
-            .BTM_EScoConnRsp = BTM_EScoConnRsp,
-            .BTM_GetNumScoLinks = BTM_GetNumScoLinks,
             .BTM_RegForEScoEvts = BTM_RegForEScoEvts,
             .BTM_RemoveSco = BTM_RemoveSco,
-            .BTM_SetEScoMode = BTM_SetEScoMode,
             .BTM_WriteVoiceSettings = BTM_WriteVoiceSettings,
+            .BTM_EScoConnRsp = BTM_EScoConnRsp,
+            .BTM_GetNumScoLinks = BTM_GetNumScoLinks,
+            .BTM_SetEScoMode = BTM_SetEScoMode,
         },
 
     .local =
         {
             .BTM_ReadLocalDeviceNameFromController =
                 BTM_ReadLocalDeviceNameFromController,
-            .BTM_SetDeviceClass = BTM_SetDeviceClass,
             .BTM_SetLocalDeviceName = BTM_SetLocalDeviceName,
+            .BTM_SetDeviceClass = BTM_SetDeviceClass,
             .BTM_IsDeviceUp = BTM_IsDeviceUp,
             .BTM_ReadDeviceClass = BTM_ReadDeviceClass,
         },
 
     .eir =
         {
+            .BTM_WriteEIR = BTM_WriteEIR,
             .BTM_GetEirSupportedServices = BTM_GetEirSupportedServices,
             .BTM_GetEirUuidList = BTM_GetEirUuidList,
             .BTM_AddEirService = BTM_AddEirService,
             .BTM_RemoveEirService = BTM_RemoveEirService,
-            .BTM_WriteEIR = BTM_WriteEIR,
         },
     .db =
         {
diff --git a/system/stack/btm/btm_iso.cc b/system/stack/btm/btm_iso.cc
index 1839e11..81bd22e 100644
--- a/system/stack/btm/btm_iso.cc
+++ b/system/stack/btm/btm_iso.cc
@@ -112,7 +112,8 @@
 }
 
 void IsoManager::TerminateBig(uint8_t big_id, uint8_t reason) {
-  pimpl_->iso_impl_->terminate_big(big_id, reason);
+  if (pimpl_->IsRunning())
+    pimpl_->iso_impl_->terminate_big(big_id, reason);
 }
 
 void IsoManager::HandleIsoData(void* p_msg) {
diff --git a/system/stack/btm/btm_iso_impl.h b/system/stack/btm/btm_iso_impl.h
index c9f17f7..94ce893 100644
--- a/system/stack/btm/btm_iso_impl.h
+++ b/system/stack/btm/btm_iso_impl.h
@@ -646,8 +646,8 @@
     if (cis->state_flags & kStateFlagIsConnected) {
       cis_disconnected_evt evt = {
           .reason = reason,
-          .cis_conn_hdl = handle,
           .cig_id = cis->cig_id,
+          .cis_conn_hdl = handle,
       };
 
       cig_callbacks_->OnCisEvent(kIsoEventCisDisconnected, &evt);
diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc
index 5d2d011..95568bd 100644
--- a/system/stack/btm/btm_sec.cc
+++ b/system/stack/btm/btm_sec.cc
@@ -247,6 +247,25 @@
 
 /*******************************************************************************
  *
+ * Function         access_secure_service_from_temp_bond
+ *
+ * Description      a utility function to test whether an access to
+ *                  secure service from temp bonding is happening
+ *
+ * Returns          true if the aforementioned condition holds,
+ *                  false otherwise
+ *
+ ******************************************************************************/
+static bool access_secure_service_from_temp_bond(const tBTM_SEC_DEV_REC* p_dev_rec,
+                                                 bool locally_initiated,
+                                                 uint16_t security_req) {
+  return !locally_initiated && (security_req & BTM_SEC_IN_AUTHENTICATE) &&
+    p_dev_rec->is_device_authenticated() &&
+    p_dev_rec->is_bond_type_temporary();
+}
+
+/*******************************************************************************
+ *
  * Function         BTM_SecRegister
  *
  * Description      Application manager calls this function to register for
@@ -1667,9 +1686,14 @@
       }
 
       if (rc == BTM_SUCCESS) {
+        if (access_secure_service_from_temp_bond(p_dev_rec, is_originator, security_required)) {
+          LOG_ERROR("Trying to access a secure service from a temp bonding, rejecting");
+          rc = BTM_FAILED_ON_SECURITY;
+        }
+
         if (p_callback)
-          (*p_callback)(&bd_addr, transport, (void*)p_ref_data, BTM_SUCCESS);
-        return (BTM_SUCCESS);
+          (*p_callback)(&bd_addr, transport, (void*)p_ref_data, rc);
+        return rc;
       }
     }
 
@@ -1927,6 +1951,11 @@
                                security_required, p_callback, p_ref_data);
     } else /* rc == BTM_SUCCESS */
     {
+      if (access_secure_service_from_temp_bond(p_dev_rec,
+          is_originator, security_required)) {
+        LOG_ERROR("Trying to access a secure rfcomm service from a temp bonding, rejecting");
+        rc = BTM_FAILED_ON_SECURITY;
+      }
       if (p_callback) {
         LOG_DEBUG("Notifying client that security access has been granted");
         (*p_callback)(&bd_addr, transport, p_ref_data, rc);
@@ -2193,18 +2222,16 @@
  *
  ******************************************************************************/
 void btm_sec_dev_reset(void) {
-  if (controller_get_interface()->supports_simple_pairing()) {
-    /* set the default IO capabilities */
-    btm_cb.devcb.loc_io_caps = btif_storage_get_local_io_caps();
-    /* add mx service to use no security */
-    BTM_SetSecurityLevel(false, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX,
-                         BTM_SEC_NONE, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0);
-    BTM_SetSecurityLevel(true, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX, BTM_SEC_NONE,
-                         BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0);
-  } else {
-    btm_cb.security_mode = BTM_SEC_MODE_SERVICE;
-  }
+  ASSERT_LOG(controller_get_interface()->supports_simple_pairing(),
+             "only controllers with SSP is supported");
 
+  /* set the default IO capabilities */
+  btm_cb.devcb.loc_io_caps = btif_storage_get_local_io_caps();
+  /* add mx service to use no security */
+  BTM_SetSecurityLevel(false, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX,
+                       BTM_SEC_NONE, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0);
+  BTM_SetSecurityLevel(true, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX, BTM_SEC_NONE,
+                       BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0);
   BTM_TRACE_DEBUG("btm_sec_dev_reset sec mode: %d", btm_cb.security_mode);
 }
 
@@ -4549,6 +4576,13 @@
     return (BTM_FAILED_ON_SECURITY);
   }
 
+  if (access_secure_service_from_temp_bond(p_dev_rec,
+                                           p_dev_rec->IsLocallyInitiated(),
+                                           p_dev_rec->security_required)) {
+    LOG_ERROR("Trying to access a secure service from a temp bonding, rejecting");
+    return (BTM_FAILED_ON_SECURITY);
+  }
+
   /* All required  security procedures already established */
   p_dev_rec->security_required &=
       ~(BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_IN_AUTHENTICATE |
diff --git a/system/stack/btm/hfp_lc3_decoder.cc b/system/stack/btm/hfp_lc3_decoder.cc
index 55e3be9..38133b5 100644
--- a/system/stack/btm/hfp_lc3_decoder.cc
+++ b/system/stack/btm/hfp_lc3_decoder.cc
@@ -19,10 +19,10 @@
 #include "hfp_lc3_decoder.h"
 
 #include <base/logging.h>
+#include <lc3.h>
 
 #include <cstring>
 
-#include "embdrv/lc3/include/lc3.h"
 #include "osi/include/allocator.h"
 #include "osi/include/log.h"
 
diff --git a/system/stack/btm/hfp_lc3_encoder.cc b/system/stack/btm/hfp_lc3_encoder.cc
index 31b9ea5..f968513 100644
--- a/system/stack/btm/hfp_lc3_encoder.cc
+++ b/system/stack/btm/hfp_lc3_encoder.cc
@@ -18,9 +18,10 @@
 
 #include "hfp_lc3_encoder.h"
 
+#include <lc3.h>
+
 #include <cstring>
 
-#include "embdrv/lc3/include/lc3.h"
 #include "osi/include/allocator.h"
 #include "osi/include/log.h"
 
diff --git a/system/stack/btu/btu_hcif.cc b/system/stack/btu/btu_hcif.cc
index e0dec1d..01e7ab8 100644
--- a/system/stack/btu/btu_hcif.cc
+++ b/system/stack/btu/btu_hcif.cc
@@ -171,9 +171,12 @@
           android::bluetooth::hci::BLE_EVT_UNKNOWN, status, reason);
       break;
     }
-    case HCI_BLE_EVENT: {
+    // Ignore these events
+    case HCI_BLE_EVENT:
       break;
-    }
+    case HCI_VENDOR_SPECIFIC_EVT:
+      break;
+
     case HCI_CONNECTION_COMP_EVT:  // EventCode::CONNECTION_COMPLETE
     case HCI_CONNECTION_REQUEST_EVT:  // EventCode::CONNECTION_REQUEST
     case HCI_DISCONNECTION_COMP_EVT:  // EventCode::DISCONNECTION_COMPLETE
diff --git a/system/stack/eatt/eatt_impl.h b/system/stack/eatt/eatt_impl.h
index ad7592f..4745a91 100644
--- a/system/stack/eatt/eatt_impl.h
+++ b/system/stack/eatt/eatt_impl.h
@@ -827,7 +827,7 @@
 
     std::vector<uint16_t> cids = {cid};
 
-    tL2CAP_LE_CFG_INFO cfg = {.mps = eatt_dev->rx_mps_, .mtu = new_mtu};
+    tL2CAP_LE_CFG_INFO cfg = {.mtu = new_mtu, .mps = eatt_dev->rx_mps_};
 
     if (!L2CA_ReconfigCreditBasedConnsReq(eatt_dev->bda_, cids, &cfg)) {
       LOG(ERROR) << __func__ << "Could not start reconfig cid: " << loghex(cid)
@@ -867,7 +867,7 @@
       return;
     }
 
-    tL2CAP_LE_CFG_INFO cfg = {.mps = eatt_dev->rx_mps_, .mtu = new_mtu};
+    tL2CAP_LE_CFG_INFO cfg = {.mtu = new_mtu, .mps = eatt_dev->rx_mps_};
 
     if (!L2CA_ReconfigCreditBasedConnsReq(eatt_dev->bda_, cids, &cfg)) {
       LOG(ERROR) << __func__ << "Could not start reconfig for device "
diff --git a/system/stack/fuzzers/smp_fuzzer.cc b/system/stack/fuzzers/smp_fuzzer.cc
index 7e790ae..076bf8d 100644
--- a/system/stack/fuzzers/smp_fuzzer.cc
+++ b/system/stack/fuzzers/smp_fuzzer.cc
@@ -64,13 +64,13 @@
   FakeBtStack() {
     test::mock::stack_acl::BTM_ReadConnectionAddr.body =
         [](const RawAddress& remote_bda, RawAddress& local_conn_addr,
-           tBLE_ADDR_TYPE* p_addr_type) {
+           tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
           local_conn_addr = kDummyAddr;
           *p_addr_type = BLE_ADDR_PUBLIC;
         };
     test::mock::stack_acl::BTM_ReadRemoteConnectionAddr.body =
         [](const RawAddress& pseudo_addr, RawAddress& conn_addr,
-           tBLE_ADDR_TYPE* p_addr_type) {
+           tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
           conn_addr = kDummyRemoteAddr;
           *p_addr_type = BLE_ADDR_PUBLIC;
           return true;
diff --git a/system/stack/gatt/connection_manager.cc b/system/stack/gatt/connection_manager.cc
index d55afe0..f1cbeb1 100644
--- a/system/stack/gatt/connection_manager.cc
+++ b/system/stack/gatt/connection_manager.cc
@@ -441,6 +441,7 @@
 }
 
 void on_connection_timed_out_from_shim(const RawAddress& address) {
+  LOG_INFO("Connection failed %s", ADDRESS_TO_LOGGABLE_CSTR(address));
   on_connection_timed_out(0x00, address);
 }
 
@@ -461,7 +462,7 @@
 
   // TODO: this would free the timer, from within the timer callback, which is
   // bad.
-  direct_connect_remove(app_id, address);
+  direct_connect_remove(app_id, address, true);
 }
 
 /** Add a device to the direct connection list. Returns true if device
@@ -517,7 +518,8 @@
   direct_connect_add(app_id, address);
 }
 
-bool direct_connect_remove(uint8_t app_id, const RawAddress& address) {
+bool direct_connect_remove(uint8_t app_id, const RawAddress& address,
+                           bool connection_timeout) {
   LOG_DEBUG("app_id=%d, address=%s", static_cast<int>(app_id),
             ADDRESS_TO_LOGGABLE_CSTR(address));
   auto it = bgconn_dev.find(address);
@@ -542,6 +544,18 @@
   it->second.doing_direct_conn.erase(app_it);
 
   if (is_anyone_interested_to_use_accept_list(it)) {
+    if (connection_timeout) {
+      /* In such case we need to add device back to allow list because,
+       * when connection timeout out, the lower layer removes device from
+       * the allow list.
+       */
+      if (!BTM_AcceptlistAdd(address)) {
+        LOG_WARN(
+            "Failed to re-add device %s to accept list after connection "
+            "timeout",
+            ADDRESS_TO_LOGGABLE_CSTR(address));
+      }
+    }
     return true;
   }
 
diff --git a/system/stack/gatt/connection_manager.h b/system/stack/gatt/connection_manager.h
index 0af1679..07dfa3e 100644
--- a/system/stack/gatt/connection_manager.h
+++ b/system/stack/gatt/connection_manager.h
@@ -51,7 +51,8 @@
 std::set<tAPP_ID> get_apps_connecting_to(const RawAddress& remote_bda);
 
 bool direct_connect_add(tAPP_ID app_id, const RawAddress& address);
-bool direct_connect_remove(tAPP_ID app_id, const RawAddress& address);
+bool direct_connect_remove(tAPP_ID app_id, const RawAddress& address,
+                           bool connection_timeout = false);
 
 void dump(int fd);
 
diff --git a/system/stack/gatt/gatt_attr.cc b/system/stack/gatt/gatt_attr.cc
index 5d1eb8f..cdab82e 100644
--- a/system/stack/gatt/gatt_attr.cc
+++ b/system/stack/gatt/gatt_attr.cc
@@ -417,14 +417,14 @@
           .permissions = 0,
       },
       {
-          .type = BTGATT_DB_CHARACTERISTIC,
           .uuid = svr_sup_feat_uuid,
+          .type = BTGATT_DB_CHARACTERISTIC,
           .properties = GATT_CHAR_PROP_BIT_READ,
           .permissions = GATT_PERM_READ,
       },
       {
-          .type = BTGATT_DB_CHARACTERISTIC,
           .uuid = cl_sup_feat_uuid,
+          .type = BTGATT_DB_CHARACTERISTIC,
           .properties = GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE,
           .permissions = GATT_PERM_READ | GATT_PERM_WRITE,
       },
diff --git a/system/stack/gatt/gatt_main.cc b/system/stack/gatt/gatt_main.cc
index fb40e27..6fa3dda 100644
--- a/system/stack/gatt/gatt_main.cc
+++ b/system/stack/gatt/gatt_main.cc
@@ -970,10 +970,8 @@
       GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT,
                           p_tcb->transport, true /* is_active */);
     } else {
-      if (bluetooth::common::init_flags::finite_att_timeout_is_enabled()) {
-        GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP,
-                            p_tcb->transport, false /* is_active */);
-      }
+      GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP,
+                          p_tcb->transport, false /* is_active */);
     }
   }
 }
diff --git a/system/stack/hid/hidh_api.cc b/system/stack/hid/hidh_api.cc
index 27595db..572f3f6 100644
--- a/system/stack/hid/hidh_api.cc
+++ b/system/stack/hid/hidh_api.cc
@@ -94,13 +94,18 @@
   p_attr =
       get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(p_rec, attr_id);
   if (p_attr != NULL) {
-    name_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
-    if (name_len < max_len) {
-      memcpy(str, (char*)p_attr->attr_value.v.array, name_len);
-      str[name_len] = '\0';
+    if (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == TEXT_STR_DESC_TYPE) {
+      name_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+      if (name_len < max_len) {
+        memcpy(str, (char*)p_attr->attr_value.v.array, name_len);
+        str[name_len] = '\0';
+      } else {
+        memcpy(str, (char*)p_attr->attr_value.v.array, max_len - 1);
+        str[max_len - 1] = '\0';
+      }
     } else {
-      memcpy(str, (char*)p_attr->attr_value.v.array, max_len - 1);
-      str[max_len - 1] = '\0';
+      str[0] = '\0';
+      LOG_ERROR("attr type not str!!");
     }
   } else
     str[0] = '\0';
@@ -152,36 +157,49 @@
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
             p_rec, ATTR_ID_HID_VIRTUAL_CABLE)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == BOOLEAN_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1 &&
       (p_attr->attr_value.v.u8)) {
     attr_mask |= HID_VIRTUAL_CABLE;
   }
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
             p_rec, ATTR_ID_HID_RECONNECT_INITIATE)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == BOOLEAN_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1 &&
       (p_attr->attr_value.v.u8)) {
     attr_mask |= HID_RECONN_INIT;
   }
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
             p_rec, ATTR_ID_HID_NORMALLY_CONNECTABLE)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == BOOLEAN_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1 &&
       (p_attr->attr_value.v.u8)) {
     attr_mask |= HID_NORMALLY_CONNECTABLE;
   }
 
+  // this attribute is deprecated, should we still keep it?
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
             p_rec, ATTR_ID_HID_SDP_DISABLE)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == BOOLEAN_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1 &&
       (p_attr->attr_value.v.u8)) {
     attr_mask |= HID_SDP_DISABLE;
   }
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
             p_rec, ATTR_ID_HID_BATTERY_POWER)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == BOOLEAN_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1 &&
       (p_attr->attr_value.v.u8)) {
     attr_mask |= HID_BATTERY_POWER;
   }
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
             p_rec, ATTR_ID_HID_REMOTE_WAKE)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == BOOLEAN_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1 &&
       (p_attr->attr_value.v.u8)) {
     attr_mask |= HID_REMOTE_WAKE;
   }
@@ -194,40 +212,54 @@
                     p_nvi->prov_name);
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
-            p_rec, ATTR_ID_HID_DEVICE_RELNUM)) != NULL)) {
+            p_rec, ATTR_ID_HID_DEVICE_RELNUM)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
     p_nvi->rel_num = p_attr->attr_value.v.u16;
   }
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
-            p_rec, ATTR_ID_HID_COUNTRY_CODE)) != NULL)) {
+            p_rec, ATTR_ID_HID_COUNTRY_CODE)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1) {
     p_nvi->ctry_code = p_attr->attr_value.v.u8;
   }
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
-            p_rec, ATTR_ID_HID_DEVICE_SUBCLASS)) != NULL)) {
+            p_rec, ATTR_ID_HID_DEVICE_SUBCLASS)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 1) {
     p_nvi->sub_class = p_attr->attr_value.v.u8;
   }
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
-            p_rec, ATTR_ID_HID_PARSER_VERSION)) != NULL)) {
+            p_rec, ATTR_ID_HID_PARSER_VERSION)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
     p_nvi->hpars_ver = p_attr->attr_value.v.u16;
   }
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
-            p_rec, ATTR_ID_HID_LINK_SUPERVISION_TO)) != NULL)) {
+            p_rec, ATTR_ID_HID_LINK_SUPERVISION_TO)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
     attr_mask |= HID_SUP_TOUT_AVLBL;
     p_nvi->sup_timeout = p_attr->attr_value.v.u16;
   }
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
-            p_rec, ATTR_ID_HID_SSR_HOST_MAX_LAT)) != NULL)) {
+            p_rec, ATTR_ID_HID_SSR_HOST_MAX_LAT)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
     attr_mask |= HID_SSR_MAX_LATENCY;
     p_nvi->ssr_max_latency = p_attr->attr_value.v.u16;
   } else
     p_nvi->ssr_max_latency = HID_SSR_PARAM_INVALID;
 
   if (((p_attr = get_legacy_stack_sdp_api()->record.SDP_FindAttributeInRec(
-            p_rec, ATTR_ID_HID_SSR_HOST_MIN_TOUT)) != NULL)) {
+            p_rec, ATTR_ID_HID_SSR_HOST_MIN_TOUT)) != NULL) &&
+      SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) == UINT_DESC_TYPE &&
+      SDP_DISC_ATTR_LEN(p_attr->attr_len_type) >= 2) {
     attr_mask |= HID_SSR_MIN_TOUT;
     p_nvi->ssr_min_tout = p_attr->attr_value.v.u16;
   } else
diff --git a/system/stack/include/acl_api.h b/system/stack/include/acl_api.h
index 3062815..78302d8 100644
--- a/system/stack/include/acl_api.h
+++ b/system/stack/include/acl_api.h
@@ -226,7 +226,8 @@
  ******************************************************************************/
 void BTM_ReadConnectionAddr(const RawAddress& remote_bda,
                             RawAddress& local_conn_addr,
-                            tBLE_ADDR_TYPE* p_addr_type);
+                            tBLE_ADDR_TYPE* p_addr_type,
+                            bool ota_address = false);
 
 /*******************************************************************************
  *
diff --git a/system/stack/include/btm_api_types.h b/system/stack/include/btm_api_types.h
index a6ab507..68fcd0a 100644
--- a/system/stack/include/btm_api_types.h
+++ b/system/stack/include/btm_api_types.h
@@ -699,9 +699,12 @@
   BTM_PM_STS_HOLD = HCI_MODE_HOLD,      // 0x01
   BTM_PM_STS_SNIFF = HCI_MODE_SNIFF,    // 0x02
   BTM_PM_STS_PARK = HCI_MODE_PARK,      // 0x03
-  BTM_PM_STS_SSR,     /* report the SSR parameters in HCI_SNIFF_SUB_RATE_EVT */
-  BTM_PM_STS_PENDING, /* when waiting for status from controller */
-  BTM_PM_STS_ERROR    /* when HCI command status returns error */
+  BTM_PM_STS_SSR,      // Hci sniff subrating event reported the SSR parameters
+  BTM_PM_STS_PENDING,  // Successful hci mode change command status received and
+                       // now waiting for mode change event to indicate
+                       // change to desired mode target.
+  BTM_PM_STS_ERROR  // Failed hci mode change command status which will result
+                    // in no mode change event.
 };
 typedef uint8_t tBTM_PM_STATUS;
 
@@ -751,7 +754,7 @@
 }
 
 inline std::string power_mode_text(tBTM_PM_MODE mode) {
-  std::string s = base::StringPrintf((mode & BTM_PM_MD_FORCE) ? "" : "forced:");
+  std::string s = base::StringPrintf((mode & BTM_PM_MD_FORCE) ? "forced:" : "");
   switch (mode & ~BTM_PM_MD_FORCE) {
     case BTM_PM_MD_ACTIVE:
       return s + std::string("active");
@@ -779,13 +782,19 @@
 /************************
  *  Power Manager Types
  ************************/
-typedef struct {
+struct tBTM_PM_PWR_MD {
   uint16_t max = 0;
   uint16_t min = 0;
   uint16_t attempt = 0;
   uint16_t timeout = 0;
   tBTM_PM_MODE mode = BTM_PM_MD_ACTIVE;  // 0
-} tBTM_PM_PWR_MD;
+
+  std::string ToString() const {
+    return base::StringPrintf(
+        "mode:%s[max:%hu min:%hu attempt:%hu timeout:%hu]",
+        power_mode_text(mode).c_str(), max, min, attempt, timeout);
+  }
+};
 
 /*************************************
  *  Power Manager Callback Functions
diff --git a/system/stack/include/btm_ble_api.h b/system/stack/include/btm_ble_api.h
index 043b725..e62c04b79 100644
--- a/system/stack/include/btm_ble_api.h
+++ b/system/stack/include/btm_ble_api.h
@@ -361,7 +361,8 @@
  ******************************************************************************/
 bool BTM_ReadRemoteConnectionAddr(const RawAddress& pseudo_addr,
                                   RawAddress& conn_addr,
-                                  tBLE_ADDR_TYPE* p_addr_type);
+                                  tBLE_ADDR_TYPE* p_addr_type,
+                                  bool ota_address);
 
 /*******************************************************************************
  *
diff --git a/system/stack/include/btm_client_interface.h b/system/stack/include/btm_client_interface.h
index e2326e8..b463728 100644
--- a/system/stack/include/btm_client_interface.h
+++ b/system/stack/include/btm_client_interface.h
@@ -57,21 +57,6 @@
     bool (*BTM_FreeSCN)(uint8_t scn);
   } scn;
 
-  // Neighbor
-  struct {
-    void (*BTM_CancelInquiry)();
-    tBTM_INQ_INFO* (*BTM_InqDbNext)(tBTM_INQ_INFO* p_cur);
-    tBTM_STATUS (*BTM_ClearInqDb)(const RawAddress* p_bda);
-    tBTM_STATUS (*BTM_SetDiscoverability)(uint16_t inq_mode);
-    tBTM_STATUS (*BTM_SetConnectability)(uint16_t page_mode);
-    tBTM_STATUS (*BTM_StartInquiry)(tBTM_INQ_RESULTS_CB* p_results_cb,
-                                    tBTM_CMPL_CB* p_cmpl_cb);
-    uint16_t (*BTM_IsInquiryActive)(void);
-    tBTM_STATUS (*BTM_SetInquiryMode)(uint8_t mode);
-    void (*BTM_EnableInterlacedInquiryScan)();
-    void (*BTM_EnableInterlacedPageScan)();
-  } neighbor;
-
   // Acl peer and lifecycle
   struct {
     struct {
@@ -125,8 +110,6 @@
                              uint8_t pin_length);
     bool (*BTM_SecAddRmtNameNotifyCallback)(tBTM_RMT_NAME_CALLBACK* p_callback);
     bool (*BTM_SecDeleteDevice)(const RawAddress& bd_addr);
-    bool (*BTM_SecDeleteRmtNameNotifyCallbac)(
-        tBTM_RMT_NAME_CALLBACK* p_callback);
     bool (*BTM_SecRegister)(const tBTM_APPL_INFO* p_cb_info);
     const char* (*BTM_SecReadDevName)(const RawAddress& bd_addr);
     tBTM_STATUS (*BTM_SecBond)(const RawAddress& bd_addr,
diff --git a/system/stack/include/l2cdefs.h b/system/stack/include/l2cdefs.h
index 1923c05..34e786c 100644
--- a/system/stack/include/l2cdefs.h
+++ b/system/stack/include/l2cdefs.h
@@ -188,6 +188,37 @@
              std::string("]");
   }
 }
+
+static inline std::string l2cap_command_code_text(uint8_t cmd) {
+  switch (cmd) {
+    CASE_RETURN_TEXT(L2CAP_CMD_REJECT);
+    CASE_RETURN_TEXT(L2CAP_CMD_CONN_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_CONN_RSP);
+    CASE_RETURN_TEXT(L2CAP_CMD_CONFIG_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_CONFIG_RSP);
+    CASE_RETURN_TEXT(L2CAP_CMD_DISC_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_DISC_RSP);
+    CASE_RETURN_TEXT(L2CAP_CMD_ECHO_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_ECHO_RSP);
+    CASE_RETURN_TEXT(L2CAP_CMD_INFO_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_INFO_RSP);
+    CASE_RETURN_TEXT(L2CAP_CMD_AMP_CONN_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_AMP_MOVE_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_BLE_UPDATE_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_BLE_UPDATE_RSP);
+    CASE_RETURN_TEXT(L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES);
+    CASE_RETURN_TEXT(L2CAP_CMD_BLE_FLOW_CTRL_CREDIT);
+    CASE_RETURN_TEXT(L2CAP_CMD_CREDIT_BASED_CONN_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_CREDIT_BASED_CONN_RES);
+    CASE_RETURN_TEXT(L2CAP_CMD_CREDIT_BASED_RECONFIG_REQ);
+    CASE_RETURN_TEXT(L2CAP_CMD_CREDIT_BASED_RECONFIG_RES);
+    default:
+      return std::string("UNKNOWN L2CAP CMD[") + std::to_string(cmd) +
+             std::string("]");
+  }
+}
+
 #undef CASE_RETURN_TEXT
 
 inline tL2CAP_CONN to_l2cap_result_code(uint16_t result) {
diff --git a/system/stack/l2cap/l2c_api.cc b/system/stack/l2cap/l2c_api.cc
index 809f9e6..308c38a 100644
--- a/system/stack/l2cap/l2c_api.cc
+++ b/system/stack/l2cap/l2c_api.cc
@@ -598,14 +598,9 @@
       L2CAP_TRACE_DEBUG("%s LE Link is up", __func__);
       // post this asynchronously to avoid out-of-order callback invocation
       // should this operation fail
-      if (bluetooth::common::init_flags::
-              asynchronously_start_l2cap_coc_is_enabled()) {
-        do_in_main_thread(
-            FROM_HERE, base::BindOnce(&l2c_csm_execute, base::Unretained(p_ccb),
-                                      L2CEVT_L2CA_CONNECT_REQ, nullptr));
-      } else {
-        l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL);
-      }
+      do_in_main_thread(
+          FROM_HERE, base::BindOnce(&l2c_csm_execute, base::Unretained(p_ccb),
+                                    L2CEVT_L2CA_CONNECT_REQ, nullptr));
     }
   }
 
@@ -892,7 +887,7 @@
  *  Description      Start reconfigure procedure on Connection Oriented Channel.
  *
  *  Parameters:      Vector of channels for which configuration should be
- *changed New local channel configuration
+ *                   changed to new local channel configuration
  *
  *  Return value:    true if peer is connected
  *
diff --git a/system/stack/l2cap/l2c_ble.cc b/system/stack/l2cap/l2c_ble.cc
index 4a929d4..f076f22 100644
--- a/system/stack/l2cap/l2c_ble.cc
+++ b/system/stack/l2cap/l2c_ble.cc
@@ -262,6 +262,7 @@
         return false;
       }
     }
+    p_lcb->link_state = LST_CONNECTING;
   } else if (role == HCI_ROLE_CENTRAL && p_lcb->link_state != LST_CONNECTING) {
     LOG_ERROR(
         "Received le acl connection as role central but not in connecting "
@@ -1799,8 +1800,7 @@
     return;
   }
 
-  if (!controller_get_interface()->supports_ble_connection_subrating_host() ||
-      !controller_get_interface()->supports_ble_connection_subrating() ||
+  if (!controller_get_interface()->supports_ble_connection_subrating() ||
       !acl_peer_supports_ble_connection_subrating(p_lcb->remote_bd_addr) ||
       !acl_peer_supports_ble_connection_subrating_host(p_lcb->remote_bd_addr)) {
     L2CAP_TRACE_DEBUG(
diff --git a/system/stack/l2cap/l2c_main.cc b/system/stack/l2cap/l2c_main.cc
index a273422..bf04b54 100644
--- a/system/stack/l2cap/l2c_main.cc
+++ b/system/stack/l2cap/l2c_main.cc
@@ -287,7 +287,8 @@
       break;
     }
 
-    LOG_DEBUG("cmd_code: %d, id:%d, cmd_len:%d", cmd_code, id, cmd_len);
+    LOG_DEBUG("cmd: %s, id:%d, cmd_len:%d",
+              l2cap_command_code_text(cmd_code).c_str(), id, cmd_len);
 
     /* Bad L2CAP packet length, look for cmd to reject */
     if (pkt_size_rej) {
diff --git a/system/stack/l2cap/l2c_utils.cc b/system/stack/l2cap/l2c_utils.cc
index 1e96a13..026a51f 100644
--- a/system/stack/l2cap/l2c_utils.cc
+++ b/system/stack/l2cap/l2c_utils.cc
@@ -2732,8 +2732,7 @@
   // be in use even without a GATT client. We only timeout if either a dynamic
   // channel or a GATT client was used, since then we expect the client to
   // manage the lifecycle of the connection.
-  if (bluetooth::common::init_flags::finite_att_timeout_is_enabled() &&
-      !p_lcb->with_active_local_clients) {
+  if (!p_lcb->with_active_local_clients) {
     return;
   }
 
diff --git a/system/stack/mmc/BUILD.gn b/system/stack/mmc/BUILD.gn
new file mode 100644
index 0000000..3900adc
--- /dev/null
+++ b/system/stack/mmc/BUILD.gn
@@ -0,0 +1,88 @@
+#
+#  Copyright 2023 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("//common-mk/install_config.gni")
+
+group("mmc") {
+  deps = [
+    ":install_dbus_config",
+    ":install_init",
+    ":install_minijail_config",
+    ":install_system_service",
+    ":install_tmpfiles_config",
+    ":mmc_service",
+  ]
+}
+
+pkg_config("target_defaults") {
+  include_dirs = [
+    "//bt/system",
+    "//bt/system/include",
+    "//bt/system/stack",
+    "//bt/system/stack/include",
+  ]
+  pkg_deps = [
+    "libchrome",
+  ]
+  if (use.fuzzer) {
+    pkg_deps += [ "protobuf" ]
+  } else {
+    pkg_deps += [ "protobuf-lite" ]
+  }
+}
+
+install_config("install_dbus_config") {
+  sources = [ "dbus_permissions/org.chromium.mmc.CodecManager.conf" ]
+  install_path = "/etc/dbus-1/system.d"
+}
+
+install_config("install_system_service") {
+  sources = [ "dbus_service/org.chromium.mmc.CodecManager.service" ]
+  install_path = "/usr/share/dbus-1/system-services"
+}
+
+install_config("install_init") {
+  sources = [ "init/mmc_service.conf" ]
+  install_path = "/etc/init"
+}
+
+install_config("install_tmpfiles_config") {
+  sources = [ "tmpfiles.d/mmc.conf" ]
+  install_path = "/usr/lib/tmpfiles.d"
+}
+
+install_config("install_minijail_config") {
+  sources = [ "minijail/mmc.conf" ]
+  install_path = "/usr/share/minijail"
+}
+
+source_set("libmmc") {
+  configs += [ ":target_defaults" ]
+  sources = [
+    "daemon/service.cc",
+  ]
+  deps = [
+    "//bt/system/common",
+    "//bt/system/stack/mmc/proto:mmc_service_proto",
+    "//bt/system/stack/mmc/codec_server:libcodec_server_hfp_lc3",
+  ]
+}
+
+executable("mmc_service") {
+  configs += [ ":target_defaults" ]
+  deps = [ ":libmmc" ]
+  sources = [ "main.cc" ]
+}
diff --git a/system/stack/mmc/codec_server/BUILD.gn b/system/stack/mmc/codec_server/BUILD.gn
new file mode 100644
index 0000000..4e5c354
--- /dev/null
+++ b/system/stack/mmc/codec_server/BUILD.gn
@@ -0,0 +1,34 @@
+#
+#  Copyright 2023 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.
+#
+
+source_set("libcodec_server_hfp_lc3"){
+  configs += [ "//bt/system/stack/mmc:target_defaults" ]
+  include_dirs = [
+    "//bt/system",
+    "//bt/system/include",
+    "//bt/system/stack",
+    "//bt/system/stack/include",
+  ]
+  deps = [
+    "//bt/system/stack/mmc/proto:mmc_config_proto",
+    "//bt/system/embdrv/lc3:liblc3",
+    "//bt/system/osi",
+  ]
+  sources = [
+    "hfp_lc3_mmc_encoder.cc",
+    "hfp_lc3_mmc_decoder.cc",
+  ]
+}
diff --git a/system/stack/mmc/codec_server/hfp_lc3_mmc_decoder.cc b/system/stack/mmc/codec_server/hfp_lc3_mmc_decoder.cc
new file mode 100644
index 0000000..414f3fd
--- /dev/null
+++ b/system/stack/mmc/codec_server/hfp_lc3_mmc_decoder.cc
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2023 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 "mmc/codec_server/hfp_lc3_mmc_decoder.h"
+
+#include <base/logging.h>
+#include <errno.h>
+
+#include "embdrv/lc3/include/lc3.h"
+#include "mmc/codec_server/lc3_utils.h"
+#include "mmc/proto/mmc_config.pb.h"
+#include "osi/include/allocator.h"
+
+namespace mmc {
+
+HfpLc3Decoder::HfpLc3Decoder() : hfp_lc3_decoder_mem_(nullptr) {}
+
+HfpLc3Decoder::~HfpLc3Decoder() { cleanup(); }
+
+int HfpLc3Decoder::init(ConfigParam config) {
+  cleanup();
+
+  if (!config.has_hfp_lc3_decoder_param()) {
+    LOG(ERROR) << "HFP LC3 decoder params are not set";
+    return -EINVAL;
+  }
+
+  param_ = config.hfp_lc3_decoder_param();
+  int dt_us = param_.dt_us();
+  int sr_hz = param_.sr_hz();
+  int sr_pcm_hz = param_.sr_pcm_hz();
+  const unsigned dec_size = lc3_decoder_size(dt_us, sr_pcm_hz);
+
+  hfp_lc3_decoder_mem_ = osi_malloc(dec_size);
+
+  hfp_lc3_decoder_ =
+      lc3_setup_decoder(dt_us, sr_hz, sr_pcm_hz, hfp_lc3_decoder_mem_);
+
+  if (hfp_lc3_decoder_ == nullptr) {
+    LOG(ERROR) << "Wrong parameters provided";
+    return -EINVAL;
+  }
+
+  return HFP_LC3_PKT_FRAME_LEN;
+}
+
+void HfpLc3Decoder::cleanup() {
+  if (hfp_lc3_decoder_mem_) {
+    osi_free_and_reset((void**)&hfp_lc3_decoder_mem_);
+    LOG(INFO) << "Released the decoder instance";
+  }
+}
+
+int HfpLc3Decoder::transcode(uint8_t* i_buf, int i_len, uint8_t* o_buf,
+                             int o_len) {
+  if (o_buf == nullptr || o_len < HFP_LC3_PCM_BYTES + 1) {
+    LOG(ERROR) << "Output buffer size is less than LC3 frame size";
+    return -EINVAL;
+  }
+
+  // Check header to decide whether it's PLC.
+  uint8_t* in_frame =
+      (i_buf[0] || i_buf[1]) ? i_buf + HFP_LC3_H2_HEADER_LEN : nullptr;
+
+  // First byte is reserved to indicate PLC.
+  uint8_t* out_frame = o_buf + 1;
+
+  /* Note this only fails when wrong parameters are supplied. */
+  int rc = lc3_decode(hfp_lc3_decoder_, in_frame, HFP_LC3_PKT_FRAME_LEN,
+                      MapLc3PcmFmt(param_.fmt()), out_frame, param_.stride());
+
+  if (rc != 0 && rc != 1) {
+    LOG(WARNING) << "Wrong decode parameters";
+    std::fill(o_buf, o_buf + o_len, 0);
+  } else
+    o_buf[0] = rc;
+  return HFP_LC3_PCM_BYTES + 1;
+}
+
+}  // namespace mmc
diff --git a/system/stack/mmc/codec_server/hfp_lc3_mmc_decoder.h b/system/stack/mmc/codec_server/hfp_lc3_mmc_decoder.h
new file mode 100644
index 0000000..cabe34b
--- /dev/null
+++ b/system/stack/mmc/codec_server/hfp_lc3_mmc_decoder.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#ifndef MMC_CODEC_SERVER_HFP_LC3_MMC_DECODER_H_
+#define MMC_CODEC_SERVER_HFP_LC3_MMC_DECODER_H_
+
+#include "embdrv/lc3/include/lc3.h"
+#include "mmc/mmc_interface/mmc_interface.h"
+#include "mmc/proto/mmc_config.pb.h"
+
+namespace mmc {
+
+// Implementation of MmcInterface.
+// HfpLc3Decoder wraps lc3 decode libraries.
+class HfpLc3Decoder : public MmcInterface {
+ public:
+  explicit HfpLc3Decoder();
+  ~HfpLc3Decoder();
+
+  // HfpLc3Decoder is neither copyable nor movable.
+  HfpLc3Decoder(const HfpLc3Decoder&) = delete;
+  HfpLc3Decoder& operator=(const HfpLc3Decoder&) = delete;
+
+  // Inits decoder instance.
+  //
+  // Returns:
+  //   Input packet size accepted by the decoder, if init succeeded.
+  //   Negative errno on error, otherwise.
+  int init(ConfigParam config) override;
+
+  // Releases decoder instance.
+  void cleanup() override;
+
+  // Decodes data from |i_buf| and stores the result in |o_buf|.
+  //
+  // Returns:
+  //   Decoded data length, if decode succeeded.
+  //   Negative errno on error, otherwise.
+  int transcode(uint8_t* i_buf, int i_len, uint8_t* o_buf, int o_len) override;
+
+ private:
+  void* hfp_lc3_decoder_mem_;
+  lc3_decoder_t hfp_lc3_decoder_;
+  Lc3Param param_;
+};
+
+}  // namespace mmc
+
+#endif  // MMC_CODEC_SERVER_HFP_LC3_MMC_DECODER_H_
diff --git a/system/stack/mmc/codec_server/hfp_lc3_mmc_encoder.cc b/system/stack/mmc/codec_server/hfp_lc3_mmc_encoder.cc
new file mode 100644
index 0000000..676c0fb
--- /dev/null
+++ b/system/stack/mmc/codec_server/hfp_lc3_mmc_encoder.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2023 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 "mmc/codec_server/hfp_lc3_mmc_encoder.h"
+
+#include <base/logging.h>
+#include <errno.h>
+
+#include <algorithm>
+
+#include "embdrv/lc3/include/lc3.h"
+#include "mmc/codec_server/lc3_utils.h"
+#include "mmc/proto/mmc_config.pb.h"
+#include "osi/include/allocator.h"
+
+namespace mmc {
+
+HfpLc3Encoder::HfpLc3Encoder() : hfp_lc3_encoder_mem_(nullptr) {}
+
+HfpLc3Encoder::~HfpLc3Encoder() { cleanup(); }
+
+int HfpLc3Encoder::init(ConfigParam config) {
+  cleanup();
+
+  if (!config.has_hfp_lc3_encoder_param()) {
+    LOG(ERROR) << "HFP LC3 encoder params are not set";
+    return -EINVAL;
+  }
+
+  param_ = config.hfp_lc3_encoder_param();
+  int dt_us = param_.dt_us();
+  int sr_hz = param_.sr_hz();
+  int sr_pcm_hz = param_.sr_pcm_hz();
+  const unsigned enc_size = lc3_encoder_size(dt_us, sr_pcm_hz);
+
+  hfp_lc3_encoder_mem_ = osi_malloc(enc_size);
+
+  hfp_lc3_encoder_ =
+      lc3_setup_encoder(dt_us, sr_hz, sr_pcm_hz, hfp_lc3_encoder_mem_);
+
+  if (hfp_lc3_encoder_ == nullptr) {
+    LOG(ERROR) << "Wrong parameters provided";
+    return -EINVAL;
+  }
+
+  return HFP_LC3_PCM_BYTES;
+}
+
+void HfpLc3Encoder::cleanup() {
+  if (hfp_lc3_encoder_mem_) {
+    osi_free_and_reset((void**)&hfp_lc3_encoder_mem_);
+    LOG(INFO) << "Released the encoder instance";
+  }
+}
+
+int HfpLc3Encoder::transcode(uint8_t* i_buf, int i_len, uint8_t* o_buf,
+                             int o_len) {
+  if (i_buf == nullptr || o_buf == nullptr) {
+    LOG(ERROR) << "Buffer is null";
+    return -EINVAL;
+  }
+
+  /* Note this only fails when wrong parameters are supplied. */
+  int rc = lc3_encode(hfp_lc3_encoder_, MapLc3PcmFmt(param_.fmt()), i_buf,
+                      param_.stride(), HFP_LC3_PKT_FRAME_LEN, o_buf);
+
+  if (rc != 0) {
+    LOG(WARNING) << "Wrong encode parameters";
+    std::fill(o_buf, o_buf + o_len, 0);
+  }
+
+  return HFP_LC3_PKT_FRAME_LEN;
+}
+
+}  // namespace mmc
diff --git a/system/stack/mmc/codec_server/hfp_lc3_mmc_encoder.h b/system/stack/mmc/codec_server/hfp_lc3_mmc_encoder.h
new file mode 100644
index 0000000..a578c9f
--- /dev/null
+++ b/system/stack/mmc/codec_server/hfp_lc3_mmc_encoder.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#ifndef MMC_CODEC_SERVER_HFP_LC3_MMC_ENCODER_H_
+#define MMC_CODEC_SERVER_HFP_LC3_MMC_ENCODER_H_
+
+#include "embdrv/lc3/include/lc3.h"
+#include "mmc/mmc_interface/mmc_interface.h"
+#include "mmc/proto/mmc_config.pb.h"
+
+namespace mmc {
+
+// Implementation of MmcInterface.
+// HfpLc3Encoder wraps lc3 encode libraries.
+class HfpLc3Encoder : public MmcInterface {
+ public:
+  explicit HfpLc3Encoder();
+  ~HfpLc3Encoder();
+
+  // HfpLc3Encoder is neither copyable nor movable.
+  HfpLc3Encoder(const HfpLc3Encoder&) = delete;
+  HfpLc3Encoder& operator=(const HfpLc3Encoder&) = delete;
+
+  // Inits encoder instance.
+  //
+  // Returns:
+  //   Input pcm frame size accepted by the encoder, if init succeeded.
+  //   Negative errno on error, otherwise.
+  int init(ConfigParam config) override;
+
+  // Releases encoder instance.
+  void cleanup() override;
+
+  // Encodes data from |i_buf|, and stores the result to |o_buf|.
+  //
+  // Returns:
+  //   Encoded data length, if encode succeeded.
+  //   Negative errno on error, otherwise.
+  int transcode(uint8_t* i_buf, int i_len, uint8_t* o_buf, int o_len) override;
+
+ private:
+  void* hfp_lc3_encoder_mem_;
+  lc3_encoder_t hfp_lc3_encoder_;
+  Lc3Param param_;
+};
+
+}  // namespace mmc
+
+#endif  // MMC_CODEC_SERVER_HFP_LC3_MMC_ENCODER_H_
diff --git a/system/stack/mmc/codec_server/lc3_utils.h b/system/stack/mmc/codec_server/lc3_utils.h
new file mode 100644
index 0000000..d3eed60
--- /dev/null
+++ b/system/stack/mmc/codec_server/lc3_utils.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#ifndef MMC_CODEC_SERVER_LC3_UTILS_H_
+#define MMC_CODEC_SERVER_LC3_UTILS_H_
+
+#include <base/logging.h>
+
+#include "embdrv/lc3/include/lc3.h"
+#include "mmc/proto/mmc_config.pb.h"
+
+namespace mmc {
+
+// HFP LC3 constants.
+const int HFP_LC3_H2_HEADER_LEN = 2;
+const int HFP_LC3_PKT_FRAME_LEN = 58;
+const int HFP_LC3_PCM_BYTES = 480;
+
+// Helper that maps MMC pcm format to lc3 pcm format.
+static lc3_pcm_format MapLc3PcmFmt(Lc3Param_PcmFmt fmt) {
+  switch (fmt) {
+    case Lc3Param::kLc3PcmFormatS16:
+      return LC3_PCM_FORMAT_S16;
+    case Lc3Param::kLc3PcmFormatS24:
+      return LC3_PCM_FORMAT_S24;
+    default:
+      LOG(INFO)
+          << "No corresponding LC3 PCM format, return `LC3_PCM_FORMAT_S16`.";
+      return LC3_PCM_FORMAT_S16;
+  }
+}
+
+}  // namespace mmc
+#endif  // MMC_CODEC_SERVER_LC3_UTILS_H_
diff --git a/system/stack/mmc/daemon/constants.h b/system/stack/mmc/daemon/constants.h
new file mode 100644
index 0000000..abcd07d
--- /dev/null
+++ b/system/stack/mmc/daemon/constants.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#ifndef MMC_DAEMON_DBUS_CONSTANTS_H_
+#define MMC_DAEMON_DBUS_CONSTANTS_H_
+
+namespace mmc {
+
+// DBus constants.
+constexpr char kMmcServiceName[] = "org.chromium.mmc.CodecManager";
+constexpr char kMmcServiceInterface[] = "org.chromium.mmc.CodecManager";
+constexpr char kMmcServicePath[] = "/org/chromium/mmc/CodecManager";
+const char kMmcServiceError[] = "org.chromium.mmc.CodecManager.Error";
+constexpr char kCodecInitMethod[] = "CodecInit";
+constexpr char kCodecCleanUpMethod[] = "CodecCleanUp";
+
+// Socket constants.
+const char kMmcSocketName[] = "/run/mmc/sockets/";
+// The maximum number of socket pending connections.
+// MMC daemon expects at most two clients, decoder and encoder of one codec.
+constexpr int kClientMaximum = 2;
+// Socket default maximum buffer size.
+constexpr int kMaximumBufferSize = 32768;
+
+// Thread constants.
+constexpr char kWorkerThreadName[] = "bt_mmc_worker_thread";
+constexpr int kThreadCheckTimeout = 1;
+}  // namespace mmc
+
+#endif  // MMC_DAEMON_DBUS_CONSTANTS_H_
diff --git a/system/stack/mmc/daemon/service.cc b/system/stack/mmc/daemon/service.cc
new file mode 100644
index 0000000..b028353
--- /dev/null
+++ b/system/stack/mmc/daemon/service.cc
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2023 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 "mmc/daemon/service.h"
+
+#include <base/functional/bind.h>
+#include <base/functional/callback_helpers.h>
+#include <base/logging.h>
+#include <base/stl_util.h>
+#include <base/task/single_thread_task_runner.h>
+#include <base/unguessable_token.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <array>
+#include <cstring>
+#include <future>
+
+#include "common/message_loop_thread.h"
+#include "mmc/codec_server/hfp_lc3_mmc_decoder.h"
+#include "mmc/codec_server/hfp_lc3_mmc_encoder.h"
+#include "mmc/daemon/constants.h"
+#include "mmc/mmc_interface/mmc_interface.h"
+#include "mmc/proto/mmc_service.pb.h"
+
+namespace mmc {
+namespace {
+// Task that would run on the thread.
+void StartSocketListener(int fd, struct sockaddr_un addr,
+                         std::promise<void> task_ended,
+                         std::unique_ptr<MmcInterface> codec_server) {
+  socklen_t addr_size = sizeof(struct sockaddr_un);
+  int client_fd = accept(fd, (struct sockaddr*)&addr, &addr_size);
+  // |fd| is only used for accept.
+  close(fd);
+
+  if (client_fd < 0) {
+    LOG(ERROR) << "Failed to accept: " << strerror(errno);
+    codec_server.release();
+    task_ended.set_value();
+    return;
+  }
+
+  std::array<uint8_t, kMaximumBufferSize> i_buf = {};
+  std::array<uint8_t, kMaximumBufferSize> o_buf = {};
+
+  struct pollfd pfd;
+  pfd.fd = client_fd;
+  pfd.events = POLLIN;
+
+  while (1) {
+    // Blocking poll.
+    int poll_ret = poll(&pfd, 1, -1);
+    if (poll_ret <= 0) {
+      LOG(ERROR) << "Poll failed: " << strerror(errno);
+      break;
+    }
+
+    // Ignore remaining data in the closed socket.
+    if (pfd.revents & (POLLHUP | POLLNVAL)) {
+      LOG(INFO) << "Socket disconnected";
+      break;
+    }
+
+    int i_data_len =
+        recv(client_fd, i_buf.data(), kMaximumBufferSize, MSG_NOSIGNAL);
+    if (i_data_len <= 0) {
+      LOG(ERROR) << "Failed to recv data: " << strerror(errno);
+      break;
+    }
+
+    // Start transcode.
+    int o_data_len = codec_server->transcode(i_buf.data(), i_data_len,
+                                             o_buf.data(), kMaximumBufferSize);
+    if (o_data_len < 0) {
+      LOG(ERROR) << "Failed to transcode: " << strerror(-o_data_len);
+      break;
+    }
+
+    int sent_rc = send(client_fd, o_buf.data(), o_data_len, MSG_NOSIGNAL);
+    if (sent_rc <= 0) {
+      LOG(ERROR) << "Failed to send data: " << strerror(errno);
+      break;
+    }
+    o_buf.fill(0);
+  }
+  close(client_fd);
+  unlink(addr.sun_path);
+  codec_server.release();
+  task_ended.set_value();
+  return;
+}
+
+}  // namespace
+
+Service::Service(base::OnceClosure shutdown_callback)
+    : shutdown_callback_(std::move(shutdown_callback)),
+      weak_ptr_factory_(this) {}
+
+bool Service::Init() {
+  // Set up the dbus service.
+  dbus::Bus::Options opts;
+  opts.bus_type = dbus::Bus::SYSTEM;
+  bus_ = new dbus::Bus(std::move(opts));
+
+  if (!bus_->Connect()) {
+    LOG(ERROR) << "Failed to connect to system bus";
+    return false;
+  }
+
+  exported_object_ = bus_->GetExportedObject(dbus::ObjectPath(kMmcServicePath));
+  if (!exported_object_) {
+    LOG(ERROR) << "Failed to export " << kMmcServicePath << " object";
+    return false;
+  }
+
+  using ServiceMethod = void (Service::*)(dbus::MethodCall*,
+                                          dbus::ExportedObject::ResponseSender);
+  const std::map<const char*, ServiceMethod> kServiceMethods = {
+      {kCodecInitMethod, &Service::CodecInit},
+      {kCodecCleanUpMethod, &Service::CodecCleanUp},
+  };
+
+  for (const auto& iter : kServiceMethods) {
+    bool ret = exported_object_->ExportMethodAndBlock(
+        kMmcServiceInterface, iter.first,
+        base::BindRepeating(iter.second, weak_ptr_factory_.GetWeakPtr()));
+    if (!ret) {
+      LOG(ERROR) << "Failed to export method: " << iter.first;
+      return false;
+    }
+  }
+
+  if (!bus_->RequestOwnershipAndBlock(kMmcServiceName,
+                                      dbus::Bus::REQUIRE_PRIMARY)) {
+    LOG(ERROR) << "Failed to take ownership of " << kMmcServiceName;
+    return false;
+  }
+  return true;
+}
+
+void Service::CodecInit(dbus::MethodCall* method_call,
+                        dbus::ExportedObject::ResponseSender sender) {
+  dbus::MessageReader reader(method_call);
+  auto dbus_response = dbus::Response::FromMethodCall(method_call);
+
+  dbus::MessageWriter writer(dbus_response.get());
+
+  CodecInitRequest request;
+  CodecInitResponse response;
+
+  if (!reader.PopArrayOfBytesAsProto(&request)) {
+    std::move(sender).Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, kMmcServiceError,
+        "Unable to parse CodecInitRequest from message"));
+    return;
+  }
+
+  if (!request.has_config()) {
+    std::move(sender).Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, kMmcServiceError, "'Config Param' must be set"));
+    return;
+  }
+
+  // Create codec server instance.
+  std::unique_ptr<MmcInterface> codec_server;
+  if (request.config().has_hfp_lc3_decoder_param()) {
+    codec_server = std::make_unique<HfpLc3Decoder>();
+  } else if (request.config().has_hfp_lc3_encoder_param()) {
+    codec_server = std::make_unique<HfpLc3Encoder>();
+  } else {
+    std::move(sender).Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, kMmcServiceError, "Codec type must be specified"));
+    return;
+  }
+
+  int frame_size = codec_server->init(request.config());
+  if (frame_size < 0) {
+    std::move(sender).Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, kMmcServiceError,
+        "Init codec server failed: " + std::string(strerror(-frame_size))));
+    return;
+  }
+  response.set_input_frame_size(frame_size);
+
+  // Generate socket name for client.
+  std::string socket_path =
+      std::string(kMmcSocketName) + base::UnguessableToken::Create().ToString();
+  response.set_socket_token(socket_path);
+
+  int skt_fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+  if (skt_fd < 0) {
+    std::move(sender).Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, kMmcServiceError,
+        "Create socket failed: " + std::string(strerror(errno))));
+    return;
+  }
+
+  struct sockaddr_un addr = {};
+  addr.sun_family = AF_UNIX;
+  strncpy(addr.sun_path, response.socket_token().c_str(),
+          sizeof(addr.sun_path) - 1);
+  unlink(addr.sun_path);
+
+  if (bind(skt_fd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un)) == -1) {
+    std::move(sender).Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, kMmcServiceError,
+        "Bind socket failed: " + std::string(strerror(errno))));
+    return;
+  }
+
+  // mmc_service group can read/write the socket.
+  int rc = chmod(addr.sun_path, 0770);
+  if (rc < 0) {
+    std::move(sender).Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, kMmcServiceError,
+        "Chmod socket failed: " + std::string(strerror(errno))));
+    return;
+  }
+
+  if (listen(skt_fd, kClientMaximum) == -1) {
+    std::move(sender).Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, kMmcServiceError,
+        "Listen socket failed: " + std::string(strerror(errno))));
+    return;
+  }
+
+  // Create a thread and pass codec server and socket fd to it.
+  if (!StartWorkerThread(skt_fd, std::move(addr), std::move(codec_server))) {
+    std::move(sender).Run(dbus::ErrorResponse::FromMethodCall(
+        method_call, kMmcServiceError, "No free thread available"));
+    return;
+  }
+
+  writer.AppendProtoAsArrayOfBytes(response);
+  std::move(sender).Run(std::move(dbus_response));
+  return;
+}
+
+void Service::CodecCleanUp(dbus::MethodCall* method_call,
+                           dbus::ExportedObject::ResponseSender sender) {
+  auto dbus_response = dbus::Response::FromMethodCall(method_call);
+  RemoveIdleThread();
+  std::move(sender).Run(std::move(dbus_response));
+  return;
+}
+
+bool Service::StartWorkerThread(int fd, struct sockaddr_un addr,
+                                std::unique_ptr<MmcInterface> codec_server) {
+  // Each thread has its associated future to indicate task completion.
+  std::promise<void> task_ended;
+  thread_pool_.push_back(std::make_pair(
+      std::make_unique<bluetooth::common::MessageLoopThread>(kWorkerThreadName),
+      std::make_unique<std::future<void>>(task_ended.get_future())));
+
+  // Start up thread and assign task to it.
+  thread_pool_.back().first->StartUp();
+  if (!thread_pool_.back().first->IsRunning()) {
+    LOG(ERROR) << "Failed to start thread";
+    return false;
+  }
+
+  // Real-time scheduling increases thread priority.
+  // Without it, the thread still works.
+  if (!thread_pool_.back().first->EnableRealTimeScheduling()) {
+    LOG(WARNING) << "Failed to enable real time scheduling";
+  }
+
+  if (!thread_pool_.back().first->DoInThread(
+          FROM_HERE,
+          base::BindOnce(&StartSocketListener, fd, std::move(addr),
+                         std::move(task_ended), std::move(codec_server)))) {
+    LOG(ERROR) << "Failed to run task";
+    return false;
+  }
+
+  return true;
+}
+
+void Service::RemoveIdleThread() {
+  for (auto thread = thread_pool_.begin(); thread != thread_pool_.end();) {
+    if (thread->second->wait_for(std::chrono::milliseconds(
+            kThreadCheckTimeout)) == std::future_status::ready) {
+      // The task is over, close the thread and remove it from the thread pool.
+      thread->first->ShutDown();
+      thread = thread_pool_.erase(thread);
+    } else {
+      thread++;
+    }
+  }
+}
+
+}  // namespace mmc
diff --git a/system/stack/mmc/daemon/service.h b/system/stack/mmc/daemon/service.h
new file mode 100644
index 0000000..129161e
--- /dev/null
+++ b/system/stack/mmc/daemon/service.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#ifndef MMC_DAEMON_SERVICE_H_
+#define MMC_DAEMON_SERVICE_H_
+
+#include <base/functional/callback.h>
+#include <base/memory/ref_counted.h>
+#include <base/memory/weak_ptr.h>
+#include <dbus/bus.h>
+#include <dbus/exported_object.h>
+#include <dbus/message.h>
+#include <sys/un.h>
+
+#include <future>
+#include <map>
+#include <memory>
+
+#include "common/message_loop_thread.h"
+#include "mmc/mmc_interface/mmc_interface.h"
+
+namespace mmc {
+
+class Service final {
+ public:
+  explicit Service(base::OnceClosure shutdown_callback);
+
+  // Service is neither copyable nor movable.
+  Service(const Service&) = delete;
+  Service& operator=(const Service&) = delete;
+
+  // Connects to DBus and exports methods for client to call.
+  bool Init();
+
+ private:
+  /* DBus Methods */
+  // Main thread creates a codec server instance and a socket,
+  // and calls |StartWorkerThread| to let one thread start listening on the
+  // socket.
+  //
+  // Expected input message:
+  //   |CodecInitRequest| with |ConfigParam| set.
+  // Response:
+  //   |CodecInitResponse|, if |CodecInit| succeeded.
+  //   ErrorResponse, otherwise.
+  void CodecInit(dbus::MethodCall* method_call,
+                 dbus::ExportedObject::ResponseSender sender);
+
+  // Main thread removes idle threads from the thread poll.
+  //
+  // No input message needed.
+  // Response:
+  //   dbus::Response, implying |CodecCleanUp| finished.
+  void CodecCleanUp(dbus::MethodCall* method_call,
+                    dbus::ExportedObject::ResponseSender sender);
+
+  /* Thread Management*/
+  // Adds a thread to the thread pool and makes it listen on the socket fd.
+  bool StartWorkerThread(int fd, struct sockaddr_un addr,
+                         std::unique_ptr<MmcInterface> codec_server);
+
+  // Removes idle threads from the thread pool.
+  void RemoveIdleThread();
+
+  base::OnceClosure shutdown_callback_;
+
+  scoped_refptr<dbus::Bus> bus_;
+  dbus::ExportedObject* exported_object_;  // Owned by the Bus object.
+
+  std::vector<std::pair<std::unique_ptr<bluetooth::common::MessageLoopThread>,
+                        std::unique_ptr<std::future<void>>>>
+      thread_pool_;
+
+  base::WeakPtrFactory<Service> weak_ptr_factory_;
+};
+
+}  // namespace mmc
+
+#endif  // MMC_DAEMON_SERVICE_H_
diff --git a/system/stack/mmc/dbus_permissions/org.chromium.mmc.CodecManager.conf b/system/stack/mmc/dbus_permissions/org.chromium.mmc.CodecManager.conf
new file mode 100644
index 0000000..a63e4e6
--- /dev/null
+++ b/system/stack/mmc/dbus_permissions/org.chromium.mmc.CodecManager.conf
@@ -0,0 +1,47 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<!--
+  Copyright (C) 2023 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.
+-->
+<busconfig>
+  <!-- User mmc_service owns the interface
+       org.chromium.mmc.CodecManager. -->
+  <policy user="mmc_service">
+    <allow own="org.chromium.mmc.CodecManager" />
+  </policy>
+
+  <policy user="bluetooth">
+    <!--User bluetooth can send messages to the owner of the given service
+        and call CodecInit or CodecCleanUp from the interface
+        org.chromium.mmc.CodecManager.-->
+    <allow send_destination="org.chromium.mmc.CodecManager"
+           send_interface="org.chromium.mmc.CodecManager"
+           send_member="CodecInit" />
+    <allow send_destination="org.chromium.mmc.CodecManager"
+           send_interface="org.chromium.mmc.CodecManager"
+           send_member="CodecCleanUp" />
+  </policy>
+
+  <!-- For testing. -->
+  <policy user="root">
+    <allow send_destination="org.chromium.mmc.CodecManager"
+           send_interface="org.chromium.mmc.CodecManager"
+           send_member="CodecInit"/>
+    <allow send_destination="org.chromium.mmc.CodecManager"
+           send_interface="org.chromium.mmc.CodecManager"
+           send_member="CodecCleanUp" />
+  </policy>
+</busconfig>
diff --git a/system/gd/btaa/BUILD.gn b/system/stack/mmc/dbus_service/org.chromium.mmc.CodecManager.service
similarity index 67%
copy from system/gd/btaa/BUILD.gn
copy to system/stack/mmc/dbus_service/org.chromium.mmc.CodecManager.service
index 9beaf1e..af0fb6b 100644
--- a/system/gd/btaa/BUILD.gn
+++ b/system/stack/mmc/dbus_service/org.chromium.mmc.CodecManager.service
@@ -1,5 +1,5 @@
 #
-#  Copyright 2021 Google, Inc.
+#  Copyright 2023 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.
@@ -12,11 +12,9 @@
 #  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.
-#
-
-source_set("BluetoothBtaaSources_linux") {
-  sources = [ "linux/activity_attribution.cc" ]
-
-  configs += [ "//bt/system/gd:gd_defaults" ]
-  deps = [ "//bt/system/gd:gd_default_deps" ]
-}
+# D-Bus on-demand service specification for CodecManager.
+# Will be installed into /usr/share/dbus-1/system-services/.
+[D-BUS Service]
+Name=org.chromium.mmc.CodecManager
+Exec=/sbin/start mmc_service
+User=root
diff --git a/system/stack/mmc/init/mmc_service.conf b/system/stack/mmc/init/mmc_service.conf
new file mode 100644
index 0000000..fb32089
--- /dev/null
+++ b/system/stack/mmc/init/mmc_service.conf
@@ -0,0 +1,45 @@
+#
+#  Copyright 2023 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.
+#
+
+description    "Start daemon for Minijailed Media Codec (MMC) Service"
+author         "ChromeOS BT <chromeos-bt-team@google.com>"
+
+# Mmc Codec Manager can recover by restarting.
+oom score -100
+
+# This service is started by D-Bus service activation through
+# org.chromium.mmc.CodecManager.service
+stop on stopping boot-services
+
+#  Minijail forks off the desired process and exits after forking.
+expect fork
+
+pre-start script
+  # Check if Boot-services is still running before starting mmc_service.
+  # This is to prevent new dbus-activated instances from getting
+  # started once the system is beginning to shut down.
+  if ! initctl status boot-services | grep -q running; then
+    stop
+    exit 0
+  fi
+end script
+
+exec minijail0 --config /usr/share/minijail/mmc.conf \
+  -- /usr/bin/mmc_service
+
+# Wait until DBus service becomes available.
+post-start exec minijail0 -u mmc_service -g mmc_service /usr/bin/gdbus \
+    wait --system --timeout 15 org.chromium.mmc.CodecManager
diff --git a/system/stack/mmc/main.cc b/system/stack/mmc/main.cc
new file mode 100644
index 0000000..e040ebefa
--- /dev/null
+++ b/system/stack/mmc/main.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2023 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 <base/at_exit.h>
+#include <base/check.h>
+#include <base/files/file_descriptor_watcher_posix.h>
+#include <base/files/file_util.h>
+#include <base/run_loop.h>
+#include <base/strings/stringprintf.h>
+#include <base/task/single_thread_task_executor.h>
+#include <sys/syslog.h>
+
+#include "mmc/daemon/service.h"
+
+// syslog.h and base/logging.h both try to #define LOG_INFO and LOG_WARNING.
+// We need to #undef at least these two before including base/logging.h.  The
+// others are included to be consistent.
+namespace {
+const int kSyslogDebug = LOG_DEBUG;
+const int kSyslogInfo = LOG_INFO;
+const int kSyslogWarning = LOG_WARNING;
+const int kSyslogError = LOG_ERR;
+const int kSyslogCritical = LOG_CRIT;
+
+#undef LOG_INFO
+#undef LOG_WARNING
+#undef LOG_ERR
+#undef LOG_CRIT
+}  // namespace
+
+#include <base/logging.h>
+
+static bool MessageHandler(int severity, const char* file, int line,
+                           size_t message_start, const std::string& message) {
+  const auto str = base::StringPrintf("%s:%d - %s", file, line,
+                                      message.substr(message_start).c_str());
+
+  switch (severity) {
+    case logging::LOGGING_INFO:
+      severity = kSyslogInfo;
+      break;
+
+    case logging::LOGGING_WARNING:
+      severity = kSyslogWarning;
+      break;
+
+    case logging::LOGGING_ERROR:
+      severity = kSyslogError;
+      break;
+
+    case logging::LOGGING_FATAL:
+      severity = kSyslogCritical;
+      break;
+
+    default:
+      severity = kSyslogDebug;
+      break;
+  }
+
+  syslog(severity, "%s", str.c_str());
+
+  if (severity == kSyslogCritical) abort();
+
+  return true;
+}
+
+int main(int argc, char* argv[]) {
+  // Set up syslog to stderr.
+  logging::LoggingSettings settings;
+  settings.logging_dest =
+      logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
+  logging::SetLogItems(false, false, false, false);
+  logging::InitLogging(settings);
+  logging::SetLogMessageHandler(MessageHandler);
+
+  LOG(INFO) << "Start MMC daemon";
+
+  // These are needed to send D-Bus signals and receive messages.
+  // Even though they are not used directly, they set up some global state
+  // needed by the D-Bus library.
+  base::SingleThreadTaskExecutor task_executor(base::MessagePumpType::IO);
+  base::FileDescriptorWatcher watcher(task_executor.task_runner());
+  base::AtExitManager at_exit_manager;
+
+  base::RunLoop run_loop;
+
+  auto service = std::make_unique<mmc::Service>(run_loop.QuitClosure());
+  CHECK(service->Init());
+
+  run_loop.Run();
+
+  return 0;
+}
diff --git a/system/stack/mmc/minijail/mmc.conf b/system/stack/mmc/minijail/mmc.conf
new file mode 100644
index 0000000..427162b
--- /dev/null
+++ b/system/stack/mmc/minijail/mmc.conf
@@ -0,0 +1,37 @@
+% minijail-config-file v0
+
+# Used jailing parameters:
+#   -e: enter new network namespace (for process that
+#     doesn't need network access);
+#   -i: minijail0 exits right after forking.
+#   -l: new IPC namespace (isolates IPC resources).
+#   -N: new cgroup namespace.
+#   -n: set no new privileges (no_new_privs bit).
+#   -p: Enter new pid namespace (implies -vr).
+#   --profile=minimalistic-mountns: Enables mount and process namespace
+#     which includes /var/empty, /, proc (RO), /dev/log, /tmp (tmpfs).
+#   -u: change userid to <user>.
+#   -g: change gid to <group>.
+#   -G: inherit supplementary groups from new uid.
+#   -c: cap_sys_nice=e (1 << 23).
+#   --uts: enters new UTS namespace. It makes changes to the host/domain
+#     name not affect the rest of the system.
+#   -k: regular mount (source, target, filesystemtype, mountflags, data)
+#   -b /run/dbus: mount /run/dbus to be able to communicate with D-bus.
+#   -b /run/mmc/sockets: mount /run/mmc/sockets to be able to create sockets.
+
+e
+i
+l
+N
+n
+p
+uts
+u = mmc_service
+g = mmc_service
+G
+c = cap_sys_nice=e
+profile = minimalistic-mountns
+mount = tmpfs,/run,tmpfs,MS_NODEV|MS_NOSUID|MS_NOEXEC,mode=755,size=10M
+bind-mount = /run/dbus
+bind-mount = /run/mmc/sockets,/run/mmc/sockets,1
diff --git a/system/stack/mmc/mmc_interface/mmc_interface.h b/system/stack/mmc/mmc_interface/mmc_interface.h
new file mode 100644
index 0000000..a6c35f0
--- /dev/null
+++ b/system/stack/mmc/mmc_interface/mmc_interface.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 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.
+ */
+
+#ifndef MMC_MMC_INTERFACE_MMC_INTERFACE_H_
+#define MMC_MMC_INTERFACE_MMC_INTERFACE_H_
+
+#include <stdint.h>
+
+#include "mmc/proto/mmc_config.pb.h"
+
+namespace mmc {
+
+// An abstract interface representing either an encoder or a decoder.
+class MmcInterface {
+ public:
+  virtual ~MmcInterface() = default;
+
+  // Builds and configures the encoder/decoder instance.
+  //
+  // Returns:
+  //   Input frame size accepted by the transcoder, if init succeeded.
+  //   Negative errno on error, otherwise.
+  virtual int init(ConfigParam config) = 0;
+
+  // Resets the encoder/decoder instance.
+  virtual void cleanup() = 0;
+
+  // Transcodes data in |i_buf|, and stores the result in |o_buf|.
+  //
+  // Returns:
+  //   Transcoded data length, if transcode succeeded.
+  //   Negative errno on error, otherwise.
+  virtual int transcode(uint8_t* i_buf, int i_len, uint8_t* o_buf,
+                        int o_len) = 0;
+};
+
+}  // namespace mmc
+
+#endif  // MMC_MMC_INTERFACE_MMC_INTERFACE_H_
diff --git a/system/stack/mmc/proto/BUILD.gn b/system/stack/mmc/proto/BUILD.gn
index 5e766a9..01a4c7c 100644
--- a/system/stack/mmc/proto/BUILD.gn
+++ b/system/stack/mmc/proto/BUILD.gn
@@ -24,3 +24,13 @@
   ]
   standalone=true
 }
+
+proto_library("mmc_service_proto") {
+  proto_in_dir = "./"
+  proto_out_dir = "include/mmc/proto"
+  sources = [
+    "${proto_in_dir}/mmc_service.proto",
+  ]
+  deps = [ ":mmc_config_proto" ]
+  standalone=true
+}
diff --git a/system/stack/mmc/proto/mmc_config.proto b/system/stack/mmc/proto/mmc_config.proto
index 1d868e9..58d3733 100644
--- a/system/stack/mmc/proto/mmc_config.proto
+++ b/system/stack/mmc/proto/mmc_config.proto
@@ -20,9 +20,10 @@
 
 option optimize_for = LITE_RUNTIME;
 
+// union of Lc3 config and codec parameters
 message Lc3Param {
   // encoder/decoder config
-  int32 dt_hz = 1;
+  int32 dt_us = 1;
   int32 sr_hz = 2;
   int32 sr_pcm_hz = 3;
 
@@ -36,12 +37,62 @@
   int32 stride = 5;
 }
 
+// corresponding to SBC_ENC_PARAMS
+message SbcEncoderParam {
+  enum SamplingFreq {
+    kSbcSf16000 = 0;
+    kSbcSf32000 = 1;
+    kSbcSf44100 = 2;
+    kSbcSf48000 = 3;
+  }
+
+  enum ChannelMode {
+    kSbcMono = 0;
+    kSbcDual = 1;
+    kSbcStereo = 2;
+    kSbcJointStereo = 3;
+  }
+
+  enum AllocationMethod {
+    kSbcLoudNess = 0;
+    kSbcSnr = 1;
+  }
+
+  // Default to be kSbcFormatGeneral for SBC if not assigned.
+  // Assigning to kSbcFormatMsbc for MSBC.
+  enum Format {
+    kSbcFormatGeneral = 0;
+    kSbcFormatMsbc = 1;
+  }
+
+  // encoder config
+  int32 num_of_subbands = 1;
+  int32 num_of_channels = 2;
+  int32 num_of_blocks = 3;
+  int32 bit_pool = 4;
+  int32 bit_rate = 5;
+  SamplingFreq sampling_freq = 6;
+  ChannelMode channel_mode = 7;
+  AllocationMethod allocation_method = 8;
+  Format format = 9;
+}
+
+message SbcDecoderParam {
+  // decoder config
+  int32 max_channels = 1;
+  int32 stride = 2;
+  bool enhanced = 3;
+}
+
 // union of different codec parameters
 message ConfigParam {
   // This determines the codec type and whether it is an encoder or a decoder.
   oneof codec_param {
-    // HFP LC3 encoder and decoder has same parameter type.
+    // HFP LC3 encoder and decoder have same parameter type.
     Lc3Param hfp_lc3_encoder_param = 1;
     Lc3Param hfp_lc3_decoder_param = 2;
+    // HFP MSBC use SBC parameters
+    SbcEncoderParam hfp_msbc_encoder_param = 3;
+    SbcDecoderParam hfp_msbc_decoder_param = 4;
   }
 }
diff --git a/system/stack/mmc/proto/mmc_service.proto b/system/stack/mmc/proto/mmc_service.proto
new file mode 100644
index 0000000..c7819b8
--- /dev/null
+++ b/system/stack/mmc/proto/mmc_service.proto
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 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.
+ */
+
+syntax = "proto3";
+
+package mmc;
+
+import "mmc_config.proto";
+
+option optimize_for = LITE_RUNTIME;
+
+message CodecInitRequest {
+  // Codec-specific parameters passed by client.
+  ConfigParam config = 1;
+}
+
+message CodecInitResponse {
+  // Socket name generated by the daemon.
+  string socket_token = 1;
+  // Input frame size accepted by the codec server.
+  int32 input_frame_size = 2;
+}
diff --git a/system/gd/btaa/BUILD.gn b/system/stack/mmc/tmpfiles.d/mmc.conf
similarity index 71%
rename from system/gd/btaa/BUILD.gn
rename to system/stack/mmc/tmpfiles.d/mmc.conf
index 9beaf1e..eb542a7 100644
--- a/system/gd/btaa/BUILD.gn
+++ b/system/stack/mmc/tmpfiles.d/mmc.conf
@@ -1,5 +1,5 @@
 #
-#  Copyright 2021 Google, Inc.
+#  Copyright 2023 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.
@@ -14,9 +14,7 @@
 #  limitations under the License.
 #
 
-source_set("BluetoothBtaaSources_linux") {
-  sources = [ "linux/activity_attribution.cc" ]
+# Setup the directory paths expected by MMC.
 
-  configs += [ "//bt/system/gd:gd_defaults" ]
-  deps = [ "//bt/system/gd:gd_default_deps" ]
-}
+# For common directories for mmc domain sockets.
+d= /run/mmc/sockets 0770 mmc_service mmc_service
diff --git a/system/stack/rfcomm/port_rfc.cc b/system/stack/rfcomm/port_rfc.cc
index fee6d29..0c080c3 100644
--- a/system/stack/rfcomm/port_rfc.cc
+++ b/system/stack/rfcomm/port_rfc.cc
@@ -58,7 +58,7 @@
  * Description      This function is called after security manager completes
  *                  required security checks.
  *
- * Returns          void
+ * Returns          PORT_SUCCESS or PORT_[ERROR]
  *
  ******************************************************************************/
 int port_open_continue(tPORT* p_port) {
@@ -104,8 +104,6 @@
  * Description      This function is called in the BTU_TASK context to
  *                  send control information
  *
- * Returns          void
- *
  ******************************************************************************/
 void port_start_control(tPORT* p_port) {
   tRFC_MCB* p_mcb = p_port->rfc.p_mcb;
@@ -122,8 +120,6 @@
  * Description      This function is called in the BTU_TASK context to
  *                  send configuration information
  *
- * Returns          void
- *
  ******************************************************************************/
 void port_start_par_neg(tPORT* p_port) {
   tRFC_MCB* p_mcb = p_port->rfc.p_mcb;
@@ -141,8 +137,6 @@
  * Description      This function is called in the BTU_TASK context to
  *                  release DLC
  *
- * Returns          void
- *
  ******************************************************************************/
 void port_start_close(tPORT* p_port) {
   tRFC_MCB* p_mcb = p_port->rfc.p_mcb;
@@ -426,8 +420,8 @@
  *                  first message in the establishment procedure port_handle
  *                  has a handle to the port control block otherwise the control
  *                  block should be found based on the muliplexer channel and
- *                  dlci.  The block should be allocated allocated before
- *                  meaning that application already made open.
+ *                  dlci.  The block should be allocated before meaning
+ *                  that application already made open.
  *
  ******************************************************************************/
 void PORT_DlcEstablishInd(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu) {
@@ -479,7 +473,7 @@
  * Description      This function is called from the RFCOMM layer when peer
  *                  acknowledges establish procedure (SABME/UA).  Send reply
  *                  to the user and set state to OPENED if result was
- *                  successfull.
+ *                  successful.
  *
  ******************************************************************************/
 void PORT_DlcEstablishCnf(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu,
@@ -755,17 +749,17 @@
 
 /*******************************************************************************
  *
- * Function         Port_TimeOutCloseMux
+ * Function         PORT_TimeOutCloseMux
  *
  * Description      This function is called when RFCOMM timesout on a command
  *                  as a result multiplexer connection is closed.
  *
  ******************************************************************************/
-void Port_TimeOutCloseMux(tRFC_MCB* p_mcb) {
+void PORT_TimeOutCloseMux(tRFC_MCB* p_mcb) {
   tPORT* p_port;
   int i;
 
-  RFCOMM_TRACE_EVENT("Port_TimeOutCloseMux");
+  RFCOMM_TRACE_EVENT("PORT_TimeOutCloseMux");
 
   p_port = &rfc_cb.port.port[0];
   for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) {
diff --git a/system/stack/rfcomm/rfc_int.h b/system/stack/rfcomm/rfc_int.h
index 8f348b4..3f32a77 100644
--- a/system/stack/rfcomm/rfc_int.h
+++ b/system/stack/rfcomm/rfc_int.h
@@ -275,7 +275,7 @@
 void PORT_StartCnf(tRFC_MCB* p_mcb, uint16_t result);
 
 void PORT_CloseInd(tRFC_MCB* p_mcb);
-void Port_TimeOutCloseMux(tRFC_MCB* p_mcb);
+void PORT_TimeOutCloseMux(tRFC_MCB* p_mcb);
 
 void PORT_DlcEstablishInd(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu);
 void PORT_DlcEstablishCnf(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu,
diff --git a/system/stack/rfcomm/rfc_port_fsm.cc b/system/stack/rfcomm/rfc_port_fsm.cc
index da830e3..7cfc409 100644
--- a/system/stack/rfcomm/rfc_port_fsm.cc
+++ b/system/stack/rfcomm/rfc_port_fsm.cc
@@ -175,7 +175,7 @@
       return;
 
     case RFC_PORT_EVENT_TIMEOUT:
-      Port_TimeOutCloseMux(p_port->rfc.p_mcb);
+      PORT_TimeOutCloseMux(p_port->rfc.p_mcb);
       RFCOMM_TRACE_ERROR("Port error state %d event %d", p_port->rfc.state,
                          event);
       return;
@@ -539,7 +539,7 @@
       return;
 
     case RFC_PORT_EVENT_TIMEOUT:
-      Port_TimeOutCloseMux(p_port->rfc.p_mcb);
+      PORT_TimeOutCloseMux(p_port->rfc.p_mcb);
       RFCOMM_TRACE_ERROR("Port error state %d event %d", p_port->rfc.state,
                          event);
       return;
diff --git a/system/stack/sdp/sdp_api.cc b/system/stack/sdp/sdp_api.cc
index fa5a7f0..e2d8e55 100644
--- a/system/stack/sdp/sdp_api.cc
+++ b/system/stack/sdp/sdp_api.cc
@@ -22,13 +22,15 @@
  *
  ******************************************************************************/
 
+#define LOG_TAG "sdp_api"
+
 #include "stack/include/sdp_api.h"
 
 #include <string.h>
 
 #include <cstdint>
-
 #include "bt_target.h"
+#include "osi/include/log.h"
 #include "osi/include/osi.h"  // PTR_TO_UINT
 #include "stack/include/bt_types.h"
 #include "stack/include/sdp_api.h"
@@ -785,17 +787,29 @@
  *
  ******************************************************************************/
 static void SDP_AttrStringCopy(char* dst, const tSDP_DISC_ATTR* p_attr,
-                               uint16_t dst_size) {
-  if (dst == NULL) return;
+                               uint16_t dst_size,
+                               uint8_t expected_type) {
+  if (dst == NULL)
+    return;
+
+  dst[0] = '\0';
+
   if (p_attr) {
-    uint16_t len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
-    if (len > dst_size - 1) {
-      len = dst_size - 1;
+    uint8_t type = SDP_DISC_ATTR_TYPE(p_attr->attr_len_type);
+
+    if (type == expected_type) {
+      uint16_t len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type);
+      if (len > dst_size - 1) {
+        len = dst_size - 1;
+      }
+      memcpy(dst, (const void*)p_attr->attr_value.v.array, len);
+      dst[len] = '\0';
+    } else {
+      LOG_ERROR("unexpected attr type=%d, expected=%d",
+                type, expected_type);
     }
-    memcpy(dst, (const void*)p_attr->attr_value.v.array, len);
-    dst[len] = '\0';
   } else {
-    dst[0] = '\0';
+    LOG_ERROR("p_attr is NULL");
   }
 }
 
@@ -836,55 +850,68 @@
     /* ClientExecutableURL is optional */
     p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_CLIENT_EXE_URL);
     SDP_AttrStringCopy(p_device_info->rec.client_executable_url, p_curr_attr,
-                       SDP_MAX_ATTR_LEN);
+                       SDP_MAX_ATTR_LEN, URL_DESC_TYPE);
 
     /* Service Description is optional */
+    /* 5.1.16 ServiceDescription attribute */
     p_curr_attr =
         SDP_FindAttributeInRec(p_curr_record, ATTR_ID_SERVICE_DESCRIPTION);
     SDP_AttrStringCopy(p_device_info->rec.service_description, p_curr_attr,
-                       SDP_MAX_ATTR_LEN);
+                       SDP_MAX_ATTR_LEN, TEXT_STR_DESC_TYPE);
 
     /* DocumentationURL is optional */
     p_curr_attr =
         SDP_FindAttributeInRec(p_curr_record, ATTR_ID_DOCUMENTATION_URL);
     SDP_AttrStringCopy(p_device_info->rec.documentation_url, p_curr_attr,
-                       SDP_MAX_ATTR_LEN);
+                       SDP_MAX_ATTR_LEN, URL_DESC_TYPE);
 
     p_curr_attr =
         SDP_FindAttributeInRec(p_curr_record, ATTR_ID_SPECIFICATION_ID);
-    if (p_curr_attr)
+    if (p_curr_attr &&
+        SDP_DISC_ATTR_TYPE(p_curr_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_curr_attr->attr_len_type) >= 2)
       p_device_info->spec_id = p_curr_attr->attr_value.v.u16;
     else
       result = SDP_ERR_ATTR_NOT_PRESENT;
 
     p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_VENDOR_ID);
-    if (p_curr_attr)
+    if (p_curr_attr &&
+        SDP_DISC_ATTR_TYPE(p_curr_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_curr_attr->attr_len_type) >= 2)
       p_device_info->rec.vendor = p_curr_attr->attr_value.v.u16;
     else
       result = SDP_ERR_ATTR_NOT_PRESENT;
 
     p_curr_attr =
         SDP_FindAttributeInRec(p_curr_record, ATTR_ID_VENDOR_ID_SOURCE);
-    if (p_curr_attr)
+    if (p_curr_attr &&
+        SDP_DISC_ATTR_TYPE(p_curr_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_curr_attr->attr_len_type) >= 2)
       p_device_info->rec.vendor_id_source = p_curr_attr->attr_value.v.u16;
     else
       result = SDP_ERR_ATTR_NOT_PRESENT;
 
     p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_PRODUCT_ID);
-    if (p_curr_attr)
+    if (p_curr_attr &&
+        SDP_DISC_ATTR_TYPE(p_curr_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_curr_attr->attr_len_type) >= 2)
       p_device_info->rec.product = p_curr_attr->attr_value.v.u16;
     else
       result = SDP_ERR_ATTR_NOT_PRESENT;
 
     p_curr_attr =
         SDP_FindAttributeInRec(p_curr_record, ATTR_ID_PRODUCT_VERSION);
-    if (p_curr_attr)
+    if (p_curr_attr &&
+        SDP_DISC_ATTR_TYPE(p_curr_attr->attr_len_type) == UINT_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_curr_attr->attr_len_type) >= 2)
       p_device_info->rec.version = p_curr_attr->attr_value.v.u16;
     else
       result = SDP_ERR_ATTR_NOT_PRESENT;
 
     p_curr_attr = SDP_FindAttributeInRec(p_curr_record, ATTR_ID_PRIMARY_RECORD);
-    if (p_curr_attr)
+    if (p_curr_attr &&
+        SDP_DISC_ATTR_TYPE(p_curr_attr->attr_len_type) == BOOLEAN_DESC_TYPE &&
+        SDP_DISC_ATTR_LEN(p_curr_attr->attr_len_type) >= 1)
       p_device_info->rec.primary_record = (bool)p_curr_attr->attr_value.v.u8;
     else
       result = SDP_ERR_ATTR_NOT_PRESENT;
diff --git a/system/stack/sdp/sdp_discovery.cc b/system/stack/sdp/sdp_discovery.cc
index 9988c54..f025df8 100644
--- a/system/stack/sdp/sdp_discovery.cc
+++ b/system/stack/sdp/sdp_discovery.cc
@@ -70,10 +70,15 @@
  *
  ******************************************************************************/
 static uint8_t* sdpu_build_uuid_seq(uint8_t* p_out, uint16_t num_uuids,
-                                    Uuid* p_uuid_list) {
+                                    Uuid* p_uuid_list, uint16_t& bytes_left) {
   uint16_t xx;
   uint8_t* p_len;
 
+  if (bytes_left < 2) {
+    DCHECK(0) << "SDP: No space for data element header";
+    return (p_out);
+  }
+
   /* First thing is the data element header */
   UINT8_TO_BE_STREAM(p_out, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
 
@@ -81,9 +86,20 @@
   p_len = p_out;
   p_out += 1;
 
+  /* Account for data element header and length */
+  bytes_left -= 2;
+
   /* Now, loop through and put in all the UUID(s) */
   for (xx = 0; xx < num_uuids; xx++, p_uuid_list++) {
     int len = p_uuid_list->GetShortestRepresentationSize();
+
+    if (len + 1 > bytes_left) {
+      DCHECK(0) << "SDP: Too many UUIDs for internal buffer";
+      break;
+    } else {
+      bytes_left -= (len + 1);
+    }
+
     if (len == Uuid::kNumBytes16) {
       UINT8_TO_BE_STREAM(p_out, (UUID_DESC_TYPE << 3) | SIZE_TWO_BYTES);
       UINT16_TO_BE_STREAM(p_out, p_uuid_list->As16Bit());
@@ -120,6 +136,7 @@
   uint8_t *p, *p_start, *p_param_len;
   BT_HDR* p_cmd = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE);
   uint16_t param_len;
+  uint16_t bytes_left = SDP_DATA_BUF_SIZE;
 
   /* Prepare the buffer for sending the packet to L2CAP */
   p_cmd->offset = L2CAP_MIN_OFFSET;
@@ -134,9 +151,24 @@
   p_param_len = p;
   p += 2;
 
-/* Build the UID sequence. */
+  /* Account for header size, max service record count and
+   * continuation state */
+  const uint16_t base_bytes = (sizeof(BT_HDR) + L2CAP_MIN_OFFSET +
+                               3u + /* service search request header */
+                               2u + /* param len */
+                               3u + ((p_cont) ? cont_len : 0));
+
+  if (base_bytes > bytes_left) {
+    DCHECK(0) << "SDP: Overran SDP data buffer";
+    osi_free(p_cmd);
+    return;
+  }
+
+  bytes_left -= base_bytes;
+
+  /* Build the UID sequence. */
   p = sdpu_build_uuid_seq(p, p_ccb->p_db->num_uuid_filters,
-                          p_ccb->p_db->uuid_filters);
+                          p_ccb->p_db->uuid_filters, bytes_left);
 
   /* Set max service record count */
   UINT16_TO_BE_STREAM(p, sdp_cb.max_recs_per_search);
@@ -562,6 +594,7 @@
   if ((cont_request_needed) || (!p_reply)) {
     BT_HDR* p_msg = (BT_HDR*)osi_malloc(SDP_DATA_BUF_SIZE);
     uint8_t* p;
+    uint16_t bytes_left = SDP_DATA_BUF_SIZE;
 
     p_msg->offset = L2CAP_MIN_OFFSET;
     p = p_start = (uint8_t*)(p_msg + 1) + L2CAP_MIN_OFFSET;
@@ -575,9 +608,24 @@
     p_param_len = p;
     p += 2;
 
-/* Build the UID sequence. */
+    /* Account for header size, max service record count and
+     * continuation state */
+    const uint16_t base_bytes = (sizeof(BT_HDR) + L2CAP_MIN_OFFSET +
+                                 3u + /* service search request header */
+                                 2u + /* param len */
+                                 3u + /* max service record count */
+                                 ((p_reply) ? (*p_reply) : 0));
+
+    if (base_bytes > bytes_left) {
+      sdp_disconnect(p_ccb, SDP_INVALID_CONT_STATE);
+      return;
+    }
+
+    bytes_left -= base_bytes;
+
+    /* Build the UID sequence. */
     p = sdpu_build_uuid_seq(p, p_ccb->p_db->num_uuid_filters,
-                            p_ccb->p_db->uuid_filters);
+                            p_ccb->p_db->uuid_filters, bytes_left);
 
     /* Max attribute byte count */
     UINT16_TO_BE_STREAM(p, sdp_cb.max_attr_list_size);
diff --git a/system/stack/sdp/sdp_utils.cc b/system/stack/sdp/sdp_utils.cc
index 6658865..5069f04 100644
--- a/system/stack/sdp/sdp_utils.cc
+++ b/system/stack/sdp/sdp_utils.cc
@@ -251,7 +251,9 @@
       case UUID_SERVCLASS_AUDIO_SINK: {
         tSDP_DISC_ATTR* p_attr =
             SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES);
-        if (p_attr == nullptr) {
+        if (p_attr == nullptr ||
+            SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != UINT_DESC_TYPE ||
+            SDP_DISC_ATTR_LEN(p_attr->attr_len_type) < 2) {
           break;
         }
         uint16_t supported_features = p_attr->attr_value.v.u16;
@@ -264,7 +266,9 @@
       case UUID_SERVCLASS_MESSAGE_ACCESS: {
         tSDP_DISC_ATTR* p_attr =
             SDP_FindAttributeInRec(p_rec, ATTR_ID_MAP_SUPPORTED_FEATURES);
-        if (p_attr == nullptr) {
+        if (p_attr == nullptr ||
+            SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != UINT_DESC_TYPE ||
+            SDP_DISC_ATTR_LEN(p_attr->attr_len_type) < 4) {
           break;
         }
         uint32_t map_supported_features = p_attr->attr_value.v.u32;
@@ -277,7 +281,9 @@
       case UUID_SERVCLASS_PBAP_PSE: {
         tSDP_DISC_ATTR* p_attr =
             SDP_FindAttributeInRec(p_rec, ATTR_ID_PBAP_SUPPORTED_FEATURES);
-        if (p_attr == nullptr) {
+        if (p_attr == nullptr ||
+            SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != UINT_DESC_TYPE ||
+            SDP_DISC_ATTR_LEN(p_attr->attr_len_type) < 4) {
           break;
         }
         uint32_t pbap_supported_features = p_attr->attr_value.v.u32;
diff --git a/system/stack/smp/smp_act.cc b/system/stack/smp/smp_act.cc
index 4b4f270..eb08abc 100644
--- a/system/stack/smp/smp_act.cc
+++ b/system/stack/smp/smp_act.cc
@@ -431,9 +431,9 @@
     tBTM_LE_KEY_VALUE key = {
         .lcsrk_key =
             {
+                .counter = 0, /* initialize the local counter */
                 .div = p_cb->div,
                 .sec_level = p_cb->sec_level,
-                .counter = 0, /* initialize the local counter */
                 .csrk = p_cb->csrk,
             },
     };
diff --git a/system/stack/smp/smp_api.cc b/system/stack/smp/smp_api.cc
index b274fc5..740c889 100644
--- a/system/stack/smp/smp_api.cc
+++ b/system/stack/smp/smp_api.cc
@@ -50,11 +50,6 @@
  *
  ******************************************************************************/
 void SMP_Init(uint8_t init_security_mode) {
-  if (bluetooth::shim::is_gd_shim_enabled()) {
-    LOG(INFO) << "Skipping legacy SMP_Init because GD is enabled";
-    return;
-  }
-
   smp_cb.init_security_mode = init_security_mode;
 
   memset(&smp_cb, 0, sizeof(tSMP_CB));
@@ -116,9 +111,6 @@
  *
  ******************************************************************************/
 bool SMP_Register(tSMP_CALLBACK* p_cback) {
-  LOG_ASSERT(!bluetooth::shim::is_gd_shim_enabled())
-      << "Legacy SMP API should not be invoked when GD Security is used";
-
   SMP_TRACE_EVENT("SMP_Register state=%d", smp_cb.state);
 
   if (smp_cb.p_callback != NULL) {
@@ -142,8 +134,6 @@
  *
  ******************************************************************************/
 tSMP_STATUS SMP_Pair(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type) {
-  LOG_ASSERT(!bluetooth::shim::is_gd_shim_enabled())
-      << "Legacy SMP API should not be invoked when GD Security is used";
   tSMP_CB* p_cb = &smp_cb;
 
   SMP_TRACE_EVENT("%s: state=%d br_state=%d flag=0x%x, bd_addr=%s", __func__,
@@ -193,9 +183,6 @@
  *
  ******************************************************************************/
 tSMP_STATUS SMP_BR_PairWith(const RawAddress& bd_addr) {
-  LOG_ASSERT(!bluetooth::shim::is_gd_shim_enabled())
-      << "Legacy SMP API should not be invoked when GD Security is used";
-
   tSMP_CB* p_cb = &smp_cb;
 
   SMP_TRACE_EVENT("%s: state=%d br_state=%d flag=0x%x, bd_addr=%s", __func__,
@@ -237,9 +224,6 @@
  *
  ******************************************************************************/
 bool SMP_PairCancel(const RawAddress& bd_addr) {
-  LOG_ASSERT(!bluetooth::shim::is_gd_shim_enabled())
-      << "Legacy SMP API should not be invoked when GD Security is used";
-
   tSMP_CB* p_cb = &smp_cb;
 
   SMP_TRACE_EVENT("SMP_CancelPair state=%d flag=0x%x ", p_cb->state,
@@ -270,9 +254,6 @@
  *
  ******************************************************************************/
 void SMP_SecurityGrant(const RawAddress& bd_addr, tSMP_STATUS res) {
-  LOG_ASSERT(!bluetooth::shim::is_gd_shim_enabled())
-      << "Legacy SMP API should not be invoked when GD Security is used";
-
   SMP_TRACE_EVENT("SMP_SecurityGrant ");
 
   // If just showing consent dialog, send response
@@ -357,9 +338,6 @@
  ******************************************************************************/
 void SMP_PasskeyReply(const RawAddress& bd_addr, uint8_t res,
                       uint32_t passkey) {
-  LOG_ASSERT(!bluetooth::shim::is_gd_shim_enabled())
-      << "Legacy SMP API should not be invoked when GD Security is used";
-
   tSMP_CB* p_cb = &smp_cb;
 
   SMP_TRACE_EVENT("SMP_PasskeyReply: Key: %d  Result:%d", passkey, res);
@@ -414,9 +392,6 @@
  *
  ******************************************************************************/
 void SMP_ConfirmReply(const RawAddress& bd_addr, uint8_t res) {
-  LOG_ASSERT(!bluetooth::shim::is_gd_shim_enabled())
-      << "Legacy SMP API should not be invoked when GD Security is used";
-
   tSMP_CB* p_cb = &smp_cb;
 
   SMP_TRACE_EVENT("%s: Result:%d", __func__, res);
@@ -462,9 +437,6 @@
  ******************************************************************************/
 void SMP_OobDataReply(const RawAddress& bd_addr, tSMP_STATUS res, uint8_t len,
                       uint8_t* p_data) {
-  LOG_ASSERT(!bluetooth::shim::is_gd_shim_enabled())
-      << "Legacy SMP API should not be invoked when GD Security is used";
-
   tSMP_CB* p_cb = &smp_cb;
   tSMP_KEY key;
 
@@ -599,9 +571,6 @@
  *
  ******************************************************************************/
 void SMP_SirkConfirmDeviceReply(const RawAddress& bd_addr, uint8_t res) {
-  LOG_ASSERT(!bluetooth::shim::is_gd_shim_enabled())
-      << "Legacy SMP API should not be invoked when GD Security is used";
-
   tSMP_CB* p_cb = &smp_cb;
 
   LOG_INFO("Result: %d", res);
diff --git a/system/stack/smp/smp_keys.cc b/system/stack/smp/smp_keys.cc
index d3f6ca4..ff9e6c0 100644
--- a/system/stack/smp/smp_keys.cc
+++ b/system/stack/smp/smp_keys.cc
@@ -322,12 +322,13 @@
   tBLE_ADDR_TYPE remote_bd_addr_type = BLE_ADDR_PUBLIC;
   /* get remote connection specific bluetooth address */
   if (!BTM_ReadRemoteConnectionAddr(p_cb->pairing_bda, remote_bda,
-                                    &remote_bd_addr_type)) {
+                                    &remote_bd_addr_type, true)) {
     SMP_TRACE_ERROR("%s: cannot obtain remote device address", __func__);
     return SMP_PAIR_FAIL_UNKNOWN;
   }
   /* get local connection specific bluetooth address */
-  BTM_ReadConnectionAddr(p_cb->pairing_bda, p_cb->local_bda, &p_cb->addr_type);
+  BTM_ReadConnectionAddr(p_cb->pairing_bda, p_cb->local_bda, &p_cb->addr_type,
+                         true);
   /* generate p1 = pres || preq || rat' || iat' */
   Octet16 p1 = smp_gen_p1_4_confirm(p_cb, remote_bd_addr_type);
   /* p1' = rand XOR p1 */
@@ -959,7 +960,7 @@
         "Use rcvd identity address as BD_ADDR of LK rcvd identity address");
     bda_for_lk = p_cb->id_addr;
   } else if ((BTM_ReadRemoteConnectionAddr(p_cb->pairing_bda, bda_for_lk,
-                                           &conn_addr_type)) &&
+                                           &conn_addr_type, true)) &&
              conn_addr_type == BLE_ADDR_PUBLIC) {
     SMP_TRACE_DEBUG("Use rcvd connection address as BD_ADDR of LK");
   } else {
diff --git a/system/stack/smp/smp_l2c.cc b/system/stack/smp/smp_l2c.cc
index 5b1395d..afb7d94 100644
--- a/system/stack/smp/smp_l2c.cc
+++ b/system/stack/smp/smp_l2c.cc
@@ -257,7 +257,8 @@
    * Classic transport shouldn't impact that.
    */
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(p_cb->pairing_bda);
-  if (smp_get_state() == SMP_STATE_BOND_PENDING &&
+  if ((smp_get_state() == SMP_STATE_BOND_PENDING ||
+       smp_get_state() == SMP_STATE_IDLE) &&
       (p_dev_rec && p_dev_rec->is_link_key_known()) &&
       alarm_is_scheduled(p_cb->delayed_auth_timer_ent)) {
     /* If we were to not return here, we would reset SMP control block, and
diff --git a/system/stack/smp/smp_utils.cc b/system/stack/smp/smp_utils.cc
index 06f4da6..27f4936 100644
--- a/system/stack/smp/smp_utils.cc
+++ b/system/stack/smp/smp_utils.cc
@@ -1444,7 +1444,7 @@
 
   SMP_TRACE_DEBUG("%s", __func__);
 
-  BTM_ReadConnectionAddr(p_cb->pairing_bda, bda, &addr_type);
+  BTM_ReadConnectionAddr(p_cb->pairing_bda, bda, &addr_type, true);
   BDADDR_TO_STREAM(p, bda);
   UINT8_TO_STREAM(p, addr_type);
 }
@@ -1466,7 +1466,7 @@
 
   SMP_TRACE_DEBUG("%s", __func__);
 
-  if (!BTM_ReadRemoteConnectionAddr(p_cb->pairing_bda, bda, &addr_type)) {
+  if (!BTM_ReadRemoteConnectionAddr(p_cb->pairing_bda, bda, &addr_type, true)) {
     SMP_TRACE_ERROR(
         "can not collect peer le addr information for unknown device");
     return;
@@ -1533,9 +1533,9 @@
       .penc_key =
           {
               .ltk = p_cb->ltk,
+              .ediv = 0,
               .sec_level = p_cb->sec_level,
               .key_size = p_cb->loc_enc_size,
-              .ediv = 0,
           },
   };
   memset(ple_key.penc_key.rand, 0, BT_OCTET8_LEN);
diff --git a/system/stack/srvc/srvc_dis.cc b/system/stack/srvc/srvc_dis.cc
index 62cfd7c..3b5d6f1 100644
--- a/system/stack/srvc/srvc_dis.cc
+++ b/system/stack/srvc/srvc_dis.cc
@@ -260,10 +260,17 @@
  ******************************************************************************/
 void dis_c_cmpl_cback(tSRVC_CLCB* p_clcb, tGATTC_OPTYPE op, tGATT_STATUS status,
                       tGATT_CL_COMPLETE* p_data) {
-  uint16_t read_type = dis_attr_uuid[dis_cb.dis_read_uuid_idx];
+  uint16_t read_type;
   uint8_t *pp = NULL, *p_str;
   uint16_t conn_id = p_clcb->conn_id;
 
+  if (dis_cb.dis_read_uuid_idx >= (sizeof(dis_attr_uuid)/sizeof(dis_attr_uuid[0]))) {
+    LOG(ERROR) << "invalid dis_cb.dis_read_uuid_idx";
+    return;
+  }
+
+  read_type = dis_attr_uuid[dis_cb.dis_read_uuid_idx];
+
   VLOG(1) << __func__
           << StringPrintf("op_code: 0x%02x  status: 0x%02x read_type: 0x%04x",
                           op, status, read_type);
diff --git a/system/stack/test/btm/stack_btm_power_mode_test.cc b/system/stack/test/btm/stack_btm_power_mode_test.cc
new file mode 100644
index 0000000..52158b9
--- /dev/null
+++ b/system/stack/test/btm/stack_btm_power_mode_test.cc
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2023 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 <cstdint>
+
+#include "btm_api_types.h"
+#include "gd/common/init_flags.h"
+#include "gd/os/log.h"
+#include "stack/include/acl_api.h"
+#include "stack/include/acl_hci_link_interface.h"
+#include "stack/include/btm_api.h"
+#include "stack/include/hci_error_code.h"
+#include "test/common/mock_functions.h"
+#include "types/raw_address.h"
+
+namespace {
+const char* test_flags[] = {
+    "INIT_default_log_level_str=LOG_DEBUG",
+    nullptr,
+};
+
+const RawAddress kRawAddress = RawAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x66});
+const uint16_t kHciHandle = 123;
+
+}  // namespace
+
+struct power_mode_callback {
+  const RawAddress bd_addr;
+  tBTM_PM_STATUS status;
+  uint16_t value;
+  tHCI_STATUS hci_status;
+};
+
+#include <deque>
+std::deque<power_mode_callback> power_mode_callback_queue;
+
+class StackBtmPowerMode : public testing::Test {
+ protected:
+  void SetUp() override {
+    power_mode_callback_queue.clear();
+    reset_mock_function_count_map();
+    bluetooth::common::InitFlags::Load(test_flags);
+    ASSERT_EQ(BTM_SUCCESS,
+              BTM_PmRegister(BTM_PM_REG_SET, &pm_id_,
+                             [](const RawAddress& p_bda, tBTM_PM_STATUS status,
+                                uint16_t value, tHCI_STATUS hci_status) {
+                               power_mode_callback_queue.push_back(
+                                   power_mode_callback{
+                                       .bd_addr = p_bda,
+                                       .status = status,
+                                       .value = value,
+                                       .hci_status = hci_status,
+                                   });
+                             }));
+  }
+
+  void TearDown() override {
+    ASSERT_EQ(BTM_SUCCESS,
+              BTM_PmRegister(BTM_PM_DEREG, &pm_id_,
+                             [](const RawAddress& p_bda, tBTM_PM_STATUS status,
+                                uint16_t value, tHCI_STATUS hci_status) {}));
+  }
+
+  uint8_t pm_id_{0};
+};
+
+class StackBtmPowerModeConnected : public StackBtmPowerMode {
+ protected:
+  void SetUp() override {
+    StackBtmPowerMode::SetUp();
+    BTM_PM_OnConnected(kHciHandle, kRawAddress);
+  }
+
+  void TearDown() override {
+    BTM_PM_OnDisconnected(kHciHandle);
+    StackBtmPowerMode::TearDown();
+  }
+};
+
+TEST_F(StackBtmPowerMode, BTM_SetPowerMode__Undefined) {
+  tBTM_PM_PWR_MD mode = {};
+  ASSERT_EQ(BTM_UNKNOWN_ADDR, BTM_SetPowerMode(pm_id_, kRawAddress, &mode));
+}
+
+TEST_F(StackBtmPowerModeConnected, BTM_SetPowerMode__AlreadyActive) {
+  tBTM_PM_PWR_MD mode = {};
+  ASSERT_EQ(BTM_SUCCESS, BTM_SetPowerMode(pm_id_, kRawAddress, &mode));
+}
+
+TEST_F(StackBtmPowerModeConnected, BTM_SetPowerMode__ActiveToSniff) {
+  tBTM_PM_PWR_MD mode = {
+      .mode = BTM_PM_MD_SNIFF,
+  };
+  ASSERT_EQ("BTM_CMD_STARTED",
+            btm_status_text(BTM_SetPowerMode(pm_id_, kRawAddress, &mode)));
+  ASSERT_EQ(1, get_func_call_count("btsnd_hcic_sniff_mode"));
+
+  // Respond with successful command status for mode command
+  btm_pm_proc_cmd_status(HCI_SUCCESS);
+
+  // Check power mode state directly
+  {
+    tBTM_PM_MODE current_power_mode;
+    ASSERT_TRUE(BTM_ReadPowerMode(kRawAddress, &current_power_mode));
+    ASSERT_EQ(BTM_PM_STS_PENDING, current_power_mode);
+  }
+
+  // Check power mode state from callback
+  ASSERT_EQ(1U, power_mode_callback_queue.size());
+  {
+    const auto cb = power_mode_callback_queue.front();
+    power_mode_callback_queue.pop_front();
+
+    ASSERT_EQ(kRawAddress, cb.bd_addr);
+    ASSERT_EQ(BTM_PM_STS_PENDING, cb.status);
+    ASSERT_EQ(0, cb.value);
+    ASSERT_EQ(HCI_SUCCESS, cb.hci_status);
+  }
+
+  // Respond with a successful mode change event
+  btm_pm_proc_mode_change(HCI_SUCCESS, kHciHandle, HCI_MODE_SNIFF, 0);
+
+  {
+    tBTM_PM_MODE current_power_mode;
+    ASSERT_TRUE(BTM_ReadPowerMode(kRawAddress, &current_power_mode));
+    ASSERT_EQ(BTM_PM_STS_SNIFF, current_power_mode);
+  }
+
+  // Check power mode state from callback
+  ASSERT_EQ(1U, power_mode_callback_queue.size());
+  {
+    const auto cb = power_mode_callback_queue.front();
+    power_mode_callback_queue.pop_front();
+
+    ASSERT_EQ(kRawAddress, cb.bd_addr);
+    ASSERT_EQ(BTM_PM_STS_SNIFF, cb.status);
+    ASSERT_EQ(0, cb.value);
+    ASSERT_EQ(HCI_SUCCESS, cb.hci_status);
+  }
+}
+
+TEST_F(StackBtmPowerModeConnected, BTM_SetPowerMode__ActiveToSniffTwice) {
+  tBTM_PM_PWR_MD mode = {
+      .mode = BTM_PM_MD_SNIFF,
+  };
+  ASSERT_EQ("BTM_CMD_STARTED",
+            btm_status_text(BTM_SetPowerMode(pm_id_, kRawAddress, &mode)));
+  ASSERT_EQ(1, get_func_call_count("btsnd_hcic_sniff_mode"));
+
+  // Respond with successful command status for mode command
+  btm_pm_proc_cmd_status(HCI_SUCCESS);
+
+  // Check power mode state directly
+  {
+    tBTM_PM_MODE current_power_mode;
+    ASSERT_TRUE(BTM_ReadPowerMode(kRawAddress, &current_power_mode));
+    ASSERT_EQ(BTM_PM_STS_PENDING, current_power_mode);
+  }
+
+  // Check power mode state from callback
+  ASSERT_EQ(1U, power_mode_callback_queue.size());
+  {
+    const auto cb = power_mode_callback_queue.front();
+    power_mode_callback_queue.pop_front();
+
+    ASSERT_EQ(kRawAddress, cb.bd_addr);
+    ASSERT_EQ(BTM_PM_STS_PENDING, cb.status);
+    ASSERT_EQ(0, cb.value);
+    ASSERT_EQ(HCI_SUCCESS, cb.hci_status);
+  }
+
+  // Send a second active to sniff command
+  ASSERT_EQ("BTM_CMD_STORED",
+            btm_status_text(BTM_SetPowerMode(pm_id_, kRawAddress, &mode)));
+  // No command should be issued
+  ASSERT_EQ(1, get_func_call_count("btsnd_hcic_sniff_mode"));
+
+  // Check power mode state directly
+  {
+    tBTM_PM_MODE current_power_mode;
+    ASSERT_TRUE(BTM_ReadPowerMode(kRawAddress, &current_power_mode));
+    // NOTE: The mixed enum values
+    ASSERT_EQ(
+        static_cast<tBTM_PM_MODE>(BTM_PM_STS_PENDING | BTM_PM_STORED_MASK),
+        current_power_mode);
+  }
+}
diff --git a/system/stack/test/btm/stack_btm_test.cc b/system/stack/test/btm/stack_btm_test.cc
index 829b714..3ec8fb5 100644
--- a/system/stack/test/btm/stack_btm_test.cc
+++ b/system/stack/test/btm/stack_btm_test.cc
@@ -493,3 +493,23 @@
 
   wipe_secrets_and_remove(device_record);
 }
+
+bool is_disconnect_reason_valid(const tHCI_REASON& reason);
+TEST_F(StackBtmWithInitFreeTest, is_disconnect_reason_valid) {
+  std::set<tHCI_REASON> valid_reason_set{
+      HCI_ERR_AUTH_FAILURE,
+      HCI_ERR_PEER_USER,
+      HCI_ERR_REMOTE_LOW_RESOURCE,
+      HCI_ERR_REMOTE_POWER_OFF,
+      HCI_ERR_UNSUPPORTED_REM_FEATURE,
+      HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED,
+      HCI_ERR_UNACCEPT_CONN_INTERVAL,
+  };
+  for (unsigned u = 0; u < 256; u++) {
+    const tHCI_REASON reason = static_cast<tHCI_REASON>(u);
+    if (valid_reason_set.count(reason))
+      ASSERT_TRUE(is_disconnect_reason_valid(reason));
+    else
+      ASSERT_FALSE(is_disconnect_reason_valid(reason));
+  }
+}
diff --git a/system/stack/test/btm_iso_test.cc b/system/stack/test/btm_iso_test.cc
index c68565a..900397f 100644
--- a/system/stack/test/btm_iso_test.cc
+++ b/system/stack/test/btm_iso_test.cc
@@ -345,8 +345,8 @@
 
 const bluetooth::hci::iso_manager::cig_create_cmpl_evt
     IsoManagerTest::kDefaultCigParamsEvt = {
-        .cig_id = 128,
         .status = 0x00,
+        .cig_id = 128,
         .conn_handles = std::vector<uint16_t>({0x0EFF, 0x00FF}),
 };
 
diff --git a/system/stack/test/common/mock_main_shim.cc b/system/stack/test/common/mock_main_shim.cc
index b74c6b0..fc651ce 100644
--- a/system/stack/test/common/mock_main_shim.cc
+++ b/system/stack/test/common/mock_main_shim.cc
@@ -36,18 +36,10 @@
   inc_func_call_count(__func__);
   return false;
 }
-bool bluetooth::shim::is_gd_shim_enabled() {
-  inc_func_call_count(__func__);
-  return false;
-}
 bool bluetooth::shim::is_gd_stack_started_up() {
   inc_func_call_count(__func__);
   return false;
 }
-bool bluetooth::shim::is_gd_link_policy_enabled() {
-  inc_func_call_count(__func__);
-  return false;
-}
 future_t* GeneralShutDown() {
   inc_func_call_count(__func__);
   return nullptr;
diff --git a/system/stack/test/fuzzers/Android.bp b/system/stack/test/fuzzers/Android.bp
index 5f1e3d2..02c199d 100644
--- a/system/stack/test/fuzzers/Android.bp
+++ b/system/stack/test/fuzzers/Android.bp
@@ -22,6 +22,7 @@
     static_libs: [
         "libFraunhoferAAC",
         "libbluetooth-dumpsys",
+        "libbluetooth-types",
         "libbluetooth_core_rs",
         "libbluetooth_gd",
         "libbt-audio-hal-interface",
@@ -33,6 +34,7 @@
         "libbt-sbc-encoder",
         "libbt-stack",
         "libbt-stack-core",
+        "libbt_shim_bridge",
         "libbtcore",
         "libbtdevice",
         "libbte",
diff --git a/system/stack/test/gatt/gatt_sr_test.cc b/system/stack/test/gatt/gatt_sr_test.cc
index d4aac1e..c99bf60 100644
--- a/system/stack/test/gatt/gatt_sr_test.cc
+++ b/system/stack/test/gatt/gatt_sr_test.cc
@@ -58,7 +58,8 @@
 bool background_connect_remove(uint8_t app_id, const RawAddress& address) {
   return false;
 }
-bool direct_connect_remove(uint8_t app_id, const RawAddress& address) {
+bool direct_connect_remove(uint8_t app_id, const RawAddress& address,
+                           bool connection_timeout) {
   return false;
 }
 bool is_background_connection(const RawAddress& address) { return false; }
diff --git a/system/stack/test/gatt/mock_gatt_utils_ref.cc b/system/stack/test/gatt/mock_gatt_utils_ref.cc
index a06935b..4c72938 100644
--- a/system/stack/test/gatt/mock_gatt_utils_ref.cc
+++ b/system/stack/test/gatt/mock_gatt_utils_ref.cc
@@ -25,7 +25,8 @@
 bool background_connect_remove(uint8_t app_id, const RawAddress& address) {
   return false;
 }
-bool direct_connect_remove(uint8_t app_id, const RawAddress& address) {
+bool direct_connect_remove(uint8_t app_id, const RawAddress& address,
+                           bool connection_timeout) {
   return false;
 }
 bool is_background_connection(const RawAddress& address) { return false; }
diff --git a/system/stack/test/gatt_connection_manager_test.cc b/system/stack/test/gatt_connection_manager_test.cc
index 7c5b624..4b6000e 100644
--- a/system/stack/test/gatt_connection_manager_test.cc
+++ b/system/stack/test/gatt_connection_manager_test.cc
@@ -354,4 +354,41 @@
   EXPECT_TRUE(background_connect_remove(CLIENT1, address1));
   Mock::VerifyAndClearExpectations(localAcceptlistMock.get());
 }
+
+TEST_F(BleConnectionManager,
+       test_re_add_to_allow_list_after_timeout_with_multiple_clients) {
+  EXPECT_CALL(*AlarmMock::Get(), AlarmNew(_)).Times(1);
+  alarm_callback_t alarm_callback = nullptr;
+  void* alarm_data = nullptr;
+
+  /* Accept adding to allow list */
+  ON_CALL(*localAcceptlistMock, AcceptlistAdd(address1))
+      .WillByDefault(Return(true));
+
+  EXPECT_CALL(*localAcceptlistMock, AcceptlistAdd(address1)).Times(1);
+  EXPECT_CALL(*localAcceptlistMock, AcceptlistRemove(_)).Times(0);
+
+  EXPECT_TRUE(background_connect_add(CLIENT1, address1));
+
+  Mock::VerifyAndClearExpectations(localAcceptlistMock.get());
+
+  EXPECT_CALL(*AlarmMock::Get(), AlarmSetOnMloop(_, _, _, _))
+      .Times(1)
+      .WillOnce(DoAll(SaveArg<2>(&alarm_callback), SaveArg<3>(&alarm_data)));
+  // Start direct connect attempt...
+  EXPECT_TRUE(direct_connect_add(CLIENT2, address1));
+
+  Mock::VerifyAndClearExpectations(localAcceptlistMock.get());
+
+  // simulate timeout seconds passed, alarm executing
+  EXPECT_CALL(*localAcceptlistMock, OnConnectionTimedOut(CLIENT2, address1))
+      .Times(1);
+  EXPECT_CALL(*localAcceptlistMock, AcceptlistRemove(_)).Times(0);
+  EXPECT_CALL(*localAcceptlistMock, AcceptlistAdd(address1)).Times(1);
+  EXPECT_CALL(*AlarmMock::Get(), AlarmFree(_)).Times(1);
+  alarm_callback(alarm_data);
+
+  Mock::VerifyAndClearExpectations(localAcceptlistMock.get());
+}
+
 }  // namespace connection_manager
diff --git a/system/stack/test/sdp/stack_sdp_test.cc b/system/stack/test/sdp/stack_sdp_test.cc
index f99bd24..5db5013 100644
--- a/system/stack/test/sdp/stack_sdp_test.cc
+++ b/system/stack/test/sdp/stack_sdp_test.cc
@@ -263,3 +263,134 @@
                                    std::numeric_limits<uint16_t>::max()))
                    .c_str());
 }
+
+static tSDP_DISCOVERY_DB db{};
+static tSDP_DISC_REC rec{};
+static tSDP_DISC_ATTR uuid_desc_attr{};
+static tSDP_DISC_ATTR client_exe_url_attr{};
+static tSDP_DISC_ATTR service_desc_attr{};
+static tSDP_DISC_ATTR doc_url_desc_attr{};
+static tSDP_DISC_ATTR spec_id_attr{};
+static tSDP_DISC_ATTR vendor_id_attr{};
+static tSDP_DISC_ATTR vendor_id_src_attr{};
+static tSDP_DISC_ATTR prod_id_attr{};
+static tSDP_DISC_ATTR prod_version_attr{};
+static tSDP_DISC_ATTR primary_rec_attr{};
+
+class SDP_GetDiRecord_Tests : public ::testing::Test {
+protected:
+
+  void SetUp() override {
+    db.p_first_rec = &rec;
+    rec.p_first_attr = &uuid_desc_attr;
+
+    uuid_desc_attr.attr_id = ATTR_ID_SERVICE_ID;
+    uuid_desc_attr.p_next_attr = &client_exe_url_attr;
+
+    client_exe_url_attr.attr_id = ATTR_ID_CLIENT_EXE_URL;
+    client_exe_url_attr.p_next_attr = &service_desc_attr;
+
+    service_desc_attr.attr_id = ATTR_ID_SERVICE_DESCRIPTION;
+    service_desc_attr.p_next_attr = &doc_url_desc_attr;
+
+    doc_url_desc_attr.attr_id = ATTR_ID_DOCUMENTATION_URL;
+    doc_url_desc_attr.p_next_attr = &spec_id_attr;
+
+    spec_id_attr.attr_id = ATTR_ID_SPECIFICATION_ID;
+    spec_id_attr.p_next_attr = &vendor_id_attr;
+
+    vendor_id_attr.attr_id = ATTR_ID_VENDOR_ID;
+    vendor_id_attr.p_next_attr = &vendor_id_src_attr;
+
+    vendor_id_src_attr.attr_id = ATTR_ID_VENDOR_ID_SOURCE;
+    vendor_id_src_attr.p_next_attr = &prod_id_attr;
+
+    prod_id_attr.attr_id = ATTR_ID_PRODUCT_ID;
+    prod_id_attr.p_next_attr = &prod_version_attr;
+
+    prod_version_attr.attr_id = ATTR_ID_PRODUCT_VERSION;
+    prod_version_attr.p_next_attr = &primary_rec_attr;
+
+    primary_rec_attr.attr_id = ATTR_ID_PRIMARY_RECORD;
+    primary_rec_attr.p_next_attr = nullptr;
+  }
+
+  void TearDown() override {
+    db = {};
+    rec = {};
+    uuid_desc_attr = {};
+    client_exe_url_attr = {};
+    service_desc_attr = {};
+    doc_url_desc_attr = {};
+    spec_id_attr = {};
+    vendor_id_attr = {};
+    vendor_id_src_attr = {};
+    prod_id_attr = {};
+    prod_version_attr = {};
+    primary_rec_attr = {};
+  }
+};
+
+// regression test for b/297831980 and others
+TEST_F(SDP_GetDiRecord_Tests, SDP_GetDiRecord_Regression_test0) {
+  // tune the type/len and value of each attribute in
+  // each test
+  uuid_desc_attr.attr_len_type = (UUID_DESC_TYPE<<12) | 2;
+  uuid_desc_attr.attr_value.v.u16 = UUID_SERVCLASS_PNP_INFORMATION;
+
+  // use a 2-byte string so that it can be
+  // saved in tSDP_DISC_ATVAL
+  const char *const text = "AB";
+  int len = strlen(text);
+  client_exe_url_attr.attr_len_type = (URL_DESC_TYPE<<12) | len;
+  memcpy(client_exe_url_attr.attr_value.v.array, text, len);
+
+  // make this attr not found by id
+  service_desc_attr.attr_id = ATTR_ID_SERVICE_DESCRIPTION + 1;
+  service_desc_attr.attr_len_type = (TEXT_STR_DESC_TYPE<<12) | len;
+  memcpy(service_desc_attr.attr_value.v.array, text, len);
+
+  // make a wrong type
+  doc_url_desc_attr.attr_len_type =(TEXT_STR_DESC_TYPE<<12) | len;
+  memcpy(doc_url_desc_attr.attr_value.v.array, text, len);
+
+  // setup unexpected sizes for the following attrs
+  spec_id_attr.attr_len_type = (UINT_DESC_TYPE << 12) | 1;
+  spec_id_attr.attr_value.v.u16 = 0x1111;
+
+  vendor_id_attr.attr_len_type = (UINT_DESC_TYPE << 12) | 1;
+  vendor_id_attr.attr_value.v.u16 = 0x2222;
+
+  vendor_id_src_attr.attr_len_type = (UINT_DESC_TYPE << 12) | 1;
+  vendor_id_src_attr.attr_value.v.u16 = 0x3333;
+
+  prod_id_attr.attr_len_type = (UINT_DESC_TYPE << 12) | 1;
+  prod_id_attr.attr_value.v.u16 = 0x4444;
+
+  prod_version_attr.attr_len_type = (UINT_DESC_TYPE << 12) | 1;
+  prod_version_attr.attr_value.v.u16 = 0x5555;
+
+  // setup wrong size for primary_rec_attr
+  primary_rec_attr.attr_len_type = (BOOLEAN_DESC_TYPE << 12) | 0;
+  primary_rec_attr.attr_value.v.u8 = 0x66;
+
+  tSDP_DI_GET_RECORD device_info{};
+
+  SDP_GetDiRecord(1, &device_info, &db);
+
+  ASSERT_STREQ(text, device_info.rec.client_executable_url);
+
+  // service description could not be found
+  ASSERT_EQ(strlen(device_info.rec.service_description), (size_t) 0);
+
+  // with a wrong attr type, the attr value won't be accepted
+  ASSERT_EQ(strlen(device_info.rec.documentation_url), (size_t) 0);
+
+  // none of the following values got setup
+  ASSERT_EQ(device_info.spec_id, 0);
+  ASSERT_EQ(device_info.rec.vendor, 0);
+  ASSERT_EQ(device_info.rec.vendor_id_source, 0);
+  ASSERT_EQ(device_info.rec.product, 0);
+  ASSERT_EQ(device_info.rec.version, 0);
+  ASSERT_FALSE(device_info.rec.primary_record);
+}
diff --git a/system/stack/test/stack_smp_test.cc b/system/stack/test/stack_smp_test.cc
index edd8232..c5e5238 100644
--- a/system/stack/test/stack_smp_test.cc
+++ b/system/stack/test/stack_smp_test.cc
@@ -88,9 +88,9 @@
     .get_pts_eatt_peripheral_collision_support =
         get_pts_eatt_peripheral_collision_support,
     .get_pts_use_eatt_for_all_services = get_pts_use_eatt_for_all_services,
-    .get_pts_l2cap_ecoc_upper_tester = get_pts_l2cap_ecoc_upper_tester,
     .get_pts_force_le_audio_multiple_contexts_metadata =
         get_pts_force_le_audio_multiple_contexts_metadata,
+    .get_pts_l2cap_ecoc_upper_tester = get_pts_l2cap_ecoc_upper_tester,
     .get_pts_l2cap_ecoc_min_key_size = get_pts_l2cap_ecoc_min_key_size,
     .get_pts_l2cap_ecoc_initial_chan_cnt = get_pts_l2cap_ecoc_initial_chan_cnt,
     .get_pts_l2cap_ecoc_connect_remaining =
@@ -212,7 +212,7 @@
   // Set local_bda to 0xA1A2A3A4A5A6
   test::mock::stack_acl::BTM_ReadConnectionAddr.body =
       [](const RawAddress& remote_bda, RawAddress& local_conn_addr,
-         tBLE_ADDR_TYPE* p_addr_type) {
+         tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
         local_conn_addr = RawAddress({0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6});
         *p_addr_type = BLE_ADDR_RANDOM;
       };
@@ -220,7 +220,7 @@
   // Set remote bda to 0xB1B2B3B4B5B6
   test::mock::stack_acl::BTM_ReadRemoteConnectionAddr.body =
       [](const RawAddress& pseudo_addr, RawAddress& conn_addr,
-         tBLE_ADDR_TYPE* p_addr_type) {
+         tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
         conn_addr = RawAddress({0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6});
         *p_addr_type = BLE_ADDR_PUBLIC;
         return true;
@@ -229,8 +229,9 @@
   RawAddress remote_bda;
   tBLE_ADDR_TYPE remote_bd_addr_type = BLE_ADDR_PUBLIC;
   BTM_ReadRemoteConnectionAddr(p_cb_.pairing_bda, remote_bda,
-                               &remote_bd_addr_type);
-  BTM_ReadConnectionAddr(p_cb_.pairing_bda, p_cb_.local_bda, &p_cb_.addr_type);
+                               &remote_bd_addr_type, true);
+  BTM_ReadConnectionAddr(p_cb_.pairing_bda, p_cb_.local_bda, &p_cb_.addr_type,
+                         true);
   Octet16 p2 = smp_gen_p2_4_confirm(&p_cb_, remote_bda);
   // Correct p2 is 0x00000000a1a2a3a4a5a6b1b2b3b4b5b6
   const char expected_p2_str[] = "00000000a1a2a3a4a5a6b1b2b3b4b5b6";
@@ -247,7 +248,7 @@
   // Set local_bda to 0xA1A2A3A4A5A6
   test::mock::stack_acl::BTM_ReadConnectionAddr.body =
       [](const RawAddress& remote_bda, RawAddress& local_conn_addr,
-         tBLE_ADDR_TYPE* p_addr_type) {
+         tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
         local_conn_addr = RawAddress({0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6});
         *p_addr_type = BLE_ADDR_RANDOM;
       };
@@ -255,7 +256,7 @@
   // Set remote bda to 0xB1B2B3B4B5B6
   test::mock::stack_acl::BTM_ReadRemoteConnectionAddr.body =
       [](const RawAddress& pseudo_addr, RawAddress& conn_addr,
-         tBLE_ADDR_TYPE* p_addr_type) {
+         tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
         conn_addr = RawAddress({0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6});
         *p_addr_type = BLE_ADDR_PUBLIC;
         return true;
@@ -264,8 +265,9 @@
   RawAddress remote_bda;
   tBLE_ADDR_TYPE remote_bd_addr_type = BLE_ADDR_PUBLIC;
   BTM_ReadRemoteConnectionAddr(p_cb_.pairing_bda, remote_bda,
-                               &remote_bd_addr_type);
-  BTM_ReadConnectionAddr(p_cb_.pairing_bda, p_cb_.local_bda, &p_cb_.addr_type);
+                               &remote_bd_addr_type, true);
+  BTM_ReadConnectionAddr(p_cb_.pairing_bda, p_cb_.local_bda, &p_cb_.addr_type,
+                         true);
   Octet16 p1 = smp_gen_p1_4_confirm(&p_cb_, remote_bd_addr_type);
   // Correct p1 is 0x05000800000302070710000001010001
   const char expected_p1_str[] = "05000800000302070710000001010001";
@@ -293,7 +295,7 @@
   // Set local_bda to 0xA1A2A3A4A5A6
   test::mock::stack_acl::BTM_ReadConnectionAddr.body =
       [](const RawAddress& remote_bda, RawAddress& local_conn_addr,
-         tBLE_ADDR_TYPE* p_addr_type) {
+         tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
         local_conn_addr = RawAddress({0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6});
         *p_addr_type = BLE_ADDR_RANDOM;
       };
@@ -301,7 +303,7 @@
   // Set remote bda to 0xB1B2B3B4B5B6
   test::mock::stack_acl::BTM_ReadRemoteConnectionAddr.body =
       [](const RawAddress& pseudo_addr, RawAddress& conn_addr,
-         tBLE_ADDR_TYPE* p_addr_type) {
+         tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
         conn_addr = RawAddress({0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6});
         *p_addr_type = BLE_ADDR_PUBLIC;
         return true;
diff --git a/system/test/common/stack_config.cc b/system/test/common/stack_config.cc
index bb783f9..4137400 100644
--- a/system/test/common/stack_config.cc
+++ b/system/test/common/stack_config.cc
@@ -72,9 +72,9 @@
     .get_pts_eatt_peripheral_collision_support =
         get_pts_eatt_peripheral_collision_support,
     .get_pts_use_eatt_for_all_services = get_pts_use_eatt_for_all_services,
-    .get_pts_l2cap_ecoc_upper_tester = get_pts_l2cap_ecoc_upper_tester,
     .get_pts_force_le_audio_multiple_contexts_metadata =
         get_pts_force_le_audio_multiple_contexts_metadata,
+    .get_pts_l2cap_ecoc_upper_tester = get_pts_l2cap_ecoc_upper_tester,
     .get_pts_l2cap_ecoc_min_key_size = get_pts_l2cap_ecoc_min_key_size,
     .get_pts_l2cap_ecoc_initial_chan_cnt = get_pts_l2cap_ecoc_initial_chan_cnt,
     .get_pts_l2cap_ecoc_connect_remaining =
diff --git a/system/test/headless/Android.bp b/system/test/headless/Android.bp
index 91c25d3..df60ceb 100644
--- a/system/test/headless/Android.bp
+++ b/system/test/headless/Android.bp
@@ -24,22 +24,6 @@
     ],
 }
 
-filegroup {
-    name: "TestHeadlessModules",
-    srcs: [
-        "connect/connect.cc",
-        "discovery/discovery.cc",
-        "dumpsys/dumpsys.cc",
-        "nop/nop.cc",
-        "pairing/pairing.cc",
-        "read/name.cc",
-        "read/read.cc",
-        "scan/scan.cc",
-        "sdp/sdp.cc",
-        "sdp/sdp_db.cc",
-    ],
-}
-
 cc_binary {
     name: "bt_headless",
     defaults: [
@@ -55,15 +39,24 @@
         "HeadlessBuildTimestamp",
     ],
     srcs: [
-        ":TestHeadlessModules",
         "bt_property.cc",
+        "connect/connect.cc",
+        "discovery/discovery.cc",
+        "dumpsys/dumpsys.cc",
         "get_options.cc",
         "handler.cc",
         "headless.cc",
         "log.cc",
         "main.cc",
         "messenger.cc",
+        "nop/nop.cc",
+        "pairing/pairing.cc",
         "property.cc",
+        "read/name.cc",
+        "read/read.cc",
+        "scan/scan.cc",
+        "sdp/sdp.cc",
+        "sdp/sdp_db.cc",
         "util.cc",
     ],
     include_dirs: [
@@ -71,9 +64,6 @@
         "packages/modules/Bluetooth/system/gd",
         "packages/modules/Bluetooth/system/stack/include",
     ],
-    whole_static_libs: [
-        "libbtcore",
-    ],
     static_libs: [
         "android.hardware.bluetooth.a2dp@1.0",
         "android.system.suspend.control-V1-ndk",
@@ -84,6 +74,7 @@
         "libFraunhoferAAC",
         "libaudio-a2dp-hw-utils",
         "libbluetooth-dumpsys",
+        "libbluetooth-types",
         "libbluetooth_core_rs",
         "libbluetooth_gd",
         "libbluetooth_rust_interop",
@@ -98,6 +89,7 @@
         "libbt-stack-core",
         "libbt_shim_bridge",
         "libbt_shim_ffi",
+        "libbtcore",
         "libbtdevice",
         "libbte",
         "libbtif",
@@ -123,6 +115,7 @@
         "android.hardware.bluetooth@1.0",
         "android.hardware.bluetooth@1.1",
         "android.system.suspend-V1-ndk",
+        "libPlatformProperties",
         "libaaudio",
         "libbase",
         "libbinder_ndk",
@@ -134,10 +127,6 @@
         "liblog", // __android_log_print
         "libstatssocket",
         "libutils",
-        "libPlatformProperties",
     ],
-    ldflags: ["-rdynamic"],
-    sanitize: {
-        cfi: false,
-    },
+    header_libs: ["libbluetooth_headers"],
 }
diff --git a/system/test/headless/headless.cc b/system/test/headless/headless.cc
index 9006afd..3b09b82 100644
--- a/system/test/headless/headless.cc
+++ b/system/test/headless/headless.cc
@@ -240,13 +240,6 @@
 // HAL HARDWARE CALLBACKS
 
 // OS CALLOUTS
-bool set_wake_alarm_co([[maybe_unused]] uint64_t delay_millis,
-                       [[maybe_unused]] bool should_wake,
-                       [[maybe_unused]] alarm_cb cb,
-                       [[maybe_unused]] void* data) {
-  LOG_INFO("%s", __func__);
-  return true;
-}
 int acquire_wake_lock_co([[maybe_unused]] const char* lock_name) {
   LOG_INFO("%s", __func__);
   return 1;
@@ -259,7 +252,6 @@
 
 bt_os_callouts_t bt_os_callouts{
     .size = sizeof(bt_os_callouts_t),
-    .set_wake_alarm = set_wake_alarm_co,
     .acquire_wake_lock = acquire_wake_lock_co,
     .release_wake_lock = release_wake_lock_co,
 };
diff --git a/system/test/mock/mock_activity_attribution.cc b/system/test/mock/mock_activity_attribution.cc
deleted file mode 100644
index 6879796..0000000
--- a/system/test/mock/mock_activity_attribution.cc
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2021 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 "gd/module.h"
-
-#include "btif/include/btif_activity_attribution.h"
-#include "main/shim/activity_attribution.h"
-#include "main/shim/shim.h"
-
-ActivityAttributionInterface*
-bluetooth::activity_attribution::get_activity_attribution_instance() {
-  return nullptr;
-}
-
-ActivityAttributionInterface*
-bluetooth::shim::get_activity_attribution_instance() {
-  return nullptr;
-}
-
-const bluetooth::ModuleFactory
-    bluetooth::activity_attribution::ActivityAttribution::Factory =
-        bluetooth::ModuleFactory([]() { return nullptr; });
diff --git a/system/test/mock/mock_bta_leaudio.cc b/system/test/mock/mock_bta_leaudio.cc
index affd643..1ac5bb7 100644
--- a/system/test/mock/mock_bta_leaudio.cc
+++ b/system/test/mock/mock_bta_leaudio.cc
@@ -80,10 +80,7 @@
   return false;
 }
 
-void LeAudioClient::Cleanup(base::Callback<void()> cleanupCb) {
-  std::move(cleanupCb).Run();
-  inc_func_call_count(__func__);
-}
+void LeAudioClient::Cleanup(void) { inc_func_call_count(__func__); }
 
 LeAudioClient* LeAudioClient::Get(void) {
   inc_func_call_count(__func__);
@@ -101,9 +98,3 @@
   inc_func_call_count(__func__);
 }
 void LeAudioClient::DebugDump(int fd) { inc_func_call_count(__func__); }
-void LeAudioClient::InitializeAudioSetConfigurationProvider() {
-  inc_func_call_count(__func__);
-}
-void LeAudioClient::CleanupAudioSetConfigurationProvider() {
-  inc_func_call_count(__func__);
-}
diff --git a/system/test/mock/mock_device_controller.cc b/system/test/mock/mock_device_controller.cc
index b4f0d65..08b3c24 100644
--- a/system/test/mock/mock_device_controller.cc
+++ b/system/test/mock/mock_device_controller.cc
@@ -93,6 +93,9 @@
 bool iso_supported{false};
 bool simple_pairing_supported{false};
 bool secure_connections_supported{false};
+bool supports_hold_mode{false};
+bool supports_sniff_mode{true};
+bool supports_park_mode{false};
 
 bool get_is_ready(void) { return readable; }
 
@@ -210,18 +213,6 @@
   return HCI_SWITCH_SUPPORTED(features_classic[0].as_array);
 }
 
-bool supports_hold_mode(void) {
-  return HCI_HOLD_MODE_SUPPORTED(features_classic[0].as_array);
-}
-
-bool supports_sniff_mode(void) {
-  return HCI_SNIFF_MODE_SUPPORTED(features_classic[0].as_array);
-}
-
-bool supports_park_mode(void) {
-  return HCI_PARK_MODE_SUPPORTED(features_classic[0].as_array);
-}
-
 bool supports_non_flushable_pb(void) {
   return HCI_NON_FLUSHABLE_PB_SUPPORTED(features_classic[0].as_array);
 }
@@ -440,9 +431,9 @@
     supports_esco_3m_phy,
     supports_3_slot_esco_edr_packets,
     supports_role_switch,
-    supports_hold_mode,
-    supports_sniff_mode,
-    supports_park_mode,
+    []() { return supports_hold_mode; },
+    []() { return supports_sniff_mode; },
+    []() { return supports_park_mode; },
     supports_non_flushable_pb,
     supports_sniff_subrating,
     supports_encryption_pause,
diff --git a/system/test/mock/mock_legacy_hci_iterface.cc b/system/test/mock/mock_legacy_hci_iterface.cc
index a879f1b..e27d31d 100644
--- a/system/test/mock/mock_legacy_hci_iterface.cc
+++ b/system/test/mock/mock_legacy_hci_iterface.cc
@@ -54,8 +54,8 @@
 
 bluetooth::legacy::hci::Interface interface_ = {
     .Disconnect = btsnd_hcic_disconnect,
-    .StartRoleSwitch = btsnd_hcic_switch_role,
     .ChangeConnectionPacketType = btsnd_hcic_change_conn_type,
+    .StartRoleSwitch = btsnd_hcic_switch_role,
 };
 
 const bluetooth::legacy::hci::Interface&
diff --git a/system/test/mock/mock_main_shim.cc b/system/test/mock/mock_main_shim.cc
index a636338..6f194c3 100644
--- a/system/test/mock/mock_main_shim.cc
+++ b/system/test/mock/mock_main_shim.cc
@@ -37,10 +37,6 @@
   inc_func_call_count(__func__);
   return false;
 }
-bool bluetooth::shim::is_gd_shim_enabled() {
-  inc_func_call_count(__func__);
-  return false;
-}
 bool bluetooth::shim::is_classic_discovery_only_enabled() {
   inc_func_call_count(__func__);
   return false;
@@ -54,10 +50,6 @@
   inc_func_call_count(__func__);
   return test::mock::bluetooth_shim_is_gd_stack_started_up;
 }
-bool bluetooth::shim::is_gd_link_policy_enabled() {
-  inc_func_call_count(__func__);
-  return false;
-}
 future_t* GeneralShutDown() {
   inc_func_call_count(__func__);
   return nullptr;
diff --git a/system/test/mock/mock_main_shim_acl_api.cc b/system/test/mock/mock_main_shim_acl_api.cc
index edd4449..6043dc6 100644
--- a/system/test/mock/mock_main_shim_acl_api.cc
+++ b/system/test/mock/mock_main_shim_acl_api.cc
@@ -65,9 +65,16 @@
 void bluetooth::shim::ACL_IgnoreAllLeConnections() {
   inc_func_call_count(__func__);
 }
-void bluetooth::shim::ACL_ReadConnectionAddress(const RawAddress& pseudo_addr,
+void bluetooth::shim::ACL_ReadConnectionAddress(uint16_t handle,
                                                 RawAddress& conn_addr,
-                                                tBLE_ADDR_TYPE* p_addr_type) {
+                                                tBLE_ADDR_TYPE* p_addr_type,
+                                                bool ota_address) {
+  inc_func_call_count(__func__);
+}
+void bluetooth::shim::ACL_ReadPeerConnectionAddress(uint16_t handle,
+                                                    RawAddress& conn_addr,
+                                                    tBLE_ADDR_TYPE* p_addr_type,
+                                                    bool ota_address) {
   inc_func_call_count(__func__);
 }
 std::optional<uint8_t> bluetooth::shim::ACL_GetAdvertisingSetConnectedTo(
diff --git a/system/test/mock/mock_main_shim_acl_api.h b/system/test/mock/mock_main_shim_acl_api.h
index 124a0e9..6ca8e43 100644
--- a/system/test/mock/mock_main_shim_acl_api.h
+++ b/system/test/mock/mock_main_shim_acl_api.h
@@ -208,20 +208,35 @@
 extern struct ACL_IgnoreLeConnectionFrom ACL_IgnoreLeConnectionFrom;
 
 // Name: ACL_ReadConnectionAddress
-// Params: const RawAddress& pseudo_addr, RawAddress& conn_addr, tBLE_ADDR_TYPE*
+// Params: uint16_t handle, RawAddress& conn_addr, tBLE_ADDR_TYPE*, bool
 // p_addr_type Return: void
 struct ACL_ReadConnectionAddress {
-  std::function<void(const RawAddress& pseudo_addr, RawAddress& conn_addr,
-                     tBLE_ADDR_TYPE* p_addr_type)>
-      body{[](const RawAddress& pseudo_addr, RawAddress& conn_addr,
-              tBLE_ADDR_TYPE* p_addr_type) {}};
-  void operator()(const RawAddress& pseudo_addr, RawAddress& conn_addr,
-                  tBLE_ADDR_TYPE* p_addr_type) {
-    body(pseudo_addr, conn_addr, p_addr_type);
+  std::function<void(uint16_t handle, RawAddress& conn_addr,
+                     tBLE_ADDR_TYPE* p_addr_type, bool ota_address)>
+      body{[](uint16_t handle, RawAddress& conn_addr,
+              tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {}};
+  void operator()(uint16_t handle, RawAddress& conn_addr,
+                  tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
+    body(handle, conn_addr, p_addr_type, ota_address);
   };
 };
 extern struct ACL_ReadConnectionAddress ACL_ReadConnectionAddress;
 
+// Name: ACL_ReadPeerConnectionAddress
+// Params: uint16_t handle, RawAddress& conn_addr, tBLE_ADDR_TYPE*, bool
+// p_addr_type Return: void
+struct ACL_ReadPeerConnectionAddress {
+  std::function<void(uint16_t handle, RawAddress& conn_addr,
+                     tBLE_ADDR_TYPE* p_addr_type, bool ota_address)>
+      body{[](uint16_t handle, RawAddress& conn_addr,
+              tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {}};
+  void operator()(uint16_t handle, RawAddress& conn_addr,
+                  tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
+    body(handle, conn_addr, p_addr_type, ota_address);
+  };
+};
+extern struct ACL_ReadPeerConnectionAddress ACL_ReadPeerConnectionAddress;
+
 // Name: ACL_GetAdvertisingSetConnectedTo
 // Params: const RawAddress& addr
 // Return: std::optional<uint8_t>
diff --git a/system/test/mock/mock_main_shim_activity_attribution.cc b/system/test/mock/mock_main_shim_activity_attribution.cc
deleted file mode 100644
index 83cfaa3..0000000
--- a/system/test/mock/mock_main_shim_activity_attribution.cc
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2021 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.
- */
-
-/*
- * Generated mock file from original source file
- *   Functions generated:3
- */
-
-#include <map>
-#include <string>
-
-#include "btif/include/btif_common.h"
-#include "main/shim/activity_attribution.h"
-#include "main/shim/entry.h"
-#include "test/common/mock_functions.h"
-
-#ifndef UNUSED_ATTR
-#define UNUSED_ATTR
-#endif
-
-ActivityAttributionInterface*
-bluetooth::shim::get_activity_attribution_instance() {
-  inc_func_call_count(__func__);
-  return nullptr;
-}
diff --git a/system/test/mock/mock_main_shim_btm_api.cc b/system/test/mock/mock_main_shim_btm_api.cc
index fb7475a..0f0c513 100644
--- a/system/test/mock/mock_main_shim_btm_api.cc
+++ b/system/test/mock/mock_main_shim_btm_api.cc
@@ -81,7 +81,7 @@
 }
 bool bluetooth::shim::BTM_ReadRemoteConnectionAddr(
     const RawAddress& pseudo_addr, RawAddress& conn_addr,
-    tBLE_ADDR_TYPE* p_addr_type) {
+    tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
   inc_func_call_count(__func__);
   return false;
 }
@@ -308,7 +308,8 @@
 }
 void bluetooth::shim::BTM_ReadConnectionAddr(const RawAddress& remote_bda,
                                              RawAddress& local_conn_addr,
-                                             tBLE_ADDR_TYPE* p_addr_type) {
+                                             tBLE_ADDR_TYPE* p_addr_type,
+                                             bool ota_address) {
   inc_func_call_count(__func__);
 }
 void bluetooth::shim::BTM_ReadDevInfo(const RawAddress& remote_bda,
diff --git a/system/test/mock/mock_main_shim_entry.cc b/system/test/mock/mock_main_shim_entry.cc
index 2018d04..4f9f62b 100644
--- a/system/test/mock/mock_main_shim_entry.cc
+++ b/system/test/mock/mock_main_shim_entry.cc
@@ -52,9 +52,6 @@
 namespace shim {
 
 Dumpsys* GetDumpsys() { return nullptr; }
-activity_attribution::ActivityAttribution* GetActivityAttribution() {
-  return nullptr;
-}
 hci::AclManager* GetAclManager() { return hci::testing::mock_acl_manager_; }
 hci::Controller* GetController() { return hci::testing::mock_controller_; }
 hci::HciLayer* GetHciLayer() { return hci::testing::mock_hci_layer_; }
diff --git a/system/test/mock/mock_stack_acl.cc b/system/test/mock/mock_stack_acl.cc
index 76433d0..acc1f5a 100644
--- a/system/test/mock/mock_stack_acl.cc
+++ b/system/test/mock/mock_stack_acl.cc
@@ -211,10 +211,11 @@
 }
 bool BTM_ReadRemoteConnectionAddr(const RawAddress& pseudo_addr,
                                   RawAddress& conn_addr,
-                                  tBLE_ADDR_TYPE* p_addr_type) {
+                                  tBLE_ADDR_TYPE* p_addr_type,
+                                  bool ota_address) {
   inc_func_call_count(__func__);
   return test::mock::stack_acl::BTM_ReadRemoteConnectionAddr(
-      pseudo_addr, conn_addr, p_addr_type);
+      pseudo_addr, conn_addr, p_addr_type, ota_address);
 }
 bool BTM_ReadRemoteVersion(const RawAddress& addr, uint8_t* lmp_version,
                            uint16_t* manufacturer, uint16_t* lmp_sub_version) {
@@ -418,10 +419,10 @@
 }
 void BTM_ReadConnectionAddr(const RawAddress& remote_bda,
                             RawAddress& local_conn_addr,
-                            tBLE_ADDR_TYPE* p_addr_type) {
+                            tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
   inc_func_call_count(__func__);
   test::mock::stack_acl::BTM_ReadConnectionAddr(remote_bda, local_conn_addr,
-                                                p_addr_type);
+                                                p_addr_type, ota_address);
 }
 void BTM_RequestPeerSCA(const RawAddress& remote_bda, tBT_TRANSPORT transport) {
   inc_func_call_count(__func__);
diff --git a/system/test/mock/mock_stack_acl.h b/system/test/mock/mock_stack_acl.h
index 0c92329..25b15c6 100644
--- a/system/test/mock/mock_stack_acl.h
+++ b/system/test/mock/mock_stack_acl.h
@@ -131,17 +131,16 @@
 };
 extern struct BTM_IsPhy2mSupported BTM_IsPhy2mSupported;
 // Name: BTM_ReadRemoteConnectionAddr
-// Params: const RawAddress& pseudo_addr, RawAddress& conn_addr,
-// tBLE_ADDR_TYPE* p_addr_type
-// Returns: bool
+// Params: const RawAddress& pseudo_addr, RawAddress& conn_addr, bool
+// ota_address tBLE_ADDR_TYPE* p_addr_type Returns: bool
 struct BTM_ReadRemoteConnectionAddr {
   std::function<bool(const RawAddress& pseudo_addr, RawAddress& conn_addr,
-                     tBLE_ADDR_TYPE* p_addr_type)>
+                     tBLE_ADDR_TYPE* p_addr_type, bool ota_address)>
       body{[](const RawAddress& pseudo_addr, RawAddress& conn_addr,
-              tBLE_ADDR_TYPE* p_addr_type) { return false; }};
+              tBLE_ADDR_TYPE* p_addr_type, bool ota_address) { return false; }};
   bool operator()(const RawAddress& pseudo_addr, RawAddress& conn_addr,
-                  tBLE_ADDR_TYPE* p_addr_type) {
-    return body(pseudo_addr, conn_addr, p_addr_type);
+                  tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
+    return body(pseudo_addr, conn_addr, p_addr_type, ota_address);
   };
 };
 extern struct BTM_ReadRemoteConnectionAddr BTM_ReadRemoteConnectionAddr;
@@ -612,16 +611,16 @@
 };
 extern struct ACL_UnregisterClient ACL_UnregisterClient;
 // Name: BTM_ReadConnectionAddr
-// Params: const RawAddress& remote_bda, RawAddress& local_conn_addr,
-// tBLE_ADDR_TYPE* p_addr_type Returns: void
+// Params: const RawAddress& remote_bda, RawAddress& local_conn_addr, bool
+// ota_address tBLE_ADDR_TYPE* p_addr_type Returns: void
 struct BTM_ReadConnectionAddr {
   std::function<void(const RawAddress& remote_bda, RawAddress& local_conn_addr,
-                     tBLE_ADDR_TYPE* p_addr_type)>
+                     tBLE_ADDR_TYPE* p_addr_type, bool ota_address)>
       body{[](const RawAddress& remote_bda, RawAddress& local_conn_addr,
-              tBLE_ADDR_TYPE* p_addr_type) { ; }};
+              tBLE_ADDR_TYPE* p_addr_type, bool ota_address) { ; }};
   void operator()(const RawAddress& remote_bda, RawAddress& local_conn_addr,
-                  tBLE_ADDR_TYPE* p_addr_type) {
-    body(remote_bda, local_conn_addr, p_addr_type);
+                  tBLE_ADDR_TYPE* p_addr_type, bool ota_address) {
+    body(remote_bda, local_conn_addr, p_addr_type, ota_address);
   };
 };
 extern struct BTM_ReadConnectionAddr BTM_ReadConnectionAddr;
diff --git a/system/test/mock/mock_stack_btm.cc b/system/test/mock/mock_stack_btm.cc
index d77fdbf..7ffe1a7 100644
--- a/system/test/mock/mock_stack_btm.cc
+++ b/system/test/mock/mock_stack_btm.cc
@@ -36,14 +36,14 @@
 
 struct btm_client_interface_t btm_client_interface = {
     .lifecycle = {
-        .BTM_GetHCIConnHandle = [](const RawAddress& remote_bda,
-                                   tBT_TRANSPORT transport) -> uint16_t {
-          return 0;
-        },
         .BTM_PmRegister = [](uint8_t mask, uint8_t* p_pm_id,
                              tBTM_PM_STATUS_CBACK* p_cb) -> tBTM_STATUS {
           return BTM_SUCCESS;
         },
+        .BTM_GetHCIConnHandle = [](const RawAddress& remote_bda,
+                                   tBT_TRANSPORT transport) -> uint16_t {
+          return 0;
+        },
         .BTM_VendorSpecificCommand = BTM_VendorSpecificCommand,
         .ACL_RegisterClient = [](struct acl_client_callback_s* callbacks) {},
         .ACL_UnregisterClient = [](struct acl_client_callback_s* callbacks) {},
@@ -53,19 +53,41 @@
         .btm_ble_free = []() {},
         .BTM_reset_complete = []() {},
     },
-    .eir = {
-        .BTM_GetEirSupportedServices =
-            [](uint32_t* p_eir_uuid, uint8_t** p, uint8_t max_num_uuid16,
-               uint8_t* p_num_uuid16) -> uint8_t { return 0; },
-        .BTM_GetEirUuidList = [](const uint8_t* p_eir, size_t eir_len,
-                                 uint8_t uuid_size, uint8_t* p_num_uuid,
-                                 uint8_t* p_uuid_list,
-                                 uint8_t max_num_uuid) -> uint8_t { return 0; },
-        .BTM_AddEirService = [](uint32_t* p_eir_uuid, uint16_t uuid16) {},
-        .BTM_RemoveEirService = [](uint32_t* p_eir_uuid, uint16_t uuid16) {},
-        .BTM_WriteEIR = [](BT_HDR* p_buff) -> tBTM_STATUS {
-          return BTM_SUCCESS;
+    .scn =
+        {
+            .BTM_AllocateSCN = BTM_AllocateSCN,
+            .BTM_TryAllocateSCN = BTM_TryAllocateSCN,
+            .BTM_FreeSCN = BTM_FreeSCN,
         },
+    .peer = {
+        .features =
+            {
+                .SupportTransparentSynchronousData =
+                    [](const RawAddress& bd_addr) -> bool { return false; },
+            },
+        .BTM_IsAclConnectionUp = [](const RawAddress& remote_bda,
+                                    tBT_TRANSPORT transport) -> bool {
+          return false;
+        },
+        .BTM_ReadConnectedTransportAddress =
+            [](RawAddress* remote_bda, tBT_TRANSPORT transport) -> bool {
+          return false;
+        },
+        .BTM_CancelRemoteDeviceName = BTM_CancelRemoteDeviceName,
+        .BTM_ReadRemoteDeviceName = BTM_ReadRemoteDeviceName,
+        .BTM_ReadRemoteFeatures = [](const RawAddress& addr) -> uint8_t* {
+          return hci_feature_bytes_per_page;
+        },
+        .BTM_ReadDevInfo = [](const RawAddress& remote_bda,
+                              tBT_DEVICE_TYPE* p_dev_type,
+                              tBLE_ADDR_TYPE* p_addr_type) {},
+        .BTM_GetMaxPacketSize = [](const RawAddress& bd_addr) -> uint16_t {
+          return 0;
+        },
+        .BTM_ReadRemoteVersion =
+            [](const RawAddress& addr, uint8_t* lmp_version,
+               uint16_t* manufacturer,
+               uint16_t* lmp_sub_version) -> bool { return false; },
     },
     .link_policy = {
         .BTM_GetRole = [](const RawAddress& remote_bd_addr, tHCI_ROLE* p_role)
@@ -79,12 +101,12 @@
                uint16_t min_loc_to) -> tBTM_STATUS { return BTM_SUCCESS; },
         .BTM_SwitchRoleToCentral = [](const RawAddress& remote_bd_addr)
             -> tBTM_STATUS { return BTM_SUCCESS; },
-        .BTM_WritePageTimeout = BTM_WritePageTimeout,
         .BTM_block_role_switch_for = [](const RawAddress& peer_addr) {},
-        .BTM_unblock_role_switch_for = [](const RawAddress& peer_addr) {},
         .BTM_block_sniff_mode_for = [](const RawAddress& peer_addr) {},
-        .BTM_unblock_sniff_mode_for = [](const RawAddress& peer_addr) {},
         .BTM_default_unblock_role_switch = []() {},
+        .BTM_unblock_role_switch_for = [](const RawAddress& peer_addr) {},
+        .BTM_unblock_sniff_mode_for = [](const RawAddress& peer_addr) {},
+        .BTM_WritePageTimeout = BTM_WritePageTimeout,
     },
     .link_controller = {
         .BTM_GetLinkSuperTout = [](const RawAddress& remote_bda,
@@ -94,71 +116,17 @@
         .BTM_ReadRSSI = [](const RawAddress& remote_bda, tBTM_CMPL_CB* p_cb)
             -> tBTM_STATUS { return BTM_SUCCESS; },
     },
-    .neighbor =
-        {
-            .BTM_CancelInquiry = BTM_CancelInquiry,
-            .BTM_ClearInqDb = BTM_ClearInqDb,
-            .BTM_InqDbNext = BTM_InqDbNext,
-            .BTM_SetConnectability = BTM_SetConnectability,
-            .BTM_SetDiscoverability = BTM_SetDiscoverability,
-            .BTM_StartInquiry = BTM_StartInquiry,
-            .BTM_IsInquiryActive = BTM_IsInquiryActive,
-            .BTM_SetInquiryMode = BTM_SetInquiryMode,
-            .BTM_EnableInterlacedInquiryScan = BTM_EnableInterlacedInquiryScan,
-            .BTM_EnableInterlacedPageScan = BTM_EnableInterlacedPageScan,
-        },
-    .peer = {
-        .features =
-            {
-                .SupportTransparentSynchronousData =
-                    [](const RawAddress& bd_addr) -> bool { return false; },
-            },
-        .BTM_CancelRemoteDeviceName = BTM_CancelRemoteDeviceName,
-        .BTM_IsAclConnectionUp = [](const RawAddress& remote_bda,
-                                    tBT_TRANSPORT transport) -> bool {
-          return false;
-        },
-        .BTM_ReadConnectedTransportAddress =
-            [](RawAddress* remote_bda, tBT_TRANSPORT transport) -> bool {
-          return false;
-        },
-        .BTM_ReadDevInfo = [](const RawAddress& remote_bda,
-                              tBT_DEVICE_TYPE* p_dev_type,
-                              tBLE_ADDR_TYPE* p_addr_type) {},
-        .BTM_ReadRemoteDeviceName = BTM_ReadRemoteDeviceName,
-        .BTM_ReadRemoteFeatures = [](const RawAddress& addr) -> uint8_t* {
-          return hci_feature_bytes_per_page;
-        },
-        .BTM_GetMaxPacketSize = [](const RawAddress& bd_addr) -> uint16_t {
-          return 0;
-        },
-        .BTM_ReadRemoteVersion =
-            [](const RawAddress& addr, uint8_t* lmp_version,
-               uint16_t* manufacturer,
-               uint16_t* lmp_sub_version) -> bool { return false; },
-    },
-    .scn =
-        {
-            .BTM_AllocateSCN = BTM_AllocateSCN,
-            .BTM_TryAllocateSCN = BTM_TryAllocateSCN,
-            .BTM_FreeSCN = BTM_FreeSCN,
-        },
     .security = {
-        .BTM_ConfirmReqReply = [](tBTM_STATUS res,
-                                  const RawAddress& bd_addr) {},
-        .BTM_PINCodeReply = [](const RawAddress& bd_addr, tBTM_STATUS res,
-                               uint8_t pin_len, uint8_t* p_pin) {},
-        .BTM_RemoteOobDataReply = [](tBTM_STATUS res, const RawAddress& bd_addr,
-                                     const Octet16& c, const Octet16& r) {},
-        .BTM_SecAddBleDevice = [](const RawAddress& bd_addr,
-                                  tBT_DEVICE_TYPE dev_type,
-                                  tBLE_ADDR_TYPE addr_type) {},
-        .BTM_SecAddBleKey = [](const RawAddress& bd_addr,
-                               tBTM_LE_KEY_VALUE* p_le_key,
-                               tBTM_LE_KEY_TYPE key_type) {},
         .BTM_SecAddDevice = BTM_SecAddDevice,
         .BTM_SecAddRmtNameNotifyCallback =
             [](tBTM_RMT_NAME_CALLBACK* p_callback) -> bool { return false; },
+        .BTM_SecDeleteDevice = BTM_SecDeleteDevice,
+        .BTM_SecRegister = [](const tBTM_APPL_INFO* p_cb_info) -> bool {
+          return false;
+        },
+        .BTM_SecReadDevName = [](const RawAddress& bd_addr) -> const char* {
+          return nullptr;
+        },
         .BTM_SecBond = [](const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
                           tBT_TRANSPORT transport, tBT_DEVICE_TYPE device_type,
                           uint8_t pin_len, uint8_t* p_pin) -> tBTM_STATUS {
@@ -167,18 +135,23 @@
         .BTM_SecBondCancel = [](const RawAddress& bd_addr) -> tBTM_STATUS {
           return BTM_SUCCESS;
         },
+        .BTM_SecAddBleKey = [](const RawAddress& bd_addr,
+                               tBTM_LE_KEY_VALUE* p_le_key,
+                               tBTM_LE_KEY_TYPE key_type) {},
+        .BTM_SecAddBleDevice = [](const RawAddress& bd_addr,
+                                  tBT_DEVICE_TYPE dev_type,
+                                  tBLE_ADDR_TYPE addr_type) {},
         .BTM_SecClearSecurityFlags = BTM_SecClearSecurityFlags,
         .BTM_SecClrService = [](uint8_t service_id) -> uint8_t { return 0; },
         .BTM_SecClrServiceByPsm = [](uint16_t psm) -> uint8_t { return 0; },
-        .BTM_SecDeleteDevice = BTM_SecDeleteDevice,
+        .BTM_RemoteOobDataReply = [](tBTM_STATUS res, const RawAddress& bd_addr,
+                                     const Octet16& c, const Octet16& r) {},
+        .BTM_PINCodeReply = [](const RawAddress& bd_addr, tBTM_STATUS res,
+                               uint8_t pin_len, uint8_t* p_pin) {},
+        .BTM_ConfirmReqReply = [](tBTM_STATUS res,
+                                  const RawAddress& bd_addr) {},
         .BTM_SecDeleteRmtNameNotifyCallback =
             [](tBTM_RMT_NAME_CALLBACK* p_callback) -> bool { return false; },
-        .BTM_SecReadDevName = [](const RawAddress& bd_addr) -> const char* {
-          return nullptr;
-        },
-        .BTM_SecRegister = [](const tBTM_APPL_INFO* p_cb_info) -> bool {
-          return false;
-        },
         .BTM_SetEncryption =
             [](const RawAddress& bd_addr, tBT_TRANSPORT transport,
                tBTM_SEC_CALLBACK* p_callback, void* p_ref_data,
@@ -196,15 +169,19 @@
         },
     },
     .ble = {
-        .BTM_BleConfirmReply = [](const RawAddress& bd_addr, uint8_t res) {},
         .BTM_BleGetEnergyInfo = [](tBTM_BLE_ENERGY_INFO_CBACK* p_ener_cback)
             -> tBTM_STATUS { return BTM_SUCCESS; },
-        .BTM_BleLoadLocalKeys = [](uint8_t key_type,
-                                   tBTM_BLE_LOCAL_KEYS* p_key) {},
         .BTM_BleObserve =
             [](bool start, uint8_t duration, tBTM_INQ_RESULTS_CB* p_results_cb,
                tBTM_CMPL_CB* p_cmpl_cb,
                bool low_latency_scan) -> tBTM_STATUS { return BTM_SUCCESS; },
+        .BTM_SetBleDataLength = [](const RawAddress& bd_addr,
+                                   uint16_t tx_pdu_length) -> tBTM_STATUS {
+          return BTM_SUCCESS;
+        },
+        .BTM_BleConfirmReply = [](const RawAddress& bd_addr, uint8_t res) {},
+        .BTM_BleLoadLocalKeys = [](uint8_t key_type,
+                                   tBTM_BLE_LOCAL_KEYS* p_key) {},
         .BTM_BlePasskeyReply = [](const RawAddress& bd_addr, uint8_t res,
                                   uint32_t passkey) {},
         .BTM_BleReadControllerFeatures =
@@ -215,32 +192,42 @@
             [](const RawAddress& bd_addr, uint16_t min_conn_int,
                uint16_t max_conn_int, uint16_t peripheral_latency,
                uint16_t supervision_tout) {},
-        .BTM_SetBleDataLength = [](const RawAddress& bd_addr,
-                                   uint16_t tx_pdu_length) -> tBTM_STATUS {
-          return BTM_SUCCESS;
-        },
         .BTM_UseLeLink = [](const RawAddress& bd_addr) -> bool {
           return false;
         }},
     .sco =
         {
             .BTM_CreateSco = BTM_CreateSco,
-            .BTM_EScoConnRsp = BTM_EScoConnRsp,
-            .BTM_GetNumScoLinks = BTM_GetNumScoLinks,
             .BTM_RegForEScoEvts = BTM_RegForEScoEvts,
             .BTM_RemoveSco = BTM_RemoveSco,
-            .BTM_SetEScoMode = BTM_SetEScoMode,
             .BTM_WriteVoiceSettings = BTM_WriteVoiceSettings,
+            .BTM_EScoConnRsp = BTM_EScoConnRsp,
+            .BTM_GetNumScoLinks = BTM_GetNumScoLinks,
+            .BTM_SetEScoMode = BTM_SetEScoMode,
         },
     .local =
         {
             .BTM_ReadLocalDeviceNameFromController =
                 BTM_ReadLocalDeviceNameFromController,
-            .BTM_SetDeviceClass = BTM_SetDeviceClass,
             .BTM_SetLocalDeviceName = BTM_SetLocalDeviceName,
+            .BTM_SetDeviceClass = BTM_SetDeviceClass,
             .BTM_IsDeviceUp = BTM_IsDeviceUp,
             .BTM_ReadDeviceClass = BTM_ReadDeviceClass,
         },
+    .eir = {
+        .BTM_WriteEIR = [](BT_HDR* p_buff) -> tBTM_STATUS {
+          return BTM_SUCCESS;
+        },
+        .BTM_GetEirSupportedServices =
+            [](uint32_t* p_eir_uuid, uint8_t** p, uint8_t max_num_uuid16,
+               uint8_t* p_num_uuid16) -> uint8_t { return 0; },
+        .BTM_GetEirUuidList = [](const uint8_t* p_eir, size_t eir_len,
+                                 uint8_t uuid_size, uint8_t* p_num_uuid,
+                                 uint8_t* p_uuid_list,
+                                 uint8_t max_num_uuid) -> uint8_t { return 0; },
+        .BTM_AddEirService = [](uint32_t* p_eir_uuid, uint16_t uuid16) {},
+        .BTM_RemoveEirService = [](uint32_t* p_eir_uuid, uint16_t uuid16) {},
+    },
     .db =
         {
             .BTM_InqDbRead = BTM_InqDbRead,
diff --git a/system/test/mock/mock_stack_btm_hfp_lc3_decoder.h b/system/test/mock/mock_stack_btm_hfp_lc3_decoder.h
index af3dadd..444d515 100644
--- a/system/test/mock/mock_stack_btm_hfp_lc3_decoder.h
+++ b/system/test/mock/mock_stack_btm_hfp_lc3_decoder.h
@@ -37,7 +37,6 @@
 
 #include <cstring>
 
-#include "embdrv/lc3/include/lc3.h"
 #include "hfp_lc3_decoder.h"
 #include "osi/include/log.h"
 #include "test/common/mock_functions.h"
diff --git a/system/test/mock/mock_stack_btm_hfp_lc3_encoder.h b/system/test/mock/mock_stack_btm_hfp_lc3_encoder.h
index 8a77c46..6681153 100644
--- a/system/test/mock/mock_stack_btm_hfp_lc3_encoder.h
+++ b/system/test/mock/mock_stack_btm_hfp_lc3_encoder.h
@@ -35,7 +35,6 @@
 //       may need attention to prune from (or add to ) the inclusion set.
 #include <cstring>
 
-#include "embdrv/lc3/include/lc3.h"
 #include "hfp_lc3_encoder.h"
 #include "osi/include/log.h"
 #include "test/common/mock_functions.h"
diff --git a/system/test/mock/mock_stack_gatt_connection_manager.cc b/system/test/mock/mock_stack_gatt_connection_manager.cc
index 2a1a846..111387f 100644
--- a/system/test/mock/mock_stack_gatt_connection_manager.cc
+++ b/system/test/mock/mock_stack_gatt_connection_manager.cc
@@ -60,7 +60,8 @@
   return false;
 }
 bool connection_manager::direct_connect_remove(uint8_t app_id,
-                                               const RawAddress& address) {
+                                               const RawAddress& address,
+                                               bool connection_timeout) {
   inc_func_call_count(__func__);
   return false;
 }
diff --git a/system/test/suite/Android.bp b/system/test/suite/Android.bp
index 4b95656..c4d6195 100644
--- a/system/test/suite/Android.bp
+++ b/system/test/suite/Android.bp
@@ -32,7 +32,6 @@
         "packages/modules/Bluetooth/system/stack/include",
         "packages/modules/Bluetooth/system/stack/l2cap",
         "packages/modules/Bluetooth/system/udrv/include",
-        "packages/modules/Bluetooth/system/vnd/include",
         "system/libfmq/include",
         "system/libhwbinder/include",
     ],
@@ -51,6 +50,7 @@
         "android.hardware.bluetooth.audio@2.0",
         "android.hardware.bluetooth.audio@2.1",
         "android.system.suspend-V1-ndk",
+        "libPlatformProperties",
         "libbinder",
         "libbinder_ndk",
         "libcrypto",
@@ -59,7 +59,6 @@
         "liblog",
         "libstatssocket",
         "libutils",
-        "libPlatformProperties",
     ],
     static_libs: [
         "android.hardware.bluetooth.a2dp@1.0",
@@ -125,6 +124,8 @@
         "android.hardware.common.fmq-V1-ndk",
         "android.media.audio.common.types-V2-ndk",
         "libbluetooth_gd",
+        "libbt-platform-protos-lite",
+        "libbt_shim_bridge",
         "libchrome",
         "libevent",
     ],
diff --git a/system/types/Android.bp b/system/types/Android.bp
index b862dfb..1a4af0b 100644
--- a/system/types/Android.bp
+++ b/system/types/Android.bp
@@ -57,6 +57,7 @@
 cc_test {
     name: "net_test_types",
     static_libs: [
+        "libbluetooth-types",
         "libchrome",
         "libosi", // strlcpy
     ],
diff --git a/system/udrv/Android.bp b/system/udrv/Android.bp
index 8b0534f..2f89f47 100644
--- a/system/udrv/Android.bp
+++ b/system/udrv/Android.bp
@@ -28,4 +28,6 @@
         "com.android.btservices",
     ],
     min_sdk_version: "Tiramisu",
+    header_libs: ["libbluetooth_headers"],
+    static_libs: ["libbt_shim_bridge"],
 }
diff --git a/system/vnd/ble/vendor_hcidefs.h b/system/vnd/ble/vendor_hcidefs.h
deleted file mode 100644
index b6782af..0000000
--- a/system/vnd/ble/vendor_hcidefs.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2003-2014 Broadcom Corporation
- *
- *  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.
- *
- ******************************************************************************/
-
-/*****************************************************************************
- *
- *  Name        vendor_hcidefs.h
- *
- *  Function    This file contains Broadcom Specific Host Controller Interface
- *              definitions.
- *
- *****************************************************************************/
-
-#ifndef VENDOR_HCIDEFS_H
-#define VENDOR_HCIDEFS_H
-
-/*****************************************************************************
- * Private address resolution VSC
- *****************************************************************************/
-
-/* VSC */
-#define HCI_VENDOR_BLE_RPA_VSC (0x0155 | HCI_GRP_VENDOR_SPECIFIC)
-
-/* Sub codes */
-#define HCI_VENDOR_BLE_RPA_ENABLE 0x01
-#define HCI_VENDOR_BLE_RPA_ADD_IRK 0x02
-#define HCI_VENDOR_BLE_RPA_REMOVE_IRK 0x03
-#define HCI_VENDOR_BLE_RPA_CLEAR_IRK 0x04
-#define HCI_VENDOR_BLE_RPA_READ_IRK 0x05
-
-/*****************************************************************************
- * Advertising data payload filter VSC
- *****************************************************************************/
-
-/* VSC */
-#define HCI_VENDOR_BLE_PCF_VSC (0x0157 | HCI_GRP_VENDOR_SPECIFIC)
-
-#endif
diff --git a/system/vnd/include/vendor_api.h b/system/vnd/include/vendor_api.h
deleted file mode 100644
index be10778..0000000
--- a/system/vnd/include/vendor_api.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/****************************************************************************
- *
- * Name:         vendor_api.h
- *
- * Description:  Vendor specific BTE API function external definitions.
- *
- * Copyright (c) 2009-2011, BROADCOM Inc., All Rights Reserved.
- * Broadcom Bluetooth Core. Proprietary and confidential.
- *
- *****************************************************************************/
-#ifndef VENDOR_API_H
-#define VENDOR_API_H
-
-#include "btm_api.h"
-
-/****************************************************************************
- *  Resolvable private address offload VSC specific definitions
- *****************************************************************************/
-
-enum { BTM_BLE_PRIVACY_ENABLE, BTM_BLE_PRIVACY_DISABLE };
-
-#endif
diff --git a/tools/OWNERS b/tools/OWNERS
index ec93a16..a8dfd4a 100644
--- a/tools/OWNERS
+++ b/tools/OWNERS
@@ -5,8 +5,5 @@
 jpawlowski@google.com
 licorne@google.com
 mylesgw@google.com
-optedoblivion@google.com
-qasimj@google.com
-rahulsabnis@google.com
 sattiraju@google.com
 siyuanh@google.com
diff --git a/tools/rootcanal/Android.bp b/tools/rootcanal/Android.bp
index 67dd3af..d1dade6 100644
--- a/tools/rootcanal/Android.bp
+++ b/tools/rootcanal/Android.bp
@@ -449,6 +449,13 @@
 }
 
 genrule {
+    name: "rootcanal_link_layer_packets_rust_gen",
+    defaults: ["pdl_rust_generator_defaults"],
+    srcs: ["packets/link_layer_packets.pdl"],
+    out: ["link_layer_packets.rs"],
+}
+
+genrule {
     name: "rootcanal_bredr_bb_packets_cxx_gen",
     tools: [
         ":pdl_cxx_generator",
diff --git a/tools/rootcanal/CMakeLists.txt b/tools/rootcanal/CMakeLists.txt
index 53aec54..f263a48 100644
--- a/tools/rootcanal/CMakeLists.txt
+++ b/tools/rootcanal/CMakeLists.txt
@@ -51,7 +51,7 @@
   get_filename_component(pdl_INPUT_ABSOLUTE ${pdl_INPUT} ABSOLUTE)
   get_filename_component(pdl_OUTPUT_ABSOLUTE ${pdl_OUTPUT} ABSOLUTE)
   get_filename_component(pdl_OUTPUT_DIR ${pdl_OUTPUT_ABSOLUTE} DIRECTORY)
-  set(${pdl_NAME} "${pdl_OUTPUT_ABSOLUTE}" PARENT_SCOPE)
+  set(${pdl_NAME} "${pdl_OUTPUT_ABSOLUTE}" CACHE STRING "PDL output filepath for ${pdl_NAME}" FORCE)
 
   file(MAKE_DIRECTORY ${pdl_OUTPUT_DIR})
 
@@ -105,6 +105,8 @@
         VERBATIM
         DEPENDS pdlc ${pdl_OUTPUT_ABSOLUTE}.json)
   endif()
+
+  add_custom_target("pdl_gen-${pdl_NAME}" DEPENDS ${pdl_OUTPUT_ABSOLUTE})
 endfunction()
 
 pdl_gen(
@@ -124,6 +126,12 @@
   INCLUDE "hci/address.h"
   USING "bluetooth::hci")
 
+pdl_gen(
+  NAME RootCanalGeneratedPackets_rs
+  INPUT ${ROOTCANAL_ROOT}/packets/link_layer_packets.pdl
+  OUTPUT link_layer_packets.rs
+  LANG rust)
+
 android_add_library(
   TARGET libscriptedbeaconpayload-protos-lite LICENSE Apache-2.0
   SOURCE_DIR ${ROOTCANAL_ROOT} SRC ${libscriptedbeaconpayload_protos_lite_src})
diff --git a/tools/rootcanal/desktop/test_environment.cc b/tools/rootcanal/desktop/test_environment.cc
index 10a1661..59e093b 100644
--- a/tools/rootcanal/desktop/test_environment.cc
+++ b/tools/rootcanal/desktop/test_environment.cc
@@ -14,22 +14,36 @@
 // limitations under the License.
 //
 
-#include "test_environment.h"
+#include "desktop/test_environment.h"
 
 #include <google/protobuf/text_format.h>
 
-#include <filesystem>  // for exists
-#include <type_traits>  // for remove_extent_t
-#include <utility>      // for move
-#include <vector>       // for vector
+#include <chrono>
+#include <filesystem>
+#include <fstream>
+#include <functional>
+#include <future>
+#include <ios>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
 
+#include "hci/pcap_filter.h"
 #include "log.h"
+#include "model/controller/controller_properties.h"
 #include "model/devices/baseband_sniffer.h"
-#include "model/devices/link_layer_socket_device.h"  // for LinkLayerSocketDevice
-#include "model/hci/hci_sniffer.h"                   // for HciSniffer
-#include "model/hci/hci_socket_transport.h"          // for HciSocketTransport
-#include "net/async_data_channel.h"                  // for AsyncDataChannel
-#include "net/async_data_channel_connector.h"  // for AsyncDataChannelConnector
+#include "model/devices/device.h"
+#include "model/devices/hci_device.h"
+#include "model/devices/link_layer_socket_device.h"
+#include "model/hci/hci_sniffer.h"
+#include "model/hci/hci_socket_transport.h"
+#include "model/setup/async_manager.h"
+#include "model/setup/test_channel_transport.h"
+#include "net/async_data_channel.h"
+#include "net/async_data_channel_connector.h"
+#include "phy.h"
+#include "rootcanal/configuration.pb.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/desktop/test_environment.h b/tools/rootcanal/desktop/test_environment.h
index 3f47ba0..5947d07 100644
--- a/tools/rootcanal/desktop/test_environment.h
+++ b/tools/rootcanal/desktop/test_environment.h
@@ -16,17 +16,19 @@
 
 #pragma once
 
-#include <chrono>      // for milliseconds
-#include <functional>  // for __base, function
-#include <future>      // for promise
-#include <memory>      // for shared_ptr, make_...
-#include <string>      // for string
+#include <chrono>
+#include <functional>
+#include <future>
+#include <memory>
+#include <string>
+#include <vector>
 
-#include "model/setup/async_manager.h"              // for AsyncTaskId, Asyn...
-#include "model/setup/test_channel_transport.h"     // for TestChannelTransport
-#include "model/setup/test_command_handler.h"       // for TestCommandHandler
-#include "model/setup/test_model.h"                 // for TestModel
-#include "net/async_data_channel_server.h"          // for AsyncDataChannelS...
+#include "model/controller/controller_properties.h"
+#include "model/setup/async_manager.h"
+#include "model/setup/test_channel_transport.h"
+#include "model/setup/test_command_handler.h"
+#include "model/setup/test_model.h"
+#include "net/async_data_channel_server.h"
 
 namespace android::net {
 class AsyncDataChannel;
diff --git a/tools/rootcanal/include/hci/address.h b/tools/rootcanal/include/hci/address.h
index a46653a..74f9390 100644
--- a/tools/rootcanal/include/hci/address.h
+++ b/tools/rootcanal/include/hci/address.h
@@ -16,18 +16,20 @@
 
 #pragma once
 
+#include <fmt/core.h>
 #include <packet_runtime.h>
 
 #include <array>
+#include <cstdint>
 #include <cstring>
+#include <functional>
 #include <initializer_list>
 #include <optional>
 #include <ostream>
 #include <string>
 #include <vector>
 
-namespace bluetooth {
-namespace hci {
+namespace bluetooth::hci {
 
 class Address final : public pdl::packet::Builder {
  public:
@@ -88,8 +90,7 @@
   return os;
 }
 
-}  // namespace hci
-}  // namespace bluetooth
+}  // namespace bluetooth::hci
 
 namespace std {
 template <>
@@ -104,3 +105,43 @@
   }
 };
 }  // namespace std
+
+template <>
+struct fmt::formatter<bluetooth::hci::Address> {
+  // Presentation format: 'x' - lowercase, 'X' - uppercase.
+  char presentation = 'x';
+
+  // Parses format specifications of the form ['x' | 'X'].
+  constexpr auto parse(format_parse_context& ctx)
+      -> format_parse_context::iterator {
+    // Parse the presentation format and store it in the formatter:
+    auto it = ctx.begin();
+    auto end = ctx.end();
+    if (it != end && (*it == 'x' || *it == 'X')) {
+      presentation = *it++;
+    }
+
+    // Check if reached the end of the range:
+    if (it != end && *it != '}') {
+      ctx.on_error("invalid format");
+    }
+
+    // Return an iterator past the end of the parsed range:
+    return it;
+  }
+
+  // Formats the address a using the parsed format specification (presentation)
+  // stored in this formatter.
+  auto format(const bluetooth::hci::Address& a, format_context& ctx) const
+      -> format_context::iterator {
+    return presentation == 'x'
+               ? fmt::format_to(ctx.out(),
+                                "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
+                                a.address[5], a.address[4], a.address[3],
+                                a.address[2], a.address[1], a.address[0])
+               : fmt::format_to(ctx.out(),
+                                "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
+                                a.address[5], a.address[4], a.address[3],
+                                a.address[2], a.address[1], a.address[0]);
+  }
+};
diff --git a/tools/rootcanal/include/hci/address_with_type.h b/tools/rootcanal/include/hci/address_with_type.h
index 51ddfd9..01968b8 100644
--- a/tools/rootcanal/include/hci/address_with_type.h
+++ b/tools/rootcanal/include/hci/address_with_type.h
@@ -16,6 +16,13 @@
 
 #pragma once
 
+#include <fmt/core.h>
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <ostream>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -24,8 +31,7 @@
 #include "hci/address.h"
 #include "packets/hci_packets.h"
 
-namespace bluetooth {
-namespace hci {
+namespace bluetooth::hci {
 
 class AddressWithType final {
  public:
@@ -118,8 +124,7 @@
   return os;
 }
 
-}  // namespace hci
-}  // namespace bluetooth
+}  // namespace bluetooth::hci
 
 namespace std {
 template <>
@@ -134,3 +139,38 @@
   }
 };
 }  // namespace std
+
+template <>
+struct fmt::formatter<bluetooth::hci::AddressWithType> {
+  // Presentation format: 'x' - lowercase, 'X' - uppercase.
+  char presentation = 'x';
+
+  // Parses format specifications of the form ['x' | 'X'].
+  constexpr auto parse(format_parse_context& ctx)
+      -> format_parse_context::iterator {
+    // Parse the presentation format and store it in the formatter:
+    auto it = ctx.begin();
+    auto end = ctx.end();
+    if (it != end && (*it == 'x' || *it == 'X')) {
+      presentation = *it++;
+    }
+
+    // Check if reached the end of the range:
+    if (it != end && *it != '}') {
+      ctx.on_error("invalid format");
+    }
+
+    // Return an iterator past the end of the parsed range:
+    return it;
+  }
+
+  // Formats the address a using the parsed format specification (presentation)
+  // stored in this formatter.
+  auto format(const bluetooth::hci::AddressWithType& a,
+              format_context& ctx) const -> format_context::iterator {
+    auto out = presentation == 'x'
+                   ? fmt::format_to(ctx.out(), "{:x}", a.GetAddress())
+                   : fmt::format_to(ctx.out(), "{:X}", a.GetAddress());
+    return fmt::format_to(out, "[{}]", AddressTypeText(a.GetAddressType()));
+  }
+};
diff --git a/tools/rootcanal/include/hci/pcap_filter.h b/tools/rootcanal/include/hci/pcap_filter.h
index 01c60eb..1731ce8 100644
--- a/tools/rootcanal/include/hci/pcap_filter.h
+++ b/tools/rootcanal/include/hci/pcap_filter.h
@@ -16,9 +16,13 @@
 
 #pragma once
 
-#include <packets/hci_packets.h>
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <utility>
+#include <vector>
 
-#include <unordered_map>
+#include "packets/hci_packets.h"
 
 namespace rootcanal {
 
@@ -63,10 +67,6 @@
       bluetooth::hci::CommandView& command);
   std::vector<uint8_t> FilterLeSetPeriodicAdvertisingData(
       bluetooth::hci::CommandView& command);
-  std::vector<uint8_t> FilterLeMultiAdvtSetData(
-      bluetooth::hci::LeMultiAdvtView& command);
-  std::vector<uint8_t> FilterLeMultiAdvtSetScanResp(
-      bluetooth::hci::LeMultiAdvtView& command);
 
   // Specific filters for HCI events.
   std::vector<uint8_t> FilterReadLocalNameComplete(
diff --git a/tools/rootcanal/include/log.h b/tools/rootcanal/include/log.h
index a9482a9..130800a 100644
--- a/tools/rootcanal/include/log.h
+++ b/tools/rootcanal/include/log.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <fmt/core.h>
 #include <fmt/format.h>
 #include <fmt/printf.h>
 
diff --git a/tools/rootcanal/include/phy.h b/tools/rootcanal/include/phy.h
index 1fa8274..1e82c15 100644
--- a/tools/rootcanal/include/phy.h
+++ b/tools/rootcanal/include/phy.h
@@ -25,4 +25,5 @@
     BR_EDR,
   };
 };
+
 }  // namespace rootcanal
diff --git a/tools/rootcanal/lib/crypto/crypto.cc b/tools/rootcanal/lib/crypto/crypto.cc
index 44ed796..560416d 100644
--- a/tools/rootcanal/lib/crypto/crypto.cc
+++ b/tools/rootcanal/lib/crypto/crypto.cc
@@ -19,7 +19,6 @@
 #include <openssl/aes.h>
 
 #include <algorithm>
-#include <cstdint>
 
 namespace rootcanal::crypto {
 
diff --git a/tools/rootcanal/lib/hci/address.cc b/tools/rootcanal/lib/hci/address.cc
index 690a1b4..3f9103c 100644
--- a/tools/rootcanal/lib/hci/address.cc
+++ b/tools/rootcanal/lib/hci/address.cc
@@ -16,18 +16,23 @@
 
 #include "hci/address.h"
 
-#include <fmt/core.h>
+#include <packet_runtime.h>
 
 #include <algorithm>
+#include <array>
 #include <cstdint>
 #include <cstdio>
+#include <cstdlib>
+#include <initializer_list>
 #include <iomanip>
+#include <ios>
+#include <iterator>
+#include <optional>
 #include <sstream>
+#include <string>
+#include <utility>
 
-#include "hci/address_with_type.h"
-
-namespace bluetooth {
-namespace hci {
+namespace bluetooth::hci {
 
 const Address Address::kAny{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 const Address Address::kEmpty{0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
@@ -123,86 +128,10 @@
 size_t Address::FromOctets(const uint8_t* from) {
   std::copy(from, from + kLength, data());
   return kLength;
-};
+}
 
 bool Address::IsValidAddress(const std::string& address) {
   return Address::FromString(address).has_value();
 }
 
-}  // namespace hci
-}  // namespace bluetooth
-
-template <>
-struct fmt::formatter<bluetooth::hci::Address> {
-  // Presentation format: 'x' - lowercase, 'X' - uppercase.
-  char presentation = 'x';
-
-  // Parses format specifications of the form ['x' | 'X'].
-  constexpr auto parse(format_parse_context& ctx)
-      -> format_parse_context::iterator {
-    // Parse the presentation format and store it in the formatter:
-    auto it = ctx.begin();
-    auto end = ctx.end();
-    if (it != end && (*it == 'x' || *it == 'X')) {
-      presentation = *it++;
-    }
-
-    // Check if reached the end of the range:
-    if (it != end && *it != '}') {
-      ctx.on_error("invalid format");
-    }
-
-    // Return an iterator past the end of the parsed range:
-    return it;
-  }
-
-  // Formats the address a using the parsed format specification (presentation)
-  // stored in this formatter.
-  auto format(const bluetooth::hci::Address& a, format_context& ctx) const
-      -> format_context::iterator {
-    return presentation == 'x'
-               ? fmt::format_to(ctx.out(),
-                                "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
-                                a.address[5], a.address[4], a.address[3],
-                                a.address[2], a.address[1], a.address[0])
-               : fmt::format_to(ctx.out(),
-                                "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
-                                a.address[5], a.address[4], a.address[3],
-                                a.address[2], a.address[1], a.address[0]);
-  }
-};
-
-template <>
-struct fmt::formatter<bluetooth::hci::AddressWithType> {
-  // Presentation format: 'x' - lowercase, 'X' - uppercase.
-  char presentation = 'x';
-
-  // Parses format specifications of the form ['x' | 'X'].
-  constexpr auto parse(format_parse_context& ctx)
-      -> format_parse_context::iterator {
-    // Parse the presentation format and store it in the formatter:
-    auto it = ctx.begin();
-    auto end = ctx.end();
-    if (it != end && (*it == 'x' || *it == 'X')) {
-      presentation = *it++;
-    }
-
-    // Check if reached the end of the range:
-    if (it != end && *it != '}') {
-      ctx.on_error("invalid format");
-    }
-
-    // Return an iterator past the end of the parsed range:
-    return it;
-  }
-
-  // Formats the address a using the parsed format specification (presentation)
-  // stored in this formatter.
-  auto format(const bluetooth::hci::AddressWithType& a,
-              format_context& ctx) const -> format_context::iterator {
-    auto out = presentation == 'x'
-                   ? fmt::format_to(ctx.out(), "{:x}", a.GetAddress())
-                   : fmt::format_to(ctx.out(), "{:X}", a.GetAddress());
-    return fmt::format_to(out, "[{}]", AddressTypeText(a.GetAddressType()));
-  }
-};
+}  // namespace bluetooth::hci
diff --git a/tools/rootcanal/lib/hci/pcap_filter.cc b/tools/rootcanal/lib/hci/pcap_filter.cc
index 7dbdeda..4a05da0 100644
--- a/tools/rootcanal/lib/hci/pcap_filter.cc
+++ b/tools/rootcanal/lib/hci/pcap_filter.cc
@@ -14,10 +14,22 @@
  * limitations under the License.
  */
 
-#include <hci/pcap_filter.h>
-#include <packets/hci_packets.h>
+#include "hci/pcap_filter.h"
+
+#include <packet_runtime.h>
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
+#include <memory>
+#include <utility>
+#include <vector>
 
 #include "log.h"
+#include "packets/hci_packets.h"
 
 using namespace bluetooth::hci;
 
@@ -74,19 +86,6 @@
       return FilterLeSetExtendedScanResponseData(command);
     case OpCode::LE_SET_PERIODIC_ADVERTISING_DATA:
       return FilterLeSetPeriodicAdvertisingData(command);
-    case OpCode::LE_MULTI_ADVT: {
-      auto le_multi_advt_command = LeMultiAdvtView::Create(command);
-      ASSERT(le_multi_advt_command.IsValid());
-      switch (le_multi_advt_command.GetSubCmd()) {
-        case SubOcf::SET_DATA:
-          return FilterLeMultiAdvtSetData(le_multi_advt_command);
-        case SubOcf::SET_SCAN_RESP:
-          return FilterLeMultiAdvtSetScanResp(le_multi_advt_command);
-        default:
-          break;
-      }
-      break;
-    }
     default:
       break;
   }
@@ -301,32 +300,6 @@
       ->SerializeToBytes();
 }
 
-// Replace the device names in the GAP entries of the advertising data.
-std::vector<uint8_t> PcapFilter::FilterLeMultiAdvtSetData(
-    bluetooth::hci::LeMultiAdvtView& command) {
-  auto parameters = LeMultiAdvtSetDataView::Create(command);
-  ASSERT(parameters.IsValid());
-
-  std::vector<uint8_t> advertising_data = parameters.GetAdvertisingData();
-  FilterGapData(advertising_data);
-  return LeMultiAdvtSetDataBuilder::Create(advertising_data,
-                                           parameters.GetAdvertisingInstance())
-      ->SerializeToBytes();
-}
-
-// Replace the device names in the GAP entries of the scan response data.
-std::vector<uint8_t> PcapFilter::FilterLeMultiAdvtSetScanResp(
-    bluetooth::hci::LeMultiAdvtView& command) {
-  auto parameters = LeMultiAdvtSetScanRespView::Create(command);
-  ASSERT(parameters.IsValid());
-
-  std::vector<uint8_t> advertising_data = parameters.GetAdvertisingData();
-  FilterGapData(advertising_data);
-  return LeMultiAdvtSetScanRespBuilder::Create(
-             advertising_data, parameters.GetAdvertisingInstance())
-      ->SerializeToBytes();
-}
-
 // Replace the local device name in the read local name complete event.
 std::vector<uint8_t> PcapFilter::FilterReadLocalNameComplete(
     bluetooth::hci::CommandCompleteView& command_complete) {
diff --git a/tools/rootcanal/lib/log.cc b/tools/rootcanal/lib/log.cc
index 9884651..9c78b16 100644
--- a/tools/rootcanal/lib/log.cc
+++ b/tools/rootcanal/lib/log.cc
@@ -17,11 +17,14 @@
 #include "log.h"
 
 #include <fmt/color.h>
+#include <fmt/core.h>
 
 #include <array>
+#include <chrono>
 #include <cstdio>
+#include <cstdlib>
+#include <cstring>
 #include <ctime>
-#include <filesystem>
 #include <optional>
 
 namespace rootcanal::log {
@@ -59,7 +62,7 @@
   auto now = std::chrono::system_clock::now();
   auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
   auto now_t = std::chrono::system_clock::to_time_t(now);
-  char time_str[19];  //"mm-dd_HH:MM:SS.mmm\0" is 19 byte long
+  char time_str[19];  // "mm-dd_HH:MM:SS.mmm\0" is 19 byte long
   auto n = std::strftime(time_str, sizeof(time_str), "%m-%d %H:%M:%S",
                          std::localtime(&now_t));
   snprintf(time_str + n, sizeof(time_str) - n, ".%03u",
diff --git a/tools/rootcanal/model/controller/acl_connection.cc b/tools/rootcanal/model/controller/acl_connection.cc
index 926e373..908747b 100644
--- a/tools/rootcanal/model/controller/acl_connection.cc
+++ b/tools/rootcanal/model/controller/acl_connection.cc
@@ -14,7 +14,13 @@
  * limitations under the License.
  */
 
-#include "acl_connection.h"
+#include "model/controller/acl_connection.h"
+
+#include <chrono>
+#include <cstdint>
+
+#include "packets/hci_packets.h"
+#include "phy.h"
 
 namespace rootcanal {
 AclConnection::AclConnection(AddressWithType address,
@@ -29,15 +35,15 @@
       last_packet_timestamp_(std::chrono::steady_clock::now()),
       timeout_(std::chrono::seconds(1)) {}
 
-void AclConnection::Encrypt() { encrypted_ = true; };
+void AclConnection::Encrypt() { encrypted_ = true; }
 
-bool AclConnection::IsEncrypted() const { return encrypted_; };
+bool AclConnection::IsEncrypted() const { return encrypted_; }
 
 void AclConnection::SetLinkPolicySettings(uint16_t settings) {
   link_policy_settings_ = settings;
 }
 
-bluetooth::hci::Role AclConnection::GetRole() const { return role_; };
+bluetooth::hci::Role AclConnection::GetRole() const { return role_; }
 
 void AclConnection::SetRole(bluetooth::hci::Role role) { role_ = role; }
 
diff --git a/tools/rootcanal/model/controller/acl_connection.h b/tools/rootcanal/model/controller/acl_connection.h
index 7945ef0..86ba951 100644
--- a/tools/rootcanal/model/controller/acl_connection.h
+++ b/tools/rootcanal/model/controller/acl_connection.h
@@ -20,6 +20,7 @@
 #include <cstdint>
 
 #include "hci/address_with_type.h"
+#include "packets/hci_packets.h"
 #include "phy.h"
 
 namespace rootcanal {
diff --git a/tools/rootcanal/model/controller/acl_connection_handler.cc b/tools/rootcanal/model/controller/acl_connection_handler.cc
index 5642c32..656c495 100644
--- a/tools/rootcanal/model/controller/acl_connection_handler.cc
+++ b/tools/rootcanal/model/controller/acl_connection_handler.cc
@@ -14,12 +14,22 @@
  * limitations under the License.
  */
 
-#include "acl_connection_handler.h"
+#include "model/controller/acl_connection_handler.h"
 
-#include <packets/hci_packets.h>
+#include <chrono>
+#include <cstdint>
+#include <functional>
+#include <optional>
+#include <utility>
+#include <vector>
 
 #include "hci/address.h"
+#include "hci/address_with_type.h"
 #include "log.h"
+#include "model/controller/acl_connection.h"
+#include "model/controller/sco_connection.h"
+#include "packets/hci_packets.h"
+#include "phy.h"
 
 namespace rootcanal {
 
@@ -275,7 +285,7 @@
 
 uint16_t AclConnectionHandler::GetAclLinkPolicySettings(uint16_t handle) const {
   return acl_connections_.at(handle).GetLinkPolicySettings();
-};
+}
 
 void AclConnectionHandler::SetAclLinkPolicySettings(uint16_t handle,
                                                     uint16_t settings) {
@@ -284,7 +294,7 @@
 
 bluetooth::hci::Role AclConnectionHandler::GetAclRole(uint16_t handle) const {
   return acl_connections_.at(handle).GetRole();
-};
+}
 
 void AclConnectionHandler::SetAclRole(uint16_t handle,
                                       bluetooth::hci::Role role) {
@@ -405,7 +415,7 @@
 }
 
 std::vector<uint16_t> AclConnectionHandler::GetAclHandles() const {
-  std::vector<uint16_t> keys;
+  std::vector<uint16_t> keys(acl_connections_.size());
 
   for (const auto& pair : acl_connections_) {
     keys.push_back(pair.first);
diff --git a/tools/rootcanal/model/controller/acl_connection_handler.h b/tools/rootcanal/model/controller/acl_connection_handler.h
index 8669bcf..146cc47 100644
--- a/tools/rootcanal/model/controller/acl_connection_handler.h
+++ b/tools/rootcanal/model/controller/acl_connection_handler.h
@@ -18,14 +18,17 @@
 
 #include <chrono>
 #include <cstdint>
-#include <set>
+#include <functional>
+#include <optional>
 #include <unordered_map>
+#include <vector>
 
-#include "acl_connection.h"
 #include "hci/address.h"
 #include "hci/address_with_type.h"
+#include "model/controller/acl_connection.h"
+#include "model/controller/sco_connection.h"
+#include "packets/hci_packets.h"
 #include "phy.h"
-#include "sco_connection.h"
 
 namespace rootcanal {
 static constexpr uint16_t kReservedHandle = 0xF00;
diff --git a/tools/rootcanal/model/controller/controller_properties.cc b/tools/rootcanal/model/controller/controller_properties.cc
index 012cdc1..228683e 100644
--- a/tools/rootcanal/model/controller/controller_properties.cc
+++ b/tools/rootcanal/model/controller/controller_properties.cc
@@ -14,16 +14,16 @@
  * limitations under the License.
  */
 
-#include "controller_properties.h"
+#include "model/controller/controller_properties.h"
 
-#include <google/protobuf/text_format.h>
-#include <inttypes.h>
-
-#include <fstream>
-#include <limits>
-#include <memory>
+#include <array>
+#include <cstdint>
+#include <utility>
+#include <vector>
 
 #include "log.h"
+#include "packets/hci_packets.h"
+#include "rootcanal/configuration.pb.h"
 
 namespace rootcanal {
 using namespace bluetooth::hci;
@@ -477,7 +477,7 @@
       lmp_page_0_reserved_bits = UINT64_C(0x7884000401000100);
       lmp_page_2_reserved_bits = UINT64_C(0xfffffffffffff080);
       break;
-  };
+  }
 
   if ((lmp_page_0_reserved_bits & lmp_features[0]) != 0) {
     INFO(
@@ -1876,6 +1876,7 @@
 
       case ControllerPreset::CSR_RCK_PTS_DONGLE:
         // Configuration extracted with the helper script controller_info.py
+        vendor_csr = true;
         br_supported = true;
         le_supported = true;
         hci_version = bluetooth::hci::HciVersion::V_4_2;
@@ -1966,6 +1967,13 @@
     // TODO(b/270606199): support send_acl_data_before_connection_complete
   }
 
+  // Apply selected vendor features.
+  if (config.has_vendor()) {
+    if (config.vendor().has_csr()) {
+      vendor_csr = config.vendor().csr();
+    }
+  }
+
   if (!CheckSupportedFeatures()) {
     INFO(
         "Warning: LMP and/or LE features are not consistent. Please make sure"
diff --git a/tools/rootcanal/model/controller/controller_properties.h b/tools/rootcanal/model/controller/controller_properties.h
index 5eb4d48..593d9e5 100644
--- a/tools/rootcanal/model/controller/controller_properties.h
+++ b/tools/rootcanal/model/controller/controller_properties.h
@@ -18,11 +18,8 @@
 
 #include <array>
 #include <cstdint>
-#include <optional>
-#include <string>
 #include <vector>
 
-#include "hci/address.h"
 #include "packets/hci_packets.h"
 #include "rootcanal/configuration.pb.h"
 
@@ -158,6 +155,9 @@
   // Provide parameters returned by vendor specific commands.
   std::vector<uint8_t> le_vendor_capabilities{};
 
+  // Enable the support for the CSR vendor command.
+  bool vendor_csr{true};
+
   bool SupportsLMPFeature(bluetooth::hci::LMPFeaturesPage0Bits bit) const {
     return (lmp_features[0] & static_cast<uint64_t>(bit)) != 0;
   }
diff --git a/tools/rootcanal/model/controller/dual_mode_controller.cc b/tools/rootcanal/model/controller/dual_mode_controller.cc
index d2b8780..51dfb85 100644
--- a/tools/rootcanal/model/controller/dual_mode_controller.cc
+++ b/tools/rootcanal/model/controller/dual_mode_controller.cc
@@ -14,18 +14,34 @@
  * limitations under the License.
  */
 
-#include "dual_mode_controller.h"
+#include "model/controller/dual_mode_controller.h"
+
+#include <packet_runtime.h>
 
 #include <algorithm>
+#include <cstdint>
+#include <functional>
 #include <memory>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
 
 #include "crypto/crypto.h"
+#include "hci/address_with_type.h"
 #include "log.h"
+#include "model/controller/acl_connection_handler.h"
+#include "model/controller/controller_properties.h"
+#include "model/controller/sco_connection.h"
+#include "model/controller/vendor_commands/csr.h"
+#include "model/devices/device.h"
+#include "packets/hci_packets.h"
+#include "packets/link_layer_packets.h"
+#include "phy.h"
 
 using bluetooth::hci::ErrorCode;
 using bluetooth::hci::LoopbackMode;
 using bluetooth::hci::OpCode;
-using std::vector;
 
 namespace rootcanal {
 constexpr uint16_t kNumCommandPackets = 0x01;
@@ -2772,8 +2788,8 @@
 }
 
 void DualModeController::LeGetVendorCapabilities(CommandView command) {
-  auto command_view = bluetooth::hci::LeGetVendorCapabilitiesView::Create(
-      bluetooth::hci::VendorCommandView::Create(command));
+  auto command_view =
+      bluetooth::hci::LeGetVendorCapabilitiesView::Create(command);
   ASSERT(command_view.IsValid());
 
   if (!properties_.supports_le_get_vendor_capabilities_command) {
@@ -2796,29 +2812,56 @@
       std::move(return_parameters)));
 }
 
-void DualModeController::LeMultiAdv(CommandView command) {
-  auto command_view = bluetooth::hci::LeMultiAdvtView::Create(command);
+void DualModeController::LeBatchScan(CommandView command) {
+  auto command_view = bluetooth::hci::LeBatchScanView::Create(command);
   ASSERT(command_view.IsValid());
-  SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_MULTI_ADVT);
+  SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_BATCH_SCAN);
 }
 
-void DualModeController::LeAdvertisingFilter(CommandView command) {
-  auto command_view = bluetooth::hci::LeAdvFilterView::Create(command);
+void DualModeController::LeApcf(CommandView command) {
+  auto command_view = bluetooth::hci::LeApcfView::Create(command);
   ASSERT(command_view.IsValid());
-  SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_ADV_FILTER);
+  SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_APCF);
 }
 
-void DualModeController::LeEnergyInfo(CommandView command) {
-  auto command_view = bluetooth::hci::LeEnergyInfoView::Create(
-      bluetooth::hci::VendorCommandView::Create(command));
+void DualModeController::LeGetControllerActivityEnergyInfo(
+    CommandView command) {
+  auto command_view =
+      bluetooth::hci::LeGetControllerActivityEnergyInfoView::Create(command);
   ASSERT(command_view.IsValid());
-  SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_ENERGY_INFO);
+  SendCommandCompleteUnknownOpCodeEvent(
+      OpCode::LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO);
+}
+
+void DualModeController::LeExSetScanParameters(CommandView command) {
+  auto command_view =
+      bluetooth::hci::LeExSetScanParametersView::Create(command);
+  ASSERT(command_view.IsValid());
+  SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_EX_SET_SCAN_PARAMETERS);
+}
+
+void DualModeController::GetControllerDebugInfo(CommandView command) {
+  auto command_view =
+      bluetooth::hci::GetControllerDebugInfoView::Create(command);
+  ASSERT(command_view.IsValid());
+
+  DEBUG(id_, "<< Get Controller Debug Info");
+
+  send_event_(bluetooth::hci::GetControllerDebugInfoCompleteBuilder::Create(
+      kNumCommandPackets, ErrorCode::SUCCESS));
 }
 
 // CSR vendor command.
 // Implement the command specific to the CSR controller
 // used specifically by the PTS tool to pass certification tests.
 void DualModeController::CsrVendorCommand(CommandView command) {
+  if (!properties_.vendor_csr) {
+    SendCommandCompleteUnknownOpCodeEvent(OpCode(CSR_VENDOR));
+    return;
+  }
+
+  DEBUG(id_, "<< CSR");
+
   // The byte order is little endian.
   // The command parameters are formatted as
   //
@@ -3135,12 +3178,6 @@
       kNumCommandPackets, status));
 }
 
-void DualModeController::LeExtendedScanParams(CommandView command) {
-  auto command_view = bluetooth::hci::LeExtendedScanParamsView::Create(command);
-  ASSERT(command_view.IsValid());
-  SendCommandCompleteUnknownOpCodeEvent(OpCode::LE_EXTENDED_SCAN_PARAMS);
-}
-
 void DualModeController::LeStartEncryption(CommandView command) {
   auto command_view = bluetooth::hci::LeStartEncryptionView::Create(command);
   ASSERT(command_view.IsValid());
@@ -4282,12 +4319,15 @@
 
         // VENDOR
         {OpCode(CSR_VENDOR), &DualModeController::CsrVendorCommand},
-        {OpCode::LE_MULTI_ADVT, &DualModeController::LeMultiAdv},
-        {OpCode::LE_ADV_FILTER, &DualModeController::LeAdvertisingFilter},
-        {OpCode::LE_EXTENDED_SCAN_PARAMS,
-         &DualModeController::LeExtendedScanParams},
-        {OpCode::LE_ENERGY_INFO, &DualModeController::LeEnergyInfo},
         {OpCode::LE_GET_VENDOR_CAPABILITIES,
-         &DualModeController::LeGetVendorCapabilities}};
+         &DualModeController::LeGetVendorCapabilities},
+        {OpCode::LE_BATCH_SCAN, &DualModeController::LeBatchScan},
+        {OpCode::LE_APCF, &DualModeController::LeApcf},
+        {OpCode::LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO,
+         &DualModeController::LeGetControllerActivityEnergyInfo},
+        {OpCode::LE_EX_SET_SCAN_PARAMETERS,
+         &DualModeController::LeExSetScanParameters},
+        {OpCode::GET_CONTROLLER_DEBUG_INFO,
+         &DualModeController::GetControllerDebugInfo}};
 
 }  // namespace rootcanal
diff --git a/tools/rootcanal/model/controller/dual_mode_controller.h b/tools/rootcanal/model/controller/dual_mode_controller.h
index c3a160d..551cf40 100644
--- a/tools/rootcanal/model/controller/dual_mode_controller.h
+++ b/tools/rootcanal/model/controller/dual_mode_controller.h
@@ -19,18 +19,21 @@
 #include <unistd.h>
 
 #include <cstdint>
+#include <functional>
 #include <memory>
 #include <random>
 #include <string>
 #include <unordered_map>
 #include <vector>
 
-#include "controller_properties.h"
 #include "hci/address.h"
-#include "link_layer_controller.h"
+#include "model/controller/controller_properties.h"
+#include "model/controller/link_layer_controller.h"
 #include "model/controller/vendor_commands/csr.h"
 #include "model/devices/device.h"
 #include "packets/hci_packets.h"
+#include "packets/link_layer_packets.h"
+#include "phy.h"
 
 namespace rootcanal {
 
@@ -501,10 +504,11 @@
 
   // Vendor-specific Commands
   void LeGetVendorCapabilities(CommandView command);
-  void LeEnergyInfo(CommandView command);
-  void LeMultiAdv(CommandView command);
-  void LeAdvertisingFilter(CommandView command);
-  void LeExtendedScanParams(CommandView command);
+  void LeBatchScan(CommandView command);
+  void LeApcf(CommandView command);
+  void LeGetControllerActivityEnergyInfo(CommandView command);
+  void LeExSetScanParameters(CommandView command);
+  void GetControllerDebugInfo(CommandView command);
 
   // CSR vendor command.
   // Implement the command specific to the CSR controller
diff --git a/tools/rootcanal/model/controller/ffi.cc b/tools/rootcanal/model/controller/ffi.cc
index 888f198..d01ff16 100644
--- a/tools/rootcanal/model/controller/ffi.cc
+++ b/tools/rootcanal/model/controller/ffi.cc
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-#include "ffi.h"
+#include "model/controller/ffi.h"
 
 #include <android-base/logging.h>
 
 #include <iostream>
 
-#include "dual_mode_controller.h"
+#include "model/controller/dual_mode_controller.h"
 
 using namespace rootcanal;
 using bluetooth::hci::Address;
diff --git a/tools/rootcanal/model/controller/le_advertiser.cc b/tools/rootcanal/model/controller/le_advertiser.cc
index 37afe98..865ceeb 100644
--- a/tools/rootcanal/model/controller/le_advertiser.cc
+++ b/tools/rootcanal/model/controller/le_advertiser.cc
@@ -14,10 +14,21 @@
  * limitations under the License.
  */
 
-#include "le_advertiser.h"
+#include "model/controller/le_advertiser.h"
 
-#include "link_layer_controller.h"
+#include <array>
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+#include <optional>
+#include <utility>
+#include <vector>
+
+#include "hci/address_with_type.h"
 #include "log.h"
+#include "model/controller/link_layer_controller.h"
+#include "packets/hci_packets.h"
+#include "packets/link_layer_packets.h"
 
 using namespace bluetooth::hci;
 using namespace std::literals;
diff --git a/tools/rootcanal/model/controller/le_advertiser.h b/tools/rootcanal/model/controller/le_advertiser.h
index 80fced0..cef6516 100644
--- a/tools/rootcanal/model/controller/le_advertiser.h
+++ b/tools/rootcanal/model/controller/le_advertiser.h
@@ -18,13 +18,13 @@
 
 #include <chrono>
 #include <cstdint>
-#include <memory>
 #include <optional>
 #include <ratio>
+#include <vector>
 
+#include "hci/address.h"
 #include "hci/address_with_type.h"
 #include "packets/hci_packets.h"
-#include "packets/link_layer_packets.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/controller/link_layer_controller.cc b/tools/rootcanal/model/controller/link_layer_controller.cc
index ed92bc1..c02887a 100644
--- a/tools/rootcanal/model/controller/link_layer_controller.cc
+++ b/tools/rootcanal/model/controller/link_layer_controller.cc
@@ -14,26 +14,44 @@
  * limitations under the License.
  */
 
-#include "link_layer_controller.h"
+#include "model/controller/link_layer_controller.h"
+
+#include <packet_runtime.h>
 
 #include <algorithm>
+#include <array>
+#include <chrono>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <functional>
+#include <memory>
+#include <optional>
+#include <utility>
+#include <vector>
 
 #include "crypto/crypto.h"
+#include "hci/address.h"
+#include "hci/address_with_type.h"
 #include "log.h"
+#include "model/controller/acl_connection.h"
+#include "model/controller/acl_connection_handler.h"
+#include "model/controller/controller_properties.h"
+#include "model/controller/le_advertiser.h"
+#include "model/controller/sco_connection.h"
 #include "packets/hci_packets.h"
-#include "rootcanal_rs.h"
+#include "packets/link_layer_packets.h"
+#include "phy.h"
+#include "rust/include/rootcanal_rs.h"
 
 using namespace std::chrono;
 using bluetooth::hci::Address;
 using bluetooth::hci::AddressType;
 using bluetooth::hci::AddressWithType;
-using bluetooth::hci::DirectAdvertisingAddressType;
-using bluetooth::hci::EventCode;
 using bluetooth::hci::LLFeaturesBits;
 using bluetooth::hci::SubeventCode;
 
 using namespace model::packets;
-using model::packets::PacketType;
 using namespace std::literals;
 
 using TaskId = rootcanal::LinkLayerController::TaskId;
@@ -2134,7 +2152,7 @@
               own_address, peer_address));
       break;
     case (OpCode::READ_REMOTE_EXTENDED_FEATURES): {
-      pdl::packet::slice page_number_slice = args.subrange(5, 2);
+      pdl::packet::slice page_number_slice = args.subrange(5, 1);
       uint8_t page_number = page_number_slice.read_le<uint8_t>();
       SendLinkLayerPacket(
           model::packets::ReadRemoteExtendedFeaturesBuilder::Create(
diff --git a/tools/rootcanal/model/controller/link_layer_controller.h b/tools/rootcanal/model/controller/link_layer_controller.h
index 2e426a8..d1b4d71 100644
--- a/tools/rootcanal/model/controller/link_layer_controller.h
+++ b/tools/rootcanal/model/controller/link_layer_controller.h
@@ -16,20 +16,31 @@
 
 #pragma once
 
+#include <packet_runtime.h>
+
 #include <algorithm>
+#include <array>
 #include <chrono>
-#include <map>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <optional>
 #include <set>
+#include <unordered_map>
+#include <utility>
 #include <vector>
 
 #include "hci/address.h"
-#include "include/phy.h"
+#include "hci/address_with_type.h"
 #include "model/controller/acl_connection_handler.h"
 #include "model/controller/controller_properties.h"
 #include "model/controller/le_advertiser.h"
+#include "model/controller/sco_connection.h"
 #include "packets/hci_packets.h"
 #include "packets/link_layer_packets.h"
-#include "rootcanal_rs.h"
+#include "phy.h"
+#include "rust/include/rootcanal_rs.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/controller/sco_connection.cc b/tools/rootcanal/model/controller/sco_connection.cc
index 3286a1c..e0fb087 100644
--- a/tools/rootcanal/model/controller/sco_connection.cc
+++ b/tools/rootcanal/model/controller/sco_connection.cc
@@ -14,13 +14,17 @@
  * limitations under the License.
  */
 
-#include "sco_connection.h"
+#include "model/controller/sco_connection.h"
 
-#include <log.h>
-#include <packets/hci_packets.h>
-
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <optional>
 #include <vector>
 
+#include "log.h"
+#include "packets/hci_packets.h"
+
 using namespace rootcanal;
 using namespace bluetooth::hci;
 
diff --git a/tools/rootcanal/model/devices/baseband_sniffer.cc b/tools/rootcanal/model/devices/baseband_sniffer.cc
index 056f7c3..fd3f018 100644
--- a/tools/rootcanal/model/devices/baseband_sniffer.cc
+++ b/tools/rootcanal/model/devices/baseband_sniffer.cc
@@ -14,12 +14,19 @@
  * limitations under the License.
  */
 
-#include "baseband_sniffer.h"
+#include "model/devices/baseband_sniffer.h"
+
+#include <cstdint>
+#include <ios>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
 
 #include "log.h"
+#include "packets/link_layer_packets.h"
 #include "pcap.h"
-
-using std::vector;
+#include "phy.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/baseband_sniffer.h b/tools/rootcanal/model/devices/baseband_sniffer.h
index d276199..96c2b7e 100644
--- a/tools/rootcanal/model/devices/baseband_sniffer.h
+++ b/tools/rootcanal/model/devices/baseband_sniffer.h
@@ -18,8 +18,13 @@
 
 #include <cstdint>
 #include <fstream>
+#include <memory>
+#include <string>
 
-#include "device.h"
+#include "hci/address.h"
+#include "model/devices/device.h"
+#include "packets/link_layer_packets.h"
+#include "phy.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/beacon.cc b/tools/rootcanal/model/devices/beacon.cc
index cb87104..d61d44b 100644
--- a/tools/rootcanal/model/devices/beacon.cc
+++ b/tools/rootcanal/model/devices/beacon.cc
@@ -14,9 +14,18 @@
  * limitations under the License.
  */
 
-#include "beacon.h"
+#include "model/devices/beacon.h"
 
+#include <chrono>
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "hci/address.h"
 #include "model/setup/device_boutique.h"
+#include "packets/link_layer_packets.h"
+#include "phy.h"
 
 namespace rootcanal {
 using namespace model::packets;
diff --git a/tools/rootcanal/model/devices/beacon.h b/tools/rootcanal/model/devices/beacon.h
index 5a387bc..6b33197 100644
--- a/tools/rootcanal/model/devices/beacon.h
+++ b/tools/rootcanal/model/devices/beacon.h
@@ -16,12 +16,16 @@
 
 #pragma once
 
+#include <array>
 #include <chrono>
 #include <cstdint>
 #include <memory>
+#include <string>
 #include <vector>
 
-#include "device.h"
+#include "model/devices/device.h"
+#include "packets/link_layer_packets.h"
+#include "phy.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/beacon_swarm.cc b/tools/rootcanal/model/devices/beacon_swarm.cc
index 45827dc..af4b755 100644
--- a/tools/rootcanal/model/devices/beacon_swarm.cc
+++ b/tools/rootcanal/model/devices/beacon_swarm.cc
@@ -14,11 +14,16 @@
  * limitations under the License.
  */
 
-#include "beacon_swarm.h"
+#include "model/devices/beacon_swarm.h"
 
+#include <chrono>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "model/devices/beacon.h"
 #include "model/setup/device_boutique.h"
-
-using std::vector;
+#include "packets/link_layer_packets.h"
 
 namespace rootcanal {
 using namespace model::packets;
@@ -27,7 +32,7 @@
 bool BeaconSwarm::registered_ =
     DeviceBoutique::Register("beacon_swarm", &BeaconSwarm::Create);
 
-BeaconSwarm::BeaconSwarm(const vector<std::string>& args) : Beacon(args) {
+BeaconSwarm::BeaconSwarm(const std::vector<std::string>& args) : Beacon(args) {
   advertising_interval_ = 1280ms;
   advertising_type_ = LegacyAdvertisingType::ADV_NONCONN_IND;
   advertising_data_ = {
diff --git a/tools/rootcanal/model/devices/beacon_swarm.h b/tools/rootcanal/model/devices/beacon_swarm.h
index d7bc6c1..69f6dd4 100644
--- a/tools/rootcanal/model/devices/beacon_swarm.h
+++ b/tools/rootcanal/model/devices/beacon_swarm.h
@@ -16,10 +16,12 @@
 
 #pragma once
 
-#include <cstdint>
+#include <memory>
+#include <string>
 #include <vector>
 
-#include "beacon.h"
+#include "model/devices/beacon.h"
+#include "model/devices/device.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/device.cc b/tools/rootcanal/model/devices/device.cc
index bf10ae6..9fadf9e 100644
--- a/tools/rootcanal/model/devices/device.cc
+++ b/tools/rootcanal/model/devices/device.cc
@@ -14,11 +14,17 @@
  * limitations under the License.
  */
 
-#include "device.h"
+#include "model/devices/device.h"
 
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <string>
 #include <vector>
 
 #include "log.h"
+#include "packets/link_layer_packets.h"
+#include "phy.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/device.h b/tools/rootcanal/model/devices/device.h
index 15ced21..3d5f258 100644
--- a/tools/rootcanal/model/devices/device.h
+++ b/tools/rootcanal/model/devices/device.h
@@ -16,10 +16,9 @@
 
 #pragma once
 
-#include <chrono>
 #include <cstdint>
 #include <functional>
-#include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -58,7 +57,7 @@
 
   virtual void ReceiveLinkLayerPacket(
       model::packets::LinkLayerPacketView /*packet*/, Phy::Type /*type*/,
-      int8_t /*rssi*/){};
+      int8_t /*rssi*/) {}
 
   void SendLinkLayerPacket(
       std::shared_ptr<model::packets::LinkLayerPacketBuilder> packet,
diff --git a/tools/rootcanal/model/devices/hci_device.cc b/tools/rootcanal/model/devices/hci_device.cc
index 710bd3a..9f0b30b 100644
--- a/tools/rootcanal/model/devices/hci_device.cc
+++ b/tools/rootcanal/model/devices/hci_device.cc
@@ -14,9 +14,17 @@
  * limitations under the License.
  */
 
-#include "hci_device.h"
+#include "model/devices/hci_device.h"
+
+#include <cstdint>
+#include <memory>
+#include <vector>
 
 #include "log.h"
+#include "model/controller/controller_properties.h"
+#include "model/controller/dual_mode_controller.h"
+#include "model/hci/hci_transport.h"
+#include "packets/link_layer_packets.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/hci_device.h b/tools/rootcanal/model/devices/hci_device.h
index 65dacd0..38be7fb 100644
--- a/tools/rootcanal/model/devices/hci_device.h
+++ b/tools/rootcanal/model/devices/hci_device.h
@@ -16,11 +16,12 @@
 
 #pragma once
 
-#include <memory>  // for shared_ptr, make_...
-#include <string>  // for string
+#include <memory>
+#include <string>
 
-#include "model/controller/dual_mode_controller.h"  // for DualModeController
-#include "model/hci/hci_transport.h"                // for HciTransport
+#include "model/controller/controller_properties.h"
+#include "model/controller/dual_mode_controller.h"
+#include "model/hci/hci_transport.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/link_layer_socket_device.cc b/tools/rootcanal/model/devices/link_layer_socket_device.cc
index 92de96e..e61badf 100644
--- a/tools/rootcanal/model/devices/link_layer_socket_device.cc
+++ b/tools/rootcanal/model/devices/link_layer_socket_device.cc
@@ -14,14 +14,21 @@
  * limitations under the License.
  */
 
-#include "link_layer_socket_device.h"
+#include "model/devices/link_layer_socket_device.h"
 
 #include <packet_runtime.h>
 
-#include <type_traits>  // for remove_extent_t
+#include <cerrno>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <utility>
+#include <vector>
 
 #include "log.h"
-#include "phy.h"  // for Phy, Phy::Type
+#include "model/devices/device.h"
+#include "packets/link_layer_packets.h"
+#include "phy.h"
 
 using std::vector;
 
diff --git a/tools/rootcanal/model/devices/link_layer_socket_device.h b/tools/rootcanal/model/devices/link_layer_socket_device.h
index b0adb15..b91263a 100644
--- a/tools/rootcanal/model/devices/link_layer_socket_device.h
+++ b/tools/rootcanal/model/devices/link_layer_socket_device.h
@@ -16,17 +16,16 @@
 
 #pragma once
 
-#include <stddef.h>  // for size_t
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
 
-#include <cstdint>  // for uint8_t, uint32_t
-#include <memory>   // for shared_ptr, make_shared
-#include <string>   // for string
-#include <vector>   // for vector
-
-#include "device.h"                      // for Device
-#include "include/phy.h"                 // for Phy, Phy::Type
-#include "net/async_data_channel.h"      // for AsyncDataChannel
-#include "packets/link_layer_packets.h"  // for LinkLayerPacketView
+#include "model/devices/device.h"
+#include "net/async_data_channel.h"
+#include "packets/link_layer_packets.h"
+#include "phy.h"
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/scripted_beacon.cc b/tools/rootcanal/model/devices/scripted_beacon.cc
index 0175ab3..7215066 100644
--- a/tools/rootcanal/model/devices/scripted_beacon.cc
+++ b/tools/rootcanal/model/devices/scripted_beacon.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "scripted_beacon.h"
+#include "model/devices/scripted_beacon.h"
 
 #include <unistd.h>
 
diff --git a/tools/rootcanal/model/devices/scripted_beacon.h b/tools/rootcanal/model/devices/scripted_beacon.h
index 8bb7b32..4d62ac6 100644
--- a/tools/rootcanal/model/devices/scripted_beacon.h
+++ b/tools/rootcanal/model/devices/scripted_beacon.h
@@ -20,7 +20,7 @@
 #include <fstream>
 #include <vector>
 
-#include "beacon.h"
+#include "model/devices/beacon.h"
 #include "model/devices/scripted_beacon_ble_payload.pb.h"
 
 using android::bluetooth::rootcanal::model::devices::ScriptedBeaconBleAdProto::
diff --git a/tools/rootcanal/model/devices/sniffer.cc b/tools/rootcanal/model/devices/sniffer.cc
index 2a4dffbc4..793b078 100644
--- a/tools/rootcanal/model/devices/sniffer.cc
+++ b/tools/rootcanal/model/devices/sniffer.cc
@@ -14,19 +14,24 @@
  * limitations under the License.
  */
 
-#include "sniffer.h"
+#include "model/devices/sniffer.h"
 
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "hci/address.h"
 #include "log.h"
 #include "model/setup/device_boutique.h"
-
-using std::vector;
+#include "packets/link_layer_packets.h"
+#include "phy.h"
 
 namespace rootcanal {
 
 bool Sniffer::registered_ =
     DeviceBoutique::Register("sniffer", &Sniffer::Create);
 
-Sniffer::Sniffer(const vector<std::string>& args) {
+Sniffer::Sniffer(const std::vector<std::string>& args) {
   if (args.size() >= 2) {
     Address::FromString(args[1], address_);
   }
diff --git a/tools/rootcanal/model/devices/sniffer.h b/tools/rootcanal/model/devices/sniffer.h
index 5ee49ff..60bee3e 100644
--- a/tools/rootcanal/model/devices/sniffer.h
+++ b/tools/rootcanal/model/devices/sniffer.h
@@ -25,8 +25,6 @@
 
 namespace rootcanal {
 
-using ::bluetooth::hci::Address;
-
 class Sniffer : public Device {
  public:
   Sniffer(const std::vector<std::string>& args);
diff --git a/tools/rootcanal/packets/hci_packets.pdl b/tools/rootcanal/packets/hci_packets.pdl
index a997744..063fc54 100644
--- a/tools/rootcanal/packets/hci_packets.pdl
+++ b/tools/rootcanal/packets/hci_packets.pdl
@@ -371,14 +371,11 @@
   // MSFT_OPCODE_xxxx below is needed for the tests.
   MSFT_OPCODE_INTEL = 0xFC1E,
   LE_GET_VENDOR_CAPABILITIES = 0xFD53,
-  LE_MULTI_ADVT = 0xFD54,
   LE_BATCH_SCAN = 0xFD56,
-  LE_ADV_FILTER = 0xFD57,
-  LE_ENERGY_INFO = 0xFD59,
-  LE_EXTENDED_SCAN_PARAMS = 0xFD5A,
-  CONTROLLER_DEBUG_INFO = 0xFD5B,
-  CONTROLLER_A2DP_OPCODE = 0xFD5D,
-  CONTROLLER_BQR = 0xFD5E,
+  LE_APCF = 0xFD57,
+  LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO = 0xFD59,
+  LE_EX_SET_SCAN_PARAMETERS = 0xFD5A,
+  GET_CONTROLLER_DEBUG_INFO = 0xFD5B,
   // MSFT_OPCODE_xxxx below are needed for the tests.
   MSFT_OPCODE_MEDIATEK = 0xFD30,
   MSFT_OPCODE_QUALCOMM = 0xFD70,
@@ -697,10 +694,6 @@
   _payload_,
 }
 
-// Packets for interfaces
-
-packet VendorCommand : Command { _payload_, }
-
 // HCI Event Packets
 
 enum EventCode : 8 {
@@ -759,14 +752,7 @@
   VENDOR_SPECIFIC = 0xFF,
 }
 
-packet Event {
-  event_code : EventCode,
-  _size_(_payload_) : 8,
-  _payload_,
-}
-
-// LE Events
-
+// LE events
 enum SubeventCode : 8 {
   CONNECTION_COMPLETE = 0x01,
   ADVERTISING_REPORT = 0x02,
@@ -807,11 +793,17 @@
 
 // Vendor specific events
 enum VseSubeventCode : 8 {
-  BLE_THRESHOLD = 0x54,
-  BLE_STCHANGE = 0x55,
-  BLE_TRACKING = 0x56,
-  DEBUG_INFO = 0x57,
-  BQR_EVENT = 0x58,
+  STORAGE_THRESHOLD_BREACH = 0x54,
+  LE_MULTI_ADVERTISING_STATE_CHANGE = 0x55,
+  LE_ADVERTISEMENT_TRACKING = 0x56,
+  CONTROLLER_DEBUG_INFO = 0x57,
+  BLUETOOTH_QUALITY_REPORT = 0x58,
+}
+
+packet Event {
+  event_code : EventCode,
+  _size_(_payload_) : 8,
+  _payload_,
 }
 
 // Common definitions for commands and events
@@ -886,6 +878,11 @@
   _payload_,
 }
 
+packet VendorSpecificEvent : Event (event_code = VENDOR_SPECIFIC) {
+  subevent_code : VseSubeventCode,
+  _payload_,
+}
+
   // Credits
 packet NoCommandComplete : CommandComplete (command_op_code = NONE) {
 }
@@ -4690,494 +4687,6 @@
 packet LeSubrateRequestStatus : CommandStatus (command_op_code = LE_SUBRATE_REQUEST) {
 }
 
-  // VENDOR_SPECIFIC
-packet LeGetVendorCapabilities : VendorCommand (op_code = LE_GET_VENDOR_CAPABILITIES) {
-}
-
-test LeGetVendorCapabilities {
-  "\x53\xfd\x00",
-}
-
-struct BaseVendorCapabilities {
-  max_advt_instances: 8,
-  offloaded_resolution_of_private_address : 8,
-  total_scan_results_storage: 16,
-  max_irk_list_sz: 8,
-  filtering_support: 8,
-  max_filter: 8,
-  activity_energy_info_support: 8,
-}
-
-packet LeGetVendorCapabilitiesComplete : CommandComplete (command_op_code = LE_GET_VENDOR_CAPABILITIES) {
-  status : ErrorCode,
-  base_vendor_capabilities : BaseVendorCapabilities,
-  _payload_,
-}
-
-packet LeGetVendorCapabilitiesComplete095 : LeGetVendorCapabilitiesComplete {
-  version_supported: 16,
-  total_num_of_advt_tracked: 16,
-  extended_scan_support: 8,
-  debug_logging_supported: 8,
-  _payload_,
-}
-
-packet LeGetVendorCapabilitiesComplete096 : LeGetVendorCapabilitiesComplete095 {
-  le_address_generation_offloading_support: 8,
-  _payload_,
-}
-
-packet LeGetVendorCapabilitiesComplete098 : LeGetVendorCapabilitiesComplete096 {
-  a2dp_source_offload_capability_mask: 32,
-  bluetooth_quality_report_support: 8
-}
-
-enum SubOcf : 8 {
-  SET_PARAM = 0x01,
-  SET_DATA = 0x02,
-  SET_SCAN_RESP = 0x03,
-  SET_RANDOM_ADDR = 0x04,
-  SET_ENABLE = 0x05,
-}
-
-packet LeMultiAdvt : Command (op_code = LE_MULTI_ADVT) {
-  sub_cmd : SubOcf,
-  _body_,
-}
-
-packet LeMultiAdvtComplete : CommandComplete (command_op_code = LE_MULTI_ADVT) {
-  status : ErrorCode,
-  sub_cmd : SubOcf,
-}
-
-packet LeMultiAdvtParam : LeMultiAdvt (sub_cmd = SET_PARAM) {
-  interval_min : 16,
-  interval_max : 16,
-  advertising_type : AdvertisingType,
-  own_address_type : OwnAddressType,
-  own_address : Address,
-  peer_address_type : PeerAddressType,
-  peer_address : Address,
-  channel_map : 8,
-  filter_policy : AdvertisingFilterPolicy,
-  _reserved_ : 6,
-  instance : 8,
-  tx_power : 8,
-}
-
-packet LeMultiAdvtParamComplete : LeMultiAdvtComplete (sub_cmd = SET_PARAM) {
-}
-
-packet LeMultiAdvtSetData : LeMultiAdvt (sub_cmd = SET_DATA) {
-  _size_(advertising_data) : 8,
-  advertising_data : 8[],
-  _padding_[31], // Zero padding to 31 bytes of advertising_data
-  advertising_instance : 8,
-}
-
-packet LeMultiAdvtSetDataComplete : LeMultiAdvtComplete (sub_cmd = SET_DATA) {
-}
-
-packet LeMultiAdvtSetScanResp : LeMultiAdvt (sub_cmd = SET_SCAN_RESP) {
-  _size_(advertising_data) : 8,
-  advertising_data : 8[],
-  _padding_[31], // Zero padding to 31 bytes of advertising_data
-  advertising_instance : 8,
-}
-
-packet LeMultiAdvtSetScanRespComplete : LeMultiAdvtComplete (sub_cmd = SET_SCAN_RESP) {
-}
-
-packet LeMultiAdvtSetRandomAddr : LeMultiAdvt (sub_cmd = SET_RANDOM_ADDR) {
-  random_address : Address,
-  advertising_instance : 8,
-}
-
-packet LeMultiAdvtSetRandomAddrComplete : LeMultiAdvtComplete (sub_cmd = SET_RANDOM_ADDR) {
-}
-
-packet LeMultiAdvtSetEnable : LeMultiAdvt (sub_cmd = SET_ENABLE) {
-  advertising_enable : Enable, // Default DISABLED
-  advertising_instance : 8,
-}
-
-packet LeMultiAdvtSetEnableComplete : LeMultiAdvtComplete (sub_cmd = SET_ENABLE) {
-}
-
-enum BatchScanOpcode : 8 {
-  ENABLE = 0x01,
-  SET_STORAGE_PARAMETERS = 0x02,
-  SET_SCAN_PARAMETERS = 0x03,
-  READ_RESULT_PARAMETERS = 0x04,
-}
-
-// https://source.android.com/devices/bluetooth/hci_requirements#batching-of-scan-results
-packet LeBatchScan : Command (op_code = LE_BATCH_SCAN) {
-  batch_scan_opcode : BatchScanOpcode,
-  _body_,
-}
-
-packet LeBatchScanComplete : CommandComplete (command_op_code = LE_BATCH_SCAN) {
-  status : ErrorCode,
-  batch_scan_opcode : BatchScanOpcode,
-  _body_,
-}
-
-packet LeBatchScanEnable : LeBatchScan (batch_scan_opcode = ENABLE) {
-  enable : Enable,
-}
-
-packet LeBatchScanEnableComplete : LeBatchScanComplete (batch_scan_opcode = ENABLE) {
-}
-
-packet LeBatchScanSetStorageParameters : LeBatchScan (batch_scan_opcode = SET_STORAGE_PARAMETERS) {
-  batch_scan_full_max_percentage : 8,
-  batch_scan_truncated_max_percentage : 8,
-  batch_scan_notify_threshold_percentage : 8,
-}
-
-packet LeBatchScanSetStorageParametersComplete : LeBatchScanComplete (batch_scan_opcode = SET_STORAGE_PARAMETERS) {
-}
-
-enum BatchScanDiscardRule : 8 {
-  OLDEST = 0x00,
-  WEAKEST_RSSI = 0x01,
-}
-
-packet LeBatchScanSetScanParameters : LeBatchScan (batch_scan_opcode = SET_SCAN_PARAMETERS) {
-  truncated_mode_enabled : 1,
-  full_mode_enabled : 1,
-  _reserved_ : 6,
-  duty_cycle_scan_window_slots : 32,
-  duty_cycle_scan_interval_slots : 32,
-  own_address_type : PeerAddressType,
-  batch_scan_discard_rule : BatchScanDiscardRule,
-}
-
-packet LeBatchScanSetScanParametersComplete : LeBatchScanComplete (batch_scan_opcode = SET_SCAN_PARAMETERS) {
-}
-
-enum BatchScanDataRead : 8 {
-  TRUNCATED_MODE_DATA = 0x01,
-  FULL_MODE_DATA = 0x02,
-}
-
-packet LeBatchScanReadResultParameters : LeBatchScan (batch_scan_opcode = READ_RESULT_PARAMETERS) {
-  batch_scan_data_read : BatchScanDataRead,
-}
-
-packet LeBatchScanReadResultParametersCompleteRaw : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) {
-  batch_scan_data_read : BatchScanDataRead,
-  num_of_records : 8,
-  raw_data : 8[],
-}
-
-packet LeBatchScanReadResultParametersComplete : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) {
-  batch_scan_data_read : BatchScanDataRead,
-  _body_,
-}
-
-struct TruncatedResult {
-  bd_addr : Address,
-  address_type : AddressType,
-  tx_power : 8,
-  rssi : 8,
-  timestamp : 16,
-}
-
-packet LeBatchScanReadTruncatedResultParametersComplete : LeBatchScanReadResultParametersComplete (batch_scan_data_read = TRUNCATED_MODE_DATA) {
-  _count_(results) : 8,
-  results : TruncatedResult[],
-}
-
-struct FullResult {
-  bd_addr : Address,
-  address_type : AddressType,
-  tx_power : 8,
-  rssi : 8,
-  timestamp : 16,
-  _size_(adv_packet) : 8,
-  adv_packet : 8[],
-  _size_(scan_response) : 8,
-  scan_response : 8[],
-}
-
-packet LeBatchScanReadFullResultParametersComplete : LeBatchScanReadResultParametersComplete (batch_scan_data_read = FULL_MODE_DATA) {
-  _count_(results) : 8,
-  results : FullResult[],
-}
-
-enum ApcfOpcode : 8 {
-  ENABLE = 0x00,
-  SET_FILTERING_PARAMETERS = 0x01,
-  BROADCASTER_ADDRESS = 0x02,
-  SERVICE_UUID = 0x03,
-  SERVICE_SOLICITATION_UUID = 0x04,
-  LOCAL_NAME = 0x05,
-  MANUFACTURER_DATA = 0x06,
-  SERVICE_DATA = 0x07,
-  TRANSPORT_DISCOVERY_DATA = 0x08,
-  AD_TYPE = 0x09,
-  READ_EXTENDED_FEATURES = 0xFF,
-}
-
-// https://source.android.com/devices/bluetooth/hci_requirements#advertising-packet-content-filter
-packet LeAdvFilter : Command (op_code = LE_ADV_FILTER) {
-  apcf_opcode : ApcfOpcode,
-  _body_,
-}
-
-packet LeAdvFilterComplete : CommandComplete (command_op_code = LE_ADV_FILTER) {
-  status : ErrorCode,
-  apcf_opcode : ApcfOpcode,
-  _body_,
-}
-
-packet LeAdvFilterEnable : LeAdvFilter (apcf_opcode = ENABLE) {
-  apcf_enable : Enable,
-}
-
-packet LeAdvFilterEnableComplete : LeAdvFilterComplete (apcf_opcode = ENABLE) {
-  apcf_enable : Enable,
-}
-
-enum ApcfAction : 8 {
-  ADD = 0x00,
-  DELETE = 0x01,
-  CLEAR = 0x02,
-}
-
-enum DeliveryMode : 8 {
-  IMMEDIATE = 0x00,
-  ONFOUND = 0x01,
-  BATCHED = 0x02,
-}
-
-// Bit masks for the selected features
-enum ApcfFilterType : 8 {
-  BROADCASTER_ADDRESS = 0x00,
-  SERVICE_DATA_CHANGE = 0x01,
-  SERVICE_UUID = 0x02,
-  SERVICE_SOLICITATION_UUID = 0x03,
-  LOCAL_NAME = 0x04,
-  MANUFACTURER_DATA = 0x05,
-  SERVICE_DATA = 0x06,
-  TRANSPORT_DISCOVERY_DATA = 0x07,
-  AD_TYPE = 0x08,
-}
-
-packet LeAdvFilterSetFilteringParameters : LeAdvFilter (apcf_opcode = SET_FILTERING_PARAMETERS) {
-  apcf_action : ApcfAction,
-  _body_,
-}
-
-packet LeAdvFilterAddFilteringParameters : LeAdvFilterSetFilteringParameters (apcf_action = ADD) {
-  apcf_filter_index : 8,
-  apcf_feature_selection : 16,
-  apcf_list_logic_type : 16,
-  apcf_filter_logic_type : 8,
-  rssi_high_thresh : 8,
-  delivery_mode : DeliveryMode,
-  onfound_timeout : 16,
-  onfound_timeout_cnt : 8,
-  rssi_low_thresh : 8,
-  onlost_timeout : 16,
-  num_of_tracking_entries : 16,
-}
-
-packet LeAdvFilterDeleteFilteringParameters : LeAdvFilterSetFilteringParameters (apcf_action = DELETE) {
-  apcf_filter_index : 8,
-}
-
-packet LeAdvFilterClearFilteringParameters : LeAdvFilterSetFilteringParameters (apcf_action = CLEAR) {
-}
-
-packet LeAdvFilterSetFilteringParametersComplete : LeAdvFilterComplete (apcf_opcode = SET_FILTERING_PARAMETERS) {
-  apcf_action : ApcfAction,
-  apcf_available_spaces : 8,
-}
-
-enum ApcfApplicationAddressType : 8 {
-  PUBLIC = 0x00,
-  RANDOM = 0x01,
-  NOT_APPLICABLE = 0x02,
-}
-
-packet LeAdvFilterBroadcasterAddress : LeAdvFilter (apcf_opcode = BROADCASTER_ADDRESS) {
-  apcf_action : ApcfAction,
-  apcf_filter_index : 8,
-  apcf_broadcaster_address : Address,
-  apcf_application_address_type : ApcfApplicationAddressType,
-}
-
-packet LeAdvFilterClearBroadcasterAddress : LeAdvFilter (apcf_opcode = BROADCASTER_ADDRESS) {
-  _fixed_ = 0x02 : 8,
-  apcf_filter_index : 8,
-}
-
-packet LeAdvFilterBroadcasterAddressComplete : LeAdvFilterComplete (apcf_opcode = BROADCASTER_ADDRESS) {
-  apcf_action : ApcfAction,
-  apcf_available_spaces : 8,
-}
-
-
-packet LeAdvFilterServiceUuid : LeAdvFilter (apcf_opcode = SERVICE_UUID) {
-  apcf_action : ApcfAction,
-  apcf_filter_index : 8,
-  acpf_uuid_data : 8[],
-}
-
-packet LeAdvFilterServiceUuidComplete : LeAdvFilterComplete (apcf_opcode = SERVICE_UUID) {
-  apcf_action : ApcfAction,
-  apcf_available_spaces : 8,
-}
-
-packet LeAdvFilterSolicitationUuid : LeAdvFilter (apcf_opcode = SERVICE_SOLICITATION_UUID) {
-  apcf_action : ApcfAction,
-  apcf_filter_index : 8,
-  acpf_uuid_data : 8[],
-}
-
-packet LeAdvFilterSolicitationUuidComplete : LeAdvFilterComplete (apcf_opcode = SERVICE_SOLICITATION_UUID) {
-  apcf_action : ApcfAction,
-  apcf_available_spaces : 8,
-}
-
-packet LeAdvFilterLocalName : LeAdvFilter (apcf_opcode = LOCAL_NAME) {
-  apcf_action : ApcfAction,
-  apcf_filter_index : 8,
-  apcf_local_name : 8[],
-}
-
-packet LeAdvFilterLocalNameComplete : LeAdvFilterComplete (apcf_opcode = LOCAL_NAME) {
-  apcf_action : ApcfAction,
-  apcf_available_spaces : 8,
-}
-
-packet LeAdvFilterManufacturerData : LeAdvFilter (apcf_opcode = MANUFACTURER_DATA) {
-  apcf_action : ApcfAction,
-  apcf_filter_index : 8,
-  apcf_manufacturer_data : 8[],
-}
-
-packet LeAdvFilterManufacturerDataComplete : LeAdvFilterComplete (apcf_opcode = MANUFACTURER_DATA) {
-  apcf_action : ApcfAction,
-  apcf_available_spaces : 8,
-}
-
-packet LeAdvFilterServiceData : LeAdvFilter (apcf_opcode = SERVICE_DATA) {
-  apcf_action : ApcfAction,
-  apcf_filter_index : 8,
-  apcf_service_data : 8[],
-}
-
-packet LeAdvFilterServiceDataComplete : LeAdvFilterComplete (apcf_opcode = SERVICE_DATA) {
-  apcf_action : ApcfAction,
-  apcf_available_spaces : 8,
-}
-
-packet LeAdvFilterTransportDiscoveryData : LeAdvFilter (apcf_opcode = TRANSPORT_DISCOVERY_DATA) {
-  apcf_action : ApcfAction,
-  apcf_filter_index : 8,
-  apcf_transport_discovery_data : 8[],
-}
-
-packet LeAdvFilterTransportDiscoveryDataComplete : LeAdvFilterComplete (apcf_opcode = TRANSPORT_DISCOVERY_DATA) {
-  apcf_action : ApcfAction,
-  apcf_available_spaces : 8,
-}
-
-packet LeAdvFilterADType : LeAdvFilter (apcf_opcode = AD_TYPE) {
-  apcf_action : ApcfAction,
-  apcf_filter_index : 8,
-  apcf_ad_type_data : 8[],
-}
-
-packet LeAdvFilterADTypeComplete : LeAdvFilterComplete (apcf_opcode = AD_TYPE) {
-  apcf_action : ApcfAction,
-  apcf_available_spaces : 8,
-}
-
-packet LeAdvFilterReadExtendedFeatures : LeAdvFilter (apcf_opcode = READ_EXTENDED_FEATURES) {
-}
-
-test LeAdvFilterReadExtendedFeatures {
-  "\x57\xfd\x01\xff",
-}
-
-packet LeAdvFilterReadExtendedFeaturesComplete : LeAdvFilterComplete (apcf_opcode = READ_EXTENDED_FEATURES) {
-  transport_discovery_data_filter : 1,
-  ad_type_filter : 1,
-  _reserved_ : 14,
-}
-
-test LeAdvFilterReadExtendedFeaturesComplete {
-  "\x0e\x07\x01\x57\xfd\x00\xff\x03\x00",
-}
-
-packet LeEnergyInfo : VendorCommand (op_code = LE_ENERGY_INFO) {
-}
-
-packet LeEnergyInfoComplete : CommandComplete (command_op_code = LE_ENERGY_INFO) {
-  status : ErrorCode,
-  total_tx_time_ms : 32,
-  total_rx_time_ms : 32,
-  total_idle_time_ms : 32,
-  total_energy_used_ma_v_ms : 32,
-}
-
-packet LeExtendedScanParams : Command (op_code = LE_EXTENDED_SCAN_PARAMS) {
-  le_scan_type : LeScanType,
-  le_scan_interval : 32, // 0x0004-0x4000 Default 0x10 (10ms)
-  le_scan_window : 32, // Default 0x10 (10ms)
-  own_address_type : OwnAddressType,
-  scanning_filter_policy : LeScanningFilterPolicy,
-}
-
-packet LeExtendedScanParamsComplete : CommandComplete (command_op_code = LE_EXTENDED_SCAN_PARAMS) {
-  status : ErrorCode,
-}
-
-packet ControllerDebugInfo : VendorCommand (op_code = CONTROLLER_DEBUG_INFO) {
-}
-
-packet ControllerDebugInfoComplete : CommandComplete (command_op_code = CONTROLLER_DEBUG_INFO) {
-  status : ErrorCode,
-}
-
-packet ControllerA2DPOpcode : VendorCommand (op_code = CONTROLLER_A2DP_OPCODE) {
-  _payload_,  // placeholder (unimplemented)
-}
-
-packet ControllerA2DPOpcodeComplete : CommandComplete (command_op_code = CONTROLLER_A2DP_OPCODE) {
-  _payload_,  // placeholder (unimplemented)
-}
-
-enum BqrReportAction : 8 {
-  ADD = 0x00,
-  DELETE = 0x01,
-  CLEAR = 0x02,
-}
-
-packet ControllerBqr : VendorCommand(op_code = CONTROLLER_BQR) {
-  bqr_report_action : BqrReportAction,
-  bqr_quality_event_mask : 32,
-  bqr_minimum_report_interval : 16,
-}
-
-test ControllerBqr {
-  "\x5e\xfd\x07\x00\x1f\x00\x07\x00\x88\x13",
-}
-
-packet ControllerBqrComplete : CommandComplete (command_op_code = CONTROLLER_BQR) {
-  status : ErrorCode,
-  current_quality_event_mask : 32
-}
-
-test ControllerBqrComplete {
-  "\x0e\x08\x01\x5e\xfd\x00\x1f\x00\x07\x00",
-}
-
 // HCI Event Packets
 
 packet InquiryComplete : Event (event_code = INQUIRY_COMPLETE) {
@@ -6018,163 +5527,6 @@
   _reserved_ : 4,
 }
 
-// Vendor specific events
-
-packet VendorSpecificEvent : Event (event_code = VENDOR_SPECIFIC) {
-  subevent_code : VseSubeventCode,
-  _payload_,
-}
-
-packet StorageThresholdBreachEvent : VendorSpecificEvent (subevent_code = BLE_THRESHOLD) {
-}
-
-enum AdvtInfoPresent : 8 {
-  ADVT_INFO_PRESENT = 0x00,
-  NO_ADVT_INFO_PRESENT = 0x01,
-}
-
-packet LEAdvertisementTrackingEvent : VendorSpecificEvent (subevent_code = BLE_TRACKING) {
-  apcf_filter_index : 8,
-  advertiser_state : 8,
-  advt_info_present : AdvtInfoPresent,
-  advertiser_address : Address,
-  advertiser_address_type : 8,
-  _body_,
-}
-
-enum VseStateChangeReason : 8 {
-  CONNECTION_RECEIVED = 0x00,
-}
-
-packet LEAdvertiseStateChangeEvent : VendorSpecificEvent (subevent_code = BLE_STCHANGE) {
-  advertising_instance : 8,
-  state_change_reason : VseStateChangeReason,
-  connection_handle : 12,
-  _reserved_ : 4,
-}
-
-packet LEAdvertisementTrackingWithInfoEvent : LEAdvertisementTrackingEvent {
-  tx_power : 8,
-  rssi : 8,
-  timestamp : 16,
-  _size_(adv_packet) : 8,
-  adv_packet : 8[],
-  _size_(scan_response) : 8,
-  scan_response : 8[],
-}
-
-enum QualityReportId : 8 {
-  MONITOR_MODE = 0x01,
-  APPROACH_LSTO = 0x02,
-  A2DP_AUDIO_CHOPPY = 0x03,
-  SCO_VOICE_CHOPPY = 0x04,
-  ROOT_INFLAMMATION = 0x05,
-  LMP_LL_MESSAGE_TRACE = 0x11,
-  BT_SCHEDULING_TRACE = 0x12,
-  CONTROLLER_DBG_INFO = 0x13,
-}
-
-packet BqrEvent : VendorSpecificEvent (subevent_code = BQR_EVENT) {
-  quality_report_id : QualityReportId,
-  _payload_,
-}
-
-enum BqrPacketType : 8 {
-  TYPE_ID = 0x01,
-  TYPE_NULL = 0x02,
-  TYPE_POLL = 0x03,
-  TYPE_FHS = 0x04,
-  TYPE_HV1 = 0x05,
-  TYPE_HV2 = 0x06,
-  TYPE_HV3 = 0x07,
-  TYPE_DV = 0x08,
-  TYPE_EV3 = 0x09,
-  TYPE_EV4 = 0x0A,
-  TYPE_EV5 = 0x0B,
-  TYPE_2EV3 = 0x0C,
-  TYPE_2EV5 = 0x0D,
-  TYPE_3EV3 = 0x0E,
-  TYPE_3EV5 = 0x0F,
-  TYPE_DM1 = 0x10,
-  TYPE_DH1 = 0x11,
-  TYPE_DM3 = 0x12,
-  TYPE_DH3 = 0x13,
-  TYPE_DM5 = 0x14,
-  TYPE_DH5 = 0x15,
-  TYPE_AUX1 = 0x16,
-  TYPE_2DH1 = 0x17,
-  TYPE_2DH3 = 0x18,
-  TYPE_2DH5 = 0x19,
-  TYPE_3DH1 = 0x1A,
-  TYPE_3DH3 = 0x1B,
-  TYPE_3DH5 = 0x1C,
-}
-
-packet BqrLinkQualityEvent : BqrEvent {
-  packet_type : BqrPacketType,
-  connection_handle : 12,
-  _reserved_ : 4,
-  connection_role : Role,
-  tx_power_level : 8,
-  rssi : 8,
-  snr : 8,
-  unused_afh_channel_count : 8,
-  afh_select_unideal_channel_count : 8,
-  lsto : 16,
-  connection_piconet_clock : 32,
-  retransmission_count : 32,
-  no_rx_count : 32,
-  nak_count : 32,
-  last_tx_ack_timestamp : 32,
-  flow_off_count : 32,
-  last_flow_on_timestamp : 32,
-  buffer_overflow_bytes : 32,
-  buffer_underflow_bytes : 32,
-  _payload_,
-}
-
-packet BqrMonitorModeEvent : BqrLinkQualityEvent (quality_report_id = MONITOR_MODE) {
- _payload_, // vendor specific parameter
-}
-
-packet BqrApproachLstoEvent : BqrLinkQualityEvent (quality_report_id = APPROACH_LSTO) {
- _payload_, // vendor specific parameter
-}
-
-packet BqrA2dpAudioChoppyEvent : BqrLinkQualityEvent (quality_report_id = A2DP_AUDIO_CHOPPY) {
-  _payload_, // vendor specific parameter
-}
-
-packet BqrScoVoiceChoppyEvent : BqrLinkQualityEvent (quality_report_id = SCO_VOICE_CHOPPY) {
- _payload_, // vendor specific parameter
-}
-
-packet BqrRootInflammationEvent : BqrEvent (quality_report_id = ROOT_INFLAMMATION) {
-  error_code : 8,
-  vendor_specific_error_code : 8,
-  _payload_, // vendor specific parameter
-}
-
-packet BqrLogDumpEvent : BqrEvent {
-  connection_handle : 12,
-  _reserved_ : 4,
-  _payload_,
-}
-
-packet BqrLmpLlMessageTraceEvent : BqrLogDumpEvent (quality_report_id = LMP_LL_MESSAGE_TRACE) {
-  _payload_, // vendor specific parameter
-}
-
-packet BqrBtSchedulingTraceEvent : BqrLogDumpEvent (quality_report_id = BT_SCHEDULING_TRACE) {
- _payload_, // vendor specific parameter
-}
-
-packet BqrControllerDbgInfoEvent : BqrLogDumpEvent (quality_report_id = CONTROLLER_DBG_INFO) {
- _payload_, // vendor specific parameter
-}
-
-// Isochronous Adaptation Layer
-
 enum IsoPacketBoundaryFlag : 2 {
   FIRST_FRAGMENT = 0,
   CONTINUATION_FRAGMENT = 1,
@@ -6220,8 +5572,492 @@
   _payload_,
 }
 
-// MSFT packets
-// Reference: https://learn.microsoft.com/en-us/windows-hardware/drivers/bluetooth/microsoft-defined-bluetooth-hci-commands-and-events
+// https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
+enum GapDataType : 8 {
+  INVALID = 0x00,
+  FLAGS = 0x01,
+  INCOMPLETE_LIST_16_BIT_UUIDS = 0x02,
+  COMPLETE_LIST_16_BIT_UUIDS = 0x03,
+  INCOMPLETE_LIST_32_BIT_UUIDS = 0x04,
+  COMPLETE_LIST_32_BIT_UUIDS = 0x05,
+  INCOMPLETE_LIST_128_BIT_UUIDS = 0x06,
+  COMPLETE_LIST_128_BIT_UUIDS = 0x07,
+  SHORTENED_LOCAL_NAME = 0x08,
+  COMPLETE_LOCAL_NAME = 0x09,
+  TX_POWER_LEVEL = 0x0A,
+  CLASS_OF_DEVICE = 0x0D,
+  SIMPLE_PAIRING_HASH_C = 0x0E,
+  SIMPLE_PAIRING_RANDOMIZER_R = 0x0F,
+  DEVICE_ID = 0x10,
+  SECURITY_MANAGER_OOB_FLAGS = 0x11,
+  SLAVE_CONNECTION_INTERVAL_RANGE = 0x12,
+  LIST_16BIT_SERVICE_SOLICITATION_UUIDS = 0x14,
+  LIST_128BIT_SERVICE_SOLICITATION_UUIDS = 0x15,
+  SERVICE_DATA_16_BIT_UUIDS = 0x16,
+  PUBLIC_TARGET_ADDRESS = 0x17,
+  RANDOM_TARGET_ADDRESS = 0x18,
+  APPEARANCE = 0x19,
+  ADVERTISING_INTERVAL = 0x1A,
+  LE_BLUETOOTH_DEVICE_ADDRESS = 0x1B,
+  LE_ROLE = 0x1C,
+  SIMPLE_PAIRING_HASH_C_256 = 0x1D,
+  SIMPLE_PAIRING_RANDOMIZER_R_256 = 0x1E,
+  LIST_32BIT_SERVICE_SOLICITATION_UUIDS = 0x1F,
+  SERVICE_DATA_32_BIT_UUIDS = 0x20,
+  SERVICE_DATA_128_BIT_UUIDS = 0x21,
+  LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE = 0x22,
+  LE_SECURE_CONNECTIONS_RANDOM_VALUE = 0x23,
+  URI = 0x24,
+  INDOOR_POSITIONING = 0x25,
+  TRANSPORT_DISCOVERY_DATA = 0x26,
+  LE_SUPPORTED_FEATURES = 0x27,
+  CHANNEL_MAP_UPDATE_INDICATION = 0x28,
+  MESH_PB_ADV = 0x29,
+  MESH_MESSAGE = 0x2A,
+  MESH_BEACON = 0x2B,
+  BIG_INFO = 0x2C,
+  BROADCAST_CODE = 0x2D,
+  THREE_D_INFORMATION_DATA = 0x3D,
+  MANUFACTURER_SPECIFIC_DATA = 0xFF,
+}
+
+// -----------------------------------------------------------------------------
+//  LE Get Vendor Capabilities Command
+//  https://source.android.com/docs/core/connect/bluetooth/hci_requirements#vendor-specific-capabilities
+// -----------------------------------------------------------------------------
+
+packet LeGetVendorCapabilities : Command (op_code = LE_GET_VENDOR_CAPABILITIES) {
+}
+
+test LeGetVendorCapabilities {
+  "\x53\xfd\x00",
+}
+
+struct BaseVendorCapabilities {
+  max_advt_instances: 8,
+  offloaded_resolution_of_private_address : 8,
+  total_scan_results_storage: 16,
+  max_irk_list_sz: 8,
+  filtering_support: 8,
+  max_filter: 8,
+  activity_energy_info_support: 8,
+}
+
+packet LeGetVendorCapabilitiesComplete : CommandComplete (command_op_code = LE_GET_VENDOR_CAPABILITIES) {
+  status : ErrorCode,
+  base_vendor_capabilities : BaseVendorCapabilities,
+  _payload_,
+}
+
+packet LeGetVendorCapabilitiesComplete095 : LeGetVendorCapabilitiesComplete {
+  version_supported: 16,
+  total_num_of_advt_tracked: 16,
+  extended_scan_support: 8,
+  debug_logging_supported: 8,
+  _payload_,
+}
+
+packet LeGetVendorCapabilitiesComplete096 : LeGetVendorCapabilitiesComplete095 {
+  le_address_generation_offloading_support: 8,
+  _payload_,
+}
+
+packet LeGetVendorCapabilitiesComplete098 : LeGetVendorCapabilitiesComplete096 {
+  a2dp_source_offload_capability_mask: 32,
+  bluetooth_quality_report_support: 8
+}
+
+// -----------------------------------------------------------------------------
+//  LE Batch Scan Command
+//  https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_batch_scan_command
+//  https://source.android.com/docs/core/connect/bluetooth/hci_requirements#storage-threshold-breach-subevent
+// -----------------------------------------------------------------------------
+
+enum BatchScanOpcode : 8 {
+  ENABLE = 0x01,
+  SET_STORAGE_PARAMETERS = 0x02,
+  SET_SCAN_PARAMETERS = 0x03,
+  READ_RESULT_PARAMETERS = 0x04,
+}
+
+packet LeBatchScan : Command (op_code = LE_BATCH_SCAN) {
+  batch_scan_opcode : BatchScanOpcode,
+  _body_,
+}
+
+packet LeBatchScanComplete : CommandComplete (command_op_code = LE_BATCH_SCAN) {
+  status : ErrorCode,
+  batch_scan_opcode : BatchScanOpcode,
+  _body_,
+}
+
+packet LeBatchScanEnable : LeBatchScan (batch_scan_opcode = ENABLE) {
+  enable : Enable,
+}
+
+packet LeBatchScanEnableComplete : LeBatchScanComplete (batch_scan_opcode = ENABLE) {
+}
+
+packet LeBatchScanSetStorageParameters : LeBatchScan (batch_scan_opcode = SET_STORAGE_PARAMETERS) {
+  batch_scan_full_max_percentage : 8,
+  batch_scan_truncated_max_percentage : 8,
+  batch_scan_notify_threshold_percentage : 8,
+}
+
+packet LeBatchScanSetStorageParametersComplete : LeBatchScanComplete (batch_scan_opcode = SET_STORAGE_PARAMETERS) {
+}
+
+enum BatchScanDiscardRule : 8 {
+  OLDEST = 0x00,
+  WEAKEST_RSSI = 0x01,
+}
+
+packet LeBatchScanSetScanParameters : LeBatchScan (batch_scan_opcode = SET_SCAN_PARAMETERS) {
+  truncated_mode_enabled : 1,
+  full_mode_enabled : 1,
+  _reserved_ : 6,
+  duty_cycle_scan_window_slots : 32,
+  duty_cycle_scan_interval_slots : 32,
+  own_address_type : PeerAddressType,
+  batch_scan_discard_rule : BatchScanDiscardRule,
+}
+
+packet LeBatchScanSetScanParametersComplete : LeBatchScanComplete (batch_scan_opcode = SET_SCAN_PARAMETERS) {
+}
+
+enum BatchScanDataRead : 8 {
+  TRUNCATED_MODE_DATA = 0x01,
+  FULL_MODE_DATA = 0x02,
+}
+
+packet LeBatchScanReadResultParameters : LeBatchScan (batch_scan_opcode = READ_RESULT_PARAMETERS) {
+  batch_scan_data_read : BatchScanDataRead,
+}
+
+packet LeBatchScanReadResultParametersCompleteRaw : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) {
+  batch_scan_data_read : BatchScanDataRead,
+  num_of_records : 8,
+  raw_data : 8[],
+}
+
+packet LeBatchScanReadResultParametersComplete : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) {
+  batch_scan_data_read : BatchScanDataRead,
+  _body_,
+}
+
+struct TruncatedResult {
+  bd_addr : Address,
+  address_type : AddressType,
+  tx_power : 8,
+  rssi : 8,
+  timestamp : 16,
+}
+
+packet LeBatchScanReadTruncatedResultParametersComplete : LeBatchScanReadResultParametersComplete (batch_scan_data_read = TRUNCATED_MODE_DATA) {
+  _count_(results) : 8,
+  results : TruncatedResult[],
+}
+
+struct FullResult {
+  bd_addr : Address,
+  address_type : AddressType,
+  tx_power : 8,
+  rssi : 8,
+  timestamp : 16,
+  _size_(adv_packet) : 8,
+  adv_packet : 8[],
+  _size_(scan_response) : 8,
+  scan_response : 8[],
+}
+
+packet LeBatchScanReadFullResultParametersComplete : LeBatchScanReadResultParametersComplete (batch_scan_data_read = FULL_MODE_DATA) {
+  _count_(results) : 8,
+  results : FullResult[],
+}
+
+packet StorageThresholdBreachEvent : VendorSpecificEvent (subevent_code = STORAGE_THRESHOLD_BREACH) {
+}
+
+// -----------------------------------------------------------------------------
+//  Advertising Packet Content Filter (APCF) Command.
+//  https://source.android.com/docs/core/connect/bluetooth/hci_requirements#advertising-packet-content-filter
+//  https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le-advertisement-tracking-sub-event
+// -----------------------------------------------------------------------------
+
+enum ApcfOpcode : 8 {
+  ENABLE = 0x00,
+  SET_FILTERING_PARAMETERS = 0x01,
+  BROADCASTER_ADDRESS = 0x02,
+  SERVICE_UUID = 0x03,
+  SERVICE_SOLICITATION_UUID = 0x04,
+  LOCAL_NAME = 0x05,
+  MANUFACTURER_DATA = 0x06,
+  SERVICE_DATA = 0x07,
+  TRANSPORT_DISCOVERY_SERVICE = 0x08,
+  AD_TYPE_FILTER = 0x09,
+  READ_EXTENDED_FEATURES = 0xFF,
+}
+
+packet LeApcf : Command (op_code = LE_APCF) {
+  apcf_opcode : ApcfOpcode,
+  _payload_,
+}
+
+packet LeApcfComplete : CommandComplete (command_op_code = LE_APCF) {
+  status : ErrorCode,
+  apcf_opcode : ApcfOpcode,
+  _payload_
+}
+
+packet LeApcfEnable : LeApcf (apcf_opcode = ENABLE) {
+  apcf_enable : Enable,
+}
+
+packet LeApcfEnableComplete : LeApcfComplete (apcf_opcode = ENABLE) {
+  apcf_enable : Enable,
+}
+
+enum ApcfAction : 8 {
+  ADD = 0x00,
+  DELETE = 0x01,
+  CLEAR = 0x02,
+}
+
+enum DeliveryMode : 8 {
+  IMMEDIATE = 0x00,
+  ONFOUND = 0x01,
+  BATCHED = 0x02,
+}
+
+// Bit mask for the APCF Feature Selection field.
+enum ApcfFeatureSelection : 8 {
+  BROADCASTER_ADDRESS = 0x00,
+  SERVICE_DATA_CHANGE = 0x01,
+  SERVICE_UUID = 0x02,
+  SERVICE_SOLICITATION_UUID = 0x03,
+  LOCAL_NAME = 0x04,
+  MANUFACTURER_DATA = 0x05,
+  SERVICE_DATA = 0x06,
+  TRANSPORT_DISCOVERY_DATA = 0x07,
+  AD_TYPE = 0x08,
+}
+
+packet LeApcfSetFilteringParameters : LeApcf (apcf_opcode = SET_FILTERING_PARAMETERS) {
+  apcf_action : ApcfAction,
+  apcf_filter_index : 8,
+  apcf_feature_selection : 16,
+  apcf_list_logic_type : 16,
+  apcf_filter_logic_type : 8,
+  rssi_high_thresh : 8,
+  delivery_mode : DeliveryMode,
+  onfound_timeout : 16,
+  onfound_timeout_cnt : 8,
+  rssi_low_thresh : 8,
+  onlost_timeout : 16,
+  num_of_tracking_entries : 16,
+}
+
+packet LeApcfSetFilteringParametersComplete : LeApcfComplete (apcf_opcode = SET_FILTERING_PARAMETERS) {
+  apcf_action : ApcfAction,
+  apcf_available_spaces : 8,
+}
+
+enum ApcfApplicationAddressType : 8 {
+  PUBLIC = 0x00,
+  RANDOM = 0x01,
+  NOT_APPLICABLE = 0x02,
+}
+
+packet LeApcfBroadcasterAddress : LeApcf (apcf_opcode = BROADCASTER_ADDRESS) {
+  apcf_action : ApcfAction,
+  apcf_filter_index : 8,
+  apcf_broadcaster_address : Address,
+  apcf_application_address_type : ApcfApplicationAddressType,
+}
+
+packet LeApcfBroadcasterAddressComplete : LeApcfComplete (apcf_opcode = BROADCASTER_ADDRESS) {
+  apcf_action : ApcfAction,
+  apcf_available_spaces : 8,
+}
+
+packet LeApcfServiceUuid : LeApcf (apcf_opcode = SERVICE_UUID) {
+  apcf_action : ApcfAction,
+  apcf_filter_index : 8,
+  acpf_uuid_data : 8[],
+}
+
+packet LeApcfServiceUuidComplete : LeApcfComplete (apcf_opcode = SERVICE_UUID) {
+  apcf_action : ApcfAction,
+  apcf_available_spaces : 8,
+}
+
+packet LeApcfSolicitationUuid : LeApcf (apcf_opcode = SERVICE_SOLICITATION_UUID) {
+  apcf_action : ApcfAction,
+  apcf_filter_index : 8,
+  acpf_uuid_data : 8[],
+}
+
+packet LeApcfSolicitationUuidComplete : LeApcfComplete (apcf_opcode = SERVICE_SOLICITATION_UUID) {
+  apcf_action : ApcfAction,
+  apcf_available_spaces : 8,
+}
+
+packet LeApcfLocalName : LeApcf (apcf_opcode = LOCAL_NAME) {
+  apcf_action : ApcfAction,
+  apcf_filter_index : 8,
+  apcf_local_name : 8[],
+}
+
+packet LeApcfLocalNameComplete : LeApcfComplete (apcf_opcode = LOCAL_NAME) {
+  apcf_action : ApcfAction,
+  apcf_available_spaces : 8,
+}
+
+packet LeApcfManufacturerData : LeApcf (apcf_opcode = MANUFACTURER_DATA) {
+  apcf_action : ApcfAction,
+  apcf_filter_index : 8,
+  apcf_manufacturer_data : 8[],
+}
+
+packet LeApcfManufacturerDataComplete : LeApcfComplete (apcf_opcode = MANUFACTURER_DATA) {
+  apcf_action : ApcfAction,
+  apcf_available_spaces : 8,
+}
+
+packet LeApcfServiceData : LeApcf (apcf_opcode = SERVICE_DATA) {
+  apcf_action : ApcfAction,
+  apcf_filter_index : 8,
+  apcf_service_data : 8[],
+}
+
+packet LeApcfServiceDataComplete : LeApcfComplete (apcf_opcode = SERVICE_DATA) {
+  apcf_action : ApcfAction,
+  apcf_available_spaces : 8,
+}
+
+packet LeApcfADType : LeApcf (apcf_opcode = AD_TYPE_FILTER) {
+  apcf_action : ApcfAction,
+  apcf_filter_index : 8,
+  apcf_ad_type_data : 8[],
+}
+
+packet LeApcfADTypeComplete : LeApcfComplete (apcf_opcode = AD_TYPE_FILTER) {
+  apcf_action : ApcfAction,
+  apcf_available_spaces : 8,
+}
+
+packet LeApcfReadExtendedFeatures : LeApcf (apcf_opcode = READ_EXTENDED_FEATURES) {
+}
+
+test LeApcfReadExtendedFeatures {
+  "\x57\xfd\x01\xff",
+}
+
+packet LeApcfReadExtendedFeaturesComplete : LeApcfComplete (apcf_opcode = READ_EXTENDED_FEATURES) {
+  transport_discovery_data_filter : 1,
+  ad_type_filter : 1,
+  _reserved_ : 14,
+}
+
+test LeApcfReadExtendedFeaturesComplete {
+  "\x0e\x07\x01\x57\xfd\x00\xff\x03\x00",
+}
+
+enum AdvertiserState : 8 {
+  ADVERTISER_FOUND = 0x0,
+  ADVERTISER_LOST = 0x1,
+}
+
+enum AdvtInfoPresent : 8 {
+  ADVT_INFO_PRESENT = 0x0,
+  ADVT_INFO_NOT_PRESENT = 0x1,
+}
+
+struct AdvtInfo {
+  tx_power : 8,
+  rssi : 8,
+  timestamp : 16,
+  _size_(adv_packet) : 8,
+  adv_packet : 8[],
+  _size_(scan_data_resp) : 8,
+  scan_data_resp : 8[],
+}
+
+packet LeAdvertisementTrackingEvent : VendorSpecificEvent (subevent_code = LE_ADVERTISEMENT_TRACKING) {
+  apcf_filter_index : 8,
+  advertiser_state : AdvertiserState,
+  advt_info_present : AdvtInfoPresent,
+  advertiser_address : Address,
+  advertiser_address_type : PeerAddressType,
+  advt_info : AdvtInfo[],
+}
+
+// -----------------------------------------------------------------------------
+//  LE Get Controller Activity Energy Info Command
+//  https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_get_controller_activity_energy_info
+// -----------------------------------------------------------------------------
+
+packet LeGetControllerActivityEnergyInfo : Command (op_code = LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO) {
+}
+
+packet LeGetControllerActivityEnergyInfoComplete : CommandComplete (command_op_code = LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO) {
+  status : ErrorCode,
+  total_tx_time_ms : 32,
+  total_rx_time_ms : 32,
+  total_idle_time_ms : 32,
+  total_energy_used : 32,
+}
+
+// -----------------------------------------------------------------------------
+//  LE Extended Set Scan Parameters Command
+//  https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le-extended-set-scan-parameters-command
+// -----------------------------------------------------------------------------
+
+enum LeExScanType : 8 {
+  PASSIVE = 0x0,
+  ACTIVE = 0x1,
+}
+
+enum LeExScanFilterPolicy : 8 {
+  ACCEPT_ALL = 0x0,
+  FILTER_ACCEPT_LIST_ONLY = 0x01,
+}
+
+packet LeExSetScanParameters : Command (op_code = LE_EX_SET_SCAN_PARAMETERS) {
+  le_ex_scan_type : LeExScanType,
+  le_ex_scan_interval : 32,
+  le_ex_scan_window : 32,
+  own_address_type : OwnAddressType,
+  le_ex_scan_filter_policy : LeExScanFilterPolicy,
+}
+
+packet LeExSetScanParametersComplete : CommandComplete (command_op_code = LE_EX_SET_SCAN_PARAMETERS) {
+  status : ErrorCode,
+}
+
+// -----------------------------------------------------------------------------
+//  Get Controller Debug Info Command
+//  https://source.android.com/docs/core/connect/bluetooth/hci_requirements#get-controller-debug-info-command
+// -----------------------------------------------------------------------------
+
+packet GetControllerDebugInfo : Command (op_code = GET_CONTROLLER_DEBUG_INFO) {
+}
+
+packet GetControllerDebugInfoComplete : CommandComplete (command_op_code = GET_CONTROLLER_DEBUG_INFO) {
+  status : ErrorCode,
+}
+
+packet ControllerDebugInfoEvent : VendorSpecificEvent (subevent_code = CONTROLLER_DEBUG_INFO) {
+  debug_block_byte_offset_start : 16,
+  last_block : 8,
+  _size_(debug_data) : 16,
+  debug_data : 8[],
+}
+
+// -----------------------------------------------------------------------------
+//  Microsoft Commands
+//  https://learn.microsoft.com/en-us/windows-hardware/drivers/bluetooth/microsoft-defined-bluetooth-hci-commands-and-events
+// -----------------------------------------------------------------------------
 
 enum MsftSubcommandOpcode : 8 {
   MSFT_READ_SUPPORTED_FEATURES = 0x00,
@@ -6233,7 +6069,7 @@
   MSFT_READ_ABSOLUTE_RSSI = 0x06,
 }
 
-// MSFT Commands don't have a constant opcode, so leave `op_code` undefined.
+// MSFT Commands do not have a constant opcode, so leave `op_code` undefined.
 packet MsftCommand : Command {
   subcommand_opcode: MsftSubcommandOpcode,
   _payload_,
@@ -6400,52 +6236,3 @@
   "\x02\x01\x00\x01\x02\x03\x04\x05\x10\x00",
   "\x02\x02\xf0\xf1\xf2\xf3\xf4\xf5\xaa\x02",
 }
-
-// https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
-enum GapDataType : 8 {
-  INVALID = 0x00,
-  FLAGS = 0x01,
-  INCOMPLETE_LIST_16_BIT_UUIDS = 0x02,
-  COMPLETE_LIST_16_BIT_UUIDS = 0x03,
-  INCOMPLETE_LIST_32_BIT_UUIDS = 0x04,
-  COMPLETE_LIST_32_BIT_UUIDS = 0x05,
-  INCOMPLETE_LIST_128_BIT_UUIDS = 0x06,
-  COMPLETE_LIST_128_BIT_UUIDS = 0x07,
-  SHORTENED_LOCAL_NAME = 0x08,
-  COMPLETE_LOCAL_NAME = 0x09,
-  TX_POWER_LEVEL = 0x0A,
-  CLASS_OF_DEVICE = 0x0D,
-  SIMPLE_PAIRING_HASH_C = 0x0E,
-  SIMPLE_PAIRING_RANDOMIZER_R = 0x0F,
-  DEVICE_ID = 0x10,
-  SECURITY_MANAGER_OOB_FLAGS = 0x11,
-  SLAVE_CONNECTION_INTERVAL_RANGE = 0x12,
-  LIST_16BIT_SERVICE_SOLICITATION_UUIDS = 0x14,
-  LIST_128BIT_SERVICE_SOLICITATION_UUIDS = 0x15,
-  SERVICE_DATA_16_BIT_UUIDS = 0x16,
-  PUBLIC_TARGET_ADDRESS = 0x17,
-  RANDOM_TARGET_ADDRESS = 0x18,
-  APPEARANCE = 0x19,
-  ADVERTISING_INTERVAL = 0x1A,
-  LE_BLUETOOTH_DEVICE_ADDRESS = 0x1B,
-  LE_ROLE = 0x1C,
-  SIMPLE_PAIRING_HASH_C_256 = 0x1D,
-  SIMPLE_PAIRING_RANDOMIZER_R_256 = 0x1E,
-  LIST_32BIT_SERVICE_SOLICITATION_UUIDS = 0x1F,
-  SERVICE_DATA_32_BIT_UUIDS = 0x20,
-  SERVICE_DATA_128_BIT_UUIDS = 0x21,
-  LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE = 0x22,
-  LE_SECURE_CONNECTIONS_RANDOM_VALUE = 0x23,
-  URI = 0x24,
-  INDOOR_POSITIONING = 0x25,
-  TRANSPORT_DISCOVERY_DATA = 0x26,
-  LE_SUPPORTED_FEATURES = 0x27,
-  CHANNEL_MAP_UPDATE_INDICATION = 0x28,
-  MESH_PB_ADV = 0x29,
-  MESH_MESSAGE = 0x2A,
-  MESH_BEACON = 0x2B,
-  BIG_INFO = 0x2C,
-  BROADCAST_CODE = 0x2D,
-  THREE_D_INFORMATION_DATA = 0x3D,
-  MANUFACTURER_SPECIFIC_DATA = 0xFF,
-}
diff --git a/tools/rootcanal/proto/rootcanal/configuration.proto b/tools/rootcanal/proto/rootcanal/configuration.proto
index 3510cc8..db0c387 100644
--- a/tools/rootcanal/proto/rootcanal/configuration.proto
+++ b/tools/rootcanal/proto/rootcanal/configuration.proto
@@ -47,6 +47,11 @@
   optional bool hardware_error_before_reset = 3;
 }
 
+message VendorFeatures {
+  // Enable the support for the CSR vendor command.
+  optional bool csr = 1;
+}
+
 message Controller {
   // Configure the controller preset. Presets come with a pre-selection
   // of features and quirks, but these can be overridden with the next fields.
@@ -61,6 +66,8 @@
   // Activate assertion checks in RootCanal for missing RootCanal features
   // or Host stack misbehavior.
   optional bool strict = 4;
+  // Configure support for vendor features.
+  optional VendorFeatures vendor = 5;
 }
 
 message TcpServer {