Merge changes I838a8a65,I51112332 into main

* changes:
  SystemServer: remove avoid_static_loading_of_native
  SystemServer: remove fast_bind_to_app
diff --git a/.clang-tidy b/.clang-tidy
index df0d649..9dc029b 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,4 +1,18 @@
 ---
 CheckOptions:
-  - key:             misc-include-cleaner.IgnoreHeaders
-    value:           (fmt/.*|bits/pthread_types\.h)
+  - key: misc-include-cleaner.IgnoreHeaders
+    value: "(\
+        fmt/.*|\
+        bits/.*.h|\
+        asm-generic/.*.h|\
+        sys/poll.h|\
+        sys/endian.h|\
+        sys/ioctl.h|\
+        arpa/inet.h|\
+        linux/if.h|\
+        linux/sockios.h|\
+        netinet/in.h|\
+        osi/include/compat.h|\
+        android_bluetooth_sysprop.h|\
+        hfp.sysprop.h|\
+        __chrono/duration.h)"
diff --git a/Android.bp b/Android.bp
index 71d6d40..476d018 100644
--- a/Android.bp
+++ b/Android.bp
@@ -85,6 +85,11 @@
     // This check implements detection of local variables which could be declared
     // as const but are not.
     "-misc-const-correctness",
+
+    // Finds classes that contain non-static data members in addition to user-declared
+    // non-static member functions and diagnose all data members declared with a
+    // non-public access specifier.
+    "-misc-non-private-member-variables-in-classes",
 ]
 
 // This default tidy checks that will be run against all the cc targets
@@ -165,6 +170,7 @@
             "-Xep:UnusedMethod:ERROR",
             "-Xep:UnusedNestedClass:ERROR",
             "-Xep:UnusedVariable:ERROR",
+            "-Xep:VariableNameSameAsType:ERROR",
             "-Xep:WaitNotInLoop:ERROR",
             "-Xep:WakelockReleasedDangerously:ERROR",
 
@@ -172,8 +178,8 @@
             "-XepExcludedPaths:.*/srcjars/.*",
 
             // The @InlineMe annotation could be made available, but it would
-            // apply on external facing API. This is not desired. For more
-            // context, see https://r.android.com/3303475
+            // apply on external facing API. This is not desired.
+            // For more context, see https://r.android.com/3303475
             "-Xep:InlineMeSuggester:OFF",
         ],
     },
diff --git a/OWNERS b/OWNERS
index 4df6c99..491c4d0 100644
--- a/OWNERS
+++ b/OWNERS
@@ -9,7 +9,7 @@
 # Per-file ownership
 
 # Build files / test_config / presubmit / preupload / linter file
-per-file PREUPLOAD.cfg,TEST_MAPPING,*.bp,*.xml,pyrightconfig.json=file:/OWNERS_build
+per-file PREUPLOAD.cfg,TEST_MAPPING,*.bp,*.xml,.clang-tidy,pyrightconfig.json=file:/OWNERS_build
 
 # ChromeOS team owns Linux build files
 # - build.py is used for Linux build
diff --git a/OWNERS_hearingaid b/OWNERS_hearingaid
index e70772a..d1c75de 100644
--- a/OWNERS_hearingaid
+++ b/OWNERS_hearingaid
@@ -1 +1 @@
-charliebout@google.com
+henrichataing@google.com
diff --git a/android/app/Android.bp b/android/app/Android.bp
index 2bac3ba..d4fccc0 100644
--- a/android/app/Android.bp
+++ b/android/app/Android.bp
@@ -73,6 +73,9 @@
         "jni_headers",
         "libbluetooth_headers",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     include_dirs: [
         "packages/modules/Bluetooth/system",
         "packages/modules/Bluetooth/system/gd",
@@ -88,6 +91,7 @@
     // is required to maintain FIPS compliance.
     stl: "libc++_static",
     static_libs: [
+        "aics",
         "android.hardware.audio.common@5.0",
         "android.hardware.bluetooth.audio@2.0",
         "android.hardware.bluetooth.audio@2.1",
@@ -267,6 +271,7 @@
         "bluetooth-protos-lite",
         "bluetooth.change-ids",
         "bluetooth.mapsapi",
+        "bluetooth_constants_java",
         "com.android.obex",
         "com.android.vcard",
         "guava",
diff --git a/android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistant.aidl b/android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistant.aidl
index bf95bb5..54b947c 100644
--- a/android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistant.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetoothLeBroadcastAssistant.aidl
@@ -60,4 +60,6 @@
     List<BluetoothLeBroadcastReceiveState> getAllSources(in BluetoothDevice sink, in AttributionSource source);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
     int getMaximumSourceCapacity(in BluetoothDevice sink, in AttributionSource source);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    BluetoothLeBroadcastMetadata getSourceMetadata(in BluetoothDevice sink, in int sourceId, in AttributionSource source);
 }
diff --git a/android/app/jni/com_android_bluetooth_vc.cpp b/android/app/jni/com_android_bluetooth_vc.cpp
index 39bd355..e7cf0ab 100644
--- a/android/app/jni/com_android_bluetooth_vc.cpp
+++ b/android/app/jni/com_android_bluetooth_vc.cpp
@@ -17,6 +17,7 @@
 
 #define LOG_TAG "BluetoothVolumeControlServiceJni"
 
+#include <aics/api.h>
 #include <bluetooth/log.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
@@ -34,9 +35,12 @@
 #include "hardware/bt_vc.h"
 #include "types/raw_address.h"
 
+using bluetooth::aics::Mute;
 using bluetooth::vc::ConnectionState;
 using bluetooth::vc::VolumeControlCallbacks;
 using bluetooth::vc::VolumeControlInterface;
+using bluetooth::vc::VolumeInputStatus;
+using bluetooth::vc::VolumeInputType;
 
 namespace android {
 static jmethodID method_onConnectionStateChanged;
@@ -58,6 +62,8 @@
 static jobject mCallbacksObj = nullptr;
 static std::shared_timed_mutex callbacks_mutex;
 
+static jfieldID sCallbacksField;
+
 class VolumeControlCallbacksImpl : public VolumeControlCallbacks {
 public:
   ~VolumeControlCallbacksImpl() = default;
@@ -217,8 +223,8 @@
                                  (jint)ext_output_id, description, addr.get());
   }
 
-  void OnExtAudioInStateChanged(const RawAddress& bd_addr, uint8_t ext_input_id, int8_t gain_val,
-                                uint8_t gain_mode, bool mute) override {
+  void OnExtAudioInStateChanged(const RawAddress& bd_addr, uint8_t ext_input_id,
+                                int8_t gain_setting, Mute mute, uint8_t gain_mode) override {
     log::info("");
 
     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
@@ -237,11 +243,11 @@
     sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
                                      reinterpret_cast<const jbyte*>(&bd_addr));
     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onExtAudioInStateChanged, (jint)ext_input_id,
-                                 (jint)gain_val, (jint)gain_mode, (jboolean)mute, addr.get());
+                                 (jint)gain_setting, (jint)mute, (jint)gain_mode, addr.get());
   }
 
   void OnExtAudioInStatusChanged(const RawAddress& bd_addr, uint8_t ext_input_id,
-                                 bluetooth::vc::VolumeInputStatus status) override {
+                                 VolumeInputStatus status) override {
     log::info("");
 
     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
@@ -264,7 +270,7 @@
   }
 
   void OnExtAudioInTypeChanged(const RawAddress& bd_addr, uint8_t ext_input_id,
-                               bluetooth::vc::VolumeInputType type) override {
+                               VolumeInputType type) override {
     log::info("");
 
     std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
@@ -358,7 +364,8 @@
     mCallbacksObj = nullptr;
   }
 
-  if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
+  if ((mCallbacksObj = env->NewGlobalRef(env->GetObjectField(object, sCallbacksField))) ==
+      nullptr) {
     log::error("Failed to allocate Global Ref for Volume control Callbacks");
     return;
   }
@@ -775,8 +782,9 @@
   return JNI_TRUE;
 }
 
-static jboolean setExtAudioInGainValueNative(JNIEnv* env, jobject /* object */, jbyteArray address,
-                                             jint ext_input_id, jint gain_val) {
+static jboolean setExtAudioInGainSettingNative(JNIEnv* env, jobject /* object */,
+                                               jbyteArray address, jint ext_input_id,
+                                               jint gain_setting) {
   log::info("");
   std::shared_lock<std::shared_timed_mutex> lock(interface_mutex);
   if (!sVolumeControlInterface) {
@@ -790,7 +798,7 @@
   }
 
   RawAddress* tmpraw = reinterpret_cast<RawAddress*>(addr);
-  sVolumeControlInterface->SetExtAudioInGainValue(*tmpraw, ext_input_id, gain_val);
+  sVolumeControlInterface->SetExtAudioInGainSetting(*tmpraw, ext_input_id, gain_setting);
   env->ReleaseByteArrayElements(address, addr, 0);
   return JNI_TRUE;
 }
@@ -871,8 +879,8 @@
            reinterpret_cast<void*>(getExtAudioInDescriptionNative)},
           {"setExtAudioInDescriptionNative", "([BILjava/lang/String;)Z",
            reinterpret_cast<void*>(setExtAudioInDescriptionNative)},
-          {"setExtAudioInGainValueNative", "([BII)Z",
-           reinterpret_cast<void*>(setExtAudioInGainValueNative)},
+          {"setExtAudioInGainSettingNative", "([BII)Z",
+           reinterpret_cast<void*>(setExtAudioInGainSettingNative)},
           {"setExtAudioInGainModeNative", "([BIZ)Z",
            reinterpret_cast<void*>(setExtAudioInGainModeNative)},
           {"setExtAudioInGainMuteNative", "([BIZ)Z",
@@ -884,6 +892,12 @@
     return result;
   }
 
+  jclass jniVolumeControlNativeInterfaceClass =
+          env->FindClass("com/android/bluetooth/vc/VolumeControlNativeInterface");
+  sCallbacksField = env->GetFieldID(jniVolumeControlNativeInterfaceClass, "mNativeCallback",
+                                    "Lcom/android/bluetooth/vc/VolumeControlNativeCallback;");
+  env->DeleteLocalRef(jniVolumeControlNativeInterfaceClass);
+
   const JNIJavaMethod javaMethods[] = {
           {"onConnectionStateChanged", "(I[B)V", &method_onConnectionStateChanged},
           {"onVolumeStateChanged", "(IZI[BZ)V", &method_onVolumeStateChanged},
@@ -893,14 +907,14 @@
           {"onExtAudioOutLocationChanged", "(II[B)V", &method_onExtAudioOutLocationChanged},
           {"onExtAudioOutDescriptionChanged", "(ILjava/lang/String;[B)V",
            &method_onExtAudioOutDescriptionChanged},
-          {"onExtAudioInStateChanged", "(IIIZ[B)V", &method_onExtAudioInStateChanged},
+          {"onExtAudioInStateChanged", "(IIII[B)V", &method_onExtAudioInStateChanged},
           {"onExtAudioInStatusChanged", "(II[B)V", &method_onExtAudioInStatusChanged},
           {"onExtAudioInTypeChanged", "(II[B)V", &method_onExtAudioInTypeChanged},
           {"onExtAudioInGainPropsChanged", "(IIII[B)V", &method_onExtAudioInGainPropsChanged},
           {"onExtAudioInDescriptionChanged", "(ILjava/lang/String;[B)V",
            &method_onExtAudioInDescriptionChanged},
   };
-  GET_JAVA_METHODS(env, "com/android/bluetooth/vc/VolumeControlNativeInterface", javaMethods);
+  GET_JAVA_METHODS(env, "com/android/bluetooth/vc/VolumeControlNativeCallback", javaMethods);
 
   return 0;
 }
diff --git a/android/app/lint-baseline.xml b/android/app/lint-baseline.xml
index 1b2c4fe..c972c11 100644
--- a/android/app/lint-baseline.xml
+++ b/android/app/lint-baseline.xml
@@ -371,7 +371,7 @@
         errorLine2="        ^">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java"
-            line="734"
+            line="672"
             column="9"/>
     </issue>
 
@@ -382,7 +382,7 @@
         errorLine2="        ^">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java"
-            line="734"
+            line="672"
             column="9"/>
     </issue>
 
@@ -393,7 +393,7 @@
         errorLine2="        ^">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java"
-            line="734"
+            line="672"
             column="9"/>
     </issue>
 
@@ -448,7 +448,7 @@
         errorLine2="        ^">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/map/BluetoothMapbMessage.java"
-            line="918"
+            line="922"
             column="9"/>
     </issue>
 
@@ -547,7 +547,7 @@
         errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/a2dp/A2dpService.java"
-            line="673"
+            line="642"
             column="31"/>
     </issue>
 
@@ -558,7 +558,7 @@
         errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/a2dp/A2dpService.java"
-            line="847"
+            line="816"
             column="33"/>
     </issue>
 
@@ -569,7 +569,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/a2dp/A2dpService.java"
-            line="855"
+            line="824"
             column="26"/>
     </issue>
 
@@ -580,7 +580,7 @@
         errorLine2="                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/a2dp/A2dpService.java"
-            line="868"
+            line="837"
             column="33"/>
     </issue>
 
@@ -591,18 +591,18 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/a2dp/A2dpService.java"
-            line="888"
+            line="857"
             column="26"/>
     </issue>
 
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="            sm.sendMessage(A2dpStateMachine.STACK_EVENT, stackEvent);"
-        errorLine2="                                            ~~~~~~~~~~~">
+        errorLine1="            sm.sendMessage(A2dpStateMachine.MESSAGE_STACK_EVENT, stackEvent);"
+        errorLine2="                                            ~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/a2dp/A2dpService.java"
-            line="963"
+            line="929"
             column="45"/>
     </issue>
 
@@ -613,7 +613,7 @@
         errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java"
-            line="477"
+            line="493"
             column="31"/>
     </issue>
 
@@ -624,7 +624,7 @@
         errorLine2="                   ~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java"
-            line="1190"
+            line="1173"
             column="20"/>
     </issue>
 
@@ -635,7 +635,7 @@
         errorLine2="                   ~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java"
-            line="1195"
+            line="1178"
             column="20"/>
     </issue>
 
@@ -646,7 +646,7 @@
         errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java"
-            line="2025"
+            line="2024"
             column="43"/>
     </issue>
 
@@ -657,7 +657,7 @@
         errorLine2="                                            ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java"
-            line="2030"
+            line="2029"
             column="45"/>
     </issue>
 
@@ -668,7 +668,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java"
-            line="2034"
+            line="2033"
             column="34"/>
     </issue>
 
@@ -679,7 +679,7 @@
         errorLine2="                                ~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java"
-            line="2266"
+            line="2265"
             column="33"/>
     </issue>
 
@@ -690,7 +690,7 @@
         errorLine2="                           ~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java"
-            line="6876"
+            line="6885"
             column="28"/>
     </issue>
 
@@ -701,7 +701,7 @@
         errorLine2="                           ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java"
-            line="6878"
+            line="6887"
             column="28"/>
     </issue>
 
@@ -1015,980 +1015,23 @@
 
     <issue
         id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="    private final Map&lt;BluetoothDevice, BassClientStateMachine> mStateMachines = new HashMap&lt;>();"
-        errorLine2="                                       ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="148"
-            column="40"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            .anyMatch(e -> e.first.equals(BassClientStateMachine.ADD_BCAST_SOURCE));"
-        errorLine2="                                                                                 ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="852"
-            column="82"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            case BassClientStateMachine.ADD_BCAST_SOURCE:"
-        errorLine2="                                        ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="878"
-            column="41"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                                                                    .ADD_BCAST_SOURCE))"
-        errorLine2="                                                                     ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="891"
-            column="70"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                                    (m.first.equals(BassClientStateMachine.ADD_BCAST_SOURCE))"
-        errorLine2="                                                                           ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="903"
-            column="76"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            case BassClientStateMachine.REMOVE_BCAST_SOURCE:"
-        errorLine2="                                        ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="921"
-            column="41"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                                m.first.equals(BassClientStateMachine.REMOVE_BCAST_SOURCE)"
-        errorLine2="                                                                      ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="926"
-            column="71"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="                BassClientStateMachine sm = getOrCreateStateMachine(device);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="969"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                        &amp;&amp; (sm.hasPendingSwitchingSourceOperation()"
-        errorLine2="                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="973"
-            column="32"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                                || sm.hasPendingSourceOperation())) {"
-        errorLine2="                                      ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="974"
-            column="39"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1136"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    stateMachine.getCurrentBroadcastMetadata(sourceId);"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1144"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            getOrCreateStateMachine(device).getAllSources();"
-        errorLine2="                                                            ~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1150"
-            column="61"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="        BassClientStateMachine stateMachine = null;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1227"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="        for (BluetoothLeBroadcastReceiveState recvState : stateMachine.getAllSources()) {"
-        errorLine2="                                                                       ~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1236"
-            column="72"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="        BassClientStateMachine stateMachine = null;"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1247"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="        List&lt;BluetoothLeBroadcastReceiveState> sources = stateMachine.getAllSources();"
-        errorLine2="                                                                      ~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1256"
-            column="71"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="    private BassClientStateMachine getOrCreateStateMachine(BluetoothDevice device) {"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1281"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine stateMachine = mStateMachines.get(device);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1287"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine sm = mStateMachines.get(device);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1359"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            sm.doQuit();"
-        errorLine2="               ~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1367"
-            column="16"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            sm.cleanup();"
-        errorLine2="               ~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1368"
-            column="16"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="                BassClientStateMachine stateMachine = getOrCreateStateMachine(device);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1404"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                stateMachine.sendMessage(BassClientStateMachine.STOP_SCAN_OFFLOAD);"
-        errorLine2="                                                                ~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1413"
-            column="65"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine stateMachine,"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1420"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="        if (stateMachine.hasPendingSourceOperation()) {"
-        errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1449"
-            column="26"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1559"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            stateMachine.sendMessage(BassClientStateMachine.CONNECT);"
-        errorLine2="                                                            ~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1565"
-            column="61"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1583"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            stateMachine.sendMessage(BassClientStateMachine.DISCONNECT);"
-        errorLine2="                                                            ~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1589"
-            column="61"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine sm = getOrCreateStateMachine(sink);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1633"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            return sm.getConnectionState();"
-        errorLine2="                      ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1638"
-            column="23"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="                BassClientStateMachine sm = getOrCreateStateMachine(device);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1664"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    connectionState = sm.getConnectionState();"
-        errorLine2="                                         ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1666"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                if (sm.isConnected()) {"
-        errorLine2="                       ~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1688"
-            column="24"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    devices.add(sm.getDevice());"
-        errorLine2="                                   ~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1689"
-            column="36"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
         errorLine1="                mDatabaseManager.setProfileConnectionPolicy("
         errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1711"
+            line="1822"
             column="34"/>
     </issue>
 
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="                                                if (sm.isConnected()) {"
-        errorLine2="                                                       ~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1850"
-            column="56"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                                                    selectSource(sm.getDevice(), result, false);"
-        errorLine2="                                                                    ~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1851"
-            column="69"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="                    BassClientStateMachine stateMachine = getOrCreateStateMachine(device);"
-        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1903"
-            column="21"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    stateMachine.sendMessage(BassClientStateMachine.START_SCAN_OFFLOAD);"
-        errorLine2="                                                                    ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="1912"
-            column="69"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="                BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2639"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                        stateMachine.obtainMessage(BassClientStateMachine.REACHED_MAX_SOURCE_LIMIT);"
-        errorLine2="                                                                          ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2645"
-            column="75"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2655"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    stateMachine.obtainMessage(BassClientStateMachine.SELECT_BCAST_SOURCE);"
-        errorLine2="                                                                      ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2661"
-            column="71"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2776"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            if (stateMachine.hasPendingSourceOperation()) {"
-        errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2789"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            stateMachine.getCurrentBroadcastMetadata(sourceId);"
-        errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2805"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                                device, BassClientStateMachine.REMOVE_BCAST_SOURCE, sourceId);"
-        errorLine2="                                                               ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2828"
-            column="64"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                                device, BassClientStateMachine.ADD_BCAST_SOURCE, sourceMetadata);"
-        errorLine2="                                                               ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2830"
-            column="64"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            stateMachine.obtainMessage(BassClientStateMachine.SWITCH_BCAST_SOURCE);"
-        errorLine2="                                                                              ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2837"
-            column="79"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                        device, BassClientStateMachine.ADD_BCAST_SOURCE, sourceMetadata);"
-        errorLine2="                                                       ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2874"
-            column="56"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            Message message = stateMachine.obtainMessage(BassClientStateMachine.ADD_BCAST_SOURCE);"
-        errorLine2="                                                                                ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2889"
-            column="81"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                message = stateMachine.obtainMessage(BassClientStateMachine.SET_BCAST_CODE);"
-        errorLine2="                                                                            ~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2900"
-            column="77"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                message.arg1 = BassClientStateMachine.ARGTYPE_METADATA;"
-        errorLine2="                                                      ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2902"
-            column="55"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2940"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);"
-        errorLine2="                                                                      ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2959"
-            column="71"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                message = stateMachine.obtainMessage(BassClientStateMachine.SET_BCAST_CODE);"
-        errorLine2="                                                                            ~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2973"
-            column="77"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                message.arg1 = BassClientStateMachine.ARGTYPE_METADATA;"
-        errorLine2="                                                      ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2975"
-            column="55"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine stateMachine = getOrCreateStateMachine(device);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="2994"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    stateMachine.getCurrentBroadcastMetadata(sourceId);"
-        errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3023"
-            column="34"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            if (metaData != null &amp;&amp; stateMachine.isSyncedToTheSource(sourceId)) {"
-        errorLine2="                                                 ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3029"
-            column="50"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                        stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);"
-        errorLine2="                                                                          ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3040"
-            column="75"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    stateMachine.obtainMessage(BassClientStateMachine.REMOVE_BCAST_SOURCE);"
-        errorLine2="                                                                      ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3054"
-            column="71"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    BassClientStateMachine.REMOVE_BCAST_SOURCE,"
-        errorLine2="                                           ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3064"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3078"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            return stateMachine.getAllSources().stream()"
-        errorLine2="                                ~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3083"
-            column="33"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="        BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);"
-        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3097"
-            column="9"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="        return stateMachine.getMaximumSourceCapacity();"
-        errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3102"
-            column="29"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="                BassClientStateMachine sm = getOrCreateStateMachine(device);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3167"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                if (sm != null &amp;&amp; sm.hasPendingSourceOperation(broadcastId)) {"
-        errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3168"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                                    BassClientStateMachine.CANCEL_PENDING_SOURCE_OPERATION);"
-        errorLine2="                                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3171"
-            column="60"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    getOrCreateStateMachine(groupDevice).getAllSources().stream()"
-        errorLine2="                                                         ~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3417"
-            column="58"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="                BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);"
-        errorLine2="                ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3580"
-            column="17"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                List&lt;BluetoothLeBroadcastReceiveState> sources = stateMachine.getAllSources();"
-        errorLine2="                                                                              ~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3581"
-            column="79"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            stateMachine.obtainMessage(BassClientStateMachine.UPDATE_BCAST_SOURCE);"
-        errorLine2="                                                                              ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3617"
-            column="79"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            sink, reason, BassClientStateMachine.ADD_BCAST_SOURCE, param.mObj2);"
-        errorLine2="                                                                 ~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3876"
-            column="66"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            BassClientStateMachine.REMOVE_BCAST_SOURCE,"
-        errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="3884"
-            column="52"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine sm = entry.getValue();"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="4192"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            if (sm.getConnectionState() == BluetoothProfile.STATE_CONNECTED) {"
-        errorLine2="                   ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="4193"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                sm.dump(sb);"
-        errorLine2="                   ~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="4194"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="            BassClientStateMachine sm = entry.getValue();"
-        errorLine2="            ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="4201"
-            column="13"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            if (sm.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {"
-        errorLine2="                   ~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="4202"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                sm.dump(sb);"
-        errorLine2="                   ~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassClientService.java"
-            line="4203"
-            column="20"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="    public BassClientStateMachine makeStateMachine("
-        errorLine2="           ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassObjectsFactory.java"
-            line="73"
-            column="12"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="        return BassClientStateMachine.make(device, svc, adapterService, looper);"
-        errorLine2="                                      ~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassObjectsFactory.java"
-            line="78"
-            column="39"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This class should only be accessed from tests or within private scope"
-        errorLine1="    public void destroyStateMachine(BassClientStateMachine stateMachine) {"
-        errorLine2="                                    ~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassObjectsFactory.java"
-            line="86"
-            column="37"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="        BassClientStateMachine.destroy(stateMachine);"
-        errorLine2="                               ~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bass_client/BassObjectsFactory.java"
-            line="87"
-            column="32"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="        mDatabaseManager.setProfileConnectionPolicy("
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/bas/BatteryService.java"
-            line="353"
+            line="325"
             column="26"/>
     </issue>
 
@@ -4078,7 +3121,7 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java"
-            line="449"
+            line="454"
             column="26"/>
     </issue>
 
@@ -4089,7 +3132,7 @@
         errorLine2="                                                              ~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java"
-            line="879"
+            line="884"
             column="63"/>
     </issue>
 
@@ -4892,7 +3935,7 @@
         errorLine2="                            ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1214"
+            line="1213"
             column="29"/>
     </issue>
 
@@ -4903,7 +3946,7 @@
         errorLine2="                                      ~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1227"
+            line="1226"
             column="39"/>
     </issue>
 
@@ -4914,7 +3957,7 @@
         errorLine2="                                                                 ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1228"
+            line="1227"
             column="66"/>
     </issue>
 
@@ -4925,7 +3968,7 @@
         errorLine2="                            ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1235"
+            line="1234"
             column="29"/>
     </issue>
 
@@ -4936,7 +3979,7 @@
         errorLine2="                                  ~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1237"
+            line="1236"
             column="35"/>
     </issue>
 
@@ -4947,7 +3990,7 @@
         errorLine2="                                                      ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1238"
+            line="1237"
             column="55"/>
     </issue>
 
@@ -4958,7 +4001,7 @@
         errorLine2="            ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1324"
+            line="1323"
             column="13"/>
     </issue>
 
@@ -4969,7 +4012,7 @@
         errorLine2="                            ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1324"
+            line="1323"
             column="29"/>
     </issue>
 
@@ -4980,7 +4023,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1325"
+            line="1324"
             column="18"/>
     </issue>
 
@@ -4991,7 +4034,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1326"
+            line="1325"
             column="18"/>
     </issue>
 
@@ -5002,7 +4045,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1327"
+            line="1326"
             column="18"/>
     </issue>
 
@@ -5013,7 +4056,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1328"
+            line="1327"
             column="18"/>
     </issue>
 
@@ -5024,7 +4067,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1330"
+            line="1329"
             column="18"/>
     </issue>
 
@@ -5035,7 +4078,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1331"
+            line="1330"
             column="18"/>
     </issue>
 
@@ -5046,7 +4089,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1332"
+            line="1331"
             column="18"/>
     </issue>
 
@@ -5057,7 +4100,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1333"
+            line="1332"
             column="18"/>
     </issue>
 
@@ -5068,7 +4111,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1335"
+            line="1334"
             column="18"/>
     </issue>
 
@@ -5079,7 +4122,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1336"
+            line="1335"
             column="18"/>
     </issue>
 
@@ -5090,6 +4133,17 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
+            line="1336"
+            column="18"/>
+    </issue>
+
+    <issue
+        id="VisibleForTests"
+        message="This method should only be accessed from tests or within private scope"
+        errorLine1="            data.setProfileConnectionPolicy("
+        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
             line="1337"
             column="18"/>
     </issue>
@@ -5101,18 +4155,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1338"
-            column="18"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            data.setProfileConnectionPolicy("
-        errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1340"
+            line="1339"
             column="18"/>
     </issue>
 
@@ -5123,7 +4166,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1342"
+            line="1341"
             column="18"/>
     </issue>
 
@@ -5134,7 +4177,7 @@
         errorLine2="                 ~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1343"
+            line="1342"
             column="18"/>
     </issue>
 
@@ -5145,7 +4188,7 @@
         errorLine2="        ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1349"
+            line="1348"
             column="9"/>
     </issue>
 
@@ -5156,7 +4199,7 @@
         errorLine2="                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1349"
+            line="1348"
             column="30"/>
     </issue>
 
@@ -5167,7 +4210,7 @@
         errorLine2="                  ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1350"
+            line="1349"
             column="19"/>
     </issue>
 
@@ -5178,7 +4221,7 @@
         errorLine2="                                ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1433"
+            line="1432"
             column="33"/>
     </issue>
 
@@ -5189,7 +4232,7 @@
         errorLine2="                 ~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1434"
+            line="1433"
             column="18"/>
     </issue>
 
@@ -5200,7 +4243,7 @@
         errorLine2="                                            ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1438"
+            line="1437"
             column="45"/>
     </issue>
 
@@ -5211,7 +4254,7 @@
         errorLine2="                                   ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1498"
+            line="1497"
             column="36"/>
     </issue>
 
@@ -5222,7 +4265,7 @@
         errorLine2="                                                   ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/storage/DatabaseManager.java"
-            line="1502"
+            line="1501"
             column="52"/>
     </issue>
 
@@ -5585,7 +4628,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="253"
+            line="273"
             column="38"/>
     </issue>
 
@@ -5596,7 +4639,7 @@
         errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="255"
+            line="275"
             column="42"/>
     </issue>
 
@@ -5607,7 +4650,7 @@
         errorLine2="                         ~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="268"
+            line="288"
             column="26"/>
     </issue>
 
@@ -5618,7 +4661,7 @@
         errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="421"
+            line="441"
             column="42"/>
     </issue>
 
@@ -5629,7 +4672,7 @@
         errorLine2="                             ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="489"
+            line="509"
             column="30"/>
     </issue>
 
@@ -5640,7 +4683,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="889"
+            line="909"
             column="48"/>
     </issue>
 
@@ -5651,7 +4694,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="937"
+            line="957"
             column="48"/>
     </issue>
 
@@ -5662,7 +4705,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="957"
+            line="977"
             column="34"/>
     </issue>
 
@@ -5673,7 +4716,7 @@
         errorLine2="                                             ~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="958"
+            line="978"
             column="46"/>
     </issue>
 
@@ -5684,7 +4727,7 @@
         errorLine2="                                ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1005"
+            line="1025"
             column="33"/>
     </issue>
 
@@ -5695,7 +4738,7 @@
         errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1033"
+            line="1053"
             column="31"/>
     </issue>
 
@@ -5706,7 +4749,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1119"
+            line="1139"
             column="38"/>
     </issue>
 
@@ -5717,7 +4760,7 @@
         errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1121"
+            line="1141"
             column="42"/>
     </issue>
 
@@ -5728,18 +4771,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1137"
-            column="48"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            AudioManager am = mSystemInterface.getAudioManager();"
-        errorLine2="                                               ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1163"
+            line="1157"
             column="48"/>
     </issue>
 
@@ -5750,51 +4782,29 @@
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1211"
+            line="1231"
             column="48"/>
     </issue>
 
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="                        mSystemInterface.getAudioManager().clearCommunicationDevice();"
-        errorLine2="                                         ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1232"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="        mNativeInterface.setScoAllowed(allowed);"
         errorLine2="                         ~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1266"
+            line="1286"
             column="26"/>
     </issue>
 
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    .getAudioManager()"
-        errorLine2="                     ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1402"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="            mNativeInterface.setActiveDevice(null);"
         errorLine2="                             ~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1410"
+            line="1421"
             column="30"/>
     </issue>
 
@@ -5805,7 +4815,7 @@
         errorLine2="                                  ~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1443"
+            line="1464"
             column="35"/>
     </issue>
 
@@ -5816,7 +4826,7 @@
         errorLine2="                                 ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1454"
+            line="1475"
             column="34"/>
     </issue>
 
@@ -5827,7 +4837,7 @@
         errorLine2="                                                                ~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1454"
+            line="1475"
             column="65"/>
     </issue>
 
@@ -5838,62 +4848,29 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1472"
+            line="1493"
             column="38"/>
     </issue>
 
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            .getAudioManager()"
-        errorLine2="                             ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1478"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            .getAudioManager()"
-        errorLine2="                             ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1492"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="                        mNativeInterface.setActiveDevice(previousActiveDevice);"
         errorLine2="                                         ~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1515"
+            line="1535"
             column="42"/>
     </issue>
 
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            .getAudioManager()"
-        errorLine2="                             ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1523"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="            if (stateMachine.getConnectionState() != BluetoothProfile.STATE_CONNECTED) {"
         errorLine2="                             ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1573"
+            line="1594"
             column="30"/>
     </issue>
 
@@ -5904,7 +4881,7 @@
         errorLine2="                                             ~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1598"
+            line="1619"
             column="46"/>
     </issue>
 
@@ -5915,7 +4892,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1808"
+            line="1829"
             column="38"/>
     </issue>
 
@@ -5926,7 +4903,7 @@
         errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1810"
+            line="1831"
             column="42"/>
     </issue>
 
@@ -5937,7 +4914,7 @@
         errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1892"
+            line="1913"
             column="35"/>
     </issue>
 
@@ -5948,7 +4925,7 @@
         errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1905"
+            line="1926"
             column="35"/>
     </issue>
 
@@ -5959,7 +4936,7 @@
         errorLine2="                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1906"
+            line="1927"
             column="34"/>
     </issue>
 
@@ -5970,7 +4947,7 @@
         errorLine2="                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1933"
+            line="1954"
             column="38"/>
     </issue>
 
@@ -5981,7 +4958,7 @@
         errorLine2="                                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1935"
+            line="1956"
             column="42"/>
     </issue>
 
@@ -5992,7 +4969,7 @@
         errorLine2="                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="1956"
+            line="1977"
             column="35"/>
     </issue>
 
@@ -6003,7 +4980,7 @@
         errorLine2="                                               ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2103"
+            line="2124"
             column="48"/>
     </issue>
 
@@ -6014,7 +4991,7 @@
         errorLine2="                                                                                     ~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2251"
+            line="2272"
             column="86"/>
     </issue>
 
@@ -6025,7 +5002,7 @@
         errorLine2="                                                   ~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2259"
+            line="2280"
             column="52"/>
     </issue>
 
@@ -6036,7 +5013,7 @@
         errorLine2="                                ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2287"
+            line="2308"
             column="33"/>
     </issue>
 
@@ -6047,7 +5024,7 @@
         errorLine2="                                     ~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2288"
+            line="2309"
             column="38"/>
     </issue>
 
@@ -6058,7 +5035,7 @@
         errorLine2="                                                       ~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2429"
+            line="2539"
             column="56"/>
     </issue>
 
@@ -6069,7 +5046,7 @@
         errorLine2="                                               ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2494"
+            line="2604"
             column="48"/>
     </issue>
 
@@ -6080,29 +5057,18 @@
         errorLine2="                                               ~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2498"
+            line="2608"
             column="48"/>
     </issue>
 
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="        boolean isScoOn = mSystemInterface.getAudioManager().isBluetoothScoOn();"
-        errorLine2="                                           ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2559"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="            ProfileService.println(sb, &quot;Telecom.isInCall(): &quot; + mSystemInterface.isInCall());"
         errorLine2="                                                                                 ~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2585"
+            line="2695"
             column="82"/>
     </issue>
 
@@ -6113,7 +5079,7 @@
         errorLine2="                                                                                  ~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2586"
+            line="2696"
             column="83"/>
     </issue>
 
@@ -6124,7 +5090,7 @@
         errorLine2="                                                                    ~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2589"
+            line="2699"
             column="69"/>
     </issue>
 
@@ -6135,7 +5101,7 @@
         errorLine2="                                          ~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetService.java"
-            line="2598"
+            line="2708"
             column="43"/>
     </issue>
 
@@ -6395,28 +5361,6 @@
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    mSystemInterface.getAudioManager().setA2dpSuspended(true);"
-        errorLine2="                                     ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java"
-            line="1334"
-            column="38"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                        mSystemInterface.getAudioManager().setLeAudioSuspended(true);"
-        errorLine2="                                         ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java"
-            line="1336"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="                                &amp;&amp; mSystemInterface.isHighDefCallInProgress()) {"
         errorLine2="                                                    ~~~~~~~~~~~~~~~~~~~~~~~">
         <location
@@ -6439,28 +5383,6 @@
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="                        mSystemInterface.getAudioManager().setA2dpSuspended(false);"
-        errorLine2="                                         ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java"
-            line="1354"
-            column="42"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="                            mSystemInterface.getAudioManager().setLeAudioSuspended(false);"
-        errorLine2="                                             ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java"
-            line="1356"
-            column="46"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="                        if (!mNativeInterface.disconnectAudio(mDevice)) {"
         errorLine2="                                              ~~~~~~~~~~~~~~~">
         <location
@@ -6483,28 +5405,6 @@
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="                    .getAudioManager()"
-        errorLine2="                     ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java"
-            line="1556"
-            column="22"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            mSystemInterface.getAudioManager().clearAudioServerStateCallback();"
-        errorLine2="                             ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java"
-            line="1567"
-            column="30"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="                        if (!mNativeInterface.disconnectAudio(mDevice)) {"
         errorLine2="                                              ~~~~~~~~~~~~~~~">
         <location
@@ -6527,17 +5427,6 @@
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="        AudioManager am = mSystemInterface.getAudioManager();"
-        errorLine2="                                           ~~~~~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java"
-            line="1870"
-            column="44"/>
-    </issue>
-
-    <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
         errorLine1="            mNativeInterface.atResponseCode(mDevice, HeadsetHalConstants.AT_RESPONSE_ERROR, 0);"
         errorLine2="                             ~~~~~~~~~~~~~~">
         <location
@@ -6762,18 +5651,18 @@
         errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java"
-            line="483"
+            line="452"
             column="31"/>
     </issue>
 
     <issue
         id="VisibleForTests"
         message="This method should only be accessed from tests or within private scope"
-        errorLine1="            sm.sendMessage(HearingAidStateMachine.STACK_EVENT, stackEvent);"
-        errorLine2="                                                  ~~~~~~~~~~~">
+        errorLine1="            sm.sendMessage(HearingAidStateMachine.MESSAGE_STACK_EVENT, stackEvent);"
+        errorLine2="                                                  ~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java"
-            line="655"
+            line="624"
             column="51"/>
     </issue>
 
@@ -6839,7 +5728,7 @@
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java"
-            line="187"
+            line="188"
             column="5"/>
     </issue>
 
@@ -6850,7 +5739,7 @@
         errorLine2="                            ~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java"
-            line="520"
+            line="531"
             column="29"/>
     </issue>
 
@@ -6861,7 +5750,7 @@
         errorLine2="                                      ~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java"
-            line="566"
+            line="577"
             column="39"/>
     </issue>
 
@@ -6872,7 +5761,7 @@
         errorLine2="                                                                                ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java"
-            line="566"
+            line="577"
             column="81"/>
     </issue>
 
@@ -6883,7 +5772,7 @@
         errorLine2="                                                   ~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java"
-            line="579"
+            line="590"
             column="52"/>
     </issue>
 
@@ -6894,7 +5783,7 @@
         errorLine2="                            ~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java"
-            line="656"
+            line="667"
             column="29"/>
     </issue>
 
@@ -6905,7 +5794,7 @@
         errorLine2="                                                   ~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java"
-            line="2644"
+            line="2690"
             column="52"/>
     </issue>
 
@@ -6916,7 +5805,7 @@
         errorLine2="                                                   ~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java"
-            line="3393"
+            line="3439"
             column="52"/>
     </issue>
 
@@ -6927,7 +5816,7 @@
         errorLine2="                              ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java"
-            line="4382"
+            line="4439"
             column="31"/>
     </issue>
 
@@ -6938,7 +5827,7 @@
         errorLine2="                            ~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java"
-            line="4521"
+            line="4578"
             column="29"/>
     </issue>
 
@@ -8027,22 +6916,11 @@
         errorLine2="                         ~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/vc/VolumeControlService.java"
-            line="416"
+            line="377"
             column="26"/>
     </issue>
 
     <issue
-        id="VisibleForTests"
-        message="This method should only be accessed from tests or within private scope"
-        errorLine1="            sm.sendMessage(VolumeControlStateMachine.STACK_EVENT, stackEvent);"
-        errorLine2="                                                     ~~~~~~~~~~~">
-        <location
-            file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/vc/VolumeControlService.java"
-            line="1316"
-            column="54"/>
-    </issue>
-
-    <issue
         id="MissingVersion"
         message="Should set `android:versionCode` to specify the application version"
         errorLine1="&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;"
@@ -8181,7 +7059,7 @@
         errorLine2="                ~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java"
-            line="6169"
+            line="6172"
             column="17"/>
     </issue>
 
@@ -8269,7 +7147,7 @@
         errorLine2="    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java"
-            line="418"
+            line="546"
             column="5"/>
     </issue>
 
@@ -8280,7 +7158,7 @@
         errorLine2="        ^">
         <location
             file="packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java"
-            line="773"
+            line="777"
             column="9"/>
     </issue>
 
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index b94a760..5c8344b 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -27,8 +27,6 @@
 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;
@@ -51,62 +49,43 @@
 public class A2dpSinkService extends ProfileService {
     private static final String TAG = A2dpSinkService.class.getSimpleName();
 
+    private static A2dpSinkService sService;
+
     // This is also used as a lock for shared data in {@link A2dpSinkService}
     @GuardedBy("mDeviceStateMap")
     private final Map<BluetoothDevice, A2dpSinkStateMachine> mDeviceStateMap =
             new ConcurrentHashMap<>(1);
 
+    private final Object mActiveDeviceLock = new Object();
+    private final Object mStreamHandlerLock = new Object();
+
+    private final AdapterService mAdapterService;
+    private final DatabaseManager mDatabaseManager;
     private final A2dpSinkNativeInterface mNativeInterface;
     private final Looper mLooper;
+    private final int mMaxConnectedAudioDevices;
 
-    private final Object mActiveDeviceLock = new Object();
+    @GuardedBy("mStreamHandlerLock")
+    private final A2dpSinkStreamHandler mA2dpSinkStreamHandler;
 
     @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;
-
-    public A2dpSinkService(Context ctx) {
-        super(ctx);
-        mNativeInterface = requireNonNull(A2dpSinkNativeInterface.getInstance());
-        mLooper = Looper.getMainLooper();
+    public A2dpSinkService(AdapterService adapterService) {
+        this(adapterService, A2dpSinkNativeInterface.getInstance(), Looper.getMainLooper());
     }
 
     @VisibleForTesting
-    A2dpSinkService(Context ctx, A2dpSinkNativeInterface nativeInterface, Looper looper) {
-        super(ctx);
+    A2dpSinkService(
+            AdapterService adapterService, A2dpSinkNativeInterface nativeInterface, Looper looper) {
+        super(requireNonNull(adapterService));
+        mAdapterService = adapterService;
+        mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
         mNativeInterface = requireNonNull(nativeInterface);
         mLooper = looper;
-    }
-
-    public static boolean isEnabled() {
-        return BluetoothProperties.isProfileA2dpSinkEnabled().orElse(false);
-    }
-
-    @Override
-    public void start() {
-        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);
-
         synchronized (mStreamHandlerLock) {
             mA2dpSinkStreamHandler = new A2dpSinkStreamHandler(this, mNativeInterface);
         }
@@ -114,6 +93,10 @@
         setA2dpSinkService(this);
     }
 
+    public static boolean isEnabled() {
+        return BluetoothProperties.isProfileA2dpSinkEnabled().orElse(false);
+    }
+
     @Override
     public void stop() {
         setA2dpSinkService(null);
@@ -125,10 +108,7 @@
             mDeviceStateMap.clear();
         }
         synchronized (mStreamHandlerLock) {
-            if (mA2dpSinkStreamHandler != null) {
-                mA2dpSinkStreamHandler.cleanup();
-                mA2dpSinkStreamHandler = null;
-            }
+            mA2dpSinkStreamHandler.cleanup();
         }
     }
 
@@ -164,7 +144,6 @@
     /** Request audio focus such that the designated device can stream audio */
     public void requestAudioFocus(BluetoothDevice device, boolean request) {
         synchronized (mStreamHandlerLock) {
-            if (mA2dpSinkStreamHandler == null) return;
             mA2dpSinkStreamHandler.requestAudioFocus(request);
         }
     }
@@ -176,17 +155,12 @@
      */
     public int getFocusState() {
         synchronized (mStreamHandlerLock) {
-            if (mA2dpSinkStreamHandler == null) return AudioManager.ERROR;
             return mA2dpSinkStreamHandler.getFocusState();
         }
     }
 
-    @RequiresPermission(BLUETOOTH_PRIVILEGED)
     boolean isA2dpPlaying(BluetoothDevice device) {
-        enforceCallingOrSelfPermission(
-                BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
         synchronized (mStreamHandlerLock) {
-            if (mA2dpSinkStreamHandler == null) return false;
             return mA2dpSinkStreamHandler.isPlaying();
         }
     }
@@ -309,6 +283,9 @@
             if (service == null) {
                 return false;
             }
+
+            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+
             return service.isA2dpPlaying(device);
         }
 
@@ -407,19 +384,14 @@
     }
 
     protected A2dpSinkStateMachine getOrCreateStateMachine(BluetoothDevice device) {
-        A2dpSinkStateMachine newStateMachine =
-                new A2dpSinkStateMachine(mLooper, device, this, mNativeInterface);
         synchronized (mDeviceStateMap) {
-            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. Else
-            // we quit the new one so we don't leak a thread
-            if (existingStateMachine == null) {
-                newStateMachine.start();
-                return newStateMachine;
+            A2dpSinkStateMachine sm = mDeviceStateMap.get(device);
+            if (sm != null) {
+                return sm;
             }
-            return existingStateMachine;
+            sm = new A2dpSinkStateMachine(this, device, mLooper, mNativeInterface);
+            mDeviceStateMap.put(device, sm);
+            return sm;
         }
     }
 
@@ -469,8 +441,8 @@
             stateMachine = mDeviceStateMap.get(device);
         }
         return (stateMachine == null)
-                    ? BluetoothProfile.STATE_DISCONNECTED
-                    : stateMachine.getState();
+                ? BluetoothProfile.STATE_DISCONNECTED
+                : stateMachine.getState();
     }
 
     /**
@@ -543,18 +515,10 @@
     /** Receive and route a stack event from the JNI */
     protected void messageFromNative(StackEvent event) {
         switch (event.mType) {
-            case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
-                onConnectionStateChanged(event);
-                return;
-            case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED:
-                onAudioStateChanged(event);
-                return;
-            case StackEvent.EVENT_TYPE_AUDIO_CONFIG_CHANGED:
-                onAudioConfigChanged(event);
-                return;
-            default:
-                Log.e(TAG, "Received unknown stack event of type " + event.mType);
-                return;
+            case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED -> onConnectionStateChanged(event);
+            case StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED -> onAudioStateChanged(event);
+            case StackEvent.EVENT_TYPE_AUDIO_CONFIG_CHANGED -> onAudioConfigChanged(event);
+            default -> Log.e(TAG, "Received unknown stack event of type " + event.mType);
         }
     }
 
@@ -570,18 +534,11 @@
     private void onAudioStateChanged(StackEvent event) {
         int state = event.mState;
         synchronized (mStreamHandlerLock) {
-            if (mA2dpSinkStreamHandler == null) {
-                Log.e(TAG, "Received audio state change before we've been started");
-                return;
-            } else if (state == StackEvent.AUDIO_STATE_STARTED) {
-                mA2dpSinkStreamHandler
-                        .obtainMessage(A2dpSinkStreamHandler.SRC_STR_START)
-                        .sendToTarget();
+            if (state == StackEvent.AUDIO_STATE_STARTED) {
+                mA2dpSinkStreamHandler.sendEmptyMessage(A2dpSinkStreamHandler.SRC_STR_START);
             } else if (state == StackEvent.AUDIO_STATE_STOPPED
                     || state == StackEvent.AUDIO_STATE_REMOTE_SUSPEND) {
-                mA2dpSinkStreamHandler
-                        .obtainMessage(A2dpSinkStreamHandler.SRC_STR_STOP)
-                        .sendToTarget();
+                mA2dpSinkStreamHandler.sendEmptyMessage(A2dpSinkStreamHandler.SRC_STR_STOP);
             } else {
                 Log.w(TAG, "Unhandled audio state change, state=" + state);
             }
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
index 48cb1bb..18d956b 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
@@ -17,6 +17,11 @@
 
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
 
 import android.annotation.RequiresPermission;
 import android.bluetooth.BluetoothA2dpSink;
@@ -41,15 +46,15 @@
     private static final String TAG = A2dpSinkStateMachine.class.getSimpleName();
 
     // 0->99 Events from Outside
-    @VisibleForTesting static final int CONNECT = 1;
-    @VisibleForTesting static final int DISCONNECT = 2;
+    @VisibleForTesting static final int MESSAGE_CONNECT = 1;
+    @VisibleForTesting static final int MESSAGE_DISCONNECT = 2;
 
     // 100->199 Internal Events
     @VisibleForTesting static final int CLEANUP = 100;
-    @VisibleForTesting static final int CONNECT_TIMEOUT = 101;
+    @VisibleForTesting static final int MESSAGE_CONNECT_TIMEOUT = 101;
 
     // 200->299 Events from Native
-    @VisibleForTesting static final int STACK_EVENT = 200;
+    @VisibleForTesting static final int MESSAGE_STACK_EVENT = 200;
 
     static final int CONNECT_TIMEOUT_MS = 10000;
 
@@ -62,13 +67,13 @@
     protected final Connected mConnected;
     protected final Disconnecting mDisconnecting;
 
-    protected int mMostRecentState = BluetoothProfile.STATE_DISCONNECTED;
+    protected int mMostRecentState = STATE_DISCONNECTED;
     protected BluetoothAudioConfig mAudioConfig = null;
 
     A2dpSinkStateMachine(
-            Looper looper,
-            BluetoothDevice device,
             A2dpSinkService service,
+            BluetoothDevice device,
+            Looper looper,
             A2dpSinkNativeInterface nativeInterface) {
         super(TAG, looper);
         mDevice = device;
@@ -88,6 +93,7 @@
 
         setInitialState(mDisconnected);
         Log.d(TAG, "[" + mDevice + "] State machine created");
+        start();
     }
 
     /**
@@ -115,17 +121,17 @@
 
     /** send the Connect command asynchronously */
     final void connect() {
-        sendMessage(CONNECT);
+        sendMessage(MESSAGE_CONNECT);
     }
 
     /** send the Disconnect command asynchronously */
     final void disconnect() {
-        sendMessage(DISCONNECT);
+        sendMessage(MESSAGE_DISCONNECT);
     }
 
     /** send the stack event asynchronously */
     final void onStackEvent(StackEvent event) {
-        sendMessage(STACK_EVENT, event);
+        sendMessage(MESSAGE_STACK_EVENT, event);
     }
 
     /**
@@ -154,56 +160,50 @@
         @Override
         public void enter() {
             Log.d(TAG, "[" + mDevice + "] Enter Disconnected");
-            if (mMostRecentState != BluetoothProfile.STATE_DISCONNECTED) {
+            if (mMostRecentState != STATE_DISCONNECTED) {
                 sendMessage(CLEANUP);
             }
-            onConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTED);
+            onConnectionStateChanged(STATE_DISCONNECTED);
         }
 
         @Override
         public boolean processMessage(Message message) {
             switch (message.what) {
-                case STACK_EVENT:
-                    processStackEvent((StackEvent) message.obj);
-                    return true;
-                case CONNECT:
+                case MESSAGE_STACK_EVENT -> processStackEvent((StackEvent) message.obj);
+                case MESSAGE_CONNECT -> {
                     Log.d(TAG, "[" + mDevice + "] Connect");
                     transitionTo(mConnecting);
-                    return true;
-                case CLEANUP:
-                    mService.removeStateMachine(A2dpSinkStateMachine.this);
-                    return true;
+                }
+                case CLEANUP -> mService.removeStateMachine(A2dpSinkStateMachine.this);
+                default -> {
+                    return false;
+                }
             }
-            return false;
+            return true;
         }
 
         @RequiresPermission(BLUETOOTH_PRIVILEGED)
         void processStackEvent(StackEvent event) {
-            switch (event.mType) {
-                case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
-                    switch (event.mState) {
-                        case StackEvent.CONNECTION_STATE_CONNECTING:
-                            if (mService.getConnectionPolicy(mDevice)
-                                    == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
-                                Log.w(
-                                        TAG,
-                                        "["
-                                                + mDevice
-                                                + "] Ignore incoming connection, profile"
-                                                + " is turned off");
-                                mNativeInterface.disconnectA2dpSink(mDevice);
-                            } else {
-                                mConnecting.mIncomingConnection = true;
-                                transitionTo(mConnecting);
-                            }
-                            break;
-                        case StackEvent.CONNECTION_STATE_CONNECTED:
-                            transitionTo(mConnected);
-                            break;
-                        case StackEvent.CONNECTION_STATE_DISCONNECTED:
-                            sendMessage(CLEANUP);
-                            break;
+            if (event.mType != StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
+                return;
+            }
+            switch (event.mState) {
+                case STATE_CONNECTING -> {
+                    if (mService.getConnectionPolicy(mDevice) == CONNECTION_POLICY_FORBIDDEN) {
+                        Log.w(
+                                TAG,
+                                "["
+                                        + mDevice
+                                        + "] Ignore incoming connection, profile"
+                                        + " is turned off");
+                        mNativeInterface.disconnectA2dpSink(mDevice);
+                    } else {
+                        mConnecting.mIncomingConnection = true;
+                        transitionTo(mConnecting);
                     }
+                }
+                case STATE_CONNECTED -> transitionTo(mConnected);
+                case STATE_DISCONNECTED -> sendMessage(CLEANUP);
             }
         }
     }
@@ -214,8 +214,8 @@
         @Override
         public void enter() {
             Log.d(TAG, "[" + mDevice + "] Enter Connecting");
-            onConnectionStateChanged(BluetoothProfile.STATE_CONNECTING);
-            sendMessageDelayed(CONNECT_TIMEOUT, CONNECT_TIMEOUT_MS);
+            onConnectionStateChanged(STATE_CONNECTING);
+            sendMessageDelayed(MESSAGE_CONNECT_TIMEOUT, CONNECT_TIMEOUT_MS);
 
             if (!mIncomingConnection) {
                 mNativeInterface.connectA2dpSink(mDevice);
@@ -227,13 +227,9 @@
         @Override
         public boolean processMessage(Message message) {
             switch (message.what) {
-                case STACK_EVENT:
-                    processStackEvent((StackEvent) message.obj);
-                    return true;
-                case CONNECT_TIMEOUT:
-                    transitionTo(mDisconnected);
-                    return true;
-                case DISCONNECT:
+                case MESSAGE_STACK_EVENT -> processStackEvent((StackEvent) message.obj);
+                case MESSAGE_CONNECT_TIMEOUT -> transitionTo(mDisconnected);
+                case MESSAGE_DISCONNECT -> {
                     Log.d(
                             TAG,
                             "["
@@ -241,28 +237,27 @@
                                     + "] Received disconnect message while connecting."
                                     + "deferred");
                     deferMessage(message);
-                    return true;
+                }
+                default -> {
+                    return false;
+                }
             }
-            return false;
+            return true;
         }
 
         void processStackEvent(StackEvent event) {
-            switch (event.mType) {
-                case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
-                    switch (event.mState) {
-                        case StackEvent.CONNECTION_STATE_CONNECTED:
-                            transitionTo(mConnected);
-                            break;
-                        case StackEvent.CONNECTION_STATE_DISCONNECTED:
-                            transitionTo(mDisconnected);
-                            break;
-                    }
+            if (event.mType != StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
+                return;
+            }
+            switch (event.mState) {
+                case STATE_CONNECTED -> transitionTo(mConnected);
+                case STATE_DISCONNECTED -> transitionTo(mDisconnected);
             }
         }
 
         @Override
         public void exit() {
-            removeMessages(CONNECT_TIMEOUT);
+            removeMessages(MESSAGE_CONNECT_TIMEOUT);
             mIncomingConnection = false;
         }
     }
@@ -271,42 +266,39 @@
         @Override
         public void enter() {
             Log.d(TAG, "[" + mDevice + "] Enter Connected");
-            onConnectionStateChanged(BluetoothProfile.STATE_CONNECTED);
+            onConnectionStateChanged(STATE_CONNECTED);
         }
 
         @Override
         public boolean processMessage(Message message) {
             switch (message.what) {
-                case DISCONNECT:
+                case MESSAGE_DISCONNECT -> {
                     transitionTo(mDisconnecting);
                     mNativeInterface.disconnectA2dpSink(mDevice);
-                    return true;
-                case STACK_EVENT:
-                    processStackEvent((StackEvent) message.obj);
-                    return true;
+                }
+                case MESSAGE_STACK_EVENT -> processStackEvent((StackEvent) message.obj);
+                default -> {
+                    return false;
+                }
             }
-            return false;
+            return true;
         }
 
         void processStackEvent(StackEvent event) {
             switch (event.mType) {
-                case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED -> {
                     switch (event.mState) {
-                        case StackEvent.CONNECTION_STATE_DISCONNECTING:
-                            transitionTo(mDisconnecting);
-                            break;
-                        case StackEvent.CONNECTION_STATE_DISCONNECTED:
-                            transitionTo(mDisconnected);
-                            break;
+                        case STATE_DISCONNECTING -> transitionTo(mDisconnecting);
+                        case STATE_DISCONNECTED -> transitionTo(mDisconnected);
                     }
-                    break;
-                case StackEvent.EVENT_TYPE_AUDIO_CONFIG_CHANGED:
+                }
+                case StackEvent.EVENT_TYPE_AUDIO_CONFIG_CHANGED -> {
                     mAudioConfig =
                             new BluetoothAudioConfig(
                                     event.mSampleRate,
                                     event.mChannelCount,
                                     AudioFormat.ENCODING_PCM_16BIT);
-                    break;
+                }
             }
         }
     }
@@ -315,7 +307,7 @@
         @Override
         public void enter() {
             Log.d(TAG, "[" + mDevice + "] Enter Disconnecting");
-            onConnectionStateChanged(BluetoothProfile.STATE_DISCONNECTING);
+            onConnectionStateChanged(STATE_DISCONNECTING);
             transitionTo(mDisconnected);
         }
     }
@@ -324,7 +316,7 @@
         if (mMostRecentState == currentState) {
             return;
         }
-        if (currentState == BluetoothProfile.STATE_CONNECTED) {
+        if (currentState == STATE_CONNECTED) {
             MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP_SINK);
         }
         Log.d(TAG, "[" + mDevice + "] Connection state: " + mMostRecentState + "->" + currentState);
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
index 6cf7d99..e62f924 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamHandler.java
@@ -70,9 +70,10 @@
     private static final int STATE_FOCUS_GRANTED = 1;
 
     // Private variables.
-    private A2dpSinkService mA2dpSinkService;
-    private A2dpSinkNativeInterface mNativeInterface;
-    private AudioManager mAudioManager;
+    private final A2dpSinkService mA2dpSinkService;
+    private final A2dpSinkNativeInterface mNativeInterface;
+    private final AudioManager mAudioManager;
+
     // Keep track if the remote device is providing audio
     private boolean mStreamAvailable = false;
     // Keep track of the relevant audio focus (None, Transient, Gain)
diff --git a/android/app/src/com/android/bluetooth/a2dpsink/StackEvent.java b/android/app/src/com/android/bluetooth/a2dpsink/StackEvent.java
index 1a1429a..0c88599 100644
--- a/android/app/src/com/android/bluetooth/a2dpsink/StackEvent.java
+++ b/android/app/src/com/android/bluetooth/a2dpsink/StackEvent.java
@@ -24,12 +24,6 @@
     static final int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
     static final int EVENT_TYPE_AUDIO_CONFIG_CHANGED = 3;
 
-    // match up with btav_connection_state_t enum of bt_av.h
-    static final int CONNECTION_STATE_DISCONNECTED = 0;
-    static final int CONNECTION_STATE_CONNECTING = 1;
-    static final int CONNECTION_STATE_CONNECTED = 2;
-    static final int CONNECTION_STATE_DISCONNECTING = 3;
-
     // match up with btav_audio_state_t enum of bt_av.h
     static final int AUDIO_STATE_REMOTE_SUSPEND = 0;
     static final int AUDIO_STATE_STOPPED = 1;
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
index 17bf92f..93579a8 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
@@ -16,6 +16,8 @@
 
 package com.android.bluetooth.avrcp;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.NonNull;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
@@ -62,14 +64,18 @@
     private final BluetoothEventLogger mMediaKeyEventLogger =
             new BluetoothEventLogger(MEDIA_KEY_EVENT_LOGGER_SIZE, MEDIA_KEY_EVENT_LOGGER_TITLE);
 
-    private AvrcpVersion mAvrcpVersion;
-    private MediaPlayerList mMediaPlayerList;
-    private PlayerSettingsManager mPlayerSettingsManager;
-    private AudioManager mAudioManager;
-    private AvrcpBroadcastReceiver mReceiver;
-    private AvrcpNativeInterface mNativeInterface;
-    private AvrcpVolumeManager mVolumeManager;
-    private ServiceFactory mFactory = new ServiceFactory();
+    // Cover Art Service (Storage + BIP Server)
+    private final AvrcpCoverArtService mAvrcpCoverArtService;
+    private final AdapterService mAdapterService;
+    private final AvrcpVersion mAvrcpVersion;
+    private final MediaPlayerList mMediaPlayerList;
+    private final PlayerSettingsManager mPlayerSettingsManager;
+    private final AudioManager mAudioManager;
+    private final AvrcpBroadcastReceiver mReceiver;
+    private final AvrcpNativeInterface mNativeInterface;
+    private final AvrcpVolumeManager mVolumeManager;
+
+    private final ServiceFactory mFactory = new ServiceFactory();
     private final BroadcastReceiver mUserUnlockedReceiver =
             new BroadcastReceiver() {
                 @Override
@@ -84,24 +90,65 @@
                         Log.e(TAG, "userChangeReceiver received an invalid EXTRA_USER_HANDLE");
                         return;
                     }
-                    if (mMediaPlayerList != null) {
-                        mMediaPlayerList.init(new ListCallback());
-                    }
+                    mMediaPlayerList.init(new ListCallback());
                 }
             };
 
     // Only used to see if the metadata has changed from its previous value
     private MediaData mCurrentData;
 
-    // Cover Art Service (Storage + BIP Server)
-    private AvrcpCoverArtService mAvrcpCoverArtService = null;
-
     private static AvrcpTargetService sInstance = null;
-    private final AdapterService mAdapterService;
 
     public AvrcpTargetService(AdapterService adapterService) {
-        super(adapterService);
+        super(requireNonNull(adapterService));
         mAdapterService = adapterService;
+        mAudioManager = requireNonNull(getSystemService(AudioManager.class));
+        mNativeInterface = requireNonNull(AvrcpNativeInterface.getInstance());
+
+        mMediaPlayerList = new MediaPlayerList(Looper.myLooper(), this);
+
+        IntentFilter userFilter = new IntentFilter();
+        userFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
+        getApplicationContext().registerReceiver(mUserUnlockedReceiver, userFilter);
+
+        Log.i(TAG, "Starting the AVRCP Target Service");
+        mCurrentData = new MediaData(null, null, null);
+
+        mPlayerSettingsManager = new PlayerSettingsManager(mMediaPlayerList, this);
+        mNativeInterface.init(this);
+
+        mAvrcpVersion = AvrcpVersion.getCurrentSystemPropertiesValue();
+        mVolumeManager = new AvrcpVolumeManager(mAdapterService, mAudioManager, mNativeInterface);
+
+        UserManager userManager = getApplicationContext().getSystemService(UserManager.class);
+        if (userManager.isUserUnlocked()) {
+            mMediaPlayerList.init(new ListCallback());
+        }
+
+        if (!getResources().getBoolean(R.bool.avrcp_target_enable_cover_art)) {
+            mAvrcpCoverArtService = null;
+        } else if (!mAvrcpVersion.isAtleastVersion(AvrcpVersion.AVRCP_VERSION_1_6)) {
+            Log.e(TAG, "Please use AVRCP version 1.6 to enable cover art");
+            mAvrcpCoverArtService = null;
+        } else {
+            AvrcpCoverArtService coverArtService = new AvrcpCoverArtService();
+            if (coverArtService.start()) {
+                mAvrcpCoverArtService = coverArtService;
+            } else {
+                Log.e(TAG, "Failed to start cover art service");
+                mAvrcpCoverArtService = null;
+            }
+        }
+
+        mReceiver = new AvrcpBroadcastReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        filter.addAction(AudioManager.ACTION_VOLUME_CHANGED);
+        registerReceiver(mReceiver, filter);
+
+        // Only allow the service to be used once it is initialized
+        sInstance = this;
     }
 
     /** Checks for profile enabled state in Bluetooth sysprops. */
@@ -113,8 +160,6 @@
     class ListCallback implements MediaPlayerList.MediaUpdateCallback {
         @Override
         public void run(MediaData data) {
-            if (mNativeInterface == null) return;
-
             boolean metadata = !Objects.equals(mCurrentData.metadata, data.metadata);
             boolean state = !MediaPlayerWrapper.playstateEquals(mCurrentData.state, data.state);
             boolean queue = isQueueUpdated(mCurrentData.queue, data.queue);
@@ -134,8 +179,6 @@
 
         @Override
         public void run(boolean availablePlayers, boolean addressedPlayers, boolean uids) {
-            if (mNativeInterface == null) return;
-
             mNativeInterface.sendFolderUpdate(availablePlayers, addressedPlayers, uids);
         }
     }
@@ -148,17 +191,18 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            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);
-                    BluetoothDevice activeDevice = getA2dpActiveDevice();
-                    if (activeDevice != null
-                            && !mVolumeManager.getAbsoluteVolumeSupported(activeDevice)) {
-                        Log.d(TAG, "stream volume change to " + volume + " " + activeDevice);
-                        mVolumeManager.storeVolumeForDevice(activeDevice, volume);
-                    }
-                }
+            if (!action.equals(AudioManager.ACTION_VOLUME_CHANGED)) {
+                return;
+            }
+            int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+            if (streamType != AudioManager.STREAM_MUSIC) {
+                return;
+            }
+            int volume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
+            BluetoothDevice activeDevice = getA2dpActiveDevice();
+            if (activeDevice != null && !mVolumeManager.getAbsoluteVolumeSupported(activeDevice)) {
+                Log.d(TAG, "stream volume change to " + volume + " " + activeDevice);
+                mVolumeManager.storeVolumeForDevice(activeDevice, volume);
             }
         }
     }
@@ -194,61 +238,6 @@
     }
 
     @Override
-    public void start() {
-        if (sInstance != null) {
-            throw new IllegalStateException("start() called twice");
-        }
-
-        IntentFilter userFilter = new IntentFilter();
-        userFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        userFilter.addAction(Intent.ACTION_USER_UNLOCKED);
-        getApplicationContext().registerReceiver(mUserUnlockedReceiver, userFilter);
-
-        Log.i(TAG, "Starting the AVRCP Target Service");
-        mCurrentData = new MediaData(null, null, null);
-
-        mAudioManager = getSystemService(AudioManager.class);
-
-        mMediaPlayerList = new MediaPlayerList(Looper.myLooper(), this);
-
-        mPlayerSettingsManager = new PlayerSettingsManager(mMediaPlayerList, this);
-
-        mNativeInterface = AvrcpNativeInterface.getInstance();
-        mNativeInterface.init(AvrcpTargetService.this);
-
-        mAvrcpVersion = AvrcpVersion.getCurrentSystemPropertiesValue();
-
-        mVolumeManager = new AvrcpVolumeManager(mAdapterService, mAudioManager, mNativeInterface);
-
-        UserManager userManager = getApplicationContext().getSystemService(UserManager.class);
-        if (userManager.isUserUnlocked()) {
-            mMediaPlayerList.init(new ListCallback());
-        }
-
-        if (getResources().getBoolean(R.bool.avrcp_target_enable_cover_art)) {
-            if (mAvrcpVersion.isAtleastVersion(AvrcpVersion.AVRCP_VERSION_1_6)) {
-                mAvrcpCoverArtService = new AvrcpCoverArtService();
-                boolean started = mAvrcpCoverArtService.start();
-                if (!started) {
-                    Log.e(TAG, "Failed to start cover art service");
-                    mAvrcpCoverArtService = null;
-                }
-            } else {
-                Log.e(TAG, "Please use AVRCP version 1.6 to enable cover art");
-            }
-        }
-
-        mReceiver = new AvrcpBroadcastReceiver();
-        IntentFilter filter = new IntentFilter();
-        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
-        filter.addAction(AudioManager.ACTION_VOLUME_CHANGED);
-        registerReceiver(mReceiver, filter);
-
-        // Only allow the service to be used once it is initialized
-        sInstance = this;
-    }
-
-    @Override
     public void stop() {
         Log.i(TAG, "Stopping the AVRCP Target Service");
 
@@ -260,22 +249,15 @@
         if (mAvrcpCoverArtService != null) {
             mAvrcpCoverArtService.stop();
         }
-        mAvrcpCoverArtService = null;
 
         sInstance = null;
         unregisterReceiver(mReceiver);
 
         // We check the interfaces first since they only get set on User Unlocked
-        if (mPlayerSettingsManager != null) mPlayerSettingsManager.cleanup();
-        if (mMediaPlayerList != null) mMediaPlayerList.cleanup();
-        if (mNativeInterface != null) mNativeInterface.cleanup();
+        mPlayerSettingsManager.cleanup();
+        mMediaPlayerList.cleanup();
+        mNativeInterface.cleanup();
         getApplicationContext().unregisterReceiver(mUserUnlockedReceiver);
-
-        mPlayerSettingsManager = null;
-        mMediaPlayerList = null;
-        mNativeInterface = null;
-        mAudioManager = null;
-        mReceiver = null;
     }
 
     /** Returns the active A2DP {@link BluetoothDevice} */
@@ -339,7 +321,7 @@
      * <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 (device == null) return;
         if (newState == BluetoothProfile.STATE_DISCONNECTED) {
             // If there is no connection, disconnectDevice() will do nothing
             if (mNativeInterface.disconnectDevice(device)) {
@@ -359,10 +341,8 @@
      */
     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);
-        }
+        // Update all the playback status info for each connected device
+        mNativeInterface.sendMediaUpdate(false, true, false);
     }
 
     /** Informs {@link AvrcpVolumeManager} that a remote device requests a volume change */
@@ -575,11 +555,6 @@
 
     /** Called from player callback to indicate new settings to remote device. */
     public void sendPlayerSettings(int repeatMode, int shuffleMode) {
-        if (mNativeInterface == null) {
-            Log.i(TAG, "Tried to send Player Settings while native interface is null");
-            return;
-        }
-
         mNativeInterface.sendPlayerSettings(repeatMode, shuffleMode);
     }
 
@@ -626,11 +601,7 @@
         StringBuilder tempBuilder = new StringBuilder();
         tempBuilder.append("AVRCP version: ").append(mAvrcpVersion).append("\n");
 
-        if (mMediaPlayerList != null) {
-            mMediaPlayerList.dump(tempBuilder);
-        } else {
-            tempBuilder.append("\nMedia Player List is empty\n");
-        }
+        mMediaPlayerList.dump(tempBuilder);
 
         mMediaKeyEventLogger.dump(tempBuilder);
         tempBuilder.append("\n");
diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java b/android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
index a8c31ea..298162a 100644
--- a/android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
+++ b/android/app/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
@@ -77,15 +77,6 @@
                                     .setBrowsable(true)
                                     .build());
             mRootNode.setCached(true);
-        } else if (!Flags.randomizeDeviceLevelMediaIds()) {
-            mRootNode =
-                    new BrowseNode(
-                            new AvrcpItem.Builder()
-                                    .setDevice(device)
-                                    .setUuid(ROOT + device.getAddress().toString())
-                                    .setTitle(Utils.getName(device))
-                                    .setBrowsable(true)
-                                    .build());
         } else {
             mRootNode =
                     new BrowseNode(
@@ -184,11 +175,7 @@
         BrowseNode(BluetoothDevice device) {
             AvrcpItem.Builder aid = new AvrcpItem.Builder();
             aid.setDevice(device);
-            if (Flags.randomizeDeviceLevelMediaIds()) {
-                aid.setUuid(ROOT + device.getAddress().toString() + UUID.randomUUID().toString());
-            } else {
-                aid.setUuid(PLAYER_PREFIX + device.getAddress().toString());
-            }
+            aid.setUuid(ROOT + device.getAddress().toString() + UUID.randomUUID().toString());
             aid.setDisplayableName(Utils.getName(device));
             aid.setTitle(Utils.getName(device));
             aid.setBrowsable(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 7a0389a..957b02c 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java
@@ -23,6 +23,7 @@
 
 import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask;
 import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState;
+import static com.android.bluetooth.flags.Flags.leaudioBroadcastApiGetLocalMetadata;
 import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment;
 import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine;
 import static com.android.bluetooth.flags.Flags.leaudioBroadcastResyncHelper;
@@ -3273,6 +3274,27 @@
         return stateMachine.getMaximumSourceCapacity();
     }
 
+    /**
+     * Get metadata of source that stored on this Broadcast Sink
+     *
+     * @param sink Broadcast Sink device
+     * @param sourceId Broadcast source id
+     * @return metadata of source that stored on this Broadcast Sink
+     */
+    BluetoothLeBroadcastMetadata getSourceMetadata(BluetoothDevice sink, int sourceId) {
+        if (!leaudioBroadcastApiGetLocalMetadata()) {
+            return null;
+        }
+
+        log("getSourceMetadata: device = " + sink + " with source id = " + sourceId);
+        BassClientStateMachine stateMachine = getOrCreateStateMachine(sink);
+        if (stateMachine == null) {
+            log("stateMachine is null");
+            return null;
+        }
+        return stateMachine.getCurrentBroadcastMetadata(sourceId);
+    }
+
     private boolean isLocalBroadcast(int broadcastId) {
         LeAudioService leAudioService = mServiceFactory.getLeAudioService();
         if (leAudioService == null) {
@@ -4630,5 +4652,16 @@
             }
             return service.getMaximumSourceCapacity(sink);
         }
+
+        @Override
+        public BluetoothLeBroadcastMetadata getSourceMetadata(
+                BluetoothDevice sink, int sourceId, AttributionSource source) {
+            BassClientService service = getServiceAndEnforceConnect(source);
+            if (service == null) {
+                Log.e(TAG, "Service is null");
+                return null;
+            }
+            return service.getSourceMetadata(sink, sourceId);
+        }
     }
 }
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 b59ff2f..021b732 100644
--- a/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bass_client/BassClientStateMachine.java
@@ -376,10 +376,6 @@
         return mPendingSourceToSwitch != null;
     }
 
-    BluetoothLeBroadcastMetadata getCurrentBroadcastMetadata(Integer sourceId) {
-        return mCurrentMetadata.getOrDefault(sourceId, null);
-    }
-
     private void setCurrentBroadcastMetadata(
             Integer sourceId, BluetoothLeBroadcastMetadata metadata) {
         if (metadata != null) {
@@ -2619,6 +2615,10 @@
         return mNumOfBroadcastReceiverStates;
     }
 
+    BluetoothLeBroadcastMetadata getCurrentBroadcastMetadata(Integer sourceId) {
+        return mCurrentMetadata.getOrDefault(sourceId, null);
+    }
+
     BluetoothDevice getDevice() {
         return mDevice;
     }
diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
index 8520bc3..4b563ac 100644
--- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
@@ -688,6 +688,14 @@
                     if (device != null) {
                         // remove LE audio active device when it is not null, and not dual mode
                         setLeAudioActiveDevice(null, true);
+                    } else {
+                        Log.d(
+                                TAG,
+                                "HFP active device is null. Try to fallback to le audio active"
+                                        + " device");
+                        synchronized (mLock) {
+                            setFallbackDeviceActiveLocked(device);
+                        }
                     }
                 }
             }
@@ -782,6 +790,13 @@
                 mLeHearingAidActiveDevice = device;
             }
 
+            if (device == null && !Utils.isDualModeAudioEnabled()) {
+                Log.d(TAG, "LE audio active device is null. Try to fallback to hfp active device.");
+                synchronized (mLock) {
+                    setFallbackDeviceActiveLocked(device);
+                }
+            }
+
             mLeAudioActiveDevice = device;
         }
     }
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
index 43752e6..851df2c 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -30,6 +30,7 @@
 import android.bluetooth.BluetoothMap;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothSap;
+import android.bluetooth.BluetoothUtils;
 import android.bluetooth.BufferConstraint;
 import android.bluetooth.BufferConstraints;
 import android.content.Context;
@@ -651,7 +652,8 @@
                                         BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
                                         prevAdapterState)
                                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                logProfileConnectionStateChange(device, newState, prevState);
+                MetricsLogger.getInstance()
+                        .logProfileConnectionStateChange(device, profile, newState, prevState);
                 Log.d(TAG, "updateOnProfileConnectionChanged: " + logInfo);
                 mService.sendBroadcastAsUser(
                         intent,
@@ -662,33 +664,7 @@
         }
     }
 
-    private void logProfileConnectionStateChange(BluetoothDevice device, int state, int prevState) {
 
-        switch (state) {
-            case BluetoothAdapter.STATE_CONNECTED:
-                MetricsLogger.getInstance()
-                        .logBluetoothEvent(
-                                device,
-                                BluetoothStatsLog
-                                        .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION,
-                                BluetoothStatsLog
-                                        .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SUCCESS,
-                                0);
-                break;
-            case BluetoothAdapter.STATE_DISCONNECTED:
-                if (prevState == BluetoothAdapter.STATE_CONNECTING) {
-                    MetricsLogger.getInstance()
-                            .logBluetoothEvent(
-                                    device,
-                                    BluetoothStatsLog
-                                            .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION,
-                                    BluetoothStatsLog
-                                            .BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__FAIL,
-                                    0);
-                }
-                break;
-        }
-    }
 
     private boolean validateProfileConnectionState(int state) {
         return (state == BluetoothProfile.STATE_DISCONNECTED
@@ -1102,7 +1078,7 @@
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         writer.println(TAG);
         writer.println("  " + "Name: " + getName());
-        writer.println("  " + "Address: " + Utils.getAddressStringFromByte(mAddress));
+        writer.println("  " + "Address: " + Utils.getRedactedAddressStringFromByte(mAddress));
         writer.println("  " + "ConnectionState: " + dumpConnectionState(getConnectionState()));
         writer.println("  " + "State: " + BluetoothAdapter.nameForState(getState()));
         writer.println("  " + "MaxConnectedAudioDevices: " + getMaxConnectedAudioDevices());
@@ -1121,7 +1097,7 @@
             if (brEdrAddress.equals(address)) {
                 writer.println(
                         "    "
-                                + address
+                                + BluetoothUtils.toAnonymizedAddress(address)
                                 + " ["
                                 + dumpDeviceType(mRemoteDevices.getType(device))
                                 + "][ 0x"
@@ -1130,9 +1106,9 @@
                                 + Utils.getName(device));
             } else {
                 sb.append("    ")
-                        .append(address)
+                        .append(BluetoothUtils.toAnonymizedAddress(address))
                         .append(" => ")
-                        .append(brEdrAddress)
+                        .append(BluetoothUtils.toAnonymizedAddress(brEdrAddress))
                         .append(" [")
                         .append(dumpDeviceType(mRemoteDevices.getType(device)))
                         .append("][ 0x")
diff --git a/android/app/src/com/android/bluetooth/btservice/InteropUtil.java b/android/app/src/com/android/bluetooth/btservice/InteropUtil.java
index 93ab3ac4..da6eb5b 100644
--- a/android/app/src/com/android/bluetooth/btservice/InteropUtil.java
+++ b/android/app/src/com/android/bluetooth/btservice/InteropUtil.java
@@ -17,6 +17,7 @@
 
 package com.android.bluetooth.btservice;
 
+import android.bluetooth.BluetoothUtils;
 import android.util.Log;
 
 /**
@@ -62,7 +63,12 @@
             return false;
         }
 
-        Log.d(TAG, "interopMatchAddr: feature=" + feature.name() + ", address=" + address);
+        Log.d(
+                TAG,
+                "interopMatchAddr: feature="
+                        + feature.name()
+                        + ", address="
+                        + BluetoothUtils.toAnonymizedAddress(address));
         if (address == null) {
             return false;
         }
@@ -121,7 +127,12 @@
             return false;
         }
 
-        Log.d(TAG, "interopMatchAddrOrName: feature=" + feature.name() + ", address=" + address);
+        Log.d(
+                TAG,
+                "interopMatchAddrOrName: feature="
+                        + feature.name()
+                        + ", address="
+                        + BluetoothUtils.toAnonymizedAddress(address));
         if (address == null) {
             return false;
         }
@@ -156,7 +167,7 @@
                 "interopDatabaseAddAddr: feature="
                         + feature.name()
                         + ", address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", length="
                         + length);
         if (address == null || (length <= 0 || length > 6)) {
@@ -184,7 +195,12 @@
             return;
         }
 
-        Log.d(TAG, "interopDatabaseRemoveAddr: feature=" + feature.name() + ", address=" + address);
+        Log.d(
+                TAG,
+                "interopDatabaseRemoveAddr: feature="
+                        + feature.name()
+                        + ", address="
+                        + BluetoothUtils.toAnonymizedAddress(address));
         if (address == null) {
             return;
         }
diff --git a/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java b/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java
index 6031e3b..9804048 100644
--- a/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java
+++ b/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java
@@ -15,11 +15,28 @@
  */
 package com.android.bluetooth.btservice;
 
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_A2DP;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_A2DP_SINK;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_BATTERY;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_CSIP_SET_COORDINATOR;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_HAP_CLIENT;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_HEADSET;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_HEADSET_CLIENT;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_HEARING_AID;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_HID_HOST;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_LE_AUDIO;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_LE_AUDIO_BROADCAST_ASSISTANT;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_MAP_CLIENT;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_PAN;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_PBAP_CLIENT;
+import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_VOLUME_CONTROL;
 import static com.android.bluetooth.BtRestrictedStatsLog.RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED;
 
 import android.app.AlarmManager;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothA2dpSink;
+import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothAvrcpController;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
@@ -714,6 +731,65 @@
         return digest.digest(name.getBytes(StandardCharsets.UTF_8));
     }
 
+    private int getProfileEnumFromProfileId(int profile) {
+        return switch (profile) {
+            case BluetoothProfile.A2DP ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_A2DP;
+            case BluetoothProfile.A2DP_SINK ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_A2DP_SINK;
+            case BluetoothProfile.HEADSET ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_HEADSET;
+            case BluetoothProfile.HEADSET_CLIENT ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_HEADSET_CLIENT;
+            case BluetoothProfile.MAP_CLIENT ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_MAP_CLIENT;
+            case BluetoothProfile.HID_HOST ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_HID_HOST;
+            case BluetoothProfile.PAN ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_PAN;
+            case BluetoothProfile.PBAP_CLIENT ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_PBAP_CLIENT;
+            case BluetoothProfile.HEARING_AID ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_HEARING_AID;
+            case BluetoothProfile.HAP_CLIENT ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_HAP_CLIENT;
+            case BluetoothProfile.VOLUME_CONTROL ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_VOLUME_CONTROL;
+            case BluetoothProfile.CSIP_SET_COORDINATOR ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_CSIP_SET_COORDINATOR;
+            case BluetoothProfile.LE_AUDIO ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_LE_AUDIO;
+            case BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_LE_AUDIO_BROADCAST_ASSISTANT;
+            case BluetoothProfile.BATTERY ->
+                    BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_BATTERY;
+            default -> BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION;
+        };
+    }
+
+    public void logProfileConnectionStateChange(
+            BluetoothDevice device, int profileId, int state, int prevState) {
+
+        switch (state) {
+            case BluetoothAdapter.STATE_CONNECTED:
+                logBluetoothEvent(
+                        device,
+                        getProfileEnumFromProfileId(profileId),
+                        BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__SUCCESS,
+                        0);
+                break;
+            case BluetoothAdapter.STATE_DISCONNECTED:
+                if (prevState == BluetoothAdapter.STATE_CONNECTING) {
+                    logBluetoothEvent(
+                            device,
+                            getProfileEnumFromProfileId(profileId),
+                            BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__FAIL,
+                            0);
+                }
+                break;
+        }
+    }
+
     /** Logs LE Audio Broadcast audio session. */
     public void logLeAudioBroadcastAudioSession(
             int broadcastId,
diff --git a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
index bd64fb6..aa8395e 100644
--- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -31,6 +31,7 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProtoEnums;
 import android.bluetooth.BluetoothSinkAudioPolicy;
+import android.bluetooth.BluetoothUtils;
 import android.bluetooth.IBluetoothConnectionCallback;
 import android.content.Context;
 import android.content.Intent;
@@ -183,7 +184,7 @@
 
                         debugLog(
                                 "reset(): address="
-                                        + address
+                                        + BluetoothUtils.toAnonymizedAddress(address)
                                         + ", connected="
                                         + bluetoothDevice.isConnected());
 
@@ -1168,7 +1169,7 @@
                         + Utils.getRedactedAddressStringFromByte(secondaryAddress));
 
         DeviceProperties deviceProperties = getDeviceProperties(device);
-        deviceProperties.mIdentityAddress = Utils.getAddressStringFromByte(secondaryAddress);
+        deviceProperties.setIdentityAddress(Utils.getAddressStringFromByte(secondaryAddress));
     }
 
     void aclStateChangeCallback(
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
index 9febc11..f1cdefc 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java
@@ -911,6 +911,16 @@
 
     /** Process a change in the bonding state for a device */
     public void handleBondStateChanged(BluetoothDevice device, int fromState, int toState) {
+        if (mHandler == null) {
+            Log.e(
+                    TAG,
+                    "mHandler is null, service is stopped. Ignore Bond State for "
+                            + device
+                            + " to state: "
+                            + toState);
+            return;
+        }
+
         mHandler.post(() -> bondStateChanged(device, toState));
     }
 
@@ -972,6 +982,15 @@
     }
 
     void handleConnectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+        if (mHandler == null) {
+            Log.e(
+                    TAG,
+                    "mHandler is null, service is stopped. Ignore Connection State for "
+                            + device
+                            + " to state: "
+                            + toState);
+            return;
+        }
         mHandler.post(() -> connectionStateChanged(device, fromState, toState));
     }
 
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index cf50002..036fded 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -1312,14 +1312,17 @@
                         + ", connId="
                         + connId
                         + ", address="
-                        + address);
+                        + BluetoothUtils.toAnonymizedAddress(address));
         int connectionState = BluetoothProtoEnums.CONNECTION_STATE_DISCONNECTED;
         if (status == 0) {
             mClientMap.addConnection(clientIf, connId, address);
 
             // Allow one writeCharacteristic operation at a time for each connected remote device.
             synchronized (mPermits) {
-                Log.d(TAG, "onConnected() - adding permit for address=" + address);
+                Log.d(
+                        TAG,
+                        "onConnected() - adding permit for address="
+                                + BluetoothUtils.toAnonymizedAddress(address));
                 mPermits.putIfAbsent(address, -1);
             }
             connectionState = BluetoothProtoEnums.CONNECTION_STATE_CONNECTED;
@@ -1342,7 +1345,7 @@
                         + ", connId="
                         + connId
                         + ", address="
-                        + address);
+                        + BluetoothUtils.toAnonymizedAddress(address));
 
         mClientMap.removeConnection(clientIf, connId);
         ContextMap<IBluetoothGattCallback>.App app = mClientMap.getById(clientIf);
@@ -1355,13 +1358,19 @@
         // device.
         if (!mClientMap.getConnectedDevices().contains(address)) {
             synchronized (mPermits) {
-                Log.d(TAG, "onDisconnected() - removing permit for address=" + address);
+                Log.d(
+                        TAG,
+                        "onDisconnected() - removing permit for address="
+                                + BluetoothUtils.toAnonymizedAddress(address));
                 mPermits.remove(address);
             }
         } else {
             synchronized (mPermits) {
                 if (mPermits.get(address) == connId) {
-                    Log.d(TAG, "onDisconnected() - set permit -1 for address=" + address);
+                    Log.d(
+                            TAG,
+                            "onDisconnected() - set permit -1 for address="
+                                    + BluetoothUtils.toAnonymizedAddress(address));
                     mPermits.put(address, -1);
                 }
             }
@@ -1399,7 +1408,7 @@
         Log.d(
                 TAG,
                 "onClientPhyRead() - address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", status="
                         + status
                         + ", clientIf="
@@ -1407,7 +1416,10 @@
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
         if (connId == null) {
-            Log.d(TAG, "onClientPhyRead() - no connection to " + address);
+            Log.d(
+                    TAG,
+                    "onClientPhyRead() - no connection to "
+                            + BluetoothUtils.toAnonymizedAddress(address));
             return;
         }
 
@@ -1488,11 +1500,19 @@
 
     void onServerPhyRead(int serverIf, String address, int txPhy, int rxPhy, int status)
             throws RemoteException {
-        Log.d(TAG, "onServerPhyRead() - address=" + address + ", status=" + status);
+        Log.d(
+                TAG,
+                "onServerPhyRead() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", status="
+                        + status);
 
         Integer connId = mServerMap.connIdByAddress(serverIf, address);
         if (connId == null) {
-            Log.d(TAG, "onServerPhyRead() - no connection to " + address);
+            Log.d(
+                    TAG,
+                    "onServerPhyRead() - no connection to "
+                            + BluetoothUtils.toAnonymizedAddress(address));
             return;
         }
 
@@ -1563,7 +1583,7 @@
     void onGetGattDb(int connId, List<GattDbElement> db) throws RemoteException {
         String address = mClientMap.addressByConnId(connId);
 
-        Log.d(TAG, "onGetGattDb() - address=" + address);
+        Log.d(TAG, "onGetGattDb() - address=" + BluetoothUtils.toAnonymizedAddress(address));
 
         ContextMap<IBluetoothGattCallback>.App app = mClientMap.getByConnId(connId);
         if (app == null || app.callback == null) {
@@ -1654,7 +1674,7 @@
         Log.d(
                 TAG,
                 "onRegisterForNotifications() - address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", status="
                         + status
                         + ", registered="
@@ -1669,7 +1689,7 @@
         Log.v(
                 TAG,
                 "onNotify() - address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", handle="
                         + handle
                         + ", length="
@@ -1698,7 +1718,7 @@
         Log.v(
                 TAG,
                 "onReadCharacteristic() - address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", status="
                         + status
                         + ", length="
@@ -1714,14 +1734,17 @@
             throws RemoteException {
         String address = mClientMap.addressByConnId(connId);
         synchronized (mPermits) {
-            Log.d(TAG, "onWriteCharacteristic() - increasing permit for address=" + address);
+            Log.d(
+                    TAG,
+                    "onWriteCharacteristic() - increasing permit for address="
+                            + BluetoothUtils.toAnonymizedAddress(address));
             mPermits.put(address, -1);
         }
 
         Log.v(
                 TAG,
                 "onWriteCharacteristic() - address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", status="
                         + status
                         + ", length="
@@ -1749,7 +1772,12 @@
 
     void onExecuteCompleted(int connId, int status) throws RemoteException {
         String address = mClientMap.addressByConnId(connId);
-        Log.v(TAG, "onExecuteCompleted() - address=" + address + ", status=" + status);
+        Log.v(
+                TAG,
+                "onExecuteCompleted() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", status="
+                        + status);
 
         ContextMap<IBluetoothGattCallback>.App app = mClientMap.getByConnId(connId);
         if (app != null) {
@@ -1763,7 +1791,7 @@
         Log.v(
                 TAG,
                 "onReadDescriptor() - address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", status="
                         + status
                         + ", length="
@@ -1781,7 +1809,7 @@
         Log.v(
                 TAG,
                 "onWriteDescriptor() - address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", status="
                         + status
                         + ", length="
@@ -1800,7 +1828,7 @@
                 "onReadRemoteRssi() - clientIf="
                         + clientIf
                         + " address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", rssi="
                         + rssi
                         + ", status="
@@ -1815,7 +1843,14 @@
     void onConfigureMTU(int connId, int status, int mtu) throws RemoteException {
         String address = mClientMap.addressByConnId(connId);
 
-        Log.d(TAG, "onConfigureMTU() address=" + address + ", status=" + status + ", mtu=" + mtu);
+        Log.d(
+                TAG,
+                "onConfigureMTU() address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", status="
+                        + status
+                        + ", mtu="
+                        + mtu);
 
         ContextMap<IBluetoothGattCallback>.App app = mClientMap.getByConnId(connId);
         if (app != null) {
@@ -2138,7 +2173,7 @@
         Log.d(
                 TAG,
                 "clientConnect() - address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", addressType="
                         + addressType
                         + ", isDirect="
@@ -2178,7 +2213,14 @@
         }
 
         mNativeInterface.gattClientConnect(
-                clientIf, address, addressType, isDirect, transport, opportunistic, phy, preferredMtu);
+                clientIf,
+                address,
+                addressType,
+                isDirect,
+                transport,
+                opportunistic,
+                phy,
+                preferredMtu);
     }
 
     @RequiresPermission(BLUETOOTH_CONNECT)
@@ -2189,7 +2231,12 @@
         }
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
-        Log.d(TAG, "clientDisconnect() - address=" + address + ", connId=" + connId);
+        Log.d(
+                TAG,
+                "clientDisconnect() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", connId="
+                        + connId);
         statsLogGattConnectionStateChange(
                 BluetoothProfile.GATT,
                 address,
@@ -2214,11 +2261,19 @@
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
         if (connId == null) {
-            Log.d(TAG, "clientSetPreferredPhy() - no connection to " + address);
+            Log.d(
+                    TAG,
+                    "clientSetPreferredPhy() - no connection to "
+                            + BluetoothUtils.toAnonymizedAddress(address));
             return;
         }
 
-        Log.d(TAG, "clientSetPreferredPhy() - address=" + address + ", connId=" + connId);
+        Log.d(
+                TAG,
+                "clientSetPreferredPhy() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", connId="
+                        + connId);
         mNativeInterface.gattClientSetPreferredPhy(clientIf, address, txPhy, rxPhy, phyOptions);
     }
 
@@ -2231,11 +2286,19 @@
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
         if (connId == null) {
-            Log.d(TAG, "clientReadPhy() - no connection to " + address);
+            Log.d(
+                    TAG,
+                    "clientReadPhy() - no connection to "
+                            + BluetoothUtils.toAnonymizedAddress(address));
             return;
         }
 
-        Log.d(TAG, "clientReadPhy() - address=" + address + ", connId=" + connId);
+        Log.d(
+                TAG,
+                "clientReadPhy() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", connId="
+                        + connId);
         mNativeInterface.gattClientReadPhy(clientIf, address);
     }
 
@@ -2271,7 +2334,7 @@
             return;
         }
 
-        Log.d(TAG, "refreshDevice() - address=" + address);
+        Log.d(TAG, "refreshDevice() - address=" + BluetoothUtils.toAnonymizedAddress(address));
         mNativeInterface.gattClientRefresh(clientIf, address);
     }
 
@@ -2283,12 +2346,21 @@
         }
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
-        Log.d(TAG, "discoverServices() - address=" + address + ", connId=" + connId);
+        Log.d(
+                TAG,
+                "discoverServices() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", connId="
+                        + connId);
 
         if (connId != null) {
             mNativeInterface.gattClientSearchService(connId, true, 0, 0);
         } else {
-            Log.e(TAG, "discoverServices() - No connection for " + address + "...");
+            Log.e(
+                    TAG,
+                    "discoverServices() - No connection for "
+                            + BluetoothUtils.toAnonymizedAddress(address)
+                            + "...");
         }
     }
 
@@ -2305,7 +2377,11 @@
             mNativeInterface.gattClientDiscoverServiceByUuid(
                     connId, uuid.getLeastSignificantBits(), uuid.getMostSignificantBits());
         } else {
-            Log.e(TAG, "discoverServiceByUuid() - No connection for " + address + "...");
+            Log.e(
+                    TAG,
+                    "discoverServiceByUuid() - No connection for "
+                            + BluetoothUtils.toAnonymizedAddress(address)
+                            + "...");
         }
     }
 
@@ -2321,11 +2397,15 @@
             return;
         }
 
-        Log.v(TAG, "readCharacteristic() - address=" + address);
+        Log.v(TAG, "readCharacteristic() - address=" + BluetoothUtils.toAnonymizedAddress(address));
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
         if (connId == null) {
-            Log.e(TAG, "readCharacteristic() - No connection for " + address + "...");
+            Log.e(
+                    TAG,
+                    "readCharacteristic() - No connection for "
+                            + BluetoothUtils.toAnonymizedAddress(address)
+                            + "...");
             return;
         }
 
@@ -2358,11 +2438,18 @@
             return;
         }
 
-        Log.v(TAG, "readUsingCharacteristicUuid() - address=" + address);
+        Log.v(
+                TAG,
+                "readUsingCharacteristicUuid() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address));
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
         if (connId == null) {
-            Log.e(TAG, "readUsingCharacteristicUuid() - No connection for " + address + "...");
+            Log.e(
+                    TAG,
+                    "readUsingCharacteristicUuid() - No connection for "
+                            + BluetoothUtils.toAnonymizedAddress(address)
+                            + "...");
             return;
         }
 
@@ -2401,7 +2488,9 @@
             return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
         }
 
-        Log.v(TAG, "writeCharacteristic() - address=" + address);
+        Log.v(
+                TAG,
+                "writeCharacteristic() - address=" + BluetoothUtils.toAnonymizedAddress(address));
 
         if (mReliableQueue.contains(address)) {
             writeType = 3; // Prepared write
@@ -2409,7 +2498,11 @@
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
         if (connId == null) {
-            Log.e(TAG, "writeCharacteristic() - No connection for " + address + "...");
+            Log.e(
+                    TAG,
+                    "writeCharacteristic() - No connection for "
+                            + BluetoothUtils.toAnonymizedAddress(address)
+                            + "...");
             return BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED;
         }
         permissionCheck(connId, handle);
@@ -2447,11 +2540,15 @@
             return;
         }
 
-        Log.v(TAG, "readDescriptor() - address=" + address);
+        Log.v(TAG, "readDescriptor() - address=" + BluetoothUtils.toAnonymizedAddress(address));
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
         if (connId == null) {
-            Log.e(TAG, "readDescriptor() - No connection for " + address + "...");
+            Log.e(
+                    TAG,
+                    "readDescriptor() - No connection for "
+                            + BluetoothUtils.toAnonymizedAddress(address)
+                            + "...");
             return;
         }
 
@@ -2482,11 +2579,15 @@
                 this, attributionSource, "GattService writeDescriptor")) {
             return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
         }
-        Log.v(TAG, "writeDescriptor() - address=" + address);
+        Log.v(TAG, "writeDescriptor() - address=" + BluetoothUtils.toAnonymizedAddress(address));
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
         if (connId == null) {
-            Log.e(TAG, "writeDescriptor() - No connection for " + address + "...");
+            Log.e(
+                    TAG,
+                    "writeDescriptor() - No connection for "
+                            + BluetoothUtils.toAnonymizedAddress(address)
+                            + "...");
             return BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED;
         }
         permissionCheck(connId, handle);
@@ -2502,7 +2603,7 @@
             return;
         }
 
-        Log.d(TAG, "beginReliableWrite() - address=" + address);
+        Log.d(TAG, "beginReliableWrite() - address=" + BluetoothUtils.toAnonymizedAddress(address));
         mReliableQueue.add(address);
     }
 
@@ -2514,7 +2615,12 @@
             return;
         }
 
-        Log.d(TAG, "endReliableWrite() - address=" + address + " execute: " + execute);
+        Log.d(
+                TAG,
+                "endReliableWrite() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + " execute: "
+                        + execute);
         mReliableQueue.remove(address);
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
@@ -2535,11 +2641,20 @@
             return;
         }
 
-        Log.d(TAG, "registerForNotification() - address=" + address + " enable: " + enable);
+        Log.d(
+                TAG,
+                "registerForNotification() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + " enable: "
+                        + enable);
 
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
         if (connId == null) {
-            Log.e(TAG, "registerForNotification() - No connection for " + address + "...");
+            Log.e(
+                    TAG,
+                    "registerForNotification() - No connection for "
+                            + BluetoothUtils.toAnonymizedAddress(address)
+                            + "...");
             return;
         }
 
@@ -2565,7 +2680,7 @@
             return;
         }
 
-        Log.d(TAG, "readRemoteRssi() - address=" + address);
+        Log.d(TAG, "readRemoteRssi() - address=" + BluetoothUtils.toAnonymizedAddress(address));
         mNativeInterface.gattClientReadRemoteRssi(clientIf, address);
     }
 
@@ -2576,12 +2691,21 @@
             return;
         }
 
-        Log.d(TAG, "configureMTU() - address=" + address + " mtu=" + mtu);
+        Log.d(
+                TAG,
+                "configureMTU() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + " mtu="
+                        + mtu);
         Integer connId = mClientMap.connIdByAddress(clientIf, address);
         if (connId != null) {
             mNativeInterface.gattClientConfigureMTU(connId, mtu);
         } else {
-            Log.e(TAG, "configureMTU() - No connection for " + address + "...");
+            Log.e(
+                    TAG,
+                    "configureMTU() - No connection for "
+                            + BluetoothUtils.toAnonymizedAddress(address)
+                            + "...");
         }
     }
 
@@ -2620,7 +2744,7 @@
         Log.d(
                 TAG,
                 "connectionParameterUpdate() - address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + " params="
                         + connectionPriority
                         + " interval="
@@ -2653,7 +2777,7 @@
         Log.d(
                 TAG,
                 "leConnectionUpdate() - address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", intervals="
                         + minInterval
                         + "/"
@@ -2887,7 +3011,7 @@
                 "onClientConnected() connId="
                         + connId
                         + ", address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", connected="
                         + connected);
 
@@ -2928,7 +3052,7 @@
                 "onServerReadCharacteristic() connId="
                         + connId
                         + ", address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", handle="
                         + handle
                         + ", requestId="
@@ -2959,7 +3083,7 @@
                 "onServerReadDescriptor() connId="
                         + connId
                         + ", address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", handle="
                         + handle
                         + ", requestId="
@@ -2998,7 +3122,7 @@
                 "onServerWriteCharacteristic() connId="
                         + connId
                         + ", address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", handle="
                         + handle
                         + ", requestId="
@@ -3040,7 +3164,7 @@
                 "onAttributeWrite() connId="
                         + connId
                         + ", address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", handle="
                         + handle
                         + ", requestId="
@@ -3073,7 +3197,7 @@
                 "onExecuteWrite() connId="
                         + connId
                         + ", address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", transId="
                         + transId);
 
@@ -3195,7 +3319,7 @@
             return;
         }
 
-        Log.d(TAG, "serverConnect() - address=" + address);
+        Log.d(TAG, "serverConnect() - address=" + BluetoothUtils.toAnonymizedAddress(address));
 
         logServerForegroundInfo(attributionSource.getUid(), isDirect);
 
@@ -3210,7 +3334,12 @@
         }
 
         Integer connId = mServerMap.connIdByAddress(serverIf, address);
-        Log.d(TAG, "serverDisconnect() - address=" + address + ", connId=" + connId);
+        Log.d(
+                TAG,
+                "serverDisconnect() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", connId="
+                        + connId);
 
         mNativeInterface.gattServerDisconnect(serverIf, address, connId != null ? connId : 0);
     }
@@ -3230,11 +3359,19 @@
 
         Integer connId = mServerMap.connIdByAddress(serverIf, address);
         if (connId == null) {
-            Log.d(TAG, "serverSetPreferredPhy() - no connection to " + address);
+            Log.d(
+                    TAG,
+                    "serverSetPreferredPhy() - no connection to "
+                            + BluetoothUtils.toAnonymizedAddress(address));
             return;
         }
 
-        Log.d(TAG, "serverSetPreferredPhy() - address=" + address + ", connId=" + connId);
+        Log.d(
+                TAG,
+                "serverSetPreferredPhy() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", connId="
+                        + connId);
         mNativeInterface.gattServerSetPreferredPhy(serverIf, address, txPhy, rxPhy, phyOptions);
     }
 
@@ -3247,11 +3384,19 @@
 
         Integer connId = mServerMap.connIdByAddress(serverIf, address);
         if (connId == null) {
-            Log.d(TAG, "serverReadPhy() - no connection to " + address);
+            Log.d(
+                    TAG,
+                    "serverReadPhy() - no connection to "
+                            + BluetoothUtils.toAnonymizedAddress(address));
             return;
         }
 
-        Log.d(TAG, "serverReadPhy() - address=" + address + ", connId=" + connId);
+        Log.d(
+                TAG,
+                "serverReadPhy() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", connId="
+                        + connId);
         mNativeInterface.gattServerReadPhy(serverIf, address);
     }
 
@@ -3339,7 +3484,12 @@
             return;
         }
 
-        Log.v(TAG, "sendResponse() - address=" + address + ", requestId=" + requestId);
+        Log.v(
+                TAG,
+                "sendResponse() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + ", requestId="
+                        + requestId);
 
         int handle = 0;
         Integer connId = 0;
@@ -3384,7 +3534,12 @@
             return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
         }
 
-        Log.v(TAG, "sendNotification() - address=" + address + " handle=" + handle);
+        Log.v(
+                TAG,
+                "sendNotification() - address="
+                        + BluetoothUtils.toAnonymizedAddress(address)
+                        + " handle="
+                        + handle);
 
         Integer connId = mServerMap.connIdByAddress(serverIf, address);
         if (connId == null || connId == 0) {
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
index a4896f2..facb63f 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientService.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -19,6 +19,8 @@
 
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
 
 import static java.util.Objects.requireNonNull;
 import static java.util.Objects.requireNonNullElseGet;
@@ -76,8 +78,9 @@
     private final AdapterService mAdapterService;
     private final DatabaseManager mDatabaseManager;
     private final Handler mHandler;
+    private final Looper mStateMachinesLooper;
     private final HandlerThread mStateMachinesThread;
-    private final HapClientNativeInterface mHapClientNativeInterface;
+    private final HapClientNativeInterface mNativeInterface;
 
     @VisibleForTesting
     @GuardedBy("mCallbacks")
@@ -114,14 +117,17 @@
     }
 
     public HapClientService(AdapterService adapterService) {
-        this(adapterService, null);
+        this(adapterService, null, null);
     }
 
     @VisibleForTesting
-    HapClientService(AdapterService adapterService, HapClientNativeInterface nativeInterface) {
+    HapClientService(
+            AdapterService adapterService,
+            Looper looper,
+            HapClientNativeInterface nativeInterface) {
         super(adapterService);
         mAdapterService = requireNonNull(adapterService);
-        mHapClientNativeInterface =
+        mNativeInterface =
                 requireNonNullElseGet(
                         nativeInterface,
                         () ->
@@ -129,14 +135,19 @@
                                         new HapClientNativeCallback(adapterService, this)));
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
 
-        // Start handler thread for state machines
-        mHandler = new Handler(Looper.getMainLooper());
-        mStateMachines.clear();
-        mStateMachinesThread = new HandlerThread("HapClientService.StateMachines");
-        mStateMachinesThread.start();
+        if (looper == null) {
+            mHandler = new Handler(requireNonNull(Looper.getMainLooper()));
+            mStateMachinesThread = new HandlerThread("HapClientService.StateMachines");
+            mStateMachinesThread.start();
+            mStateMachinesLooper = mStateMachinesThread.getLooper();
+        } else {
+            mHandler = new Handler(looper);
+            mStateMachinesThread = null;
+            mStateMachinesLooper = looper;
+        }
 
         // Initialize native interface
-        mHapClientNativeInterface.init();
+        mNativeInterface.init();
 
         // Mark service as started
         setHapClient(this);
@@ -162,23 +173,24 @@
         synchronized (mStateMachines) {
             for (HapClientStateMachine sm : mStateMachines.values()) {
                 sm.doQuit();
-                sm.cleanup();
             }
             mStateMachines.clear();
         }
 
-        try {
-            mStateMachinesThread.quitSafely();
-            mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS);
-        } catch (InterruptedException e) {
-            // Do not rethrow as we are shutting down anyway
+        if (mStateMachinesThread != null) {
+            try {
+                mStateMachinesThread.quitSafely();
+                mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS);
+            } catch (InterruptedException e) {
+                // Do not rethrow as we are shutting down anyway
+            }
         }
 
         // Unregister Handler and stop all queued messages.
         mHandler.removeCallbacksAndMessages(null);
 
         // Cleanup GATT interface
-        mHapClientNativeInterface.cleanup();
+        mNativeInterface.cleanup();
 
         // Cleanup the internals
         mDeviceCurrentPresetMap.clear();
@@ -233,7 +245,6 @@
             }
             Log.i(TAG, "removeStateMachine: removing state machine for device: " + device);
             sm.doQuit();
-            sm.cleanup();
             mStateMachines.remove(device);
         }
     }
@@ -423,7 +434,7 @@
             if (smConnect == null) {
                 Log.e(TAG, "Cannot connect to " + device + " : no state machine");
             }
-            smConnect.sendMessage(HapClientStateMachine.CONNECT);
+            smConnect.sendMessage(HapClientStateMachine.MESSAGE_CONNECT);
         }
 
         return true;
@@ -444,7 +455,7 @@
         synchronized (mStateMachines) {
             HapClientStateMachine sm = mStateMachines.get(device);
             if (sm != null) {
-                sm.sendMessage(HapClientStateMachine.DISCONNECT);
+                sm.sendMessage(HapClientStateMachine.MESSAGE_DISCONNECT);
             }
         }
 
@@ -470,12 +481,7 @@
                 return null;
             }
             Log.d(TAG, "Creating a new state machine for " + device);
-            sm =
-                    HapClientStateMachine.make(
-                            device,
-                            this,
-                            mHapClientNativeInterface,
-                            mStateMachinesThread.getLooper());
+            sm = new HapClientStateMachine(this, device, mNativeInterface, mStateMachinesLooper);
             mStateMachines.put(device, sm);
             return sm;
         }
@@ -542,7 +548,7 @@
             return;
         }
 
-        mHapClientNativeInterface.selectActivePreset(device, presetIndex);
+        mNativeInterface.selectActivePreset(device, presetIndex);
     }
 
     @VisibleForTesting
@@ -558,23 +564,23 @@
             return;
         }
 
-        mHapClientNativeInterface.groupSelectActivePreset(groupId, presetIndex);
+        mNativeInterface.groupSelectActivePreset(groupId, presetIndex);
     }
 
     void switchToNextPreset(BluetoothDevice device) {
-        mHapClientNativeInterface.nextActivePreset(device);
+        mNativeInterface.nextActivePreset(device);
     }
 
     void switchToNextPresetForGroup(int groupId) {
-        mHapClientNativeInterface.groupNextActivePreset(groupId);
+        mNativeInterface.groupNextActivePreset(groupId);
     }
 
     void switchToPreviousPreset(BluetoothDevice device) {
-        mHapClientNativeInterface.previousActivePreset(device);
+        mNativeInterface.previousActivePreset(device);
     }
 
     void switchToPreviousPresetForGroup(int groupId) {
-        mHapClientNativeInterface.groupPreviousActivePreset(groupId);
+        mNativeInterface.groupPreviousActivePreset(groupId);
     }
 
     BluetoothHapPresetInfo getPresetInfo(BluetoothDevice device, int presetIndex) {
@@ -585,7 +591,7 @@
             /* We want native to be called for PTS testing even we have all
              * the data in the cache here
              */
-            mHapClientNativeInterface.getPresetInfo(device, presetIndex);
+            mNativeInterface.getPresetInfo(device, presetIndex);
         }
         List<BluetoothHapPresetInfo> current_presets = mPresetsMap.get(device);
         if (current_presets != null) {
@@ -614,20 +620,19 @@
     }
 
     private int stackEventPresetInfoReasonToProfileStatus(int statusCode) {
-        switch (statusCode) {
-            case HapClientStackEvent.PRESET_INFO_REASON_ALL_PRESET_INFO:
-                return BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST;
-            case HapClientStackEvent.PRESET_INFO_REASON_PRESET_INFO_UPDATE:
-                return BluetoothStatusCodes.REASON_REMOTE_REQUEST;
-            case HapClientStackEvent.PRESET_INFO_REASON_PRESET_DELETED:
-                return BluetoothStatusCodes.REASON_REMOTE_REQUEST;
-            case HapClientStackEvent.PRESET_INFO_REASON_PRESET_AVAILABILITY_CHANGED:
-                return BluetoothStatusCodes.REASON_REMOTE_REQUEST;
-            case HapClientStackEvent.PRESET_INFO_REASON_PRESET_INFO_REQUEST_RESPONSE:
-                return BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST;
-            default:
-                return BluetoothStatusCodes.ERROR_UNKNOWN;
-        }
+        return switch (statusCode) {
+            case HapClientStackEvent.PRESET_INFO_REASON_ALL_PRESET_INFO ->
+                    BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST;
+            case HapClientStackEvent.PRESET_INFO_REASON_PRESET_INFO_UPDATE ->
+                    BluetoothStatusCodes.REASON_REMOTE_REQUEST;
+            case HapClientStackEvent.PRESET_INFO_REASON_PRESET_DELETED ->
+                    BluetoothStatusCodes.REASON_REMOTE_REQUEST;
+            case HapClientStackEvent.PRESET_INFO_REASON_PRESET_AVAILABILITY_CHANGED ->
+                    BluetoothStatusCodes.REASON_REMOTE_REQUEST;
+            case HapClientStackEvent.PRESET_INFO_REASON_PRESET_INFO_REQUEST_RESPONSE ->
+                    BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST;
+            default -> BluetoothStatusCodes.ERROR_UNKNOWN;
+        };
     }
 
     private void notifyPresetInfoChanged(BluetoothDevice device, int infoReason) {
@@ -643,24 +648,23 @@
     }
 
     private int stackEventStatusToProfileStatus(int statusCode) {
-        switch (statusCode) {
-            case HapClientStackEvent.STATUS_SET_NAME_NOT_ALLOWED:
-                return BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED;
-            case HapClientStackEvent.STATUS_OPERATION_NOT_SUPPORTED:
-                return BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED;
-            case HapClientStackEvent.STATUS_OPERATION_NOT_POSSIBLE:
-                return BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED;
-            case HapClientStackEvent.STATUS_INVALID_PRESET_NAME_LENGTH:
-                return BluetoothStatusCodes.ERROR_HAP_PRESET_NAME_TOO_LONG;
-            case HapClientStackEvent.STATUS_INVALID_PRESET_INDEX:
-                return BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX;
-            case HapClientStackEvent.STATUS_GROUP_OPERATION_NOT_SUPPORTED:
-                return BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED;
-            case HapClientStackEvent.STATUS_PROCEDURE_ALREADY_IN_PROGRESS:
-                return BluetoothStatusCodes.ERROR_UNKNOWN;
-            default:
-                return BluetoothStatusCodes.ERROR_UNKNOWN;
-        }
+        return switch (statusCode) {
+            case HapClientStackEvent.STATUS_SET_NAME_NOT_ALLOWED ->
+                    BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED;
+            case HapClientStackEvent.STATUS_OPERATION_NOT_SUPPORTED ->
+                    BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED;
+            case HapClientStackEvent.STATUS_OPERATION_NOT_POSSIBLE ->
+                    BluetoothStatusCodes.ERROR_REMOTE_OPERATION_REJECTED;
+            case HapClientStackEvent.STATUS_INVALID_PRESET_NAME_LENGTH ->
+                    BluetoothStatusCodes.ERROR_HAP_PRESET_NAME_TOO_LONG;
+            case HapClientStackEvent.STATUS_INVALID_PRESET_INDEX ->
+                    BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX;
+            case HapClientStackEvent.STATUS_GROUP_OPERATION_NOT_SUPPORTED ->
+                    BluetoothStatusCodes.ERROR_REMOTE_OPERATION_NOT_SUPPORTED;
+            case HapClientStackEvent.STATUS_PROCEDURE_ALREADY_IN_PROGRESS ->
+                    BluetoothStatusCodes.ERROR_UNKNOWN;
+            default -> BluetoothStatusCodes.ERROR_UNKNOWN;
+        };
     }
 
     private boolean isPresetIndexValid(BluetoothDevice device, int presetIndex) {
@@ -717,7 +721,7 @@
         // WARNING: We should check cache if preset exists and is writable, but then we would still
         //          need a way to trigger this action with an invalid index or on a non-writable
         //          preset for tests purpose.
-        mHapClientNativeInterface.setPresetName(device, presetIndex, name);
+        mNativeInterface.setPresetName(device, presetIndex, name);
     }
 
     @VisibleForTesting
@@ -733,7 +737,7 @@
             return;
         }
 
-        mHapClientNativeInterface.groupSetPresetName(groupId, presetIndex, name);
+        mNativeInterface.groupSetPresetName(groupId, presetIndex, name);
     }
 
     @Override
@@ -912,8 +916,7 @@
             if (sm == null) {
                 if (stackEvent.type == HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED) {
                     switch (stackEvent.valueInt1) {
-                        case HapClientStackEvent.CONNECTION_STATE_CONNECTED,
-                                HapClientStackEvent.CONNECTION_STATE_CONNECTING -> {
+                        case STATE_CONNECTED, STATE_CONNECTING -> {
                             sm = getOrCreateStateMachine(device);
                         }
                         default -> {}
@@ -924,7 +927,7 @@
                 Log.e(TAG, "Cannot process stack event: no state machine: " + stackEvent);
                 return;
             }
-            sm.sendMessage(HapClientStateMachine.STACK_EVENT, stackEvent);
+            sm.sendMessage(HapClientStateMachine.MESSAGE_STACK_EVENT, stackEvent);
         }
     }
 
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientStackEvent.java b/android/app/src/com/android/bluetooth/hap/HapClientStackEvent.java
index 841baaf..a0e978e 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientStackEvent.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientStackEvent.java
@@ -17,6 +17,8 @@
 
 package com.android.bluetooth.hap;
 
+import static android.bluetooth.BluetoothProfile.getConnectionStateName;
+
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.IBluetoothHapClient;
 
@@ -40,12 +42,6 @@
     public static final int EVENT_TYPE_ON_PRESET_NAME_SET_ERROR = 7;
     public static final int EVENT_TYPE_ON_PRESET_INFO_ERROR = 8;
 
-    // Connection state values as defined in bt_has.h
-    static final int CONNECTION_STATE_DISCONNECTED = 0;
-    static final int CONNECTION_STATE_CONNECTING = 1;
-    static final int CONNECTION_STATE_CONNECTED = 2;
-    static final int CONNECTION_STATE_DISCONNECTING = 3;
-
     // Possible operation results
     /* WARNING: Matches status codes defined in bta_has.h */
     static final int STATUS_NO_ERROR = 0;
@@ -117,7 +113,7 @@
     private String eventTypeValueInt1ToString(int type, int value) {
         switch (type) {
             case EVENT_TYPE_CONNECTION_STATE_CHANGED:
-                return "{state: " + connectionStateValueToString(value) + "}";
+                return "{state: " + getConnectionStateName(value) + "}";
             case EVENT_TYPE_DEVICE_AVAILABLE:
                 return "{features: " + featuresToString(value) + "}";
             case EVENT_TYPE_DEVICE_FEATURES:
@@ -181,21 +177,6 @@
         }
     }
 
-    private String connectionStateValueToString(int value) {
-        switch (value) {
-            case CONNECTION_STATE_DISCONNECTED:
-                return "CONNECTION_STATE_DISCONNECTED";
-            case CONNECTION_STATE_CONNECTING:
-                return "CONNECTION_STATE_CONNECTING";
-            case CONNECTION_STATE_CONNECTED:
-                return "CONNECTION_STATE_CONNECTED";
-            case CONNECTION_STATE_DISCONNECTING:
-                return "CONNECTION_STATE_DISCONNECTING";
-            default:
-                return "CONNECTION_STATE_UNKNOWN!";
-        }
-    }
-
     private String statusCodeValueToString(int value) {
         switch (value) {
             case STATUS_NO_ERROR:
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientStateMachine.java b/android/app/src/com/android/bluetooth/hap/HapClientStateMachine.java
index e0b6ded..4695103 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientStateMachine.java
@@ -15,23 +15,39 @@
  * limitations under the License.
  */
 
-/**
- * Bluetooth Hap Client StateMachine. There is one instance per remote device. - "Disconnected" and
- * "Connected" are steady states. - "Connecting" and "Disconnecting" are transient states until the
- * connection / disconnection is completed.
- *
- * <p>(Disconnected) | ^ CONNECT | | DISCONNECTED V | (Connecting)<--->(Disconnecting) | ^ CONNECTED
- * | | DISCONNECT V | (Connected) NOTES: - If state machine is in "Connecting" state and the remote
- * device sends DISCONNECT request, the state machine transitions to "Disconnecting" state. -
- * Similarly, if the state machine is in "Disconnecting" state and the remote device sends CONNECT
- * request, the state machine transitions to "Connecting" state.
- *
- * <p>DISCONNECT (Connecting) ---------------> (Disconnecting) <--------------- CONNECT
- */
+// Bluetooth Hap Client StateMachine. There is one instance per remote device.
+//  - "Disconnected" and "Connected" are steady states.
+//  - "Connecting" and "Disconnecting" are transient states until the
+//     connection / disconnection is completed.
+
+//                        (Disconnected)
+//                           |       ^
+//                   CONNECT |       | DISCONNECTED
+//                           V       |
+//                 (Connecting)<--->(Disconnecting)
+//                           |       ^
+//                 CONNECTED |       | DISCONNECT
+//                           V       |
+//                          (Connected)
+// NOTES:
+//  - If state machine is in "Connecting" state and the remote device sends
+//    DISCONNECT request, the state machine transitions to "Disconnecting" state.
+//  - Similarly, if the state machine is in "Disconnecting" state and the remote device
+//    sends CONNECT request, the state machine transitions to "Connecting" state.
+
+//                    DISCONNECT
+//    (Connecting) ---------------> (Disconnecting)
+//                 <---------------
+//                      CONNECT
 package com.android.bluetooth.hap;
 
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
+import static android.bluetooth.BluetoothProfile.getConnectionStateName;
 
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHapClient;
@@ -49,40 +65,40 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.time.Duration;
 import java.util.Scanner;
 
 final class HapClientStateMachine extends StateMachine {
-    static final int CONNECT = 1;
-    static final int DISCONNECT = 2;
-    @VisibleForTesting static final int STACK_EVENT = 101;
-    private static final String TAG = "HapClientStateMachine";
-    @VisibleForTesting static final int CONNECT_TIMEOUT = 201;
+    private static final String TAG = HapClientStateMachine.class.getSimpleName();
 
-    // NOTE: the value is not "final" - it is modified in the unit tests
-    @VisibleForTesting static int sConnectTimeoutMs = 30000; // 30s
+    static final int MESSAGE_CONNECT = 1;
+    static final int MESSAGE_DISCONNECT = 2;
+    static final int MESSAGE_STACK_EVENT = 101;
+    @VisibleForTesting static final int MESSAGE_CONNECT_TIMEOUT = 201;
+
+    @VisibleForTesting static final Duration CONNECT_TIMEOUT = Duration.ofSeconds(30);
 
     private final Disconnected mDisconnected;
     private final Connecting mConnecting;
     private final Disconnecting mDisconnecting;
     private final Connected mConnected;
-    private int mConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+
+    private int mConnectionState = STATE_DISCONNECTED;
     private int mLastConnectionState = -1;
 
     private final HapClientService mService;
     private final HapClientNativeInterface mNativeInterface;
-
     private final BluetoothDevice mDevice;
 
     HapClientStateMachine(
-            BluetoothDevice device,
             HapClientService svc,
+            BluetoothDevice device,
             HapClientNativeInterface gattInterface,
             Looper looper) {
         super(TAG, looper);
         mDevice = device;
         mService = svc;
         mNativeInterface = gattInterface;
-
         mDisconnected = new Disconnected();
         mConnecting = new Connecting();
         mDisconnecting = new Disconnecting();
@@ -94,61 +110,24 @@
         addState(mConnected);
 
         setInitialState(mDisconnected);
-    }
-
-    static HapClientStateMachine make(
-            BluetoothDevice device,
-            HapClientService svc,
-            HapClientNativeInterface gattInterface,
-            Looper looper) {
-        Log.i(TAG, "make for device " + device);
-        HapClientStateMachine hearingAccessSm =
-                new HapClientStateMachine(device, svc, gattInterface, looper);
-        hearingAccessSm.start();
-        return hearingAccessSm;
+        start();
     }
 
     private static String messageWhatToString(int what) {
-        switch (what) {
-            case CONNECT:
-                return "CONNECT";
-            case DISCONNECT:
-                return "DISCONNECT";
-            case STACK_EVENT:
-                return "STACK_EVENT";
-            case CONNECT_TIMEOUT:
-                return "CONNECT_TIMEOUT";
-            default:
-                break;
-        }
-        return Integer.toString(what);
-    }
-
-    private static String profileStateToString(int state) {
-        switch (state) {
-            case BluetoothProfile.STATE_DISCONNECTED:
-                return "DISCONNECTED";
-            case BluetoothProfile.STATE_CONNECTING:
-                return "CONNECTING";
-            case BluetoothProfile.STATE_CONNECTED:
-                return "CONNECTED";
-            case BluetoothProfile.STATE_DISCONNECTING:
-                return "DISCONNECTING";
-            default:
-                break;
-        }
-        return Integer.toString(state);
+        return switch (what) {
+            case MESSAGE_CONNECT -> "CONNECT";
+            case MESSAGE_DISCONNECT -> "DISCONNECT";
+            case MESSAGE_STACK_EVENT -> "STACK_EVENT";
+            case MESSAGE_CONNECT_TIMEOUT -> "CONNECT_TIMEOUT";
+            default -> Integer.toString(what);
+        };
     }
 
     public void doQuit() {
-        log("doQuit for device " + mDevice);
+        Log.d(TAG, "doQuit for " + mDevice);
         quitNow();
     }
 
-    public void cleanup() {
-        log("cleanup for device " + mDevice);
-    }
-
     int getConnectionState() {
         return mConnectionState;
     }
@@ -158,24 +137,22 @@
     }
 
     synchronized boolean isConnected() {
-        return (getConnectionState() == BluetoothProfile.STATE_CONNECTED);
+        return (getConnectionState() == STATE_CONNECTED);
     }
 
-    // This method does not check for error condition (newState == prevState)
-    private void broadcastConnectionState(int newState, int prevState) {
-        log(
-                "Connection state "
-                        + mDevice
-                        + ": "
-                        + profileStateToString(prevState)
+    private void broadcastConnectionState() {
+        Log.d(
+                TAG,
+                ("Connection state " + mDevice + ": ")
+                        + getConnectionStateName(mLastConnectionState)
                         + "->"
-                        + profileStateToString(newState));
+                        + getConnectionStateName(mConnectionState));
 
-        mService.connectionStateChanged(mDevice, prevState, newState);
+        mService.connectionStateChanged(mDevice, mLastConnectionState, mConnectionState);
         Intent intent =
                 new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED)
-                        .putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState)
-                        .putExtra(BluetoothProfile.EXTRA_STATE, newState)
+                        .putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, mLastConnectionState)
+                        .putExtra(BluetoothProfile.EXTRA_STATE, mConnectionState)
                         .putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice)
                         .addFlags(
                                 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
@@ -202,86 +179,60 @@
         scanner.close();
     }
 
-    @Override
-    protected void log(String msg) {
-        super.log(msg);
-    }
-
     @VisibleForTesting
     class Disconnected extends State {
+        private final String mStateLog = "Disconnected(" + mDevice + "): ";
+
         @Override
         public void enter() {
-            Log.i(
-                    TAG,
-                    "Enter Disconnected("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(getCurrentMessage().what));
-            mConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+            Log.i(TAG, "Enter " + mStateLog + messageWhatToString(getCurrentMessage().what));
 
-            removeDeferredMessages(DISCONNECT);
+            removeDeferredMessages(MESSAGE_DISCONNECT);
 
-            if (mLastConnectionState != -1) {
-                // Don't broadcast during startup
-                broadcastConnectionState(BluetoothProfile.STATE_DISCONNECTED, mLastConnectionState);
+            mConnectionState = STATE_DISCONNECTED;
+            if (mLastConnectionState != -1) { // Don't broadcast during startup
+                broadcastConnectionState();
             }
         }
 
         @Override
         public void exit() {
-            log(
-                    "Exit Disconnected("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(getCurrentMessage().what));
-            mLastConnectionState = BluetoothProfile.STATE_DISCONNECTED;
+            Log.d(TAG, "Exit " + mStateLog + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = STATE_DISCONNECTED;
         }
 
         @Override
         public boolean processMessage(Message message) {
-            log(
-                    "Disconnected: process message("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(message.what));
+            Log.d(TAG, mStateLog + "processMessage: " + messageWhatToString(message.what));
 
             switch (message.what) {
-                case CONNECT:
-                    log("Connecting to " + mDevice);
+                case MESSAGE_CONNECT -> {
                     if (!mNativeInterface.connectHapClient(mDevice)) {
-                        Log.e(TAG, "Disconnected: error connecting to " + mDevice);
+                        Log.e(TAG, mStateLog + "native cannot connect");
                         break;
                     }
                     if (mService.okToConnect(mDevice)) {
                         transitionTo(mConnecting);
                     } else {
-                        // Reject the request and stay in Disconnected state
-                        Log.w(
-                                TAG,
-                                "Outgoing HearingAccess Connecting request rejected: " + mDevice);
+                        Log.w(TAG, mStateLog + "outgoing connect request rejected");
                     }
-                    break;
-                case DISCONNECT:
-                    Log.d(TAG, "Disconnected: DISCONNECT: call native disconnect for " + mDevice);
+                }
+                case MESSAGE_DISCONNECT -> {
                     mNativeInterface.disconnectHapClient(mDevice);
-                    break;
-                case STACK_EVENT:
+                }
+                case MESSAGE_STACK_EVENT -> {
                     HapClientStackEvent event = (HapClientStackEvent) message.obj;
-                    Log.d(TAG, "Disconnected: stack event: " + event);
-                    if (!mDevice.equals(event.device)) {
-                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
-                    }
                     switch (event.type) {
-                        case HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                        case HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED -> {
                             processConnectionEvent(event.valueInt1);
-                            break;
-                        default:
-                            Log.e(TAG, "Disconnected: ignoring stack event: " + event);
-                            break;
+                        }
+                        default -> Log.e(TAG, mStateLog + "ignoring stack event: " + event);
                     }
-                    break;
-                default:
+                }
+                default -> {
+                    Log.e(TAG, mStateLog + "not handled: " + messageWhatToString(message.what));
                     return NOT_HANDLED;
+                }
             }
             return HANDLED;
         }
@@ -289,114 +240,83 @@
         // in Disconnected state
         private void processConnectionEvent(int state) {
             switch (state) {
-                case HapClientStackEvent.CONNECTION_STATE_DISCONNECTED:
-                    Log.w(TAG, "Ignore HearingAccess DISCONNECTED event: " + mDevice);
-                    break;
-                case HapClientStackEvent.CONNECTION_STATE_CONNECTING:
+                case STATE_CONNECTING -> {
                     if (mService.okToConnect(mDevice)) {
-                        Log.i(
-                                TAG,
-                                "Incoming HearingAccess Connecting request accepted: " + mDevice);
+                        Log.i(TAG, mStateLog + "Incoming connecting request accepted");
                         transitionTo(mConnecting);
                     } else {
-                        // Reject the connection and stay in Disconnected state itself
-                        Log.w(
-                                TAG,
-                                "Incoming HearingAccess Connecting request rejected: " + mDevice);
+                        Log.w(TAG, mStateLog + "Incoming connecting request rejected");
                         mNativeInterface.disconnectHapClient(mDevice);
                     }
-                    break;
-                case HapClientStackEvent.CONNECTION_STATE_CONNECTED:
+                }
+                case STATE_CONNECTED -> {
                     Log.w(TAG, "HearingAccess Connected from Disconnected state: " + mDevice);
                     if (mService.okToConnect(mDevice)) {
-                        Log.i(TAG, "Incoming HearingAccess Connected request accepted: " + mDevice);
+                        Log.w(TAG, mStateLog + "Incoming connected transition accepted");
                         transitionTo(mConnected);
                     } else {
-                        // Reject the connection and stay in Disconnected state itself
-                        Log.w(TAG, "Incoming HearingAccess Connected request rejected: " + mDevice);
+                        Log.w(TAG, mStateLog + "Incoming connected transition rejected");
                         mNativeInterface.disconnectHapClient(mDevice);
                     }
-                    break;
-                case HapClientStackEvent.CONNECTION_STATE_DISCONNECTING:
-                    Log.w(TAG, "Ignore HearingAccess DISCONNECTING event: " + mDevice);
-                    break;
-                default:
-                    Log.e(TAG, "Incorrect state: " + state + " device: " + mDevice);
-                    break;
+                }
+                default -> Log.e(TAG, mStateLog + "Incorrect state: " + state);
             }
         }
     }
 
     @VisibleForTesting
     class Connecting extends State {
+        private final String mStateLog = "Connecting(" + mDevice + "): ";
+
         @Override
         public void enter() {
-            Log.i(
-                    TAG,
-                    "Enter Connecting("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(getCurrentMessage().what));
-            sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs);
-            mConnectionState = BluetoothProfile.STATE_CONNECTING;
-            broadcastConnectionState(BluetoothProfile.STATE_CONNECTING, mLastConnectionState);
+            Log.i(TAG, "Enter " + mStateLog + messageWhatToString(getCurrentMessage().what));
+            sendMessageDelayed(MESSAGE_CONNECT_TIMEOUT, CONNECT_TIMEOUT.toMillis());
+            mConnectionState = STATE_CONNECTING;
+            broadcastConnectionState();
         }
 
         @Override
         public void exit() {
-            log(
-                    "Exit Connecting("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(getCurrentMessage().what));
-            mLastConnectionState = BluetoothProfile.STATE_CONNECTING;
-            removeMessages(CONNECT_TIMEOUT);
+            Log.d(TAG, "Exit " + mStateLog + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = STATE_CONNECTING;
+            removeMessages(MESSAGE_CONNECT_TIMEOUT);
         }
 
         @Override
         public boolean processMessage(Message message) {
-            log(
-                    "Connecting: process message("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(message.what));
+            Log.d(TAG, mStateLog + "processMessage: " + messageWhatToString(message.what));
 
             switch (message.what) {
-                case CONNECT:
-                    deferMessage(message);
-                    break;
-                case CONNECT_TIMEOUT:
-                    Log.w(TAG, "Connecting connection timeout: " + mDevice);
+                case MESSAGE_CONNECT -> deferMessage(message);
+                case MESSAGE_CONNECT_TIMEOUT -> {
+                    Log.w(TAG, mStateLog + "connection timeout");
                     mNativeInterface.disconnectHapClient(mDevice);
                     HapClientStackEvent disconnectEvent =
                             new HapClientStackEvent(
                                     HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
                     disconnectEvent.device = mDevice;
-                    disconnectEvent.valueInt1 = HapClientStackEvent.CONNECTION_STATE_DISCONNECTED;
-                    sendMessage(STACK_EVENT, disconnectEvent);
-                    break;
-                case DISCONNECT:
-                    log("Connecting: connection canceled to " + mDevice);
+                    disconnectEvent.valueInt1 = STATE_DISCONNECTED;
+                    sendMessage(MESSAGE_STACK_EVENT, disconnectEvent);
+                }
+                case MESSAGE_DISCONNECT -> {
+                    Log.d(TAG, mStateLog + "connection canceled");
                     mNativeInterface.disconnectHapClient(mDevice);
                     transitionTo(mDisconnected);
-                    break;
-                case STACK_EVENT:
+                }
+                case MESSAGE_STACK_EVENT -> {
                     HapClientStackEvent event = (HapClientStackEvent) message.obj;
-                    log("Connecting: stack event: " + event);
-                    if (!mDevice.equals(event.device)) {
-                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
-                    }
                     switch (event.type) {
-                        case HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                        case HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED -> {
                             processConnectionEvent(event.valueInt1);
-                            break;
-                        default:
-                            Log.e(TAG, "Connecting: ignoring stack event: " + event);
-                            break;
+                        }
+                        default -> Log.e(TAG, mStateLog + "ignoring stack event: " + event);
                     }
-                    break;
-                default:
+                }
+                default -> {
+                    Log.e(TAG, mStateLog + "not handled: " + messageWhatToString(message.what));
                     return NOT_HANDLED;
+                }
             }
             return HANDLED;
         }
@@ -404,98 +324,69 @@
         // in Connecting state
         private void processConnectionEvent(int state) {
             switch (state) {
-                case HapClientStackEvent.CONNECTION_STATE_DISCONNECTED:
-                    Log.w(TAG, "Connecting device disconnected: " + mDevice);
+                case STATE_DISCONNECTED -> {
+                    Log.i(TAG, mStateLog + "device disconnected");
                     transitionTo(mDisconnected);
-                    break;
-                case HapClientStackEvent.CONNECTION_STATE_CONNECTED:
-                    transitionTo(mConnected);
-                    break;
-                case HapClientStackEvent.CONNECTION_STATE_CONNECTING:
-                    break;
-                case HapClientStackEvent.CONNECTION_STATE_DISCONNECTING:
-                    Log.w(TAG, "Connecting interrupted: device is disconnecting: " + mDevice);
+                }
+                case STATE_CONNECTED -> transitionTo(mConnected);
+                case STATE_DISCONNECTING -> {
+                    Log.i(TAG, mStateLog + "device disconnecting");
                     transitionTo(mDisconnecting);
-                    break;
-                default:
-                    Log.e(TAG, "Incorrect state: " + state);
-                    break;
+                }
+                default -> Log.e(TAG, mStateLog + "Incorrect state: " + state);
             }
         }
     }
 
     @VisibleForTesting
     class Disconnecting extends State {
+        private final String mStateLog = "Disconnecting(" + mDevice + "): ";
+
         @Override
         public void enter() {
-            Log.i(
-                    TAG,
-                    "Enter Disconnecting("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(getCurrentMessage().what));
-            sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs);
-            mConnectionState = BluetoothProfile.STATE_DISCONNECTING;
-            broadcastConnectionState(BluetoothProfile.STATE_DISCONNECTING, mLastConnectionState);
+            Log.i(TAG, "Enter " + mStateLog + messageWhatToString(getCurrentMessage().what));
+            sendMessageDelayed(MESSAGE_CONNECT_TIMEOUT, CONNECT_TIMEOUT.toMillis());
+            mConnectionState = STATE_DISCONNECTING;
+            broadcastConnectionState();
         }
 
         @Override
         public void exit() {
-            log(
-                    "Exit Disconnecting("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(getCurrentMessage().what));
-            mLastConnectionState = BluetoothProfile.STATE_DISCONNECTING;
-            removeMessages(CONNECT_TIMEOUT);
+            Log.d(TAG, "Exit " + mStateLog + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = STATE_DISCONNECTING;
+            removeMessages(MESSAGE_CONNECT_TIMEOUT);
         }
 
         @Override
         public boolean processMessage(Message message) {
-            log(
-                    "Disconnecting: process message("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(message.what));
+            Log.d(TAG, mStateLog + "processMessage: " + messageWhatToString(message.what));
 
             switch (message.what) {
-                case CONNECT:
-                    deferMessage(message);
-                    break;
-                case CONNECT_TIMEOUT:
-                    {
-                        Log.w(TAG, "Disconnecting connection timeout: " + mDevice);
-                        mNativeInterface.disconnectHapClient(mDevice);
+                case MESSAGE_CONNECT, MESSAGE_DISCONNECT -> deferMessage(message);
+                case MESSAGE_CONNECT_TIMEOUT -> {
+                    Log.w(TAG, mStateLog + "connection timeout");
+                    mNativeInterface.disconnectHapClient(mDevice);
 
-                        HapClientStackEvent disconnectEvent =
-                                new HapClientStackEvent(
-                                        HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-                        disconnectEvent.device = mDevice;
-                        disconnectEvent.valueInt1 =
-                                HapClientStackEvent.CONNECTION_STATE_DISCONNECTED;
-                        sendMessage(STACK_EVENT, disconnectEvent);
-                        break;
-                    }
-                case DISCONNECT:
-                    deferMessage(message);
-                    break;
-                case STACK_EVENT:
+                    HapClientStackEvent disconnectEvent =
+                            new HapClientStackEvent(
+                                    HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+                    disconnectEvent.device = mDevice;
+                    disconnectEvent.valueInt1 = STATE_DISCONNECTED;
+                    sendMessage(MESSAGE_STACK_EVENT, disconnectEvent);
+                }
+                case MESSAGE_STACK_EVENT -> {
                     HapClientStackEvent event = (HapClientStackEvent) message.obj;
-                    log("Disconnecting: stack event: " + event);
-                    if (!mDevice.equals(event.device)) {
-                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
-                    }
                     switch (event.type) {
-                        case HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
+                        case HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED -> {
                             processConnectionEvent(event.valueInt1);
-                            break;
-                        default:
-                            Log.e(TAG, "Disconnecting: ignoring stack event: " + event);
-                            break;
+                        }
+                        default -> Log.e(TAG, mStateLog + "ignoring stack event: " + event);
                     }
-                    break;
-                default:
+                }
+                default -> {
+                    Log.e(TAG, mStateLog + "not handled: " + messageWhatToString(message.what));
                     return NOT_HANDLED;
+                }
             }
             return HANDLED;
         }
@@ -503,105 +394,79 @@
         // in Disconnecting state
         private void processConnectionEvent(int state) {
             switch (state) {
-                case HapClientStackEvent.CONNECTION_STATE_DISCONNECTED:
-                    Log.i(TAG, "Disconnected: " + mDevice);
+                case STATE_DISCONNECTED -> {
+                    Log.i(TAG, mStateLog + "Disconnected");
                     transitionTo(mDisconnected);
-                    break;
-                case HapClientStackEvent.CONNECTION_STATE_CONNECTED:
+                }
+                case STATE_CONNECTED -> {
                     if (mService.okToConnect(mDevice)) {
-                        Log.w(TAG, "Disconnecting interrupted: device is connected: " + mDevice);
+                        Log.w(TAG, mStateLog + "interrupted: device is connected");
                         transitionTo(mConnected);
                     } else {
                         // Reject the connection and stay in Disconnecting state
-                        Log.w(TAG, "Incoming HearingAccess Connected request rejected: " + mDevice);
+                        Log.w(TAG, mStateLog + "Incoming connect request rejected");
                         mNativeInterface.disconnectHapClient(mDevice);
                     }
-                    break;
-                case HapClientStackEvent.CONNECTION_STATE_CONNECTING:
+                }
+                case STATE_CONNECTING -> {
                     if (mService.okToConnect(mDevice)) {
-                        Log.i(TAG, "Disconnecting interrupted: try to reconnect: " + mDevice);
+                        Log.i(TAG, mStateLog + "interrupted: device try to reconnect");
                         transitionTo(mConnecting);
                     } else {
                         // Reject the connection and stay in Disconnecting state
-                        Log.w(
-                                TAG,
-                                "Incoming HearingAccess Connecting request rejected: " + mDevice);
+                        Log.w(TAG, mStateLog + "Incoming connecting request rejected");
                         mNativeInterface.disconnectHapClient(mDevice);
                     }
-                    break;
-                case HapClientStackEvent.CONNECTION_STATE_DISCONNECTING:
-                    break;
-                default:
-                    Log.e(TAG, "Incorrect state: " + state);
-                    break;
+                }
+                default -> Log.e(TAG, mStateLog + "Incorrect state: " + state);
             }
         }
     }
 
     @VisibleForTesting
     class Connected extends State {
+        private final String mStateLog = "Connected(" + mDevice + "): ";
+
         @Override
         public void enter() {
-            Log.i(
-                    TAG,
-                    "Enter Connected("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(getCurrentMessage().what));
-            mConnectionState = BluetoothProfile.STATE_CONNECTED;
-            removeDeferredMessages(CONNECT);
-            broadcastConnectionState(BluetoothProfile.STATE_CONNECTED, mLastConnectionState);
+            Log.i(TAG, "Enter " + mStateLog + messageWhatToString(getCurrentMessage().what));
+            removeDeferredMessages(MESSAGE_CONNECT);
+            mConnectionState = STATE_CONNECTED;
+            broadcastConnectionState();
         }
 
         @Override
         public void exit() {
-            log(
-                    "Exit Connected("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(getCurrentMessage().what));
-            mLastConnectionState = BluetoothProfile.STATE_CONNECTED;
+            Log.d(TAG, "Exit " + mStateLog + messageWhatToString(getCurrentMessage().what));
+            mLastConnectionState = STATE_CONNECTED;
         }
 
         @Override
         public boolean processMessage(Message message) {
-            log(
-                    "Connected: process message("
-                            + mDevice
-                            + "): "
-                            + messageWhatToString(message.what));
+            Log.d(TAG, mStateLog + "processMessage: " + messageWhatToString(message.what));
 
             switch (message.what) {
-                case CONNECT:
-                    Log.w(TAG, "Connected: CONNECT ignored: " + mDevice);
-                    break;
-                case DISCONNECT:
-                    log("Disconnecting from " + mDevice);
+                case MESSAGE_DISCONNECT -> {
                     if (!mNativeInterface.disconnectHapClient(mDevice)) {
                         // If error in the native stack, transition directly to Disconnected state.
-                        Log.e(TAG, "Connected: error disconnecting from " + mDevice);
+                        Log.e(TAG, mStateLog + "native cannot disconnect");
                         transitionTo(mDisconnected);
                         break;
                     }
                     transitionTo(mDisconnecting);
-                    break;
-                case STACK_EVENT:
+                }
+                case MESSAGE_STACK_EVENT -> {
                     HapClientStackEvent event = (HapClientStackEvent) message.obj;
-                    log("Connected: stack event: " + event);
-                    if (!mDevice.equals(event.device)) {
-                        Log.wtf(TAG, "Device(" + mDevice + "): event mismatch: " + event);
-                    }
                     switch (event.type) {
-                        case HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
-                            processConnectionEvent(event.valueInt1);
-                            break;
-                        default:
-                            Log.e(TAG, "Connected: ignoring stack event: " + event);
-                            break;
+                        case HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED ->
+                                processConnectionEvent(event.valueInt1);
+                        default -> Log.e(TAG, mStateLog + "ignoring stack event: " + event);
                     }
-                    break;
-                default:
+                }
+                default -> {
+                    Log.e(TAG, mStateLog + "not handled: " + messageWhatToString(message.what));
                     return NOT_HANDLED;
+                }
             }
             return HANDLED;
         }
@@ -609,17 +474,15 @@
         // in Connected state
         private void processConnectionEvent(int state) {
             switch (state) {
-                case HapClientStackEvent.CONNECTION_STATE_DISCONNECTED:
-                    Log.i(TAG, "Disconnected from " + mDevice + " but still in Allowlist");
+                case STATE_DISCONNECTED -> {
+                    Log.i(TAG, mStateLog + "Disconnected but still in allowlist");
                     transitionTo(mDisconnected);
-                    break;
-                case HapClientStackEvent.CONNECTION_STATE_DISCONNECTING:
-                    Log.i(TAG, "Disconnecting from " + mDevice);
+                }
+                case STATE_DISCONNECTING -> {
+                    Log.i(TAG, mStateLog + "Disconnecting");
                     transitionTo(mDisconnecting);
-                    break;
-                default:
-                    Log.e(TAG, "Connection State Device: " + mDevice + " bad state: " + state);
-                    break;
+                }
+                default -> Log.e(TAG, mStateLog + "Incorrect state: " + state);
             }
         }
     }
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index 52cc028..c706e2c 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -2389,6 +2389,15 @@
                 Log.d(TAG, "Processing command: " + atString);
                 if (processAndroidAtSinkAudioPolicy(args, device)) {
                     mNativeInterface.atResponseCode(device, HeadsetHalConstants.AT_RESPONSE_OK, 0);
+                    if (getHfpCallAudioPolicy().getActiveDevicePolicyAfterConnection()
+                                    == BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED
+                            && mDevice.equals(mHeadsetService.getActiveDevice())) {
+                        Log.d(
+                                TAG,
+                                "Remove the active device because the active device policy after"
+                                        + " connection is not allowed");
+                        mHeadsetService.setActiveDevice(null);
+                    }
                 } else {
                     Log.w(TAG, "Invalid SinkAudioPolicy parameters!");
                     mNativeInterface.atResponseCode(
diff --git a/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java b/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java
index 7438921..a7e3bff 100644
--- a/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java
+++ b/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java
@@ -16,6 +16,7 @@
 
 package com.android.bluetooth.le_scan;
 
+import android.annotation.Nullable;
 import android.bluetooth.BluetoothProtoEnums;
 import android.bluetooth.le.ScanFilter;
 import android.bluetooth.le.ScanSettings;
@@ -104,6 +105,7 @@
         public int scanMode;
         public int scanCallbackType;
         public StringBuilder filterString;
+        @Nullable public String attributionTag;
 
         LastScan(
                 long timestamp,
@@ -112,7 +114,8 @@
                 boolean isCallbackScan,
                 int scannerId,
                 int scanMode,
-                int scanCallbackType) {
+                int scanCallbackType,
+                @Nullable String attributionTag) {
             this.duration = 0;
             this.timestamp = timestamp;
             this.reportDelayMillis = reportDelayMillis;
@@ -126,6 +129,7 @@
             this.isAutoBatchScan = false;
             this.scanMode = scanMode;
             this.scanCallbackType = scanCallbackType;
+            this.attributionTag = attributionTag;
             this.results = 0;
             this.scannerId = scannerId;
             this.suspendDuration = 0;
@@ -239,7 +243,8 @@
             List<ScanFilter> filters,
             boolean isFilterScan,
             boolean isCallbackScan,
-            int scannerId) {
+            int scannerId,
+            @Nullable String attributionTag) {
         LastScan existingScan = getScanFromScannerId(scannerId);
         if (existingScan != null) {
             return;
@@ -255,7 +260,8 @@
                         isCallbackScan,
                         scannerId,
                         settings.getScanMode(),
-                        settings.getCallbackType());
+                        settings.getCallbackType(),
+                        attributionTag);
         if (settings != null) {
             scan.isOpportunisticScan = scan.scanMode == ScanSettings.SCAN_MODE_OPPORTUNISTIC;
             scan.isBackgroundScan =
@@ -1065,6 +1071,9 @@
                 }
                 sb.append(scan.results).append(" results");
                 sb.append(" (").append(scan.scannerId).append(") ");
+                if (scan.attributionTag != null) {
+                    sb.append(" [").append(scan.attributionTag).append("] ");
+                }
                 if (scan.isCallbackScan) {
                     sb.append("CB ");
                 } else {
diff --git a/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java b/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java
index 09f3626..a6470bd 100644
--- a/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java
+++ b/android/app/src/com/android/bluetooth/le_scan/TransitionalScanHelper.java
@@ -28,6 +28,7 @@
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothUtils;
 import android.bluetooth.le.BluetoothLeScanner;
 import android.bluetooth.le.IPeriodicAdvertisingCallback;
 import android.bluetooth.le.IScannerCallback;
@@ -342,7 +343,7 @@
                         + ", addressType="
                         + addressType
                         + ", address="
-                        + address
+                        + BluetoothUtils.toAnonymizedAddress(address)
                         + ", primaryPhy="
                         + primaryPhy
                         + ", secondaryPhy="
@@ -1245,7 +1246,13 @@
             if (cbApp != null) {
                 isCallbackScan = cbApp.mCallback != null;
             }
-            app.recordScanStart(settings, filters, isFilteredScan, isCallbackScan, scannerId);
+            app.recordScanStart(
+                    settings,
+                    filters,
+                    isFilteredScan,
+                    isCallbackScan,
+                    scannerId,
+                    cbApp == null ? null : cbApp.mAttributionTag);
         }
 
         mScanManager.startScan(scanClient);
@@ -1352,7 +1359,12 @@
             scanClient.stats = scanStats;
             boolean isFilteredScan = (piInfo.filters != null) && !piInfo.filters.isEmpty();
             scanStats.recordScanStart(
-                    piInfo.settings, piInfo.filters, isFilteredScan, false, scannerId);
+                    piInfo.settings,
+                    piInfo.filters,
+                    isFilteredScan,
+                    false,
+                    scannerId,
+                    app.mAttributionTag);
         }
 
         mScanManager.startScan(scanClient);
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlInputDescriptor.java b/android/app/src/com/android/bluetooth/vc/VolumeControlInputDescriptor.java
index e5324ae..4e81095 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlInputDescriptor.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlInputDescriptor.java
@@ -16,56 +16,44 @@
 
 package com.android.bluetooth.vc;
 
-import android.bluetooth.BluetoothVolumeControl;
 import android.util.Log;
 
 import com.android.bluetooth.btservice.ProfileService;
 
-import java.util.HashMap;
-import java.util.Map;
+import bluetooth.constants.AudioInputType;
+import bluetooth.constants.aics.AudioInputStatus;
+import bluetooth.constants.aics.GainMode;
+import bluetooth.constants.aics.Mute;
 
 class VolumeControlInputDescriptor {
-    private static final String TAG = "VolumeControlInputDescriptor";
-    final Map<Integer, Descriptor> mVolumeInputs = new HashMap<>();
+    private static final String TAG = VolumeControlInputDescriptor.class.getSimpleName();
 
-    public static final int AUDIO_INPUT_TYPE_UNSPECIFIED = 0x00;
-    public static final int AUDIO_INPUT_TYPE_BLUETOOTH = 0x01;
-    public static final int AUDIO_INPUT_TYPE_MICROPHONE = 0x02;
-    public static final int AUDIO_INPUT_TYPE_ANALOG = 0x03;
-    public static final int AUDIO_INPUT_TYPE_DIGITAL = 0x04;
-    public static final int AUDIO_INPUT_TYPE_RADIO = 0x05;
-    public static final int AUDIO_INPUT_TYPE_STREAMING = 0x06;
-    public static final int AUDIO_INPUT_TYPE_AMBIENT = 0x07;
+    final Descriptor[] mVolumeInputs;
+
+    VolumeControlInputDescriptor(int numberOfExternalInputs) {
+        mVolumeInputs = new Descriptor[numberOfExternalInputs];
+        // Stack delivers us number of audio inputs. ids are countinous from [0;n[
+        for (int i = 0; i < numberOfExternalInputs; i++) {
+            mVolumeInputs[i] = new Descriptor();
+        }
+    }
 
     private static class Descriptor {
-        /* True when input is active, false otherwise */
-        boolean mIsActive = false;
+        int mStatus = AudioInputStatus.INACTIVE;
 
-        /* Defined as in Assigned Numbers in the BluetoothVolumeControl.AUDIO_INPUT_TYPE_ */
-        int mType = AUDIO_INPUT_TYPE_UNSPECIFIED;
+        int mType = AudioInputType.UNSPECIFIED;
 
-        int mGainValue = 0;
+        int mGainSetting = 0;
 
-        /* As per AICS 1.0
-         * 3.1.3. Gain_Mode field
-         * The Gain_Mode field shall be set to a value that reflects whether gain modes are
-         *  manual or automatic.
-         *
-         * If the Gain_Mode field value is Manual Only, the server allows only manual gain.
-         * If the Gain_Mode field is Automatic Only, the server allows only automatic gain.
-         *
-         * For all other Gain_Mode field values, the server allows switchable
-         * automatic/manual gain.
-         */
-        int mGainMode = 0;
+        int mGainMode = GainMode.MANUAL_ONLY;
 
-        boolean mIsMute = false;
+        int mMute = Mute.DISABLED;
 
-        /* As per AICS 1.0
-         * The Gain_Setting (mGainValue) field is a signed value for which a single increment
-         * or decrement should result in a corresponding increase or decrease of the input
-         * amplitude by the value of the Gain_Setting_Units (mGainSettingsUnits)
-         * field of the Gain Setting Properties characteristic value.
+        /* See AICS 1.0
+         * The Gain_Setting (mGainSetting) field is a signed value for which a single increment or
+         * decrement should result in a corresponding increase or decrease of the input amplitude by
+         * the value of the Gain_Setting_Units (mGainSettingsUnits) field of the Gain Setting
+         * Properties characteristic value.
          */
         int mGainSettingsUnits = 0;
 
@@ -76,150 +64,91 @@
     }
 
     int size() {
-        return mVolumeInputs.size();
+        return mVolumeInputs.length;
     }
 
-    void add(int id) {
-        if (!mVolumeInputs.containsKey(id)) {
-            mVolumeInputs.put(id, new Descriptor());
-        }
-    }
-
-    boolean setActive(int id, boolean active) {
-        Descriptor desc = mVolumeInputs.get(id);
-        if (desc == null) {
-            Log.e(TAG, "setActive, Id " + id + " is unknown");
+    private boolean isValidId(int id) {
+        if (id >= size() || id < 0) {
+            Log.e(TAG, "Request fail. Illegal id argument: " + id);
             return false;
         }
-        desc.mIsActive = active;
         return true;
     }
 
-    boolean isActive(int id) {
-        Descriptor desc = mVolumeInputs.get(id);
-        if (desc == null) {
-            Log.e(TAG, "isActive, Id " + id + " is unknown");
-            return false;
-        }
-        return desc.mIsActive;
+    void setStatus(int id, int status) {
+        if (!isValidId(id)) return;
+        mVolumeInputs[id].mStatus = status;
     }
 
-    boolean setDescription(int id, String description) {
-        Descriptor desc = mVolumeInputs.get(id);
-        if (desc == null) {
-            Log.e(TAG, "setDescription, Id " + id + " is unknown");
-            return false;
-        }
-        desc.mDescription = description;
-        return true;
+    int getStatus(int id) {
+        if (!isValidId(id)) return AudioInputStatus.INACTIVE;
+        return mVolumeInputs[id].mStatus;
+    }
+
+    void setDescription(int id, String description) {
+        if (!isValidId(id)) return;
+        mVolumeInputs[id].mDescription = description;
     }
 
     String getDescription(int id) {
-        Descriptor desc = mVolumeInputs.get(id);
-        if (desc == null) {
-            Log.e(TAG, "getDescription, Id " + id + " is unknown");
-            return null;
-        }
-        return desc.mDescription;
+        if (!isValidId(id)) return null;
+        return mVolumeInputs[id].mDescription;
     }
 
-    boolean setType(int id, int type) {
-        Descriptor desc = mVolumeInputs.get(id);
-        if (desc == null) {
-            Log.e(TAG, "setType, Id " + id + " is unknown");
-            return false;
-        }
-
-        if (type > AUDIO_INPUT_TYPE_AMBIENT) {
-            Log.e(TAG, "setType, Type " + type + "for id " + id + " is invalid");
-            return false;
-        }
-
-        desc.mType = type;
-        return true;
+    void setType(int id, int type) {
+        if (!isValidId(id)) return;
+        mVolumeInputs[id].mType = type;
     }
 
     int getType(int id) {
-        Descriptor desc = mVolumeInputs.get(id);
-        if (desc == null) {
-            Log.e(TAG, "getType, Id " + id + " is unknown");
-            return AUDIO_INPUT_TYPE_UNSPECIFIED;
-        }
-        return desc.mType;
+        if (!isValidId(id)) return AudioInputType.UNSPECIFIED;
+        return mVolumeInputs[id].mType;
     }
 
-    int getGain(int id) {
-        Descriptor desc = mVolumeInputs.get(id);
-        if (desc == null) {
-            Log.e(TAG, "getGain, Id " + id + " is unknown");
-            return 0;
-        }
-        return desc.mGainValue;
+    int getGainSetting(int id) {
+        if (!isValidId(id)) return 0;
+        return mVolumeInputs[id].mGainSetting;
     }
 
-    boolean isMuted(int id) {
-        Descriptor desc = mVolumeInputs.get(id);
-        if (desc == null) {
-            Log.e(TAG, "isMuted, Id " + id + " is unknown");
-            return false;
-        }
-        return desc.mIsMute;
+    int getMute(int id) {
+        if (!isValidId(id)) return Mute.DISABLED;
+        return mVolumeInputs[id].mMute;
     }
 
-    boolean setPropSettings(int id, int gainUnit, int gainMin, int gainMax) {
-        Descriptor desc = mVolumeInputs.get(id);
-        if (desc == null) {
-            Log.e(TAG, "setPropSettings, Id " + id + " is unknown");
-            return false;
-        }
+    void setPropSettings(int id, int gainUnit, int gainMin, int gainMax) {
+        if (!isValidId(id)) return;
 
-        desc.mGainSettingsUnits = gainUnit;
-        desc.mGainSettingsMinSetting = gainMin;
-        desc.mGainSettingsMaxSetting = gainMax;
-
-        return true;
+        mVolumeInputs[id].mGainSettingsUnits = gainUnit;
+        mVolumeInputs[id].mGainSettingsMinSetting = gainMin;
+        mVolumeInputs[id].mGainSettingsMaxSetting = gainMax;
     }
 
-    boolean setState(int id, int gainValue, int gainMode, boolean mute) {
-        Descriptor desc = mVolumeInputs.get(id);
-        if (desc == null) {
-            Log.e(TAG, "Id " + id + " is unknown");
-            return false;
+    void setState(int id, int gainSetting, int mute, int gainMode) {
+        if (!isValidId(id)) return;
+
+        Descriptor desc = mVolumeInputs[id];
+
+        if (gainSetting > desc.mGainSettingsMaxSetting
+                || gainSetting < desc.mGainSettingsMinSetting) {
+            Log.e(TAG, "Request fail. Illegal gainSetting argument: " + gainSetting);
+            return;
         }
 
-        if (gainValue > desc.mGainSettingsMaxSetting || gainValue < desc.mGainSettingsMinSetting) {
-            Log.e(TAG, "Invalid gainValue " + gainValue);
-            return false;
-        }
-
-        desc.mGainValue = gainValue;
+        desc.mGainSetting = gainSetting;
         desc.mGainMode = gainMode;
-        desc.mIsMute = mute;
-
-        return true;
-    }
-
-    void remove(int id) {
-        Log.d(TAG, "remove, id: " + id);
-        mVolumeInputs.remove(id);
-    }
-
-    void clear() {
-        Log.d(TAG, "clear all inputs");
-        mVolumeInputs.clear();
+        desc.mMute = mute;
     }
 
     void dump(StringBuilder sb) {
-        for (Map.Entry<Integer, Descriptor> entry : mVolumeInputs.entrySet()) {
-            Descriptor desc = entry.getValue();
-            Integer id = entry.getKey();
-            ProfileService.println(sb, "        id: " + id);
+        for (int i = 0; i < mVolumeInputs.length; i++) {
+            Descriptor desc = mVolumeInputs[i];
+            ProfileService.println(sb, "      id: " + i);
             ProfileService.println(sb, "        description: " + desc.mDescription);
             ProfileService.println(sb, "        type: " + desc.mType);
-            ProfileService.println(sb, "        isActive: " + desc.mIsActive);
-            ProfileService.println(sb, "        gainValue: " + desc.mGainValue);
+            ProfileService.println(sb, "        status: " + desc.mStatus);
+            ProfileService.println(sb, "        gainSetting: " + desc.mGainSetting);
             ProfileService.println(sb, "        gainMode: " + desc.mGainMode);
-            ProfileService.println(sb, "        mute: " + desc.mIsMute);
+            ProfileService.println(sb, "        mute: " + desc.mMute);
             ProfileService.println(sb, "        units:" + desc.mGainSettingsUnits);
             ProfileService.println(sb, "        minGain:" + desc.mGainSettingsMinSetting);
             ProfileService.println(sb, "        maxGain:" + desc.mGainSettingsMaxSetting);
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlNativeCallback.java b/android/app/src/com/android/bluetooth/vc/VolumeControlNativeCallback.java
new file mode 100644
index 0000000..56f4cde
--- /dev/null
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlNativeCallback.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2024 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.vc;
+
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED;
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_DEVICE_AVAILABLE;
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED;
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED;
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED;
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED;
+
+import static java.util.Objects.requireNonNull;
+
+import android.bluetooth.BluetoothDevice;
+import android.util.Log;
+
+import com.android.bluetooth.btservice.AdapterService;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+class VolumeControlNativeCallback {
+    private static final String TAG = VolumeControlNativeCallback.class.getSimpleName();
+
+    private final AdapterService mAdapterService;
+    private final VolumeControlService mVolumeControlService;
+
+    VolumeControlNativeCallback(
+            AdapterService adapterService, VolumeControlService volumeControlService) {
+        mAdapterService = requireNonNull(adapterService);
+        mVolumeControlService = requireNonNull(volumeControlService);
+    }
+
+    private BluetoothDevice getDevice(byte[] address) {
+        return mAdapterService.getDeviceFromByte(address);
+    }
+
+    private void sendMessageToService(Consumer<VolumeControlService> action) {
+        if (!mVolumeControlService.isAvailable()) {
+            StringBuilder sb = new StringBuilder();
+            Arrays.stream(new Throwable().getStackTrace())
+                    .skip(1) // skip the inlineStackTrace method in the outputted stack trace
+                    .forEach(trace -> sb.append(" [at ").append(trace).append("]"));
+            Log.e(TAG, "Action ignored, service not available: " + sb.toString());
+            return;
+        }
+        action.accept(mVolumeControlService);
+    }
+
+    @VisibleForTesting
+    void onConnectionStateChanged(int state, byte[] address) {
+        VolumeControlStackEvent event =
+                new VolumeControlStackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = state;
+
+        Log.d(TAG, "onConnectionStateChanged: " + event);
+        mVolumeControlService.messageFromNative(event);
+    }
+
+    @VisibleForTesting
+    void onVolumeStateChanged(
+            int volume, boolean mute, int flags, byte[] address, boolean isAutonomous) {
+        VolumeControlStackEvent event =
+                new VolumeControlStackEvent(EVENT_TYPE_VOLUME_STATE_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = -1;
+        event.valueInt2 = volume;
+        event.valueInt3 = flags;
+        event.valueBool1 = mute;
+        event.valueBool2 = isAutonomous;
+
+        Log.d(TAG, "onVolumeStateChanged: " + event);
+        mVolumeControlService.messageFromNative(event);
+    }
+
+    @VisibleForTesting
+    void onGroupVolumeStateChanged(int volume, boolean mute, int groupId, boolean isAutonomous) {
+        VolumeControlStackEvent event =
+                new VolumeControlStackEvent(EVENT_TYPE_VOLUME_STATE_CHANGED);
+        event.device = null;
+        event.valueInt1 = groupId;
+        event.valueInt2 = volume;
+        event.valueBool1 = mute;
+        event.valueBool2 = isAutonomous;
+
+        Log.d(TAG, "onGroupVolumeStateChanged: " + event);
+        mVolumeControlService.messageFromNative(event);
+    }
+
+    @VisibleForTesting
+    void onDeviceAvailable(int numOfExternalOutputs, int numOfExternalInputs, byte[] address) {
+        VolumeControlStackEvent event = new VolumeControlStackEvent(EVENT_TYPE_DEVICE_AVAILABLE);
+        event.device = getDevice(address);
+        event.valueInt1 = numOfExternalOutputs;
+        event.valueInt2 = numOfExternalInputs;
+
+        Log.d(TAG, "onDeviceAvailable: " + event);
+        mVolumeControlService.messageFromNative(event);
+    }
+
+    @VisibleForTesting
+    void onExtAudioOutVolumeOffsetChanged(int externalOutputId, int offset, byte[] address) {
+        VolumeControlStackEvent event =
+                new VolumeControlStackEvent(EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = externalOutputId;
+        event.valueInt2 = offset;
+
+        Log.d(TAG, "onExtAudioOutVolumeOffsetChanged: " + event);
+        mVolumeControlService.messageFromNative(event);
+    }
+
+    @VisibleForTesting
+    void onExtAudioOutLocationChanged(int externalOutputId, int location, byte[] address) {
+        VolumeControlStackEvent event =
+                new VolumeControlStackEvent(EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = externalOutputId;
+        event.valueInt2 = location;
+
+        Log.d(TAG, "onExtAudioOutLocationChanged: " + event);
+        mVolumeControlService.messageFromNative(event);
+    }
+
+    @VisibleForTesting
+    void onExtAudioOutDescriptionChanged(int externalOutputId, String descr, byte[] address) {
+        VolumeControlStackEvent event =
+                new VolumeControlStackEvent(EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED);
+        event.device = getDevice(address);
+        event.valueInt1 = externalOutputId;
+        event.valueString1 = descr;
+
+        Log.d(TAG, "onExtAudioOutLocationChanged: " + event);
+        mVolumeControlService.messageFromNative(event);
+    }
+
+    @VisibleForTesting
+    void onExtAudioInStateChanged(int id, int gainSetting, int gainMode, int mute, byte[] address) {
+        sendMessageToService(
+                s ->
+                        s.onExtAudioInStateChanged(
+                                getDevice(address), id, gainSetting, mute, gainMode));
+    }
+
+    @VisibleForTesting
+    void onExtAudioInStatusChanged(int id, int status, byte[] address) {
+        sendMessageToService(s -> s.onExtAudioInStatusChanged(getDevice(address), id, status));
+    }
+
+    @VisibleForTesting
+    void onExtAudioInTypeChanged(int id, int type, byte[] address) {
+        sendMessageToService(s -> s.onExtAudioInTypeChanged(getDevice(address), id, type));
+    }
+
+    @VisibleForTesting
+    void onExtAudioInDescriptionChanged(int id, String descr, byte[] address) {
+        sendMessageToService(s -> s.onExtAudioInDescriptionChanged(getDevice(address), id, descr));
+    }
+
+    @VisibleForTesting
+    void onExtAudioInGainPropsChanged(int id, int unit, int min, int max, byte[] address) {
+        sendMessageToService(
+                s -> s.onExtAudioInGainPropsChanged(getDevice(address), id, unit, min, max));
+    }
+}
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java b/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
index 7167293..95db676 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlNativeInterface.java
@@ -17,327 +17,32 @@
 
 package com.android.bluetooth.vc;
 
-import android.bluetooth.BluetoothAdapter;
+import static java.util.Objects.requireNonNull;
+
 import android.bluetooth.BluetoothDevice;
-import android.util.Log;
 
 import com.android.bluetooth.Utils;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Native;
 
 public class VolumeControlNativeInterface {
-    private static final String TAG = "VolumeControlNativeInterface";
-    private BluetoothAdapter mAdapter;
+    private static final String TAG = VolumeControlNativeInterface.class.getSimpleName();
 
-    @GuardedBy("INSTANCE_LOCK")
-    private static VolumeControlNativeInterface sInstance;
+    // Value access from native, see com_android_bluetooth_vc.cpp
+    @Native private final VolumeControlNativeCallback mNativeCallback;
 
-    private static final Object INSTANCE_LOCK = new Object();
-
-    private VolumeControlNativeInterface() {
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
-        if (mAdapter == null) {
-            Log.wtf(TAG, "No Bluetooth Adapter Available");
-        }
+    VolumeControlNativeInterface(VolumeControlNativeCallback nativeCallback) {
+        mNativeCallback = requireNonNull(nativeCallback);
     }
 
-    /** Get singleton instance. */
-    public static VolumeControlNativeInterface getInstance() {
-        synchronized (INSTANCE_LOCK) {
-            if (sInstance == null) {
-                sInstance = new VolumeControlNativeInterface();
-            }
-            return sInstance;
-        }
-    }
-
-    /** Set singleton instance. */
-    @VisibleForTesting
-    public static void setInstance(VolumeControlNativeInterface instance) {
-        synchronized (INSTANCE_LOCK) {
-            sInstance = instance;
-        }
-    }
-
-    /**
-     * Initializes the native interface.
-     *
-     * <p>priorities to configure.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void init() {
+    void init() {
         initNative();
     }
 
-    /** Cleanup the native interface. */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void cleanup() {
+    void cleanup() {
         cleanupNative();
     }
 
-    /**
-     * Initiates VolumeControl connection to a remote device.
-     *
-     * @param device the remote device
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean connectVolumeControl(BluetoothDevice device) {
-        return connectVolumeControlNative(getByteAddress(device));
-    }
-
-    /**
-     * Disconnects VolumeControl from a remote device.
-     *
-     * @param device the remote device
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean disconnectVolumeControl(BluetoothDevice device) {
-        return disconnectVolumeControlNative(getByteAddress(device));
-    }
-
-    /** Sets the VolumeControl volume */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void setVolume(BluetoothDevice device, int volume) {
-        setVolumeNative(getByteAddress(device), volume);
-    }
-
-    /** Sets the VolumeControl volume for the group */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void setGroupVolume(int groupId, int volume) {
-        setGroupVolumeNative(groupId, volume);
-    }
-
-    /** Mute the VolumeControl volume */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void mute(BluetoothDevice device) {
-        muteNative(getByteAddress(device));
-    }
-
-    /** Mute the VolumeControl volume in the group */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void muteGroup(int groupId) {
-        muteGroupNative(groupId);
-    }
-
-    /** Unmute the VolumeControl volume */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void unmute(BluetoothDevice device) {
-        unmuteNative(getByteAddress(device));
-    }
-
-    /** Unmute the VolumeControl volume group */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void unmuteGroup(int groupId) {
-        unmuteGroupNative(groupId);
-    }
-
-    /**
-     * Gets external audio output volume offset from a remote device.
-     *
-     * @param device the remote device
-     * @param externalOutputId external audio output id
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean getExtAudioOutVolumeOffset(BluetoothDevice device, int externalOutputId) {
-        return getExtAudioOutVolumeOffsetNative(getByteAddress(device), externalOutputId);
-    }
-
-    /**
-     * Sets external audio output volume offset to a remote device.
-     *
-     * @param device the remote device
-     * @param externalOutputId external audio output id
-     * @param offset requested offset
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean setExtAudioOutVolumeOffset(
-            BluetoothDevice device, int externalOutputId, int offset) {
-        if (Utils.isPtsTestMode()) {
-            setVolumeNative(getByteAddress(device), offset);
-            return true;
-        }
-        return setExtAudioOutVolumeOffsetNative(getByteAddress(device), externalOutputId, offset);
-    }
-
-    /**
-     * Gets external audio output location from a remote device.
-     *
-     * @param device the remote device
-     * @param externalOutputId external audio output id
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean getExtAudioOutLocation(BluetoothDevice device, int externalOutputId) {
-        return getExtAudioOutLocationNative(getByteAddress(device), externalOutputId);
-    }
-
-    /**
-     * Sets external audio volume offset to a remote device.
-     *
-     * @param device the remote device
-     * @param externalOutputId external audio output id
-     * @param location requested location
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean setExtAudioOutLocation(
-            BluetoothDevice device, int externalOutputId, int location) {
-        return setExtAudioOutLocationNative(getByteAddress(device), externalOutputId, location);
-    }
-
-    /**
-     * Gets external audio output description from a remote device.
-     *
-     * @param device the remote device
-     * @param externalOutputId external audio output id
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean getExtAudioOutDescription(BluetoothDevice device, int externalOutputId) {
-        return getExtAudioOutDescriptionNative(getByteAddress(device), externalOutputId);
-    }
-
-    /**
-     * Sets external audio volume description to a remote device.
-     *
-     * @param device the remote device
-     * @param externalOutputId external audio output id
-     * @param descr requested description
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean setExtAudioOutDescription(
-            BluetoothDevice device, int externalOutputId, String descr) {
-        return setExtAudioOutDescriptionNative(getByteAddress(device), externalOutputId, descr);
-    }
-
-    /**
-     * Gets external audio input state from a remote device.
-     *
-     * <p>note: state describes configuration
-     *
-     * @param device the remote device
-     * @param externalInputId external audio input id
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean getExtAudioInState(BluetoothDevice device, int externalInputId) {
-        return getExtAudioInStateNative(getByteAddress(device), externalInputId);
-    }
-
-    /**
-     * Gets external audio input status from a remote device.
-     *
-     * <p>note: status says if input is active or not.
-     *
-     * @param device the remote device
-     * @param externalInputId external audio input id
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean getExtAudioInStatus(BluetoothDevice device, int externalInputId) {
-        return getExtAudioInStatusNative(getByteAddress(device), externalInputId);
-    }
-
-    /**
-     * Gets external audio input type from a remote device.
-     *
-     * @param device the remote device
-     * @param externalInputId external audio input id
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean getExtAudioInType(BluetoothDevice device, int externalInputId) {
-        return getExtAudioInTypeNative(getByteAddress(device), externalInputId);
-    }
-
-    /**
-     * Gets external audio input gain properties from a remote device.
-     *
-     * @param device the remote device
-     * @param externalInputId external audio input id
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean getExtAudioInGainProps(BluetoothDevice device, int externalInputId) {
-        return getExtAudioInGainPropsNative(getByteAddress(device), externalInputId);
-    }
-
-    /**
-     * Gets external audio input description from a remote device.
-     *
-     * @param device the remote device
-     * @param externalInputId external audio input id
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean getExtAudioInDescription(BluetoothDevice device, int externalInputId) {
-        return getExtAudioInDescriptionNative(getByteAddress(device), externalInputId);
-    }
-
-    /**
-     * Sets external audio input description from a remote device.
-     *
-     * @param device the remote device
-     * @param externalInputId external audio input id
-     * @param descr requested description
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean setExtAudioInDescription(
-            BluetoothDevice device, int externalInputId, String descr) {
-        return setExtAudioInDescriptionNative(getByteAddress(device), externalInputId, descr);
-    }
-
-    /**
-     * Sets external audio input gain value to a remote device.
-     *
-     * @param device the remote device
-     * @param externalInputId external audio input id
-     * @param value requested gain value
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean setExtAudioInGainValue(BluetoothDevice device, int externalInputId, int value) {
-        return setExtAudioInGainValueNative(getByteAddress(device), externalInputId, value);
-    }
-
-    /**
-     * Sets external audio input gain mode to a remote device.
-     *
-     * @param device the remote device
-     * @param externalInputId external audio input id
-     * @param autoMode true when auto mode requested
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean setExtAudioInGainMode(
-            BluetoothDevice device, int externalInputId, boolean autoMode) {
-        return setExtAudioInGainModeNative(getByteAddress(device), externalInputId, autoMode);
-    }
-
-    /**
-     * Sets external audio input gain mode to a remote device.
-     *
-     * @param device the remote device
-     * @param externalInputId external audio input id
-     * @param mute true when mute requested
-     * @return true on success, otherwise false.
-     */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public boolean setExtAudioInGainMute(
-            BluetoothDevice device, int externalInputId, boolean mute) {
-        return setExtAudioInGainMuteNative(getByteAddress(device), externalInputId, mute);
-    }
-
-    private BluetoothDevice getDevice(byte[] address) {
-        return mAdapter.getRemoteDevice(address);
-    }
-
     private byte[] getByteAddress(BluetoothDevice device) {
         if (device == null) {
             return Utils.getBytesFromAddress("00:00:00:00:00:00");
@@ -345,182 +50,100 @@
         return Utils.getBytesFromAddress(device.getAddress());
     }
 
-    private void sendMessageToService(VolumeControlStackEvent event) {
-        VolumeControlService service = VolumeControlService.getVolumeControlService();
-        if (service != null) {
-            service.messageFromNative(event);
-        } else {
-            Log.e(TAG, "Event ignored, service not available: " + event);
+    boolean connectVolumeControl(BluetoothDevice device) {
+        return connectVolumeControlNative(getByteAddress(device));
+    }
+
+    boolean disconnectVolumeControl(BluetoothDevice device) {
+        return disconnectVolumeControlNative(getByteAddress(device));
+    }
+
+    void setVolume(BluetoothDevice device, int volume) {
+        setVolumeNative(getByteAddress(device), volume);
+    }
+
+    void setGroupVolume(int groupId, int volume) {
+        setGroupVolumeNative(groupId, volume);
+    }
+
+    void mute(BluetoothDevice device) {
+        muteNative(getByteAddress(device));
+    }
+
+    void muteGroup(int groupId) {
+        muteGroupNative(groupId);
+    }
+
+    void unmute(BluetoothDevice device) {
+        unmuteNative(getByteAddress(device));
+    }
+
+    void unmuteGroup(int groupId) {
+        unmuteGroupNative(groupId);
+    }
+
+    boolean getExtAudioOutVolumeOffset(BluetoothDevice device, int externalOutputId) {
+        return getExtAudioOutVolumeOffsetNative(getByteAddress(device), externalOutputId);
+    }
+
+    boolean setExtAudioOutVolumeOffset(BluetoothDevice device, int externalOutputId, int offset) {
+        if (Utils.isPtsTestMode()) {
+            setVolumeNative(getByteAddress(device), offset);
+            return true;
         }
+        return setExtAudioOutVolumeOffsetNative(getByteAddress(device), externalOutputId, offset);
     }
 
-    // Callbacks from the native stack back into the Java framework.
-    // All callbacks are routed via the Service which will disambiguate which
-    // state machine the message should be routed to.
-    @VisibleForTesting
-    void onConnectionStateChanged(int state, byte[] address) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        event.device = getDevice(address);
-        event.valueInt1 = state;
-
-        Log.d(TAG, "onConnectionStateChanged: " + event);
-        sendMessageToService(event);
+    boolean getExtAudioOutLocation(BluetoothDevice device, int externalOutputId) {
+        return getExtAudioOutLocationNative(getByteAddress(device), externalOutputId);
     }
 
-    @VisibleForTesting
-    void onVolumeStateChanged(
-            int volume, boolean mute, int flags, byte[] address, boolean isAutonomous) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
-        event.device = getDevice(address);
-        event.valueInt1 = -1;
-        event.valueInt2 = volume;
-        event.valueInt3 = flags;
-        event.valueBool1 = mute;
-        event.valueBool2 = isAutonomous;
-
-        Log.d(TAG, "onVolumeStateChanged: " + event);
-        sendMessageToService(event);
+    boolean setExtAudioOutLocation(BluetoothDevice device, int externalOutputId, int location) {
+        return setExtAudioOutLocationNative(getByteAddress(device), externalOutputId, location);
     }
 
-    @VisibleForTesting
-    void onGroupVolumeStateChanged(int volume, boolean mute, int groupId, boolean isAutonomous) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
-        event.device = null;
-        event.valueInt1 = groupId;
-        event.valueInt2 = volume;
-        event.valueBool1 = mute;
-        event.valueBool2 = isAutonomous;
-
-        Log.d(TAG, "onGroupVolumeStateChanged: " + event);
-        sendMessageToService(event);
+    boolean getExtAudioOutDescription(BluetoothDevice device, int externalOutputId) {
+        return getExtAudioOutDescriptionNative(getByteAddress(device), externalOutputId);
     }
 
-    @VisibleForTesting
-    void onDeviceAvailable(int numOfExternalOutputs, int numOfExternalInputs, byte[] address) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(VolumeControlStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
-        event.device = getDevice(address);
-        event.valueInt1 = numOfExternalOutputs;
-        event.valueInt2 = numOfExternalInputs;
-
-        Log.d(TAG, "onDeviceAvailable: " + event);
-        sendMessageToService(event);
+    boolean setExtAudioOutDescription(BluetoothDevice device, int externalOutputId, String descr) {
+        return setExtAudioOutDescriptionNative(getByteAddress(device), externalOutputId, descr);
     }
 
-    @VisibleForTesting
-    void onExtAudioOutVolumeOffsetChanged(int externalOutputId, int offset, byte[] address) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED);
-        event.device = getDevice(address);
-        event.valueInt1 = externalOutputId;
-        event.valueInt2 = offset;
-
-        Log.d(TAG, "onExtAudioOutVolumeOffsetChanged: " + event);
-        sendMessageToService(event);
+    boolean getExtAudioInState(BluetoothDevice device, int externalInputId) {
+        return getExtAudioInStateNative(getByteAddress(device), externalInputId);
     }
 
-    @VisibleForTesting
-    void onExtAudioOutLocationChanged(int externalOutputId, int location, byte[] address) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED);
-        event.device = getDevice(address);
-        event.valueInt1 = externalOutputId;
-        event.valueInt2 = location;
-
-        Log.d(TAG, "onExtAudioOutLocationChanged: " + event);
-        sendMessageToService(event);
+    boolean getExtAudioInStatus(BluetoothDevice device, int externalInputId) {
+        return getExtAudioInStatusNative(getByteAddress(device), externalInputId);
     }
 
-    @VisibleForTesting
-    void onExtAudioOutDescriptionChanged(int externalOutputId, String descr, byte[] address) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED);
-        event.device = getDevice(address);
-        event.valueInt1 = externalOutputId;
-        event.valueString1 = descr;
-
-        Log.d(TAG, "onExtAudioOutLocationChanged: " + event);
-        sendMessageToService(event);
+    boolean getExtAudioInType(BluetoothDevice device, int externalInputId) {
+        return getExtAudioInTypeNative(getByteAddress(device), externalInputId);
     }
 
-    @VisibleForTesting
-    void onExtAudioInStateChanged(
-            int externalInputId, int gainValue, int gainMode, boolean mute, byte[] address) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_STATE_CHANGED);
-        event.device = getDevice(address);
-        event.valueInt1 = externalInputId;
-        event.valueInt2 = gainValue;
-        event.valueInt3 = gainMode;
-        event.valueBool1 = mute;
-
-        Log.d(TAG, "onExtAudioInStateChanged: " + event);
-        sendMessageToService(event);
+    boolean getExtAudioInGainProps(BluetoothDevice device, int externalInputId) {
+        return getExtAudioInGainPropsNative(getByteAddress(device), externalInputId);
     }
 
-    @VisibleForTesting
-    void onExtAudioInStatusChanged(int externalInputId, int status, byte[] address) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_STATUS_CHANGED);
-        event.device = getDevice(address);
-        event.valueInt1 = externalInputId;
-        event.valueInt2 = status;
-
-        Log.d(TAG, "onExtAudioInStatusChanged: " + event);
-        sendMessageToService(event);
+    boolean getExtAudioInDescription(BluetoothDevice device, int externalInputId) {
+        return getExtAudioInDescriptionNative(getByteAddress(device), externalInputId);
     }
 
-    @VisibleForTesting
-    void onExtAudioInTypeChanged(int externalInputId, int type, byte[] address) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_TYPE_CHANGED);
-        event.device = getDevice(address);
-        event.valueInt1 = externalInputId;
-        event.valueInt2 = type;
-
-        Log.d(TAG, "onExtAudioInTypeChanged: " + event);
-        sendMessageToService(event);
+    boolean setExtAudioInDescription(BluetoothDevice device, int externalInputId, String descr) {
+        return setExtAudioInDescriptionNative(getByteAddress(device), externalInputId, descr);
     }
 
-    @VisibleForTesting
-    void onExtAudioInDescriptionChanged(int externalInputId, String descr, byte[] address) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_DESCR_CHANGED);
-        event.device = getDevice(address);
-        event.valueInt1 = externalInputId;
-        event.valueString1 = descr;
-
-        Log.d(TAG, "onExtAudioInDescriptionChanged: " + event);
-        sendMessageToService(event);
+    boolean setExtAudioInGainSetting(BluetoothDevice device, int externalInputId, int gainSetting) {
+        return setExtAudioInGainSettingNative(getByteAddress(device), externalInputId, gainSetting);
     }
 
-    @VisibleForTesting
-    void onExtAudioInGainPropsChanged(
-            int externalInputId, int unit, int min, int max, byte[] address) {
-        VolumeControlStackEvent event =
-                new VolumeControlStackEvent(
-                        VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_GAIN_PROPS_CHANGED);
-        event.device = getDevice(address);
-        event.valueInt1 = externalInputId;
-        event.valueInt2 = unit;
-        event.valueInt3 = min;
-        event.valueInt4 = max;
+    boolean setExtAudioInGainMode(BluetoothDevice device, int externalInputId, boolean autoMode) {
+        return setExtAudioInGainModeNative(getByteAddress(device), externalInputId, autoMode);
+    }
 
-        Log.d(TAG, "onExtAudioInGainPropsChanged: " + event);
-        sendMessageToService(event);
+    boolean setExtAudioInGainMute(BluetoothDevice device, int externalInputId, boolean mute) {
+        return setExtAudioInGainMuteNative(getByteAddress(device), externalInputId, mute);
     }
 
     // Native methods that call into the JNI interface
@@ -573,8 +196,8 @@
     private native boolean setExtAudioInDescriptionNative(
             byte[] address, int externalInputId, String descr);
 
-    private native boolean setExtAudioInGainValueNative(
-            byte[] address, int externalInputId, int gainValue);
+    private native boolean setExtAudioInGainSettingNative(
+            byte[] address, int externalInputId, int gainSetting);
 
     private native boolean setExtAudioInGainModeNative(
             byte[] address, int externalInputId, boolean modeAuto);
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
index 99f296b..23c287b 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java
@@ -29,6 +29,7 @@
 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
 
 import static java.util.Objects.requireNonNull;
+import static java.util.Objects.requireNonNullElseGet;
 
 import android.annotation.RequiresPermission;
 import android.bluetooth.BluetoothDevice;
@@ -63,6 +64,9 @@
 
 import libcore.util.SneakyThrow;
 
+import bluetooth.constants.AudioInputType;
+import bluetooth.constants.aics.AudioInputStatus;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -111,7 +115,7 @@
     @VisibleForTesting ServiceFactory mFactory = new ServiceFactory();
 
     public VolumeControlService(AdapterService adapterService) {
-        this(adapterService, null, VolumeControlNativeInterface.getInstance());
+        this(adapterService, null, null);
     }
 
     @VisibleForTesting
@@ -122,7 +126,12 @@
         super(requireNonNull(adapterService));
         mAdapterService = adapterService;
         mDatabaseManager = requireNonNull(mAdapterService.getDatabase());
-        mNativeInterface = requireNonNull(nativeInterface);
+        mNativeInterface =
+                requireNonNullElseGet(
+                        nativeInterface,
+                        () ->
+                                new VolumeControlNativeInterface(
+                                        new VolumeControlNativeCallback(adapterService, this)));
         mAudioManager = requireNonNull(getSystemService(AudioManager.class));
         if (looper == null) {
             mHandler = new Handler(requireNonNull(Looper.getMainLooper()));
@@ -935,23 +944,11 @@
     void handleExternalInputs(BluetoothDevice device, int numberOfExternalInputs) {
         if (numberOfExternalInputs == 0) {
             Log.i(TAG, "Volume offset not available");
+            mAudioInputs.remove(device);
             return;
         }
 
-        VolumeControlInputDescriptor inputs = mAudioInputs.get(device);
-        if (inputs == null) {
-            inputs = new VolumeControlInputDescriptor();
-            mAudioInputs.put(device, inputs);
-        } else if (inputs.size() != numberOfExternalInputs) {
-            Log.i(TAG, "Number of inputs changed: ");
-            inputs.clear();
-        }
-
-        /* Stack delivers us number of audio inputs.
-         * Offset ids a countinous from 1 to numberOfExternalInputs*/
-        for (int i = 1; i <= numberOfExternalInputs; i++) {
-            inputs.add(i);
-        }
+        mAudioInputs.put(device, new VolumeControlInputDescriptor(numberOfExternalInputs));
     }
 
     void handleDeviceAvailable(
@@ -1033,138 +1030,119 @@
         }
     }
 
-    void handleDeviceExtInputStateChanged(
-            BluetoothDevice device, int id, int gainValue, int gainMode, boolean mute) {
-        Log.d(
-                TAG,
-                ("handleDeviceExtInputStateChanged, device: " + device)
-                        + (" inputId: " + id)
-                        + (" gainValue: " + gainValue)
+    void onExtAudioInStateChanged(
+            BluetoothDevice device, int id, int gainSetting, int mute, int gainMode) {
+        String logInfo =
+                "onExtAudioInStateChanged("
+                        + ("device:" + device)
+                        + (", id" + id)
+                        + (" gainSetting: " + gainSetting)
                         + (" gainMode: " + gainMode)
-                        + (" mute: " + mute));
+                        + (" mute: " + mute)
+                        + ")";
 
         VolumeControlInputDescriptor input = mAudioInputs.get(device);
         if (input == null) {
-            Log.e(
-                    TAG,
-                    ("handleDeviceExtInputStateChanged, inputId: " + id)
-                            + (" not found for device: " + device));
+            Log.e(TAG, logInfo + " This device has no audio input control");
             return;
         }
-        if (!input.setState(id, gainValue, gainMode, mute)) {
-            Log.e(
-                    TAG,
-                    ("handleDeviceExtInputStateChanged, error while setting inputId: " + id)
-                            + ("for: " + device));
-        }
+
+        Log.d(TAG, logInfo);
+        input.setState(id, gainSetting, mute, gainMode);
     }
 
-    void handleDeviceExtInputStatusChanged(BluetoothDevice device, int id, int status) {
-        Log.d(TAG, " device: " + device + " inputId: " + id + " status: " + status);
+    void onExtAudioInStatusChanged(BluetoothDevice device, int id, int status) {
+        String logInfo =
+                "onExtAudioInStatusChanged("
+                        + ("device:" + device)
+                        + (", id" + id)
+                        + (", status" + status)
+                        + ")";
 
         VolumeControlInputDescriptor input = mAudioInputs.get(device);
         if (input == null) {
-            Log.e(TAG, " inputId: " + id + " not found for device: " + device);
+            Log.e(TAG, logInfo + " This device has no audio input control");
             return;
         }
 
-        /*
-         * As per ACIS 1.0. Status
-         * Inactive: 0x00
-         * Active: 0x01
-         */
-        if (status > 1 || status < 0) {
-            Log.e(
-                    TAG,
-                    ("handleDeviceExtInputStatusChanged, invalid status: " + status)
-                            + (" for: " + device));
+        if (status != AudioInputStatus.INACTIVE && status != AudioInputStatus.ACTIVE) {
+            Log.e(TAG, logInfo + ": Invalid status argument");
             return;
         }
 
-        boolean active = (status == 0x01);
-        if (!input.setActive(id, active)) {
-            Log.e(
-                    TAG,
-                    ("handleDeviceExtInputStatusChanged, error while setting inputId: " + id)
-                            + ("for: " + device));
-        }
+        Log.d(TAG, logInfo);
+        input.setStatus(id, status);
     }
 
-    void handleDeviceExtInputTypeChanged(BluetoothDevice device, int id, int type) {
-        Log.d(
-                TAG,
-                ("handleDeviceExtInputTypeChanged, device: " + device)
-                        + (" inputId: " + id)
-                        + (" type: " + type));
+    void onExtAudioInTypeChanged(BluetoothDevice device, int id, int type) {
+        String logInfo =
+                "onExtAudioInTypeChanged("
+                        + ("device:" + device)
+                        + (", id" + id)
+                        + (", type" + type)
+                        + ")";
 
         VolumeControlInputDescriptor input = mAudioInputs.get(device);
         if (input == null) {
-            Log.e(
-                    TAG,
-                    ("handleDeviceExtInputTypeChanged, inputId: " + id)
-                            + (" not found for device: " + device));
+            Log.e(TAG, logInfo + ": This device has no audio input control");
             return;
         }
 
-        if (!input.setType(id, type)) {
-            Log.e(
-                    TAG,
-                    ("handleDeviceExtInputTypeChanged, error while setting inputId: " + id)
-                            + ("for: " + device));
+        if (type > AudioInputType.AMBIENT) {
+            Log.e(TAG, logInfo + ": Invalid type argument");
+            return;
         }
+
+        Log.d(TAG, logInfo);
+        input.setType(id, type);
     }
 
-    void handleDeviceExtInputDescriptionChanged(
-            BluetoothDevice device, int id, String description) {
-        Log.d(
-                TAG,
-                ("handleDeviceExtInputDescriptionChanged, device: " + device)
-                        + (" inputId: " + id)
-                        + (" description: " + description));
+    void onExtAudioInDescriptionChanged(BluetoothDevice device, int id, String description) {
+        String logInfo =
+                "onExtAudioInDescriptionChanged("
+                        + ("device:" + device)
+                        + (", id" + id)
+                        + (", description" + description)
+                        + ")";
 
         VolumeControlInputDescriptor input = mAudioInputs.get(device);
         if (input == null) {
-            Log.e(
-                    TAG,
-                    ("handleDeviceExtInputDescriptionChanged, inputId: " + id)
-                            + (" not found for device: " + device));
+            Log.e(TAG, logInfo + ": This device has no audio input control");
             return;
         }
 
-        if (!input.setDescription(id, description)) {
-            Log.e(
-                    TAG,
-                    ("handleDeviceExtInputDescriptionChanged, error while setting inputId: " + id)
-                            + ("for: " + device));
+        if (description == null) {
+            Log.e(TAG, logInfo + ": Invalid description argument");
+            return;
         }
+
+        Log.d(TAG, logInfo);
+        input.setDescription(id, description);
     }
 
-    void handleDeviceExtInputGainPropsChanged(
-            BluetoothDevice device, int id, int unit, int min, int max) {
-        Log.d(
-                TAG,
-                ("handleDeviceExtInputGainPropsChanged, device: " + device)
-                        + (" inputId: " + id)
-                        + (" unit: " + unit + " min" + min + " max:" + max));
+    void onExtAudioInGainPropsChanged(BluetoothDevice device, int id, int unit, int min, int max) {
+        String logInfo =
+                "onExtAudioInGainPropsChanged("
+                        + ("device:" + device)
+                        + (", id" + id)
+                        + (" unit: " + unit + " min" + min + " max:" + max)
+                        + ")";
 
         VolumeControlInputDescriptor input = mAudioInputs.get(device);
         if (input == null) {
-            Log.e(
-                    TAG,
-                    ("handleDeviceExtInputGainPropsChanged, inputId: " + id)
-                            + (" not found for device: " + device));
+            Log.e(TAG, logInfo + ": This device has no audio input control");
             return;
         }
 
-        if (!input.setPropSettings(id, unit, min, max)) {
-            Log.e(
-                    TAG,
-                    ("handleDeviceExtInputGainPropsChanged, error while setting inputId: " + id)
-                            + ("for: " + device));
-        }
+        Log.d(TAG, logInfo);
+        input.setPropSettings(id, unit, min, max);
     }
 
     void messageFromNative(VolumeControlStackEvent stackEvent) {
+        if (!isAvailable()) {
+            Log.e(TAG, "Event ignored, service not available: " + stackEvent);
+            return;
+        }
         Log.d(TAG, "messageFromNative: " + stackEvent);
 
         if (stackEvent.type == VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED) {
@@ -1204,42 +1182,6 @@
             return;
         }
 
-        if (stackEvent.type == VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_STATE_CHANGED) {
-            handleDeviceExtInputStateChanged(
-                    device,
-                    stackEvent.valueInt1,
-                    stackEvent.valueInt2,
-                    stackEvent.valueInt3,
-                    stackEvent.valueBool1);
-            return;
-        }
-
-        if (stackEvent.type == VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_STATUS_CHANGED) {
-            handleDeviceExtInputStatusChanged(device, stackEvent.valueInt1, stackEvent.valueInt2);
-            return;
-        }
-
-        if (stackEvent.type == VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_TYPE_CHANGED) {
-            handleDeviceExtInputTypeChanged(device, stackEvent.valueInt1, stackEvent.valueInt2);
-            return;
-        }
-
-        if (stackEvent.type == VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_DESCR_CHANGED) {
-            handleDeviceExtInputDescriptionChanged(
-                    device, stackEvent.valueInt1, stackEvent.valueString1);
-            return;
-        }
-
-        if (stackEvent.type == VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_GAIN_PROPS_CHANGED) {
-            handleDeviceExtInputGainPropsChanged(
-                    device,
-                    stackEvent.valueInt1,
-                    stackEvent.valueInt2,
-                    stackEvent.valueInt3,
-                    stackEvent.valueInt4);
-            return;
-        }
-
         synchronized (mStateMachines) {
             VolumeControlStateMachine sm = mStateMachines.get(device);
             if (sm == null) {
diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlStackEvent.java b/android/app/src/com/android/bluetooth/vc/VolumeControlStackEvent.java
index d102952..b1f2840 100644
--- a/android/app/src/com/android/bluetooth/vc/VolumeControlStackEvent.java
+++ b/android/app/src/com/android/bluetooth/vc/VolumeControlStackEvent.java
@@ -29,18 +29,12 @@
     public static final int EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED = 4;
     public static final int EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED = 5;
     public static final int EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED = 6;
-    public static final int EVENT_TYPE_EXT_AUDIO_IN_STATE_CHANGED = 7;
-    public static final int EVENT_TYPE_EXT_AUDIO_IN_STATUS_CHANGED = 8;
-    public static final int EVENT_TYPE_EXT_AUDIO_IN_TYPE_CHANGED = 9;
-    public static final int EVENT_TYPE_EXT_AUDIO_IN_DESCR_CHANGED = 10;
-    public static final int EVENT_TYPE_EXT_AUDIO_IN_GAIN_PROPS_CHANGED = 11;
 
     public int type;
     public BluetoothDevice device;
     public int valueInt1;
     public int valueInt2;
     public int valueInt3;
-    public int valueInt4;
     public boolean valueBool1;
     public boolean valueBool2;
     public String valueString1;
@@ -60,7 +54,6 @@
         result.append(", valueInt1:").append(eventTypeValue1ToString(type, valueInt1));
         result.append(", valueInt2:").append(eventTypeValue2ToString(type, valueInt2));
         result.append(", valueInt3:").append(eventTypeValue3ToString(type, valueInt3));
-        result.append(", valueInt4:").append(eventTypeValue4ToString(type, valueInt4));
         result.append(", valueBool1:").append(eventTypeValueBool1ToString(type, valueBool1));
         result.append(", valueBool2:").append(eventTypeValueBool2ToString(type, valueBool2));
         result.append(", valueString1:").append(eventTypeString1ToString(type, valueString1));
@@ -84,16 +77,6 @@
                 return "EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED";
             case EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED:
                 return "EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED";
-            case EVENT_TYPE_EXT_AUDIO_IN_STATE_CHANGED:
-                return "EVENT_TYPE_EXT_AUDIO_IN_STATE_CHANGED";
-            case EVENT_TYPE_EXT_AUDIO_IN_STATUS_CHANGED:
-                return "EVENT_TYPE_EXT_AUDIO_IN_STATUS_CHANGED";
-            case EVENT_TYPE_EXT_AUDIO_IN_TYPE_CHANGED:
-                return "EVENT_TYPE_EXT_AUDIO_IN_TYPE_CHANGED";
-            case EVENT_TYPE_EXT_AUDIO_IN_DESCR_CHANGED:
-                return "EVENT_TYPE_EXT_AUDIO_IN_DESCR_CHANGED";
-            case EVENT_TYPE_EXT_AUDIO_IN_GAIN_PROPS_CHANGED:
-                return "EVENT_TYPE_EXT_AUDIO_IN_GAIN_PROPS_CHANGED";
             default:
                 return "EVENT_TYPE_UNKNOWN:" + type;
         }
@@ -111,12 +94,6 @@
             case EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED:
             case EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED:
                 return "{ext output id:" + value + "}";
-            case EVENT_TYPE_EXT_AUDIO_IN_STATE_CHANGED:
-            case EVENT_TYPE_EXT_AUDIO_IN_STATUS_CHANGED:
-            case EVENT_TYPE_EXT_AUDIO_IN_TYPE_CHANGED:
-            case EVENT_TYPE_EXT_AUDIO_IN_DESCR_CHANGED:
-            case EVENT_TYPE_EXT_AUDIO_IN_GAIN_PROPS_CHANGED:
-                return "{ext input id:" + value + "}";
             default:
                 break;
         }
@@ -131,12 +108,6 @@
                 return "{volume:" + value + "}";
             case EVENT_TYPE_DEVICE_AVAILABLE:
                 return "{num_ext_inputs:" + value + "}";
-            case EVENT_TYPE_EXT_AUDIO_IN_STATE_CHANGED:
-                return "{ext gain val:" + value + "}";
-            case EVENT_TYPE_EXT_AUDIO_IN_STATUS_CHANGED:
-                return "{status:" + value + "}";
-            case EVENT_TYPE_EXT_AUDIO_IN_TYPE_CHANGED:
-                return "{type:" + value + "}";
             default:
                 break;
         }
@@ -145,8 +116,6 @@
 
     private static String eventTypeValue3ToString(int type, int value) {
         switch (type) {
-            case EVENT_TYPE_EXT_AUDIO_IN_STATE_CHANGED:
-                return "{ext gain mode:" + value + "}";
             case EVENT_TYPE_VOLUME_STATE_CHANGED:
                 return "{flags:" + value + "}";
             default:
@@ -155,20 +124,9 @@
         return Integer.toString(value);
     }
 
-    private static String eventTypeValue4ToString(int type, int value) {
-        switch (type) {
-            case EVENT_TYPE_EXT_AUDIO_IN_GAIN_PROPS_CHANGED:
-                return "{gain set max:" + value + "}";
-            default:
-                break;
-        }
-        return Integer.toString(value);
-    }
-
     private static String eventTypeValueBool1ToString(int type, boolean value) {
         switch (type) {
             case EVENT_TYPE_VOLUME_STATE_CHANGED:
-            case EVENT_TYPE_EXT_AUDIO_IN_STATE_CHANGED:
                 return "{muted:" + value + "}";
             default:
                 break;
@@ -189,7 +147,6 @@
     private static String eventTypeString1ToString(int type, String value) {
         switch (type) {
             case EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED:
-            case EVENT_TYPE_EXT_AUDIO_IN_DESCR_CHANGED:
                 return "{description:" + value + "}";
             default:
                 break;
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 2503b16..97a0aac 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
@@ -15,6 +15,12 @@
  */
 package com.android.bluetooth.a2dpsink;
 
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
@@ -26,11 +32,10 @@
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.media.AudioFormat;
+import android.media.AudioManager;
 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.TestUtils;
@@ -52,69 +57,44 @@
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class A2dpSinkServiceTest {
-    private A2dpSinkService mService = null;
-    private BluetoothAdapter mAdapter = null;
-    private Context mTargetContext;
-
-    @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
-
     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
     @Mock private AdapterService mAdapterService;
     @Mock private DatabaseManager mDatabaseManager;
     @Mock private A2dpSinkNativeInterface mNativeInterface;
 
-    private BluetoothDevice mDevice1;
-    private BluetoothDevice mDevice2;
-    private BluetoothDevice mDevice3;
-    private BluetoothDevice mDevice4;
-    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;
 
+    private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+    private final BluetoothDevice mDevice1 = mAdapter.getRemoteDevice("11:11:11:11:11:11");
+    private final BluetoothDevice mDevice2 = mAdapter.getRemoteDevice("22:22:22:22:22:22");
+
+    private TestLooper mLooper;
+    private A2dpSinkService mService;
+
     @Before
     public void setUp() throws Exception {
-        mTargetContext = InstrumentationRegistry.getTargetContext();
-
-        mLooper = new TestLooper();
-
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
-        assertThat(mAdapter).isNotNull();
-        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};
-
-        doReturn(true).when(mDatabaseManager).setProfileConnectionPolicy(any(), anyInt(), anyInt());
+        BluetoothDevice[] bondedDevices = new BluetoothDevice[] {mDevice1, mDevice2};
 
         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
         doReturn(bondedDevices).when(mAdapterService).getBondedDevices();
         doReturn(1).when(mAdapterService).getMaxConnectedAudioDevices();
+        TestUtils.mockGetSystemService(mAdapterService, Context.AUDIO_SERVICE, AudioManager.class);
 
-        TestUtils.setAdapterService(mAdapterService);
+        doReturn(true).when(mDatabaseManager).setProfileConnectionPolicy(any(), anyInt(), anyInt());
 
         doReturn(true).when(mNativeInterface).setActiveDevice(any());
 
-        mService = new A2dpSinkService(mTargetContext, mNativeInterface, mLooper.getLooper());
-        mService.start();
-        assertThat(mLooper.nextMessage()).isNull();
+        mLooper = new TestLooper();
+
+        mService = new A2dpSinkService(mAdapterService, mNativeInterface, mLooper.getLooper());
     }
 
     @After
     public void tearDown() throws Exception {
-        assertThat(mLooper.nextMessage()).isNull();
-
         mService.stop();
         assertThat(A2dpSinkService.getA2dpSinkService()).isNull();
-        TestUtils.clearAdapterService(mAdapterService);
     }
 
     private void syncHandler(int... what) {
@@ -123,17 +103,15 @@
 
     private void setupDeviceConnection(BluetoothDevice device) {
         assertThat(mLooper.nextMessage()).isNull();
-        assertThat(mService.getConnectionState(device))
-                .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mService.getConnectionState(device)).isEqualTo(STATE_DISCONNECTED);
         assertThat(mLooper.nextMessage()).isNull();
 
         assertThat(mService.connect(device)).isTrue();
-        syncHandler(-2 /* SM_INIT_CMD */, A2dpSinkStateMachine.CONNECT);
-        StackEvent nativeEvent =
-                StackEvent.connectionStateChanged(device, StackEvent.CONNECTION_STATE_CONNECTED);
+        syncHandler(-2 /* SM_INIT_CMD */, A2dpSinkStateMachine.MESSAGE_CONNECT);
+        StackEvent nativeEvent = StackEvent.connectionStateChanged(device, STATE_CONNECTED);
         mService.messageFromNative(nativeEvent);
-        syncHandler(A2dpSinkStateMachine.STACK_EVENT);
-        assertThat(mService.getConnectionState(device)).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_STACK_EVENT);
+        assertThat(mService.getConnectionState(device)).isEqualTo(STATE_CONNECTED);
     }
 
     /**
@@ -143,43 +121,48 @@
      * @param priority - The priority value you want the device to have
      */
     private void mockDevicePriority(BluetoothDevice device, int priority) {
-        when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.A2DP_SINK))
-                .thenReturn(priority);
+        doReturn(priority)
+                .when(mDatabaseManager)
+                .getProfileConnectionPolicy(device, BluetoothProfile.A2DP_SINK);
     }
 
     /** Test that initialization of the service completes and that we can get a instance */
     @Test
     public void testInitialize() {
         assertThat(A2dpSinkService.getA2dpSinkService()).isEqualTo(mService);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that asking to connect with a null device fails */
     @Test
     public void testConnectNullDevice() {
         assertThrows(IllegalArgumentException.class, () -> mService.connect(null));
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that a CONNECTION_POLICY_ALLOWED device can connected */
     @Test
     public void testConnectPolicyAllowedDevice() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
         setupDeviceConnection(mDevice1);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that a CONNECTION_POLICY_FORBIDDEN device is not allowed to connect */
     @Test
     public void testConnectPolicyForbiddenDevice() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_FORBIDDEN);
         assertThat(mService.connect(mDevice1)).isFalse();
-        assertThat(mService.getConnectionState(mDevice1))
-                .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mService.getConnectionState(mDevice1)).isEqualTo(STATE_DISCONNECTED);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that a CONNECTION_POLICY_UNKNOWN device is allowed to connect */
     @Test
     public void testConnectPolicyUnknownDevice() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_UNKNOWN);
         setupDeviceConnection(mDevice1);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that we can connect multiple devices */
@@ -187,44 +170,40 @@
     public void testConnectMultipleDevices() {
         doReturn(5).when(mAdapterService).getMaxConnectedAudioDevices();
 
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        mockDevicePriority(mDevice2, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        mockDevicePriority(mDevice3, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        mockDevicePriority(mDevice4, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        mockDevicePriority(mDevice5, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        mockDevicePriority(mDevice6, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice2, CONNECTION_POLICY_ALLOWED);
 
         setupDeviceConnection(mDevice1);
         setupDeviceConnection(mDevice2);
-        setupDeviceConnection(mDevice3);
-        setupDeviceConnection(mDevice4);
-        setupDeviceConnection(mDevice5);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test to make sure we can disconnect a connected device */
     @Test
     public void testDisconnect() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
         setupDeviceConnection(mDevice1);
 
         assertThat(mService.disconnect(mDevice1)).isTrue();
-        syncHandler(A2dpSinkStateMachine.DISCONNECT);
-        assertThat(mService.getConnectionState(mDevice1))
-                .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_DISCONNECT);
+        assertThat(mService.getConnectionState(mDevice1)).isEqualTo(STATE_DISCONNECTED);
 
         syncHandler(A2dpSinkStateMachine.CLEANUP, -1 /* SM_QUIT_CMD */);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Assure disconnect() fails with a device that's not connected */
     @Test
     public void testDisconnectDeviceDoesNotExist() {
         assertThat(mService.disconnect(mDevice1)).isFalse();
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Assure disconnect() fails with an invalid device */
     @Test
     public void testDisconnectNullDevice() {
         assertThrows(IllegalArgumentException.class, () -> mService.disconnect(null));
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Assure dump() returns something and does not crash */
@@ -233,6 +212,7 @@
         StringBuilder sb = new StringBuilder();
         mService.dump(sb);
         assertThat(sb.toString()).isNotNull();
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /**
@@ -241,10 +221,11 @@
      */
     @Test
     public void testSetActiveDevice() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
         assertThat(mService.getActiveDevice()).isNotEqualTo(mDevice1);
         assertThat(mService.setActiveDevice(mDevice1)).isTrue();
         assertThat(mService.getActiveDevice()).isEqualTo(mDevice1);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that calls to set a null active device succeed in unsetting the active device */
@@ -252,24 +233,26 @@
     public void testSetActiveDeviceNullDevice() {
         assertThat(mService.setActiveDevice(null)).isTrue();
         assertThat(mService.getActiveDevice()).isNull();
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Make sure we can receive the set audio configuration */
     @Test
     public void testGetAudioConfiguration() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
         setupDeviceConnection(mDevice1);
 
         StackEvent audioConfigChanged =
                 StackEvent.audioConfigChanged(mDevice1, TEST_SAMPLE_RATE, TEST_CHANNEL_COUNT);
         mService.messageFromNative(audioConfigChanged);
-        syncHandler(A2dpSinkStateMachine.STACK_EVENT);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_STACK_EVENT);
 
         BluetoothAudioConfig expected =
                 new BluetoothAudioConfig(
                         TEST_SAMPLE_RATE, TEST_CHANNEL_COUNT, AudioFormat.ENCODING_PCM_16BIT);
         BluetoothAudioConfig config = mService.getAudioConfig(mDevice1);
         assertThat(config).isEqualTo(expected);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Make sure we ignore audio configuration changes for disconnected/unknown devices */
@@ -279,31 +262,34 @@
                 StackEvent.audioConfigChanged(null, TEST_SAMPLE_RATE, TEST_CHANNEL_COUNT);
         mService.messageFromNative(audioConfigChanged);
         assertThat(mService.getAudioConfig(null)).isNull();
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Make sure we ignore audio configuration changes for disconnected/unknown devices */
     @Test
     public void testOnAudioConfigChanged_withUnknownDevice_eventDropped() {
-        assertThat(mService.getConnectionState(mDevice1))
-                .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mService.getConnectionState(mDevice1)).isEqualTo(STATE_DISCONNECTED);
         StackEvent audioConfigChanged =
                 StackEvent.audioConfigChanged(mDevice1, TEST_SAMPLE_RATE, TEST_CHANNEL_COUNT);
         mService.messageFromNative(audioConfigChanged);
         assertThat(mService.getAudioConfig(mDevice1)).isNull();
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Getting an audio config for a device that hasn't received one yet should return null */
     @Test
     public void testGetAudioConfigWithConfigUnset() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
         setupDeviceConnection(mDevice1);
         assertThat(mService.getAudioConfig(mDevice1)).isNull();
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Getting an audio config for a null device should return null */
     @Test
     public void testGetAudioConfigNullDevice() {
         assertThat(mService.getAudioConfig(null)).isNull();
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that a newly connected device ends up in the set returned by getConnectedDevices */
@@ -312,11 +298,12 @@
         ArrayList<BluetoothDevice> expected = new ArrayList<BluetoothDevice>();
         expected.add(mDevice1);
 
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
         setupDeviceConnection(mDevice1);
 
         List<BluetoothDevice> devices = mService.getConnectedDevices();
         assertThat(devices).isEqualTo(expected);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /**
@@ -327,13 +314,13 @@
     public void testGetDevicesMatchingConnectionStatesConnected() {
         ArrayList<BluetoothDevice> expected = new ArrayList<BluetoothDevice>();
         expected.add(mDevice1);
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
         setupDeviceConnection(mDevice1);
 
         List<BluetoothDevice> devices =
-                mService.getDevicesMatchingConnectionStates(
-                        new int[] {BluetoothProfile.STATE_CONNECTED});
+                mService.getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED});
         assertThat(devices).isEqualTo(expected);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /**
@@ -345,67 +332,55 @@
         ArrayList<BluetoothDevice> expected = new ArrayList<BluetoothDevice>();
         expected.add(mDevice1);
         expected.add(mDevice2);
-        expected.add(mDevice3);
-        expected.add(mDevice4);
-        expected.add(mDevice5);
-        expected.add(mDevice6);
 
         List<BluetoothDevice> devices =
-                mService.getDevicesMatchingConnectionStates(
-                        new int[] {BluetoothProfile.STATE_DISCONNECTED});
+                mService.getDevicesMatchingConnectionStates(new int[] {STATE_DISCONNECTED});
         assertThat(devices).isEqualTo(expected);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that GetConnectionPolicy() can get a device with policy "Allowed" */
     @Test
     public void testGetConnectionPolicyDeviceAllowed() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        assertThat(mService.getConnectionPolicy(mDevice1))
-                .isEqualTo(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
+        assertThat(mService.getConnectionPolicy(mDevice1)).isEqualTo(CONNECTION_POLICY_ALLOWED);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that GetConnectionPolicy() can get a device with policy "Forbidden" */
     @Test
     public void testGetConnectionPolicyDeviceForbidden() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
-        assertThat(mService.getConnectionPolicy(mDevice1))
-                .isEqualTo(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_FORBIDDEN);
+        assertThat(mService.getConnectionPolicy(mDevice1)).isEqualTo(CONNECTION_POLICY_FORBIDDEN);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that GetConnectionPolicy() can get a device with policy "Unknown" */
     @Test
     public void testGetConnectionPolicyDeviceUnknown() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
-        assertThat(mService.getConnectionPolicy(mDevice1))
-                .isEqualTo(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_UNKNOWN);
+        assertThat(mService.getConnectionPolicy(mDevice1)).isEqualTo(CONNECTION_POLICY_UNKNOWN);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that SetConnectionPolicy() can change a device's policy to "Allowed" */
     @Test
     public void testSetConnectionPolicyDeviceAllowed() {
-        assertThat(
-                        mService.setConnectionPolicy(
-                                mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED))
-                .isTrue();
+        assertThat(mService.setConnectionPolicy(mDevice1, CONNECTION_POLICY_ALLOWED)).isTrue();
         verify(mDatabaseManager)
                 .setProfileConnectionPolicy(
-                        mDevice1,
-                        BluetoothProfile.A2DP_SINK,
-                        BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+                        mDevice1, BluetoothProfile.A2DP_SINK, CONNECTION_POLICY_ALLOWED);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that SetConnectionPolicy() can change a device's policy to "Forbidden" */
     @Test
     public void testSetConnectionPolicyDeviceForbiddenWhileNotConnected() {
-        assertThat(
-                        mService.setConnectionPolicy(
-                                mDevice1, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN))
-                .isTrue();
+        assertThat(mService.setConnectionPolicy(mDevice1, CONNECTION_POLICY_FORBIDDEN)).isTrue();
         verify(mDatabaseManager)
                 .setProfileConnectionPolicy(
-                        mDevice1,
-                        BluetoothProfile.A2DP_SINK,
-                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+                        mDevice1, BluetoothProfile.A2DP_SINK, CONNECTION_POLICY_FORBIDDEN);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /**
@@ -414,57 +389,48 @@
      */
     @Test
     public void testSetConnectionPolicyDeviceForbiddenWhileConnected() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
         setupDeviceConnection(mDevice1);
 
-        assertThat(
-                        mService.setConnectionPolicy(
-                                mDevice1, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN))
-                .isTrue();
+        assertThat(mService.setConnectionPolicy(mDevice1, CONNECTION_POLICY_FORBIDDEN)).isTrue();
         verify(mDatabaseManager)
                 .setProfileConnectionPolicy(
-                        mDevice1,
-                        BluetoothProfile.A2DP_SINK,
-                        BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+                        mDevice1, BluetoothProfile.A2DP_SINK, CONNECTION_POLICY_FORBIDDEN);
 
-        syncHandler(A2dpSinkStateMachine.DISCONNECT);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_DISCONNECT);
         verify(mNativeInterface).disconnectA2dpSink(eq(mDevice1));
-        assertThat(mService.getConnectionState(mDevice1))
-                .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mService.getConnectionState(mDevice1)).isEqualTo(STATE_DISCONNECTED);
 
         syncHandler(A2dpSinkStateMachine.CLEANUP, -1 /* SM_QUIT_CMD */);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that SetConnectionPolicy() can change a device's policy to "Unknown" */
     @Test
     public void testSetConnectionPolicyDeviceUnknown() {
-        assertThat(
-                        mService.setConnectionPolicy(
-                                mDevice1, BluetoothProfile.CONNECTION_POLICY_UNKNOWN))
-                .isTrue();
+        assertThat(mService.setConnectionPolicy(mDevice1, CONNECTION_POLICY_UNKNOWN)).isTrue();
         verify(mDatabaseManager)
                 .setProfileConnectionPolicy(
-                        mDevice1,
-                        BluetoothProfile.A2DP_SINK,
-                        BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+                        mDevice1, BluetoothProfile.A2DP_SINK, CONNECTION_POLICY_UNKNOWN);
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     /** Test that SetConnectionPolicy is robust to DatabaseManager failures */
     @Test
     public void testSetConnectionPolicyDatabaseWriteFails() {
-        when(mDatabaseManager.setProfileConnectionPolicy(any(), anyInt(), anyInt()))
-                .thenReturn(false);
-        assertThat(
-                        mService.setConnectionPolicy(
-                                mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED))
-                .isFalse();
+        doReturn(false)
+                .when(mDatabaseManager)
+                .setProfileConnectionPolicy(any(), anyInt(), anyInt());
+        assertThat(mService.setConnectionPolicy(mDevice1, CONNECTION_POLICY_ALLOWED)).isFalse();
+        assertThat(mLooper.nextMessage()).isNull();
     }
 
     @Test
     public void testDumpDoesNotCrash() {
-        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        mockDevicePriority(mDevice1, CONNECTION_POLICY_ALLOWED);
         setupDeviceConnection(mDevice1);
 
         mService.dump(new StringBuilder());
+        assertThat(mLooper.nextMessage()).isNull();
     }
 }
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 55aeb0b..c635bfa 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
@@ -15,10 +15,13 @@
  */
 package com.android.bluetooth.a2dpsink;
 
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
+
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -45,46 +48,33 @@
 
 @RunWith(AndroidJUnit4.class)
 public class A2dpSinkStateMachineTest {
-    private static final String DEVICE_ADDRESS = "11:11:11:11:11:11";
-    private static final int UNHANDLED_MESSAGE = 9999;
-
     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
     @Mock private A2dpSinkService mService;
     @Mock private A2dpSinkNativeInterface mNativeInterface;
 
+    private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+    private final BluetoothDevice mDevice = mAdapter.getRemoteDevice("11:11:11:11:11:11");
+
     private A2dpSinkStateMachine mStateMachine;
-    private BluetoothAdapter mAdapter;
-    private BluetoothDevice mDevice;
     private TestLooper mLooper;
 
     @Before
     public void setUp() throws Exception {
         mLooper = new TestLooper();
 
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
-        assertThat(mAdapter).isNotNull();
-        mDevice = mAdapter.getRemoteDevice(DEVICE_ADDRESS);
-
-        doNothing().when(mService).removeStateMachine(any(A2dpSinkStateMachine.class));
-
         mStateMachine =
-                new A2dpSinkStateMachine(mLooper.getLooper(), mDevice, mService, mNativeInterface);
-        mStateMachine.start();
+                new A2dpSinkStateMachine(mService, mDevice, mLooper.getLooper(), mNativeInterface);
         syncHandler(-2 /* SM_INIT_CMD */);
 
         assertThat(mStateMachine.getDevice()).isEqualTo(mDevice);
         assertThat(mStateMachine.getAudioConfig()).isNull();
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
     }
 
     @After
     public void tearDown() throws Exception {
         assertThat(mLooper.nextMessage()).isNull();
-
-        mStateMachine = null;
-        mDevice = null;
-        mAdapter = null;
     }
 
     private void syncHandler(int... what) {
@@ -97,16 +87,16 @@
 
     private void sendConnectionEvent(int state) {
         mStateMachine.sendMessage(
-                A2dpSinkStateMachine.STACK_EVENT,
+                A2dpSinkStateMachine.MESSAGE_STACK_EVENT,
                 StackEvent.connectionStateChanged(mDevice, state));
-        syncHandler(A2dpSinkStateMachine.STACK_EVENT);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_STACK_EVENT);
     }
 
     private void sendAudioConfigChangedEvent(int sampleRate, int channelCount) {
         mStateMachine.sendMessage(
-                A2dpSinkStateMachine.STACK_EVENT,
+                A2dpSinkStateMachine.MESSAGE_STACK_EVENT,
                 StackEvent.audioConfigChanged(mDevice, sampleRate, channelCount));
-        syncHandler(A2dpSinkStateMachine.STACK_EVENT);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_STACK_EVENT);
     }
 
     /**********************************************************************************************
@@ -116,37 +106,37 @@
     @Test
     public void testConnectInDisconnected() {
         mStateMachine.connect();
-        syncHandler(A2dpSinkStateMachine.CONNECT);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_CONNECT);
         verify(mNativeInterface).connectA2dpSink(mDevice);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTING);
     }
 
     @Test
     public void testDisconnectInDisconnected() {
         mStateMachine.disconnect();
-        syncHandler(A2dpSinkStateMachine.DISCONNECT);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_DISCONNECT);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
     }
 
     @Test
     public void testAudioConfigChangedInDisconnected() {
         sendAudioConfigChangedEvent(44, 1);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
         assertThat(mStateMachine.getAudioConfig()).isNull();
     }
 
     @Test
     public void testIncomingConnectedInDisconnected() {
-        sendConnectionEvent(BluetoothProfile.STATE_CONNECTED);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+        sendConnectionEvent(STATE_CONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTED);
     }
 
     @Test
     public void testAllowedIncomingConnectionInDisconnected() {
         mockDeviceConnectionPolicy(mDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
 
-        sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+        sendConnectionEvent(STATE_CONNECTING);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTING);
         verify(mNativeInterface, times(0)).connectA2dpSink(mDevice);
     }
 
@@ -154,24 +144,24 @@
     public void testForbiddenIncomingConnectionInDisconnected() {
         mockDeviceConnectionPolicy(mDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
 
-        sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
+        sendConnectionEvent(STATE_CONNECTING);
         verify(mNativeInterface).disconnectA2dpSink(mDevice);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
     }
 
     @Test
     public void testUnknownIncomingConnectionInDisconnected() {
         mockDeviceConnectionPolicy(mDevice, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
 
-        sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+        sendConnectionEvent(STATE_CONNECTING);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTING);
         verify(mNativeInterface, times(0)).connectA2dpSink(mDevice);
     }
 
     @Test
     public void testIncomingDisconnectInDisconnected() {
-        sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTED);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        sendConnectionEvent(STATE_DISCONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
 
         syncHandler(A2dpSinkStateMachine.CLEANUP);
         verify(mService).removeStateMachine(mStateMachine);
@@ -179,19 +169,20 @@
 
     @Test
     public void testIncomingDisconnectingInDisconnected() {
-        sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTING);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        sendConnectionEvent(STATE_DISCONNECTING);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
         verify(mService, times(0)).removeStateMachine(mStateMachine);
     }
 
     @Test
     public void testIncomingConnectingInDisconnected() {
-        sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        sendConnectionEvent(STATE_CONNECTING);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
     }
 
     @Test
     public void testUnhandledMessageInDisconnected() {
+        final int UNHANDLED_MESSAGE = 9999;
         mStateMachine.sendMessage(UNHANDLED_MESSAGE);
         mStateMachine.sendMessage(UNHANDLED_MESSAGE, 0 /* arbitrary payload */);
         syncHandler(UNHANDLED_MESSAGE, UNHANDLED_MESSAGE);
@@ -205,32 +196,32 @@
     public void testConnectedInConnecting() {
         testConnectInDisconnected();
 
-        sendConnectionEvent(BluetoothProfile.STATE_CONNECTED);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+        sendConnectionEvent(STATE_CONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTED);
     }
 
     @Test
     public void testConnectingInConnecting() {
         testConnectInDisconnected();
 
-        sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+        sendConnectionEvent(STATE_CONNECTING);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTING);
     }
 
     @Test
     public void testDisconnectingInConnecting() {
         testConnectInDisconnected();
 
-        sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTING);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+        sendConnectionEvent(STATE_DISCONNECTING);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTING);
     }
 
     @Test
     public void testDisconnectedInConnecting() {
         testConnectInDisconnected();
 
-        sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTED);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        sendConnectionEvent(STATE_DISCONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
 
         syncHandler(A2dpSinkStateMachine.CLEANUP);
         verify(mService).removeStateMachine(mStateMachine);
@@ -241,8 +232,8 @@
         testConnectInDisconnected();
 
         mLooper.moveTimeForward(120_000); // Skip time so the timeout fires
-        syncHandler(A2dpSinkStateMachine.CONNECT_TIMEOUT);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_CONNECT_TIMEOUT);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
 
         syncHandler(A2dpSinkStateMachine.CLEANUP);
         verify(mService).removeStateMachine(mStateMachine);
@@ -253,7 +244,7 @@
         testConnectInDisconnected();
 
         sendAudioConfigChangedEvent(44, 1);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTING);
         assertThat(mStateMachine.getAudioConfig()).isNull();
     }
 
@@ -262,8 +253,8 @@
         testConnectInDisconnected();
 
         mStateMachine.connect();
-        syncHandler(A2dpSinkStateMachine.CONNECT);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_CONNECT);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTING);
     }
 
     @Test
@@ -271,16 +262,16 @@
         testConnectInDisconnected();
 
         mStateMachine.disconnect();
-        syncHandler(A2dpSinkStateMachine.DISCONNECT);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTING);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_DISCONNECT);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTING);
 
         // send connected, disconnect should get processed
-        sendConnectionEvent(BluetoothProfile.STATE_CONNECTED);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+        sendConnectionEvent(STATE_CONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTED);
 
-        syncHandler(A2dpSinkStateMachine.DISCONNECT); // message was defer
+        syncHandler(A2dpSinkStateMachine.MESSAGE_DISCONNECT); // message was defer
         verify(mNativeInterface).disconnectA2dpSink(mDevice);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
 
         syncHandler(A2dpSinkStateMachine.CLEANUP);
         verify(mService).removeStateMachine(mStateMachine);
@@ -295,8 +286,8 @@
         testConnectedInConnecting();
 
         mStateMachine.connect();
-        syncHandler(A2dpSinkStateMachine.CONNECT);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_CONNECT);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTED);
     }
 
     @Test
@@ -304,9 +295,9 @@
         testConnectedInConnecting();
 
         mStateMachine.disconnect();
-        syncHandler(A2dpSinkStateMachine.DISCONNECT);
+        syncHandler(A2dpSinkStateMachine.MESSAGE_DISCONNECT);
         verify(mNativeInterface).disconnectA2dpSink(mDevice);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
 
         syncHandler(A2dpSinkStateMachine.CLEANUP);
         verify(mService).removeStateMachine(mStateMachine);
@@ -317,7 +308,7 @@
         testConnectedInConnecting();
 
         sendAudioConfigChangedEvent(44, 1);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTED);
 
         BluetoothAudioConfig expected =
                 new BluetoothAudioConfig(44, 1, AudioFormat.ENCODING_PCM_16BIT);
@@ -328,24 +319,24 @@
     public void testConnectedInConnected() {
         testConnectedInConnecting();
 
-        sendConnectionEvent(BluetoothProfile.STATE_CONNECTED);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+        sendConnectionEvent(STATE_CONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTED);
     }
 
     @Test
     public void testConnectingInConnected() {
         testConnectedInConnecting();
 
-        sendConnectionEvent(BluetoothProfile.STATE_CONNECTING);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_CONNECTED);
+        sendConnectionEvent(STATE_CONNECTING);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_CONNECTED);
     }
 
     @Test
     public void testDisconnectingInConnected() {
         testConnectedInConnecting();
 
-        sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTING);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        sendConnectionEvent(STATE_DISCONNECTING);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
 
         syncHandler(A2dpSinkStateMachine.CLEANUP);
         verify(mService).removeStateMachine(mStateMachine);
@@ -355,8 +346,8 @@
     public void testDisconnectedInConnected() {
         testConnectedInConnecting();
 
-        sendConnectionEvent(BluetoothProfile.STATE_DISCONNECTED);
-        assertThat(mStateMachine.getState()).isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        sendConnectionEvent(STATE_DISCONNECTED);
+        assertThat(mStateMachine.getState()).isEqualTo(STATE_DISCONNECTED);
 
         syncHandler(A2dpSinkStateMachine.CLEANUP);
         verify(mService).removeStateMachine(mStateMachine);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/StackEventTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/StackEventTest.java
index 7f73380..1f19faa 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/StackEventTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/StackEventTest.java
@@ -19,6 +19,7 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -48,22 +49,22 @@
 
     @Test
     public void testCreateConnectionStateChangedDisconnectedEvent() {
-        testConnectionStateChangedBase(StackEvent.CONNECTION_STATE_DISCONNECTED);
+        testConnectionStateChangedBase(BluetoothProfile.STATE_DISCONNECTED);
     }
 
     @Test
     public void testCreateConnectionStateChangedConnectingEvent() {
-        testConnectionStateChangedBase(StackEvent.CONNECTION_STATE_CONNECTING);
+        testConnectionStateChangedBase(BluetoothProfile.STATE_CONNECTING);
     }
 
     @Test
     public void testCreateConnectionStateChangedConnectedEvent() {
-        testConnectionStateChangedBase(StackEvent.CONNECTION_STATE_CONNECTED);
+        testConnectionStateChangedBase(BluetoothProfile.STATE_CONNECTED);
     }
 
     @Test
     public void testCreateConnectionStateChangedDisconnectingEvent() {
-        testConnectionStateChangedBase(StackEvent.CONNECTION_STATE_DISCONNECTING);
+        testConnectionStateChangedBase(BluetoothProfile.STATE_DISCONNECTING);
     }
 
     private void testConnectionStateChangedBase(int state) {
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 deabdbe..4aeb448 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
@@ -31,8 +31,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.media.AudioManager;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 import android.support.v4.media.session.PlaybackStateCompat;
 
 import androidx.test.InstrumentationRegistry;
@@ -44,7 +42,6 @@
 import com.android.bluetooth.a2dpsink.A2dpSinkService;
 import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService.BrowseResult;
 import com.android.bluetooth.btservice.AdapterService;
-import com.android.bluetooth.flags.Flags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -69,8 +66,6 @@
     private AvrcpControllerService mService = null;
     private BluetoothAdapter mAdapter = null;
 
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     @Rule
     public final ServiceTestRule mBluetoothBrowserMediaServiceTestRule = new ServiceTestRule();
 
@@ -516,7 +511,6 @@
      * first device by checking that it has remained as the active device.
      */
     @Test
-    @EnableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS)
     public void testActiveDeviceMaintainsAudioFocusWhenOtherDeviceConnects_audioFocusMaintained() {
         mService.onConnectionStateChanged(true, true, mRemoteDevice);
         // check set active device is called
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 70fac79..dbe35e1 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
@@ -31,7 +31,6 @@
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.Looper;
-import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.support.v4.media.MediaMetadataCompat;
@@ -467,21 +466,7 @@
 
     /** Get the root of the device */
     @Test
-    @DisableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS)
-    public void testGetDeviceRootNode_flagRandomDeviceIdDisabled_rootNodeMatchesUuidFormat() {
-        // create new state machine to follow current flags rule
-        mAvrcpStateMachine = makeStateMachine(mTestDevice);
-        setUpConnectedState(true, true);
-        final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();
-        // Get the root of the device
-        BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName);
-        Assert.assertEquals(rootName, results.getID());
-    }
-
-    /** Get the root of the device */
-    @Test
-    @EnableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS)
-    public void testGetDeviceRootNode_flagRandomDeviceIdEnabled_rootNodeMatchesUuidFormat() {
+    public void testGetDeviceRootNode_rootNodeMatchesUuidFormat() {
         // create new state machine to follow current flags rule
         mAvrcpStateMachine = makeStateMachine(mTestDevice);
         setUpConnectedState(true, true);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseNodeTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseNodeTest.java
index f91b44e..6241e65 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseNodeTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseNodeTest.java
@@ -21,7 +21,6 @@
 import android.annotation.SuppressLint;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 
@@ -80,19 +79,7 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS)
-    public void constructor_withBluetoothDevice() {
-        BrowseNode browseNode = mBrowseTree.new BrowseNode(mTestDevice);
-
-        assertThat(browseNode.getID()).isNotNull();
-        assertThat(browseNode.getDevice()).isEqualTo(mTestDevice);
-        assertThat(browseNode.isPlayer()).isFalse();
-        assertThat(browseNode.isBrowsable()).isTrue();
-    }
-
-    @Test
-    @EnableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS)
-    public void constructor_withBluetoothDevice_withRandomUuid() {
+    public void constructor_withBluetoothDevice_createsRandomUuid() {
         BrowseNode browseNode1 = mBrowseTree.new BrowseNode(mTestDevice);
 
         assertThat(browseNode1.getID()).isNotNull();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseTreeTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseTreeTest.java
index d27dcbd..275b394 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseTreeTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/BrowseTreeTest.java
@@ -20,15 +20,10 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
 
 import com.android.bluetooth.avrcpcontroller.BrowseTree.BrowseNode;
-import com.android.bluetooth.flags.Flags;
 
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 
 import java.util.Set;
@@ -38,8 +33,6 @@
     private static final String TEST_HANDLE = "test_handle";
     private static final String TEST_NODE_ID = "test_node_id";
 
-    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
     private final byte[] mTestAddress = new byte[] {01, 01, 01, 01, 01, 01};
     private BluetoothAdapter mAdapter;
     private BluetoothDevice mTestDevice = null;
@@ -102,7 +95,6 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS)
     public void sameDeviceDifferentBrowseTrees_uniqueMediaIds() {
         BrowseTree browseTree1 = new BrowseTree(mTestDevice);
         BrowseTree browseTree2 = new BrowseTree(mTestDevice);
@@ -120,14 +112,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS)
-    public void findBrowseNodeByIDForDevice() {
-        BrowseTree browseTree = new BrowseTree(mTestDevice);
-        final String deviceId = BrowseTree.ROOT + mTestDevice.getAddress().toString();
-        assertThat(browseTree.findBrowseNodeByID(deviceId)).isEqualTo(browseTree.mRootNode);
-    }
-
-    @Test
     public void findBrowseNodeByIDForIllegalId() {
         BrowseTree browseTree = new BrowseTree(mTestDevice);
         assertThat(browseTree.findBrowseNodeByID(ILLEGAL_ID)).isNull();
@@ -143,8 +127,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_RANDOMIZE_DEVICE_LEVEL_MEDIA_IDS)
-    public void findBrowseNodeByIDForDevice_withRandomDeviceID_nodeIsFound() {
+    public void findBrowseNodeByIDForDevice_nodeIsFound() {
         BrowseTree browseTree = new BrowseTree(mTestDevice);
         final String deviceId = browseTree.mRootNode.getID();
         assertThat(browseTree.findBrowseNodeByID(deviceId)).isEqualTo(browseTree.mRootNode);
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 12a0f7d..e6e9f40 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
@@ -2039,6 +2039,40 @@
         }
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_LEAUDIO_BROADCAST_API_GET_LOCAL_METADATA)
+    public void testGetSourceMetadata() {
+        prepareConnectedDeviceGroup();
+        startSearchingForSources();
+        onScanResult(mSourceDevice, TEST_BROADCAST_ID);
+        onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE);
+        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
+
+        for (BassClientStateMachine sm : mStateMachines.values()) {
+            injectRemoteSourceStateSourceAdded(
+                    sm,
+                    meta,
+                    TEST_SOURCE_ID,
+                    BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
+                    meta.isEncrypted()
+                            ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
+                            : BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
+                    null);
+            doReturn(null).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
+            assertThat(mBassClientService.getSourceMetadata(sm.getDevice(), TEST_SOURCE_ID))
+                    .isEqualTo(null);
+
+            doReturn(meta).when(sm).getCurrentBroadcastMetadata(eq(TEST_SOURCE_ID));
+            doReturn(true).when(sm).isSyncedToTheSource(eq(TEST_SOURCE_ID));
+            assertThat(mBassClientService.getSourceMetadata(sm.getDevice(), TEST_SOURCE_ID))
+                    .isEqualTo(meta);
+        }
+
+        for (BassClientStateMachine sm : mStateMachines.values()) {
+            injectRemoteSourceStateRemoval(sm, TEST_SOURCE_ID);
+        }
+    }
+
     /** Test whether the group operation flag is set on addSource() and removed on removeSource */
     @Test
     public void testGroupStickyFlagSetUnset() {
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 541f965..9acb612 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
@@ -383,6 +383,24 @@
     }
 
     @Test
+    public void headsetRemoveActive_fallbackToLeAudio() {
+        when(mHeadsetService.getFallbackDevice()).thenReturn(mHeadsetDevice);
+
+        leAudioConnected(mLeAudioDevice);
+        mTestLooper.dispatchAll();
+        verify(mLeAudioService, times(1)).setActiveDevice(mLeAudioDevice);
+
+        headsetConnected(mHeadsetDevice, false);
+        mTestLooper.dispatchAll();
+        verify(mHeadsetService).setActiveDevice(mHeadsetDevice);
+
+        // HFP activce device to null. Expect to fallback to LeAudio.
+        headsetActiveDeviceChanged(null);
+        mTestLooper.dispatchAll();
+        verify(mLeAudioService, times(2)).setActiveDevice(mLeAudioDevice);
+    }
+
+    @Test
     public void a2dpConnectedButHeadsetNotConnected_setA2dpActive() {
         when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);
         a2dpConnected(mA2dpHeadsetDevice, true);
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 1f9540f..7372288 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
@@ -45,7 +45,6 @@
 import com.android.bluetooth.hid.HidHostNativeInterface;
 import com.android.bluetooth.le_audio.LeAudioNativeInterface;
 import com.android.bluetooth.pan.PanNativeInterface;
-import com.android.bluetooth.vc.VolumeControlNativeInterface;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -94,7 +93,6 @@
     @Mock private PanNativeInterface mPanNativeInterface;
     @Mock private CsipSetCoordinatorNativeInterface mCsipSetCoordinatorInterface;
     @Mock private LeAudioNativeInterface mLeAudioInterface;
-    @Mock private VolumeControlNativeInterface mVolumeControlInterface;
 
     private void setProfileState(int profile, int state) {
         FutureTask task =
@@ -157,6 +155,7 @@
                         .filter(
                                 profile ->
                                         profile != BluetoothProfile.HAP_CLIENT
+                                                && profile != BluetoothProfile.VOLUME_CONTROL
                                                 && profile != BluetoothProfile.GATT)
                         .toArray();
         TestUtils.setAdapterService(mAdapterService);
@@ -175,7 +174,6 @@
         PanNativeInterface.setInstance(mPanNativeInterface);
         CsipSetCoordinatorNativeInterface.setInstance(mCsipSetCoordinatorInterface);
         LeAudioNativeInterface.setInstance(mLeAudioInterface);
-        VolumeControlNativeInterface.setInstance(mVolumeControlInterface);
     }
 
     @After
@@ -196,7 +194,6 @@
         PanNativeInterface.setInstance(null);
         CsipSetCoordinatorNativeInterface.setInstance(null);
         LeAudioNativeInterface.setInstance(null);
-        VolumeControlNativeInterface.setInstance(null);
     }
 
     /**
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceTest.java
index 4f26e51..81c4865 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceTest.java
@@ -17,8 +17,14 @@
 
 package com.android.bluetooth.hap;
 
-import static android.bluetooth.BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED;
+import static android.bluetooth.BluetoothDevice.BOND_BONDED;
+import static android.bluetooth.BluetoothDevice.BOND_BONDING;
+import static android.bluetooth.BluetoothDevice.BOND_NONE;
 import static android.bluetooth.BluetoothHapClient.ACTION_HAP_DEVICE_AVAILABLE;
+import static android.bluetooth.BluetoothHapClient.PRESET_INDEX_UNAVAILABLE;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
+import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
 import static android.bluetooth.BluetoothProfile.EXTRA_PREVIOUS_STATE;
 import static android.bluetooth.BluetoothProfile.EXTRA_STATE;
 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
@@ -31,13 +37,12 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.hamcrest.core.AllOf.allOf;
-import static org.mockito.Mockito.after;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -51,8 +56,11 @@
 import android.bluetooth.BluetoothStatusCodes;
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetoothHapClientCallback;
+import android.content.Intent;
 import android.os.Binder;
 import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.test.TestLooper;
 
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -64,8 +72,9 @@
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.bluetooth.csip.CsipSetCoordinatorService;
 
+import org.hamcrest.Matcher;
+import org.hamcrest.core.AllOf;
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -74,6 +83,7 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.hamcrest.MockitoHamcrest;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
@@ -82,17 +92,10 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
-import java.util.concurrent.TimeoutException;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class HapClientServiceTest {
-    private static final int TIMEOUT_MS = 1000;
-    private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
-    private final BluetoothDevice mDevice = TestUtils.getTestDevice(mAdapter, 0);
-    private final BluetoothDevice mDevice2 = TestUtils.getTestDevice(mAdapter, 1);
-    private final BluetoothDevice mDevice3 = TestUtils.getTestDevice(mAdapter, 2);
-
     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
     @Mock private AdapterService mAdapterService;
@@ -103,22 +106,34 @@
     @Mock private IBluetoothHapClientCallback mFrameworkCallback;
     @Mock private Binder mBinder;
 
+    private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+    private final BluetoothDevice mDevice = TestUtils.getTestDevice(mAdapter, 0);
+    private final BluetoothDevice mDevice2 = TestUtils.getTestDevice(mAdapter, 1);
+    private final BluetoothDevice mDevice3 = TestUtils.getTestDevice(mAdapter, 2);
+
     private HapClientService mService;
     private HapClientNativeCallback mNativeCallback;
+    private InOrder mInOrder;
+    private TestLooper mLooper;
 
     @Before
-    public void setUp() throws Exception {
-        HapClientStateMachine.sConnectTimeoutMs = TIMEOUT_MS;
-
-        doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
+    public void setUp() {
         doReturn(mDevice).when(mAdapterService).getDeviceFromByte(eq(getByteAddress(mDevice)));
         doReturn(mDevice2).when(mAdapterService).getDeviceFromByte(eq(getByteAddress(mDevice2)));
         doReturn(mDevice3).when(mAdapterService).getDeviceFromByte(eq(getByteAddress(mDevice3)));
+        doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
+
+        doReturn(CONNECTION_POLICY_ALLOWED)
+                .when(mDatabaseManager)
+                .getProfileConnectionPolicy(any(), anyInt());
 
         doReturn(mCsipService).when(mServiceFactory).getCsipSetCoordinatorService();
 
         doReturn(mBinder).when(mFrameworkCallback).asBinder();
 
+        doReturn(true).when(mNativeInterface).connectHapClient(any(BluetoothDevice.class));
+        doReturn(true).when(mNativeInterface).disconnectHapClient(any(BluetoothDevice.class));
+
         /* Prepare CAS groups */
         doReturn(List.of(0x02, 0x03)).when(mCsipService).getAllGroupIds(BluetoothUuid.CAP);
 
@@ -147,8 +162,13 @@
                 .getRemoteUuids(any(BluetoothDevice.class));
         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
 
-        startService();
+        mInOrder = inOrder(mAdapterService);
+        mLooper = new TestLooper();
+
+        mService = new HapClientService(mAdapterService, mLooper.getLooper(), mNativeInterface);
+        mService.setAvailable(true);
         mNativeCallback = new HapClientNativeCallback(mAdapterService, mService);
+
         mService.mFactory = mServiceFactory;
         synchronized (mService.mCallbacks) {
             mService.mCallbacks.register(mFrameworkCallback);
@@ -156,257 +176,114 @@
     }
 
     @After
-    public void tearDown() throws Exception {
-        if (mService == null) {
-            return;
-        }
-
+    public void tearDown() {
         synchronized (mService.mCallbacks) {
             mService.mCallbacks.unregister(mFrameworkCallback);
         }
 
-        stopService();
-    }
-
-    private void startService() throws TimeoutException {
-        mService = new HapClientService(mAdapterService, mNativeInterface);
-        mService.start();
-        mService.setAvailable(true);
-    }
-
-    private void stopService() throws TimeoutException {
         mService.stop();
-        mService = HapClientService.getHapClientService();
-        Assert.assertNull(mService);
+        assertThat(HapClientService.getHapClientService()).isNull();
     }
 
     @Test
-    public void testGetHapService() {
-        Assert.assertEquals(mService, HapClientService.getHapClientService());
+    public void getHapService() {
+        assertThat(HapClientService.getHapClientService()).isEqualTo(mService);
     }
 
     @Test
-    public void testGetSetPolicy() throws Exception {
-        when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
-        Assert.assertEquals(
-                "Initial device policy",
-                BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
-                mService.getConnectionPolicy(mDevice));
-
-        when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
-        Assert.assertEquals(
-                "Setting device policy to POLICY_FORBIDDEN",
-                BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
-                mService.getConnectionPolicy(mDevice));
-
-        when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        int policy = mService.getConnectionPolicy(mDevice);
-        Assert.assertEquals(
-                "Setting device policy to POLICY_ALLOWED",
-                BluetoothProfile.CONNECTION_POLICY_ALLOWED,
-                policy);
+    public void getConnectionPolicy() {
+        for (int policy :
+                List.of(
+                        CONNECTION_POLICY_UNKNOWN,
+                        CONNECTION_POLICY_FORBIDDEN,
+                        CONNECTION_POLICY_ALLOWED)) {
+            doReturn(policy).when(mDatabaseManager).getProfileConnectionPolicy(any(), anyInt());
+            assertThat(mService.getConnectionPolicy(mDevice)).isEqualTo(policy);
+        }
     }
 
     @Test
-    public void testGetPolicyAfterStopped() {
-        mService.stop();
-        when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
-        Assert.assertEquals(
-                "Initial device policy",
-                BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
-                mService.getConnectionPolicy(mDevice));
-    }
-
-    @Test
-    public void testOkToConnect() {
+    public void canConnect_whenNotBonded_returnFalse() {
         int badPolicyValue = 1024;
         int badBondState = 42;
-        testOkToConnectCase(
-                mDevice,
-                BluetoothDevice.BOND_NONE,
-                BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
-                false);
-        testOkToConnectCase(
-                mDevice,
-                BluetoothDevice.BOND_NONE,
-                BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
-                false);
-        testOkToConnectCase(
-                mDevice,
-                BluetoothDevice.BOND_NONE,
-                BluetoothProfile.CONNECTION_POLICY_ALLOWED,
-                false);
-        testOkToConnectCase(mDevice, BluetoothDevice.BOND_NONE, badPolicyValue, false);
-        testOkToConnectCase(
-                mDevice,
-                BluetoothDevice.BOND_BONDING,
-                BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
-                false);
-        testOkToConnectCase(
-                mDevice,
-                BluetoothDevice.BOND_BONDING,
-                BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
-                false);
-        testOkToConnectCase(
-                mDevice,
-                BluetoothDevice.BOND_BONDING,
-                BluetoothProfile.CONNECTION_POLICY_ALLOWED,
-                false);
-        testOkToConnectCase(mDevice, BluetoothDevice.BOND_BONDING, badPolicyValue, false);
-        testOkToConnectCase(
-                mDevice,
-                BluetoothDevice.BOND_BONDED,
-                BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
-                true);
-        testOkToConnectCase(
-                mDevice,
-                BluetoothDevice.BOND_BONDED,
-                BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
-                false);
-        testOkToConnectCase(
-                mDevice,
-                BluetoothDevice.BOND_BONDED,
-                BluetoothProfile.CONNECTION_POLICY_ALLOWED,
-                true);
-        testOkToConnectCase(mDevice, BluetoothDevice.BOND_BONDED, badPolicyValue, false);
-        testOkToConnectCase(
-                mDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
-        testOkToConnectCase(
-                mDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
-        testOkToConnectCase(
-                mDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
-        testOkToConnectCase(mDevice, badBondState, badPolicyValue, false);
+        for (int bondState : List.of(BOND_NONE, BOND_BONDING, badBondState)) {
+            for (int policy :
+                    List.of(
+                            CONNECTION_POLICY_UNKNOWN,
+                            CONNECTION_POLICY_FORBIDDEN,
+                            CONNECTION_POLICY_ALLOWED,
+                            badPolicyValue)) {
+                doReturn(bondState).when(mAdapterService).getBondState(any());
+                doReturn(policy).when(mDatabaseManager).getProfileConnectionPolicy(any(), anyInt());
+                assertThat(mService.okToConnect(mDevice)).isEqualTo(false);
+            }
+        }
     }
 
     @Test
-    public void testOutgoingConnectMissingHasUuid() {
-        // Update the device policy so okToConnect() returns true
-        when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mNativeInterface).connectHapClient(any(BluetoothDevice.class));
-        doReturn(true).when(mNativeInterface).disconnectHapClient(any(BluetoothDevice.class));
+    public void canConnect_whenBonded() {
+        int badPolicyValue = 1024;
+        doReturn(BOND_BONDED).when(mAdapterService).getBondState(any());
 
-        // Return No UUID
+        for (int policy : List.of(CONNECTION_POLICY_FORBIDDEN, badPolicyValue)) {
+            doReturn(policy).when(mDatabaseManager).getProfileConnectionPolicy(any(), anyInt());
+            assertThat(mService.okToConnect(mDevice)).isEqualTo(false);
+        }
+        for (int policy : List.of(CONNECTION_POLICY_UNKNOWN, CONNECTION_POLICY_ALLOWED)) {
+            doReturn(policy).when(mDatabaseManager).getProfileConnectionPolicy(any(), anyInt());
+            assertThat(mService.okToConnect(mDevice)).isEqualTo(true);
+        }
+    }
+
+    @Test
+    public void connectToDevice_whenUuidIsMissing_returnFalse() {
         doReturn(new ParcelUuid[] {})
                 .when(mAdapterService)
                 .getRemoteUuids(any(BluetoothDevice.class));
 
-        // Send a connect request
-        Assert.assertFalse("Connect expected to fail", mService.connect(mDevice));
+        assertThat(mService.connect(mDevice)).isFalse();
     }
 
     @Test
-    public void testOutgoingConnectExistingHasUuid() {
-        // Update the device policy so okToConnect() returns true
-        when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mNativeInterface).connectHapClient(any(BluetoothDevice.class));
-        doReturn(true).when(mNativeInterface).disconnectHapClient(any(BluetoothDevice.class));
+    public void connectToDevice_whenPolicyForbid_returnFalse() {
+        doReturn(CONNECTION_POLICY_FORBIDDEN)
+                .when(mDatabaseManager)
+                .getProfileConnectionPolicy(any(), anyInt());
 
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-
-        // Send a connect request
-        Assert.assertTrue("Connect expected to succeed", mService.connect(mDevice));
-
-        verify(mAdapterService, timeout(TIMEOUT_MS)).sendBroadcastMultiplePermissions(any(), any());
+        assertThat(mService.connect(mDevice)).isFalse();
     }
 
     @Test
-    public void testOutgoingConnectPolicyForbidden() {
-        doReturn(true).when(mNativeInterface).connectHapClient(any(BluetoothDevice.class));
-        doReturn(true).when(mNativeInterface).disconnectHapClient(any(BluetoothDevice.class));
+    public void outgoingConnect_whenTimeOut_isDisconnected() {
+        assertThat(mService.connect(mDevice)).isTrue();
+        mLooper.dispatchAll();
 
-        // Set the device policy to POLICY_FORBIDDEN so connect() should fail
-        when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
+        verifyConnectionStateIntent(mDevice, STATE_CONNECTING, STATE_DISCONNECTED);
 
-        // Send a connect request
-        Assert.assertFalse("Connect expected to fail", mService.connect(mDevice));
+        mLooper.moveTimeForward(HapClientStateMachine.CONNECT_TIMEOUT.toMillis());
+        mLooper.dispatchAll();
+
+        verifyConnectionStateIntent(mDevice, STATE_DISCONNECTED, STATE_CONNECTING);
     }
 
     @Test
-    public void testOutgoingConnectTimeout() throws Exception {
-        InOrder order = inOrder(mAdapterService);
+    public void connectTwoDevices() {
+        testConnectingDevice(mDevice);
+        testConnectingDevice(mDevice2);
 
-        // Update the device policy so okToConnect() returns true
-        when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mNativeInterface).connectHapClient(any(BluetoothDevice.class));
-        doReturn(true).when(mNativeInterface).disconnectHapClient(any(BluetoothDevice.class));
-
-        // Send a connect request
-        Assert.assertTrue("Connect failed", mService.connect(mDevice));
-
-        order.verify(mAdapterService, timeout(TIMEOUT_MS))
-                .sendBroadcastMultiplePermissions(
-                        argThat(
-                                allOf(
-                                        hasAction(ACTION_HAP_CONNECTION_STATE_CHANGED),
-                                        hasExtra(BluetoothDevice.EXTRA_DEVICE, mDevice),
-                                        hasExtra(EXTRA_STATE, STATE_CONNECTING),
-                                        hasExtra(EXTRA_PREVIOUS_STATE, STATE_DISCONNECTED))),
-                        any());
-
-        Assert.assertEquals(
-                BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(mDevice));
-
-        // Verify the connection state broadcast, and that we are in Disconnected state via binder
-        order.verify(mAdapterService, timeout(HapClientStateMachine.sConnectTimeoutMs * 2L))
-                .sendBroadcastMultiplePermissions(
-                        argThat(
-                                allOf(
-                                        hasAction(ACTION_HAP_CONNECTION_STATE_CHANGED),
-                                        hasExtra(BluetoothDevice.EXTRA_DEVICE, mDevice),
-                                        hasExtra(EXTRA_STATE, STATE_DISCONNECTED),
-                                        hasExtra(EXTRA_PREVIOUS_STATE, STATE_CONNECTING))),
-                        any());
-
-        int state = mService.getConnectionState(mDevice);
-        Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, state);
+        assertThat(mService.getConnectedDevices()).containsExactly(mDevice, mDevice2);
     }
 
     @Test
-    public void testConnectTwo() throws Exception {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-
-        // Send a connect request for the 1st device
-        testConnectingDevice(order, mDevice);
-
-        // Send a connect request for the 2nd device
-        BluetoothDevice Device2 = TestUtils.getTestDevice(mAdapter, 1);
-        testConnectingDevice(order, Device2);
-
-        List<BluetoothDevice> devices = mService.getConnectedDevices();
-        Assert.assertTrue(devices.contains(mDevice));
-        Assert.assertTrue(devices.contains(Device2));
-        Assert.assertNotEquals(mDevice, Device2);
+    public void getActivePresetIndex_whenNoConnected_isUnavailable() {
+        assertThat(mService.getActivePresetIndex(mDevice)).isEqualTo(PRESET_INDEX_UNAVAILABLE);
     }
 
     @Test
-    public void testCallsForNotConnectedDevice() {
-        Assert.assertEquals(
-                BluetoothHapClient.PRESET_INDEX_UNAVAILABLE,
-                mService.getActivePresetIndex(mDevice));
-    }
-
-    @Test
-    public void testGetHapGroupCoordinatedOps() throws Exception {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-        testConnectingDevice(order, mDevice);
-        testConnectingDevice(order, mDevice2);
-        testConnectingDevice(order, mDevice3);
+    public void testGetHapGroupCoordinatedOps() {
+        testConnectingDevice(mDevice);
+        testConnectingDevice(mDevice2);
+        testConnectingDevice(mDevice3);
 
         mNativeCallback.onFeaturesUpdate(getByteAddress(mDevice), 0x04);
         mNativeCallback.onFeaturesUpdate(getByteAddress(mDevice3), 0x04);
@@ -418,26 +295,22 @@
         mNativeCallback.onFeaturesUpdate(getByteAddress(mDevice2), 0);
 
         /* Two devices support coordinated operations thus shall report valid group ID */
-        Assert.assertEquals(2, mService.getHapGroup(mDevice));
-        Assert.assertEquals(3, mService.getHapGroup(mDevice3));
+        assertThat(mService.getHapGroup(mDevice)).isEqualTo(2);
+        assertThat(mService.getHapGroup(mDevice3)).isEqualTo(3);
 
         /* Third one has no coordinated operations support but is part of the group */
         int hapGroup = mService.getHapGroup(mDevice2);
-        Assert.assertEquals(2, hapGroup);
+        assertThat(hapGroup).isEqualTo(2);
     }
 
     @Test
-    public void testSelectPresetNative() throws Exception {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-        testConnectingDevice(order, mDevice);
+    public void testSelectPresetNative() throws RemoteException {
+        testConnectingDevice(mDevice);
 
         // Verify Native Interface call
         mService.selectPreset(mDevice, 0x00);
         verify(mNativeInterface, never()).selectActivePreset(eq(mDevice), eq(0x00));
-        verify(mFrameworkCallback, after(TIMEOUT_MS))
+        verify(mFrameworkCallback)
                 .onPresetSelectionFailed(
                         eq(mDevice), eq(BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX));
 
@@ -446,19 +319,15 @@
     }
 
     @Test
-    public void testGroupSelectActivePresetNative() throws Exception {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-        testConnectingDevice(order, mDevice3);
+    public void testGroupSelectActivePresetNative() throws RemoteException {
+        testConnectingDevice(mDevice3);
 
         int flags = 0x01;
         mNativeCallback.onFeaturesUpdate(getByteAddress(mDevice3), flags);
 
         // Verify Native Interface call
         mService.selectPresetForGroup(0x03, 0x00);
-        verify(mFrameworkCallback, after(TIMEOUT_MS))
+        verify(mFrameworkCallback)
                 .onPresetSelectionForGroupFailed(
                         eq(0x03), eq(BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX));
 
@@ -468,11 +337,7 @@
 
     @Test
     public void testSwitchToNextPreset() {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-        testConnectingDevice(order, mDevice);
+        testConnectingDevice(mDevice);
 
         // Verify Native Interface call
         mService.switchToNextPreset(mDevice);
@@ -481,11 +346,7 @@
 
     @Test
     public void testSwitchToNextPresetForGroup() {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-        testConnectingDevice(order, mDevice3);
+        testConnectingDevice(mDevice3);
         int flags = 0x01;
         mNativeCallback.onFeaturesUpdate(getByteAddress(mDevice3), flags);
 
@@ -496,11 +357,7 @@
 
     @Test
     public void testSwitchToPreviousPreset() {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-        testConnectingDevice(order, mDevice);
+        testConnectingDevice(mDevice);
 
         // Verify Native Interface call
         mService.switchToPreviousPreset(mDevice);
@@ -509,12 +366,8 @@
 
     @Test
     public void testSwitchToPreviousPresetForGroup() {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-        testConnectingDevice(order, mDevice);
-        testConnectingDevice(order, mDevice2);
+        testConnectingDevice(mDevice);
+        testConnectingDevice(mDevice2);
 
         int flags = 0x01;
         mNativeCallback.onFeaturesUpdate(getByteAddress(mDevice), flags);
@@ -525,56 +378,40 @@
     }
 
     @Test
-    public void testGetActivePresetIndex() throws Exception {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-        testConnectingDevice(order, mDevice);
+    public void testGetActivePresetIndex() throws RemoteException {
+        testConnectingDevice(mDevice);
         testOnPresetSelected(mDevice, 0x01);
 
-        // Verify cached value via binder
-        int presetIndex = mService.getActivePresetIndex(mDevice);
-        Assert.assertEquals(0x01, presetIndex);
+        assertThat(mService.getActivePresetIndex(mDevice)).isEqualTo(0x01);
     }
 
     @Test
-    public void testGetPresetInfoAndActivePresetInfo() throws Exception {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-        testConnectingDevice(order, mDevice2);
+    public void testGetPresetInfoAndActivePresetInfo() throws RemoteException {
+        testConnectingDevice(mDevice2);
 
         // Check when active preset is not known yet
         List<BluetoothHapPresetInfo> presetList = mService.getAllPresetInfo(mDevice2);
 
         BluetoothHapPresetInfo presetInfo = mService.getPresetInfo(mDevice2, 0x01);
-        Assert.assertTrue(presetList.contains(presetInfo));
-        Assert.assertEquals(0x01, presetInfo.getIndex());
+        assertThat(presetList).contains(presetInfo);
+        assertThat(presetInfo.getIndex()).isEqualTo(0x01);
 
-        Assert.assertEquals(
-                BluetoothHapClient.PRESET_INDEX_UNAVAILABLE,
-                mService.getActivePresetIndex(mDevice2));
-        Assert.assertEquals(null, mService.getActivePresetInfo(mDevice2));
+        assertThat(mService.getActivePresetIndex(mDevice2)).isEqualTo(PRESET_INDEX_UNAVAILABLE);
+        assertThat(mService.getActivePresetInfo(mDevice2)).isNull();
 
         // Inject active preset change event
         testOnPresetSelected(mDevice2, 0x01);
 
         // Check when active preset is known
-        Assert.assertEquals(0x01, mService.getActivePresetIndex(mDevice2));
+        assertThat(mService.getActivePresetIndex(mDevice2)).isEqualTo(0x01);
         BluetoothHapPresetInfo info = mService.getActivePresetInfo(mDevice2);
-        Assert.assertNotNull(info);
-        Assert.assertEquals("One", info.getName());
+        assertThat(info).isNotNull();
+        assertThat(info.getName()).isEqualTo("One");
     }
 
     @Test
-    public void testSetPresetNameNative() throws Exception {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-        testConnectingDevice(order, mDevice);
+    public void testSetPresetNameNative() throws RemoteException {
+        testConnectingDevice(mDevice);
 
         mService.setPresetName(mDevice, 0x00, "ExamplePresetName");
         verify(mNativeInterface, never())
@@ -589,26 +426,22 @@
     }
 
     @Test
-    public void testSetPresetNameForGroup() throws Exception {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
+    public void testSetPresetNameForGroup() throws RemoteException {
         int test_group = 0x02;
 
-        testConnectingDevice(order, mDevice);
-        testConnectingDevice(order, mDevice2);
+        testConnectingDevice(mDevice);
+        testConnectingDevice(mDevice2);
 
         int flags = 0x21;
         mNativeCallback.onFeaturesUpdate(getByteAddress(mDevice), flags);
 
         mService.setPresetNameForGroup(test_group, 0x00, "ExamplePresetName");
-        verify(mFrameworkCallback, after(TIMEOUT_MS))
+        verify(mFrameworkCallback)
                 .onSetPresetNameForGroupFailed(
                         eq(test_group), eq(BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX));
 
         mService.setPresetNameForGroup(-1, 0x01, "ExamplePresetName");
-        verify(mFrameworkCallback, after(TIMEOUT_MS))
+        verify(mFrameworkCallback)
                 .onSetPresetNameForGroupFailed(
                         eq(-1), eq(BluetoothStatusCodes.ERROR_CSIP_INVALID_GROUP_ID));
 
@@ -635,7 +468,7 @@
     }
 
     @Test
-    public void testStackEventOnPresetSelected() throws Exception {
+    public void testStackEventOnPresetSelected() throws RemoteException {
         int presetIndex = 0x01;
 
         mNativeCallback.onActivePresetSelected(getByteAddress(mDevice), presetIndex);
@@ -649,7 +482,7 @@
     }
 
     @Test
-    public void testStackEventOnActivePresetSelectError() throws Exception {
+    public void testStackEventOnActivePresetSelectError() throws RemoteException {
         mNativeCallback.onActivePresetSelectError(getByteAddress(mDevice), 0x05);
 
         verify(mFrameworkCallback)
@@ -658,14 +491,8 @@
     }
 
     @Test
-    public void testStackEventOnPresetInfo() throws Exception {
-        InOrder order = inOrder(mAdapterService);
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-
-        // Connect and inject initial presets
-        testConnectingDevice(order, mDevice);
+    public void testStackEventOnPresetInfo() throws RemoteException {
+        testConnectingDevice(mDevice);
 
         int infoReason = HapClientStackEvent.PRESET_INFO_REASON_PRESET_INFO_UPDATE;
         BluetoothHapPresetInfo[] info = {
@@ -686,17 +513,18 @@
                         eq(BluetoothStatusCodes.REASON_REMOTE_REQUEST));
 
         List<BluetoothHapPresetInfo> presets = presetsCaptor.getValue();
-        Assert.assertEquals(3, presets.size());
+        assertThat(presets.size()).isEqualTo(3);
 
         Optional<BluetoothHapPresetInfo> preset =
                 presetsCaptor.getValue().stream().filter(p -> 0x01 == p.getIndex()).findFirst();
-        Assert.assertEquals("OneChangedToUnavailable", preset.get().getName());
-        Assert.assertFalse(preset.get().isAvailable());
-        Assert.assertTrue(preset.get().isWritable());
+        assertThat(preset.get().getName()).isEqualTo("OneChangedToUnavailable");
+        assertThat(preset.get().isAvailable()).isFalse();
+        ;
+        assertThat(preset.get().isWritable()).isTrue();
     }
 
     @Test
-    public void testStackEventOnPresetNameSetError() throws Exception {
+    public void testStackEventOnPresetNameSetError() throws RemoteException {
         /* Not a valid name length */
         mNativeCallback.onPresetNameSetError(
                 getByteAddress(mDevice),
@@ -736,7 +564,7 @@
     }
 
     @Test
-    public void testStackEventOnGroupPresetNameSetError() throws Exception {
+    public void testStackEventOnGroupPresetNameSetError() throws RemoteException {
         int groupId = 0x01;
         int presetIndex = 0x04;
         /* Not a valid name length */
@@ -776,30 +604,25 @@
     }
 
     @Test
-    public void testServiceBinderGetDevicesMatchingConnectionStates() throws Exception {
-        List<BluetoothDevice> devices = mService.getDevicesMatchingConnectionStates(null);
-        Assert.assertEquals(0, devices.size());
+    public void getDevicesMatchingConnectionStates_whenNull_isEmpty() {
+        assertThat(mService.getDevicesMatchingConnectionStates(null)).isEmpty();
     }
 
     @Test
-    public void testServiceBinderSetConnectionPolicy() throws Exception {
-        Assert.assertTrue(
-                mService.setConnectionPolicy(mDevice, BluetoothProfile.CONNECTION_POLICY_UNKNOWN));
+    public void setConnectionPolicy() {
+        assertThat(mService.setConnectionPolicy(mDevice, CONNECTION_POLICY_UNKNOWN)).isTrue();
         verify(mDatabaseManager)
                 .setProfileConnectionPolicy(
-                        mDevice,
-                        BluetoothProfile.HAP_CLIENT,
-                        BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+                        mDevice, BluetoothProfile.HAP_CLIENT, CONNECTION_POLICY_UNKNOWN);
     }
 
     @Test
-    public void testServiceBinderGetFeatures() throws Exception {
-        int features = mService.getFeatures(mDevice);
-        Assert.assertEquals(0x00, features);
+    public void getFeatures() {
+        assertThat(mService.getFeatures(mDevice)).isEqualTo(0x00);
     }
 
     @Test
-    public void testServiceBinderRegisterUnregisterCallback() throws Exception {
+    public void registerUnregisterCallback() {
         IBluetoothHapClientCallback callback = Mockito.mock(IBluetoothHapClientCallback.class);
         Binder binder = Mockito.mock(Binder.class);
         when(callback.asBinder()).thenReturn(binder);
@@ -807,101 +630,51 @@
         synchronized (mService.mCallbacks) {
             int size = mService.mCallbacks.getRegisteredCallbackCount();
             mService.registerCallback(callback);
-            Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount());
+            assertThat(mService.mCallbacks.getRegisteredCallbackCount()).isEqualTo(size + 1);
 
             mService.unregisterCallback(callback);
-            Assert.assertEquals(size, mService.mCallbacks.getRegisteredCallbackCount());
+            assertThat(mService.mCallbacks.getRegisteredCallbackCount()).isEqualTo(size);
         }
     }
 
     @Test
-    public void testDumpDoesNotCrash() {
-        // Update the device policy so okToConnect() returns true
-        when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mNativeInterface).connectHapClient(any(BluetoothDevice.class));
-        doReturn(true).when(mNativeInterface).disconnectHapClient(any(BluetoothDevice.class));
-
-        doReturn(new ParcelUuid[] {BluetoothUuid.HAS})
-                .when(mAdapterService)
-                .getRemoteUuids(any(BluetoothDevice.class));
-
+    public void dumpDoesNotCrash() {
         // Add state machine for testing dump()
         mService.connect(mDevice);
-
-        verify(mAdapterService, timeout(TIMEOUT_MS)).sendBroadcastMultiplePermissions(any(), any());
+        mLooper.dispatchAll();
 
         mService.dump(new StringBuilder());
     }
 
     /** Helper function to test device connecting */
-    private void prepareConnectingDevice(BluetoothDevice device) {
-        // Prepare intent queue and all the mocks
-        when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
-        doReturn(true).when(mNativeInterface).connectHapClient(any(BluetoothDevice.class));
-        doReturn(true).when(mNativeInterface).disconnectHapClient(any(BluetoothDevice.class));
+    private void testConnectingDevice(BluetoothDevice device) {
+        assertThat(mService.connect(device)).isTrue();
+        mLooper.dispatchAll();
+        verifyConnectingDevice(device);
     }
 
     /** Helper function to test device connecting */
-    private void testConnectingDevice(InOrder order, BluetoothDevice device) {
-        prepareConnectingDevice(device);
-        // Send a connect request
-        Assert.assertTrue("Connect expected to succeed", mService.connect(device));
-        verifyConnectingDevice(order, device);
-    }
+    private void verifyConnectingDevice(BluetoothDevice device) {
+        verifyConnectionStateIntent(device, STATE_CONNECTING, STATE_DISCONNECTED);
+        generateConnectionMessageFromNative(device, STATE_CONNECTED, STATE_CONNECTING);
 
-    /** Helper function to test device connecting */
-    private void verifyConnectingDevice(InOrder order, BluetoothDevice device) {
-        // Verify the connection state broadcast, and that we are in Connecting state
-        order.verify(mAdapterService, timeout(TIMEOUT_MS))
-                .sendBroadcastMultiplePermissions(
-                        argThat(
-                                allOf(
-                                        hasAction(ACTION_HAP_CONNECTION_STATE_CHANGED),
-                                        hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
-                                        hasExtra(EXTRA_STATE, STATE_CONNECTING),
-                                        hasExtra(EXTRA_PREVIOUS_STATE, STATE_DISCONNECTED))),
-                        any());
-        Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(device));
-
-        // Send a message to trigger connection completed
         HapClientStackEvent evt =
-                new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        evt.device = device;
-        evt.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTED;
-        mService.messageFromNative(evt);
-
-        // Verify the connection state broadcast, and that we are in Connected state
-        order.verify(mAdapterService, timeout(TIMEOUT_MS))
-                .sendBroadcastMultiplePermissions(
-                        argThat(
-                                allOf(
-                                        hasAction(ACTION_HAP_CONNECTION_STATE_CHANGED),
-                                        hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
-                                        hasExtra(EXTRA_STATE, STATE_CONNECTED),
-                                        hasExtra(EXTRA_PREVIOUS_STATE, STATE_CONNECTING))),
-                        any());
-        Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(device));
-
-        evt = new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
+                new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
         evt.device = device;
         evt.valueInt1 = 0x01; // features
         mService.messageFromNative(evt);
+        mLooper.dispatchAll();
 
-        order.verify(mAdapterService, timeout(TIMEOUT_MS))
-                .sendBroadcastMultiplePermissions(
-                        argThat(
-                                allOf(
-                                        hasAction(ACTION_HAP_DEVICE_AVAILABLE),
-                                        hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
-                                        hasExtra(BluetoothHapClient.EXTRA_HAP_FEATURES, 0x01))),
-                        any());
+        verifyIntentSent(
+                hasAction(ACTION_HAP_DEVICE_AVAILABLE),
+                hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
+                hasExtra(BluetoothHapClient.EXTRA_HAP_FEATURES, 0x01));
 
         evt = new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_DEVICE_FEATURES);
         evt.device = device;
         evt.valueInt1 = 0x01; // features
         mService.messageFromNative(evt);
+        mLooper.dispatchAll();
 
         // Inject some initial presets
         List<BluetoothHapPresetInfo> presets =
@@ -923,29 +696,21 @@
                 device, HapClientStackEvent.PRESET_INFO_REASON_ALL_PRESET_INFO, presets);
     }
 
-    private void testOnPresetSelected(BluetoothDevice device, int index) throws Exception {
+    private void testOnPresetSelected(BluetoothDevice device, int index) throws RemoteException {
         HapClientStackEvent evt =
                 new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_ON_ACTIVE_PRESET_SELECTED);
         evt.device = device;
         evt.valueInt1 = index;
         mService.messageFromNative(evt);
+        mLooper.dispatchAll();
 
-        verify(mFrameworkCallback, after(TIMEOUT_MS))
+        verify(mFrameworkCallback)
                 .onPresetSelected(
                         eq(device),
                         eq(evt.valueInt1),
                         eq(BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST));
     }
 
-    /** Helper function to test okToConnect() method */
-    private void testOkToConnectCase(
-            BluetoothDevice device, int bondState, int policy, boolean expected) {
-        doReturn(bondState).when(mAdapterService).getBondState(device);
-        when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HAP_CLIENT))
-                .thenReturn(policy);
-        Assert.assertEquals(expected, mService.okToConnect(device));
-    }
-
     /** Helper function to get byte array for a device address */
     private byte[] getByteAddress(BluetoothDevice device) {
         if (device == null) {
@@ -953,4 +718,32 @@
         }
         return Utils.getBytesFromAddress(device.getAddress());
     }
+
+    @SafeVarargs
+    private void verifyIntentSent(Matcher<Intent>... matchers) {
+        mInOrder.verify(mAdapterService)
+                .sendBroadcastMultiplePermissions(
+                        MockitoHamcrest.argThat(AllOf.allOf(matchers)), any());
+    }
+
+    private void verifyConnectionStateIntent(BluetoothDevice device, int newState, int prevState) {
+        verifyIntentSent(
+                hasAction(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED),
+                hasExtra(BluetoothDevice.EXTRA_DEVICE, device),
+                hasExtra(EXTRA_STATE, newState),
+                hasExtra(EXTRA_PREVIOUS_STATE, prevState));
+        assertThat(mService.getConnectionState(device)).isEqualTo(newState);
+    }
+
+    private void generateConnectionMessageFromNative(
+            BluetoothDevice device, int newState, int oldState) {
+        HapClientStackEvent event =
+                new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.device = device;
+        event.valueInt1 = newState;
+        mService.messageFromNative(event);
+        mLooper.dispatchAll();
+
+        verifyConnectionStateIntent(device, newState, oldState);
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStackEventTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStackEventTest.java
index d307a5c..249914b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStackEventTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStackEventTest.java
@@ -37,28 +37,6 @@
         eventStr = event.toString();
         assertThat(eventStr).contains("EVENT_TYPE_UNKNOWN");
 
-        event = new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        event.valueInt1 = -1;
-        eventStr = event.toString();
-        assertThat(eventStr).contains("EVENT_TYPE_CONNECTION_STATE_CHANGED");
-        assertThat(eventStr).contains("CONNECTION_STATE_UNKNOWN");
-
-        event.valueInt1 = HapClientStackEvent.CONNECTION_STATE_DISCONNECTED;
-        eventStr = event.toString();
-        assertThat(eventStr).contains("CONNECTION_STATE_DISCONNECTED");
-
-        event.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTING;
-        eventStr = event.toString();
-        assertThat(eventStr).contains("CONNECTION_STATE_CONNECTING");
-
-        event.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTED;
-        eventStr = event.toString();
-        assertThat(eventStr).contains("CONNECTION_STATE_CONNECTED");
-
-        event.valueInt1 = HapClientStackEvent.CONNECTION_STATE_DISCONNECTING;
-        eventStr = event.toString();
-        assertThat(eventStr).contains("CONNECTION_STATE_DISCONNECTING");
-
         event = new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
         eventStr = event.toString();
         assertThat(eventStr).contains("EVENT_TYPE_DEVICE_AVAILABLE");
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStateMachineTest.java
index a61e4ea..1c25fad 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientStateMachineTest.java
@@ -17,373 +17,253 @@
 
 package com.android.bluetooth.hap;
 
+import static android.bluetooth.BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED;
+import static android.bluetooth.BluetoothProfile.EXTRA_PREVIOUS_STATE;
+import static android.bluetooth.BluetoothProfile.EXTRA_STATE;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
+
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+
+import static com.android.bluetooth.hap.HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED;
+import static com.android.bluetooth.hap.HapClientStateMachine.CONNECT_TIMEOUT;
+import static com.android.bluetooth.hap.HapClientStateMachine.MESSAGE_CONNECT;
+import static com.android.bluetooth.hap.HapClientStateMachine.MESSAGE_DISCONNECT;
+import static com.android.bluetooth.hap.HapClientStateMachine.MESSAGE_STACK_EVENT;
+
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
 import android.content.Intent;
-import android.os.HandlerThread;
-import android.os.Message;
+import android.os.test.TestLooper;
 
-import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.TestUtils;
-import com.android.bluetooth.btservice.AdapterService;
-
-import org.hamcrest.core.IsInstanceOf;
-import org.junit.After;
-import org.junit.Assert;
+import org.hamcrest.Matcher;
+import org.hamcrest.core.AllOf;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
-import org.mockito.Mockito;
+import org.mockito.hamcrest.MockitoHamcrest;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-@MediumTest
+@SmallTest
 @RunWith(AndroidJUnit4.class)
 public class HapClientStateMachineTest {
-    private BluetoothAdapter mAdapter;
-    private HandlerThread mHandlerThread;
-    private HapClientStateMachine mHapClientStateMachine;
-    private BluetoothDevice mTestDevice;
-    private static final int TIMEOUT_MS = 1000;
-    boolean mIsAdapterServiceSet;
-
     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
-    @Mock private AdapterService mAdapterService;
-    @Mock private HapClientService mHapClientService;
-    @Mock private HapClientNativeInterface mHearingAccessGattClientInterface;
+    @Mock private HapClientService mService;
+    @Mock private HapClientNativeInterface mNativeInterface;
+
+    private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+    private final BluetoothDevice mDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
+
+    private HapClientStateMachine mStateMachine;
+    private InOrder mInOrder;
+    private TestLooper mLooper;
 
     @Before
     public void setUp() throws Exception {
-        TestUtils.setAdapterService(mAdapterService);
-        mIsAdapterServiceSet = true;
+        doReturn(true).when(mService).okToConnect(any());
 
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        doReturn(true).when(mNativeInterface).connectHapClient(any());
+        doReturn(true).when(mNativeInterface).disconnectHapClient(any());
 
-        // Get a device for testing
-        mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
+        mInOrder = inOrder(mService);
+        mLooper = new TestLooper();
 
-        // Set up thread and looper
-        mHandlerThread = new HandlerThread("HapClientStateMachineTestHandlerThread");
-        mHandlerThread.start();
-        mHapClientStateMachine =
-                new HapClientStateMachine(
-                        mTestDevice,
-                        mHapClientService,
-                        mHearingAccessGattClientInterface,
-                        mHandlerThread.getLooper());
-        // Override the timeout value to speed up the test
-        HapClientStateMachine.sConnectTimeoutMs = 1000; // 1s
-        mHapClientStateMachine.start();
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (!mIsAdapterServiceSet) {
-            return;
-        }
-        if (mHapClientStateMachine != null) {
-            mHapClientStateMachine.doQuit();
-            mHandlerThread.quit();
-        }
-        TestUtils.clearAdapterService(mAdapterService);
-    }
-
-    /** Test that default state is disconnected */
-    @Test
-    public void testDefaultDisconnectedState() {
-        Assert.assertEquals(
-                BluetoothProfile.STATE_DISCONNECTED, mHapClientStateMachine.getConnectionState());
-    }
-
-    /**
-     * Allow/disallow connection to any device.
-     *
-     * @param allow if true, connection is allowed
-     */
-    private void allowConnection(boolean allow) {
-        doReturn(allow).when(mHapClientService).okToConnect(any(BluetoothDevice.class));
-    }
-
-    /** Test that an incoming connection with policy forbidding connection is rejected */
-    @Test
-    public void testIncomingPolicyReject() {
-        allowConnection(false);
-
-        // Inject an event for when incoming connection is requested
-        HapClientStackEvent connStCh =
-                new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connStCh.device = mTestDevice;
-        connStCh.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTED;
-        mHapClientStateMachine.sendMessage(HapClientStateMachine.STACK_EVENT, connStCh);
-
-        // Verify that no connection state broadcast is executed
-        verify(mHapClientService, after(TIMEOUT_MS).never())
-                .sendBroadcast(any(Intent.class), anyString());
-        // Check that we are in Disconnected state
-        Assert.assertThat(
-                mHapClientStateMachine.getCurrentState(),
-                IsInstanceOf.instanceOf(HapClientStateMachine.Disconnected.class));
-    }
-
-    /** Test that an incoming connection with policy allowing connection is accepted */
-    @Test
-    public void testIncomingPolicyAccept() {
-        allowConnection(true);
-
-        // Inject an event for when incoming connection is requested
-        HapClientStackEvent connStCh =
-                new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connStCh.device = mTestDevice;
-        connStCh.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTING;
-        mHapClientStateMachine.sendMessage(HapClientStateMachine.STACK_EVENT, connStCh);
-
-        // Verify that one connection state broadcast is executed
-        ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
-        verify(mHapClientService, timeout(TIMEOUT_MS))
-                .sendBroadcastWithMultiplePermissions(
-                        intentArgument1.capture(), any(String[].class));
-        Assert.assertEquals(
-                BluetoothProfile.STATE_CONNECTING,
-                intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
-
-        // Check that we are in Connecting state
-        Assert.assertThat(
-                mHapClientStateMachine.getCurrentState(),
-                IsInstanceOf.instanceOf(HapClientStateMachine.Connecting.class));
-
-        // Send a message to trigger connection completed
-        HapClientStackEvent connCompletedEvent =
-                new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connCompletedEvent.device = mTestDevice;
-        connCompletedEvent.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTED;
-        mHapClientStateMachine.sendMessage(HapClientStateMachine.STACK_EVENT, connCompletedEvent);
-
-        // Verify that the expected number of broadcasts are executed:
-        // - two calls to broadcastConnectionState(): Disconnected -> Connecting -> Connected
-        ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
-        verify(mHapClientService, timeout(TIMEOUT_MS).times(2))
-                .sendBroadcastWithMultiplePermissions(
-                        intentArgument2.capture(), any(String[].class));
-        // Check that we are in Connected state
-        Assert.assertThat(
-                mHapClientStateMachine.getCurrentState(),
-                IsInstanceOf.instanceOf(HapClientStateMachine.Connected.class));
-    }
-
-    /** Test that an outgoing connection times out */
-    @Test
-    public void testOutgoingTimeout() {
-        allowConnection(true);
-        doReturn(true)
-                .when(mHearingAccessGattClientInterface)
-                .connectHapClient(any(BluetoothDevice.class));
-        doReturn(true)
-                .when(mHearingAccessGattClientInterface)
-                .disconnectHapClient(any(BluetoothDevice.class));
-
-        // Send a connect request
-        mHapClientStateMachine.sendMessage(HapClientStateMachine.CONNECT, mTestDevice);
-
-        // Verify that one connection state broadcast is executed
-        ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
-        verify(mHapClientService, timeout(TIMEOUT_MS))
-                .sendBroadcastWithMultiplePermissions(
-                        intentArgument1.capture(), any(String[].class));
-        Assert.assertEquals(
-                BluetoothProfile.STATE_CONNECTING,
-                intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
-
-        // Check that we are in Connecting state
-        Assert.assertThat(
-                mHapClientStateMachine.getCurrentState(),
-                IsInstanceOf.instanceOf(HapClientStateMachine.Connecting.class));
-
-        // Verify that one connection state broadcast is executed
-        ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
-        verify(mHapClientService, timeout(HapClientStateMachine.sConnectTimeoutMs * 2L).times(2))
-                .sendBroadcastWithMultiplePermissions(
-                        intentArgument2.capture(), any(String[].class));
-        Assert.assertEquals(
-                BluetoothProfile.STATE_DISCONNECTED,
-                intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
-
-        // Check that we are in Disconnected state
-        Assert.assertThat(
-                mHapClientStateMachine.getCurrentState(),
-                IsInstanceOf.instanceOf(HapClientStateMachine.Disconnected.class));
-        verify(mHearingAccessGattClientInterface).disconnectHapClient(eq(mTestDevice));
-    }
-
-    /** Test that an incoming connection times out */
-    @Test
-    public void testIncomingTimeout() {
-        allowConnection(true);
-        doReturn(true)
-                .when(mHearingAccessGattClientInterface)
-                .connectHapClient(any(BluetoothDevice.class));
-        doReturn(true)
-                .when(mHearingAccessGattClientInterface)
-                .disconnectHapClient(any(BluetoothDevice.class));
-
-        // Inject an event for when incoming connection is requested
-        HapClientStackEvent connStCh =
-                new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connStCh.device = mTestDevice;
-        connStCh.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTING;
-        mHapClientStateMachine.sendMessage(HapClientStateMachine.STACK_EVENT, connStCh);
-
-        // Verify that one connection state broadcast is executed
-        ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
-        verify(mHapClientService, timeout(TIMEOUT_MS))
-                .sendBroadcastWithMultiplePermissions(
-                        intentArgument1.capture(), any(String[].class));
-        Assert.assertEquals(
-                BluetoothProfile.STATE_CONNECTING,
-                intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
-
-        // Check that we are in Connecting state
-        Assert.assertThat(
-                mHapClientStateMachine.getCurrentState(),
-                IsInstanceOf.instanceOf(HapClientStateMachine.Connecting.class));
-
-        // Verify that one connection state broadcast is executed
-        ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
-        verify(mHapClientService, timeout(HapClientStateMachine.sConnectTimeoutMs * 2L).times(2))
-                .sendBroadcastWithMultiplePermissions(
-                        intentArgument2.capture(), any(String[].class));
-        Assert.assertEquals(
-                BluetoothProfile.STATE_DISCONNECTED,
-                intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
-
-        // Check that we are in Disconnected state
-        Assert.assertThat(
-                mHapClientStateMachine.getCurrentState(),
-                IsInstanceOf.instanceOf(HapClientStateMachine.Disconnected.class));
-        verify(mHearingAccessGattClientInterface).disconnectHapClient(eq(mTestDevice));
+        mStateMachine =
+                new HapClientStateMachine(mService, mDevice, mNativeInterface, mLooper.getLooper());
     }
 
     @Test
-    public void testStatesChangesWithMessages() {
-        allowConnection(true);
-        doReturn(true)
-                .when(mHearingAccessGattClientInterface)
-                .connectHapClient(any(BluetoothDevice.class));
-
-        // Check that we are in Disconnected state
-        Assert.assertThat(
-                mHapClientStateMachine.getCurrentState(),
-                IsInstanceOf.instanceOf(HapClientStateMachine.Disconnected.class));
-
-        mHapClientStateMachine.sendMessage(HapClientStateMachine.DISCONNECT);
-        // verify disconnectHapClient was called
-        verify(mHearingAccessGattClientInterface, timeout(TIMEOUT_MS).times(1))
-                .disconnectHapClient(any(BluetoothDevice.class));
-
-        // disconnected -> connecting
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.CONNECT),
-                HapClientStateMachine.Connecting.class);
-        // connecting -> disconnected
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.CONNECT_TIMEOUT),
-                HapClientStateMachine.Disconnected.class);
-
-        // disconnected -> connecting
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.CONNECT),
-                HapClientStateMachine.Connecting.class);
-        // connecting -> disconnected
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.DISCONNECT),
-                HapClientStateMachine.Disconnected.class);
-
-        // disconnected -> connecting
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.CONNECT),
-                HapClientStateMachine.Connecting.class);
-        // connecting -> disconnecting
-        HapClientStackEvent connStCh =
-                new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connStCh.device = mTestDevice;
-        connStCh.valueInt1 = HapClientStackEvent.CONNECTION_STATE_DISCONNECTING;
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.STACK_EVENT, connStCh),
-                HapClientStateMachine.Disconnecting.class);
-        // disconnecting -> connecting
-        connStCh = new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connStCh.device = mTestDevice;
-        connStCh.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTING;
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.STACK_EVENT, connStCh),
-                HapClientStateMachine.Connecting.class);
-        // connecting -> connected
-        connStCh = new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connStCh.device = mTestDevice;
-        connStCh.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTED;
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.STACK_EVENT, connStCh),
-                HapClientStateMachine.Connected.class);
-        // connected -> disconnecting
-        connStCh = new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connStCh.device = mTestDevice;
-        connStCh.valueInt1 = HapClientStackEvent.CONNECTION_STATE_DISCONNECTING;
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.STACK_EVENT, connStCh),
-                HapClientStateMachine.Disconnecting.class);
-        // disconnecting -> disconnected
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.CONNECT_TIMEOUT),
-                HapClientStateMachine.Disconnected.class);
-
-        // disconnected -> connected
-        connStCh = new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connStCh.device = mTestDevice;
-        connStCh.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTED;
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.STACK_EVENT, connStCh),
-                HapClientStateMachine.Connected.class);
-        // connected -> disconnected
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.DISCONNECT),
-                HapClientStateMachine.Disconnected.class);
-
-        // disconnected -> connected
-        connStCh = new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connStCh.device = mTestDevice;
-        connStCh.valueInt1 = HapClientStackEvent.CONNECTION_STATE_CONNECTED;
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.STACK_EVENT, connStCh),
-                HapClientStateMachine.Connected.class);
-        // connected -> disconnected
-        connStCh = new HapClientStackEvent(HapClientStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-        connStCh.device = mTestDevice;
-        connStCh.valueInt1 = HapClientStackEvent.CONNECTION_STATE_DISCONNECTED;
-        sendMessageAndVerifyTransition(
-                mHapClientStateMachine.obtainMessage(HapClientStateMachine.STACK_EVENT, connStCh),
-                HapClientStateMachine.Disconnected.class);
+    public void initialState_isDisconnected() {
+        assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_DISCONNECTED);
     }
 
-    private <T> void sendMessageAndVerifyTransition(Message msg, Class<T> type) {
-        Mockito.clearInvocations(mHapClientService);
-        mHapClientStateMachine.sendMessage(msg);
-        // Verify that one connection state broadcast is executed
-        verify(mHapClientService, timeout(TIMEOUT_MS))
-                .sendBroadcastWithMultiplePermissions(any(Intent.class), any(String[].class));
-        Assert.assertThat(mHapClientStateMachine.getCurrentState(), IsInstanceOf.instanceOf(type));
+    @Test
+    public void incomingConnect_whenNotOkToConnect_isRejected() {
+        doReturn(false).when(mService).okToConnect(any());
+
+        generateUnexpectedConnectionMessageFromNative(STATE_CONNECTED);
+
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Disconnected.class);
+        assertThat(mLooper.nextMessage()).isNull();
+    }
+
+    @Test
+    public void incomingConnect_whenOkToConnect_isConnected() {
+        generateConnectionMessageFromNative(STATE_CONNECTING, STATE_DISCONNECTED);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Connecting.class);
+
+        generateConnectionMessageFromNative(STATE_CONNECTED, STATE_CONNECTING);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Connected.class);
+    }
+
+    @Test
+    public void outgoingConnect_whenTimeOut_isDisconnectedAndInAcceptList() {
+        sendAndDispatchMessage(MESSAGE_CONNECT);
+        verifyConnectionStateIntent(STATE_CONNECTING, STATE_DISCONNECTED);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Connecting.class);
+
+        mLooper.moveTimeForward(CONNECT_TIMEOUT.toMillis());
+        mLooper.dispatchAll();
+
+        verifyConnectionStateIntent(STATE_DISCONNECTED, STATE_CONNECTING);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void incomingConnect_whenTimeOut_isDisconnectedAndInAcceptList() {
+        generateConnectionMessageFromNative(STATE_CONNECTING, STATE_DISCONNECTED);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Connecting.class);
+
+        mLooper.moveTimeForward(CONNECT_TIMEOUT.toMillis());
+        mLooper.dispatchAll();
+
+        verifyConnectionStateIntent(STATE_DISCONNECTED, STATE_CONNECTING);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void disconnect_whenDisconnected_callNativeDisconnect() {
+        mStateMachine.sendMessage(HapClientStateMachine.MESSAGE_DISCONNECT);
+        mLooper.dispatchAll();
+
+        verify(mNativeInterface).disconnectHapClient(any(BluetoothDevice.class));
+    }
+
+    @Test
+    public void timeout_whenOutgoingConnect_isDisconnected() {
+        sendAndDispatchMessage(MESSAGE_CONNECT);
+        verifyConnectionStateIntent(STATE_CONNECTING, STATE_DISCONNECTED);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Connecting.class);
+
+        mLooper.moveTimeForward(CONNECT_TIMEOUT.toMillis());
+        mLooper.dispatchAll();
+
+        verifyConnectionStateIntent(STATE_DISCONNECTED, STATE_CONNECTING);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void disconnect_whenConnecting_isDisconnected() {
+        sendAndDispatchMessage(MESSAGE_CONNECT);
+        verifyConnectionStateIntent(STATE_CONNECTING, STATE_DISCONNECTED);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Connecting.class);
+
+        sendAndDispatchMessage(MESSAGE_DISCONNECT);
+        verifyConnectionStateIntent(STATE_DISCONNECTED, STATE_CONNECTING);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void remoteToggleDisconnect_whenConnecting_isDisconnected() {
+        sendAndDispatchMessage(MESSAGE_CONNECT);
+        verifyConnectionStateIntent(STATE_CONNECTING, STATE_DISCONNECTED);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Connecting.class);
+
+        generateConnectionMessageFromNative(STATE_DISCONNECTING, STATE_CONNECTING);
+        generateConnectionMessageFromNative(STATE_CONNECTING, STATE_DISCONNECTING);
+        generateConnectionMessageFromNative(STATE_CONNECTED, STATE_CONNECTING);
+        generateConnectionMessageFromNative(STATE_DISCONNECTING, STATE_CONNECTED);
+
+        mLooper.moveTimeForward(CONNECT_TIMEOUT.toMillis());
+        mLooper.dispatchAll();
+
+        verifyConnectionStateIntent(STATE_DISCONNECTED, STATE_DISCONNECTING);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void timeout_whenOutgoingDisConnect_isDisconnected() {
+        generateConnectionMessageFromNative(STATE_CONNECTING, STATE_DISCONNECTED);
+        generateConnectionMessageFromNative(STATE_CONNECTED, STATE_CONNECTING);
+        generateConnectionMessageFromNative(STATE_DISCONNECTING, STATE_CONNECTED);
+
+        mLooper.moveTimeForward(CONNECT_TIMEOUT.toMillis());
+        mLooper.dispatchAll();
+
+        verifyConnectionStateIntent(STATE_DISCONNECTED, STATE_DISCONNECTING);
+        assertThat(mStateMachine.getCurrentState())
+                .isInstanceOf(HapClientStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void incomingConnect_whenDisconnected_isConnected() {
+        generateConnectionMessageFromNative(STATE_CONNECTED, STATE_DISCONNECTED);
+        assertThat(mStateMachine.getConnectionState()).isEqualTo(STATE_CONNECTED);
+    }
+
+    private void sendAndDispatchMessage(int what) {
+        mStateMachine.sendMessage(what);
+        mLooper.dispatchAll();
+    }
+
+    @SafeVarargs
+    private void verifyIntentSent(Matcher<Intent>... matchers) {
+        mInOrder.verify(mService)
+                .sendBroadcastWithMultiplePermissions(
+                        MockitoHamcrest.argThat(AllOf.allOf(matchers)), any());
+    }
+
+    private void verifyConnectionStateIntent(int newState, int oldState) {
+        verifyIntentSent(
+                hasAction(ACTION_HAP_CONNECTION_STATE_CHANGED),
+                hasExtra(BluetoothDevice.EXTRA_DEVICE, mDevice),
+                hasExtra(EXTRA_STATE, newState),
+                hasExtra(EXTRA_PREVIOUS_STATE, oldState));
+        assertThat(mStateMachine.getConnectionState()).isEqualTo(newState);
+    }
+
+    private void generateConnectionMessageFromNative(int newState, int oldState) {
+        HapClientStackEvent event = new HapClientStackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.device = mDevice;
+        event.valueInt1 = newState;
+
+        mStateMachine.sendMessage(MESSAGE_STACK_EVENT, event);
+        mLooper.dispatchAll();
+
+        verifyConnectionStateIntent(newState, oldState);
+    }
+
+    private void generateUnexpectedConnectionMessageFromNative(int newConnectionState) {
+        HapClientStackEvent event = new HapClientStackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.device = mDevice;
+        event.valueInt1 = newConnectionState;
+
+        mStateMachine.sendMessage(MESSAGE_STACK_EVENT, event);
+        mLooper.dispatchAll();
+
+        mInOrder.verify(mService, never()).sendBroadcast(any(), any(), any());
     }
 }
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 e713084..a438a8e 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
@@ -1924,6 +1924,14 @@
     }
 
     @Test
+    public void testCheckAndProcessAndroidAt_handleConnectingTimePolicyNotAllowed() {
+        when(mHeadsetService.getActiveDevice()).thenReturn(mTestDevice);
+        mHeadsetStateMachine.checkAndProcessAndroidAt(
+                "+ANDROID=SINKAUDIOPOLICY,0,2,2", mTestDevice);
+        verify(mHeadsetService).setActiveDevice(null);
+    }
+
+    @Test
     public void testCheckAndProcessAndroidAt_replyAndroidAtFeatureRequest() {
         // Commands that will be handled
         Assert.assertTrue(mHeadsetStateMachine.checkAndProcessAndroidAt("+ANDROID=?", mTestDevice));
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java
index c70d425..4ae3df5 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/AppScanStatsTest.java
@@ -112,7 +112,8 @@
         boolean isCallbackScan = false;
         int scannerId = 0;
 
-        appScanStats.recordScanStart(settings, filters, isFilterScan, isCallbackScan, scannerId);
+        appScanStats.recordScanStart(
+                settings, filters, isFilterScan, isCallbackScan, scannerId, "tag");
         appScanStats.isRegistered = true;
 
         StringBuilder stringBuilder = new StringBuilder();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java
index a6692a3..b33a9c4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java
@@ -291,7 +291,7 @@
 
         ScanClient client = new ScanClient(id, scanSettings, scanFilterList, appUid);
         client.stats = appScanStats;
-        client.stats.recordScanStart(scanSettings, scanFilterList, isFiltered, false, id);
+        client.stats.recordScanStart(scanSettings, scanFilterList, isFiltered, false, id, null);
         return client;
     }
 
@@ -389,7 +389,7 @@
 
         ScanClient client = new ScanClient(id, scanSettings, scanFilterList);
         client.stats = mMockAppScanStats;
-        client.stats.recordScanStart(scanSettings, scanFilterList, isFiltered, false, id);
+        client.stats.recordScanStart(scanSettings, scanFilterList, isFiltered, false, id, null);
         return client;
     }
 
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java
index 3176c78..a614fba 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/TransitionalScanHelperTest.java
@@ -174,7 +174,7 @@
         mScanHelper.continuePiStartScan(scannerId, mApp);
 
         verify(appScanStats)
-                .recordScanStart(mPiInfo.settings, mPiInfo.filters, false, false, scannerId);
+                .recordScanStart(mPiInfo.settings, mPiInfo.filters, false, false, scannerId, null);
         verify(mScanManager).startScan(any());
     }
 
@@ -192,7 +192,7 @@
         mScanHelper.continuePiStartScan(scannerId, mApp);
 
         verify(appScanStats)
-                .recordScanStart(mPiInfo.settings, mPiInfo.filters, false, false, scannerId);
+                .recordScanStart(mPiInfo.settings, mPiInfo.filters, false, false, scannerId, null);
         verify(mScanManager)
                 .startScan(
                         argThat(
diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlInputDescriptorTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlInputDescriptorTest.java
index 61db244..0811f4e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlInputDescriptorTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlInputDescriptorTest.java
@@ -19,167 +19,161 @@
 
 import static org.mockito.Mockito.*;
 
-import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
 
-import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.flags.Flags;
+import bluetooth.constants.aics.Mute;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@MediumTest
+@SmallTest
 @RunWith(AndroidJUnit4.class)
 public class VolumeControlInputDescriptorTest {
+    private static final int NUMBER_OF_INPUT = 3;
+    private static final int NUMBER_OF_FIELD_IN_STRUCT = 9;
+    private static final int VALID_ID = 1;
+    private static final int INVALID_ID = NUMBER_OF_INPUT;
+    private static final int INVALID_ID2 = -1;
 
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
+    private VolumeControlInputDescriptor mDescriptor;
+
     @Before
-    public void setUp() throws Exception {
-        // placeholder
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        // placeholder
+    public void setUp() {
+        mDescriptor = new VolumeControlInputDescriptor(NUMBER_OF_INPUT);
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_LEAUDIO_ADD_AICS_SUPPORT)
-    public void testVolumeControlInputDescriptorInvalidIdOperations() throws Exception {
-        VolumeControlInputDescriptor descriptor = new VolumeControlInputDescriptor();
+    public void dump_noCrash() {
+        StringBuilder sb = new StringBuilder();
 
-        int invalidId = 1;
-        int testGainValue = 100;
-        int testGainMode = 1;
-        boolean testGainMute = true;
-        String testDesc = "testDescription";
-        int testType = VolumeControlInputDescriptor.AUDIO_INPUT_TYPE_AMBIENT;
-        int testGainSettingsMax = 100;
-        int testGainSettingsMin = 0;
-        int testGainSettingsUnit = 1;
-
-        // Test adding all props using invalid ID
-        assertThat(descriptor.isActive(invalidId)).isFalse();
-        assertThat(descriptor.setActive(invalidId, true)).isFalse();
-        assertThat(descriptor.setDescription(invalidId, testDesc)).isFalse();
-        assertThat(descriptor.getDescription(invalidId)).isNull();
-        assertThat(descriptor.setType(invalidId, testType)).isFalse();
-        assertThat(descriptor.getType(invalidId))
-                .isEqualTo(VolumeControlInputDescriptor.AUDIO_INPUT_TYPE_UNSPECIFIED);
-
-        assertThat(descriptor.getGain(invalidId)).isEqualTo(0);
-        assertThat(descriptor.isMuted(invalidId)).isFalse();
-        assertThat(
-                        descriptor.setPropSettings(
-                                invalidId,
-                                testGainSettingsUnit,
-                                testGainSettingsMin,
-                                testGainSettingsMax))
-                .isFalse();
-        assertThat(descriptor.setState(invalidId, testGainValue, testGainMode, testGainMute))
-                .isFalse();
+        mDescriptor.dump(sb);
+        int nLines = NUMBER_OF_INPUT * (NUMBER_OF_FIELD_IN_STRUCT + 1); // +1 for the id
+        assertThat(sb.toString()).containsMatch("(        .*\n){" + nLines + "}");
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_LEAUDIO_ADD_AICS_SUPPORT)
-    public void testVolumeControlInputDescriptorMultipleInstanceAdded() throws Exception {
-
-        VolumeControlInputDescriptor descriptor = new VolumeControlInputDescriptor();
-
-        int validId = 10;
-
-        // Verify that adding descriptor works increase descriptor size
-        assertThat(descriptor.size()).isEqualTo(0);
-        descriptor.add(validId);
-        assertThat(descriptor.size()).isEqualTo(1);
-
-        // Check if adding same id will not increase descriptor count.
-        descriptor.add(validId);
-        assertThat(descriptor.size()).isEqualTo(1);
+    public void setFoo_withAllValidId_valuesAreUpdated() {
+        for (int i = 0; i < NUMBER_OF_INPUT; i++) {
+            assertThat(mDescriptor.getStatus(i)).isEqualTo(0); // AudioInputStatus.INACTIVE);
+            mDescriptor.setStatus(i, 1); // AudioInputStatus.ACTIVE);
+            assertThat(mDescriptor.getStatus(i)).isEqualTo(1); // AudioInputStatus.ACTIVE);
+        }
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_LEAUDIO_ADD_AICS_SUPPORT)
-    public void testVolumeControlInputDescriptorInstanceRemoveAndClear() throws Exception {
-
-        VolumeControlInputDescriptor descriptor = new VolumeControlInputDescriptor();
-
-        int id_1 = 10;
-        int id_2 = 20;
-        int invalidId = 1;
-
-        // Verify that adding descriptor works increase descriptor size
-        assertThat(descriptor.size()).isEqualTo(0);
-        descriptor.add(id_1);
-        assertThat(descriptor.size()).isEqualTo(1);
-        descriptor.add(id_2);
-
-        // Remove valid id
-        descriptor.remove(id_1);
-        assertThat(descriptor.size()).isEqualTo(1);
-
-        // Remove invalid id not change number of descriptors
-        descriptor.remove(invalidId);
-        assertThat(descriptor.size()).isEqualTo(1);
-
-        // Check clear API
-        descriptor.clear();
-        assertThat(descriptor.size()).isEqualTo(0);
+    public void getStatus_whenNeverSet_defaultToInactive() {
+        assertThat(mDescriptor.getStatus(VALID_ID)).isEqualTo(0); // AudioInputStatus.INACTIVE);
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_LEAUDIO_ADD_AICS_SUPPORT)
-    public void testVolumeControlInputDescriptorAllValidApiCalls() throws Exception {
+    public void setStatus_withValidId_valueIsUpdated() {
+        int newStatus = 1; // AudioInputStatus.ACTIVE;
+        mDescriptor.setStatus(VALID_ID, newStatus);
 
-        VolumeControlInputDescriptor descriptor = new VolumeControlInputDescriptor();
+        assertThat(mDescriptor.getStatus(VALID_ID)).isEqualTo(newStatus);
+    }
 
-        int validId = 10;
-        int testGainValue = 100;
-        int testGainMode = 1;
-        boolean testGainMute = true;
-        String defaultDesc = "";
-        String testDesc = "testDescription";
-        int testType = VolumeControlInputDescriptor.AUDIO_INPUT_TYPE_AMBIENT;
-        int testGainSettingsMax = 100;
-        int testGainSettingsMin = 0;
-        int testGainSettingsUnit = 1;
+    @Test
+    public void setStatus_withInvalidId_valueIsNotUpdated() {
+        int newStatus = 1; // AudioInputStatus.ACTIVE;
+        mDescriptor.setStatus(INVALID_ID, newStatus);
 
-        descriptor.add(validId);
+        assertThat(mDescriptor.getStatus(INVALID_ID)).isNotEqualTo(newStatus);
+    }
 
-        // Active state
-        assertThat(descriptor.isActive(validId)).isFalse();
-        assertThat(descriptor.setActive(validId, true)).isTrue();
-        assertThat(descriptor.isActive(validId)).isTrue();
+    @Test
+    public void getType_whenNeverSet_defaultToUnspecified() {
+        assertThat(mDescriptor.getType(VALID_ID)).isEqualTo(0); // AudioInputType.UNSPECIFIED);
+    }
 
-        // Descriptor
-        assertThat(descriptor.getDescription(validId)).isEqualTo(defaultDesc);
-        assertThat(descriptor.setDescription(validId, testDesc)).isTrue();
-        assertThat(descriptor.getDescription(validId)).isEqualTo(testDesc);
+    @Test
+    public void setType_withValidId_valueIsUpdated() {
+        int newType = 7; // AudioInputType.AMBIENT;
+        mDescriptor.setType(VALID_ID, newType);
 
-        // Type
-        assertThat(descriptor.getType(validId))
-                .isEqualTo(VolumeControlInputDescriptor.AUDIO_INPUT_TYPE_UNSPECIFIED);
-        assertThat(descriptor.setType(validId, testType)).isTrue();
-        assertThat(descriptor.getType(validId)).isEqualTo(testType);
+        assertThat(mDescriptor.getType(VALID_ID)).isEqualTo(newType);
+    }
 
-        // Properties
-        assertThat(
-                        descriptor.setPropSettings(
-                                validId,
-                                testGainSettingsUnit,
-                                testGainSettingsMin,
-                                testGainSettingsMax))
-                .isTrue();
+    @Test
+    public void setType_withInvalidId_valueIsNotUpdated() {
+        int newType = 1; // AudioInputType.BLUETOOTH;
+        mDescriptor.setType(INVALID_ID2, newType);
 
-        // State
-        assertThat(descriptor.setState(validId, testGainValue, testGainMode, testGainMute))
-                .isTrue();
-        assertThat(descriptor.getGain(validId)).isEqualTo(testGainValue);
+        assertThat(mDescriptor.getType(INVALID_ID2)).isNotEqualTo(newType);
+    }
+
+    @Test
+    public void setState_withValidIdButIncorrectSettings_valueIsNotUpdated() {
+        int newGainValue = 42;
+        int newGainMode = 42;
+        int mute = Mute.NOT_MUTED;
+        mDescriptor.setState(VALID_ID, newGainMode, newGainMode, mute);
+
+        assertThat(mDescriptor.getGainSetting(VALID_ID)).isNotEqualTo(newGainValue);
+        // assertThat(mDescriptor.getGainMode(VALID_ID)).isNotEqualTo(newGainMode);
+        assertThat(mDescriptor.getMute(VALID_ID)).isNotEqualTo(mute);
+    }
+
+    @Test
+    public void setState_withValidIdAndCorrectSettings_valueIsUpdated() {
+        int newMax = 100;
+        int newMin = 0;
+        int newUnit = 1;
+        mDescriptor.setPropSettings(VALID_ID, newUnit, newMin, newMax);
+
+        int newGainValue = 42;
+        int newGainMode = 42;
+        int mute = Mute.MUTED;
+        mDescriptor.setState(VALID_ID, newGainMode, mute, newGainMode);
+
+        assertThat(mDescriptor.getGainSetting(VALID_ID)).isEqualTo(newGainValue);
+        // assertThat(mDescriptor.getGainMode(VALID_ID)).isNotEqualTo(newGainMode);
+        assertThat(mDescriptor.getMute(VALID_ID)).isEqualTo(mute);
+    }
+
+    @Test
+    public void setState_withInvalidId_valueIsNotUpdated() {
+        int newMax = 100;
+        int newMin = 0;
+        int newUnit = 1;
+        // Should be no-op but we want to copy the working case test, just with an invalid id
+        mDescriptor.setPropSettings(INVALID_ID, newUnit, newMin, newMax);
+
+        int newGainValue = 42;
+        int newGainMode = 42;
+        int mute = Mute.MUTED;
+        mDescriptor.setState(INVALID_ID, newGainMode, newGainMode, mute);
+
+        assertThat(mDescriptor.getGainSetting(INVALID_ID)).isNotEqualTo(newGainValue);
+        // assertThat(mDescriptor.getGainMode(VALID_ID)).isNotEqualTo(newGainMode);
+        assertThat(mDescriptor.getMute(INVALID_ID)).isEqualTo(Mute.DISABLED);
+    }
+
+    @Test
+    public void getDescription_whenNeverSet_defaultToEmptyString() {
+        assertThat(mDescriptor.getDescription(VALID_ID)).isEmpty();
+    }
+
+    @Test
+    public void setDescription_withValidId_valueIsUpdated() {
+        String newDescription = "what a nice description";
+        mDescriptor.setDescription(VALID_ID, newDescription);
+
+        assertThat(mDescriptor.getDescription(VALID_ID)).isEqualTo(newDescription);
+    }
+
+    @Test
+    public void setDescription_withInvalidId_valueIsNotUpdated() {
+        String newDescription = "what a nice description";
+        mDescriptor.setDescription(INVALID_ID, newDescription);
+
+        assertThat(mDescriptor.getDescription(INVALID_ID)).isNotEqualTo(newDescription);
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlNativeCallbackTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlNativeCallbackTest.java
new file mode 100644
index 0000000..51b5f57
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlNativeCallbackTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2024 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.vc;
+
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED;
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_DEVICE_AVAILABLE;
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED;
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED;
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED;
+import static com.android.bluetooth.vc.VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothProfile;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.btservice.AdapterService;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(AndroidJUnit4.class)
+public class VolumeControlNativeCallbackTest {
+    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
+    @Rule public Expect expect = Expect.create();
+
+    @Mock private AdapterService mAdapterService;
+    @Mock private VolumeControlService mService;
+    @Captor private ArgumentCaptor<VolumeControlStackEvent> mEvent;
+
+    private VolumeControlNativeCallback mNativeCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        doReturn(true).when(mService).isAvailable();
+
+        mNativeCallback = new VolumeControlNativeCallback(mAdapterService, mService);
+    }
+
+    @Test
+    public void onConnectionStateChanged() {
+        int state = BluetoothProfile.STATE_CONNECTED;
+
+        mNativeCallback.onConnectionStateChanged(state, null);
+        verify(mService).messageFromNative(mEvent.capture());
+        VolumeControlStackEvent event = mEvent.getValue();
+
+        expect.that(event.type).isEqualTo(EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        expect.that(event.valueInt1).isEqualTo(state);
+    }
+
+    @Test
+    public void onVolumeStateChanged() {
+        int volume = 3;
+        boolean mute = false;
+        int flags = 1;
+        boolean isAutonomous = false;
+
+        mNativeCallback.onVolumeStateChanged(volume, mute, flags, null, isAutonomous);
+        verify(mService).messageFromNative(mEvent.capture());
+        VolumeControlStackEvent event = mEvent.getValue();
+
+        expect.that(event.type).isEqualTo(EVENT_TYPE_VOLUME_STATE_CHANGED);
+    }
+
+    @Test
+    public void onGroupVolumeStateChanged() {
+        int volume = 3;
+        boolean mute = false;
+        int groupId = 1;
+        boolean isAutonomous = false;
+
+        mNativeCallback.onGroupVolumeStateChanged(volume, mute, groupId, isAutonomous);
+        verify(mService).messageFromNative(mEvent.capture());
+        VolumeControlStackEvent event = mEvent.getValue();
+
+        expect.that(event.type).isEqualTo(EVENT_TYPE_VOLUME_STATE_CHANGED);
+        expect.that(event.valueInt1).isEqualTo(groupId);
+        expect.that(event.valueInt2).isEqualTo(volume);
+        expect.that(event.valueBool1).isEqualTo(mute);
+        expect.that(event.valueBool2).isEqualTo(isAutonomous);
+    }
+
+    @Test
+    public void onDeviceAvailable() {
+        int numOfExternalOutputs = 3;
+        int numOfExternalInputs = 0;
+
+        mNativeCallback.onDeviceAvailable(numOfExternalOutputs, numOfExternalInputs, null);
+        verify(mService).messageFromNative(mEvent.capture());
+        VolumeControlStackEvent event = mEvent.getValue();
+
+        expect.that(event.type).isEqualTo(EVENT_TYPE_DEVICE_AVAILABLE);
+    }
+
+    @Test
+    public void onExtAudioOutVolumeOffsetChanged() {
+        int externalOutputId = 2;
+        int offset = 0;
+
+        mNativeCallback.onExtAudioOutVolumeOffsetChanged(externalOutputId, offset, null);
+        verify(mService).messageFromNative(mEvent.capture());
+        VolumeControlStackEvent event = mEvent.getValue();
+
+        expect.that(event.type).isEqualTo(EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED);
+    }
+
+    @Test
+    public void onExtAudioOutLocationChanged() {
+        int externalOutputId = 2;
+        int location = 100;
+
+        mNativeCallback.onExtAudioOutLocationChanged(externalOutputId, location, null);
+        verify(mService).messageFromNative(mEvent.capture());
+        VolumeControlStackEvent event = mEvent.getValue();
+
+        expect.that(event.type).isEqualTo(EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED);
+    }
+
+    @Test
+    public void onExtAudioOutDescriptionChanged() {
+        int externalOutputId = 2;
+        String descr = "test-descr";
+
+        mNativeCallback.onExtAudioOutDescriptionChanged(externalOutputId, descr, null);
+        verify(mService).messageFromNative(mEvent.capture());
+        VolumeControlStackEvent event = mEvent.getValue();
+
+        expect.that(event.type).isEqualTo(EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED);
+    }
+
+    @Test
+    public void onExtAudioInStateChanged() {
+        int id = 2;
+        int gainSetting = 1;
+        int gainMode = 0;
+        int mute = 0;
+
+        mNativeCallback.onExtAudioInStateChanged(id, gainSetting, mute, gainMode, null);
+        verify(mService)
+                .onExtAudioInStateChanged(any(), eq(id), eq(gainSetting), eq(mute), eq(gainMode));
+    }
+
+    @Test
+    public void onExtAudioInStatusChanged() {
+        int id = 2;
+        int status = 1;
+
+        mNativeCallback.onExtAudioInStatusChanged(id, status, null);
+        verify(mService).onExtAudioInStatusChanged(any(), eq(id), eq(status));
+    }
+
+    @Test
+    public void onExtAudioInTypeChanged() {
+        int id = 2;
+        int type = 1;
+
+        mNativeCallback.onExtAudioInTypeChanged(id, type, null);
+        verify(mService).onExtAudioInTypeChanged(any(), eq(id), eq(type));
+    }
+
+    @Test
+    public void onExtAudioInDescriptionChanged() {
+        int id = 2;
+        String descr = "microphone";
+
+        mNativeCallback.onExtAudioInDescriptionChanged(id, descr, null);
+        verify(mService).onExtAudioInDescriptionChanged(any(), eq(id), eq(descr));
+    }
+
+    @Test
+    public void onExtAudioInGainPropsChanged() {
+        int id = 2;
+        int unit = 1;
+        int min = 0;
+        int max = 100;
+
+        mNativeCallback.onExtAudioInGainPropsChanged(id, unit, min, max, null);
+        verify(mService).onExtAudioInGainPropsChanged(any(), eq(id), eq(unit), eq(min), eq(max));
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlNativeInterfaceTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlNativeInterfaceTest.java
deleted file mode 100644
index 9b0aa48..0000000
--- a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlNativeInterfaceTest.java
+++ /dev/null
@@ -1,245 +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.
- */
-
-package com.android.bluetooth.vc;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.bluetooth.BluetoothProfile;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-@RunWith(AndroidJUnit4.class)
-public class VolumeControlNativeInterfaceTest {
-    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
-
-    @Mock private VolumeControlService mService;
-
-    private VolumeControlNativeInterface mNativeInterface;
-
-    @Before
-    public void setUp() throws Exception {
-        when(mService.isAvailable()).thenReturn(true);
-        VolumeControlService.setVolumeControlService(mService);
-        mNativeInterface = VolumeControlNativeInterface.getInstance();
-    }
-
-    @After
-    public void tearDown() {
-        VolumeControlService.setVolumeControlService(null);
-    }
-
-    @Test
-    public void onConnectionStateChanged() {
-        int state = BluetoothProfile.STATE_CONNECTED;
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-
-        mNativeInterface.onConnectionStateChanged(state, address);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
-    }
-
-    @Test
-    public void onVolumeStateChanged() {
-        int volume = 3;
-        boolean mute = false;
-        int flags = 1;
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-        boolean isAutonomous = false;
-
-        mNativeInterface.onVolumeStateChanged(volume, mute, flags, address, isAutonomous);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
-    }
-
-    @Test
-    public void onGroupVolumeStateChanged() {
-        int volume = 3;
-        boolean mute = false;
-        int groupId = 1;
-        boolean isAutonomous = false;
-
-        mNativeInterface.onGroupVolumeStateChanged(volume, mute, groupId, isAutonomous);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
-        assertThat(event.getValue().valueInt1).isEqualTo(groupId);
-    }
-
-    @Test
-    public void onDeviceAvailable() {
-        int numOfExternalOutputs = 3;
-        int numOfExternalInputs = 0;
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-
-        mNativeInterface.onDeviceAvailable(numOfExternalOutputs, numOfExternalInputs, address);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
-    }
-
-    @Test
-    public void onExtAudioOutVolumeOffsetChanged() {
-        int externalOutputId = 2;
-        int offset = 0;
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-
-        mNativeInterface.onExtAudioOutVolumeOffsetChanged(externalOutputId, offset, address);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED);
-    }
-
-    @Test
-    public void onExtAudioOutLocationChanged() {
-        int externalOutputId = 2;
-        int location = 100;
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-
-        mNativeInterface.onExtAudioOutLocationChanged(externalOutputId, location, address);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED);
-    }
-
-    @Test
-    public void onExtAudioOutDescriptionChanged() {
-        int externalOutputId = 2;
-        String descr = "test-descr";
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-
-        mNativeInterface.onExtAudioOutDescriptionChanged(externalOutputId, descr, address);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED);
-    }
-
-    @Test
-    public void onExtAudioInStateChanged() {
-        int externalInputId = 2;
-        int gainValue = 1;
-        int gainMode = 0;
-        boolean mute = false;
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-
-        mNativeInterface.onExtAudioInStateChanged(
-                externalInputId, gainValue, gainMode, mute, address);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_STATE_CHANGED);
-    }
-
-    @Test
-    public void onExtAudioInStatusChanged() {
-        int externalInputId = 2;
-        int status = 1;
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-
-        mNativeInterface.onExtAudioInStatusChanged(externalInputId, status, address);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_STATUS_CHANGED);
-    }
-
-    @Test
-    public void onExtAudioInTypeChanged() {
-        int externalInputId = 2;
-        int type = 1;
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-
-        mNativeInterface.onExtAudioInTypeChanged(externalInputId, type, address);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_TYPE_CHANGED);
-    }
-
-    @Test
-    public void onExtAudioInDescriptionChanged() {
-        int externalInputId = 2;
-        String descr = "microphone";
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-
-        mNativeInterface.onExtAudioInDescriptionChanged(externalInputId, descr, address);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_DESCR_CHANGED);
-    }
-
-    @Test
-    public void onExtAudioInGainPropsChanged() {
-        int externalInputId = 2;
-        int unit = 1;
-        int min = 0;
-        int max = 100;
-        byte[] address = new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
-
-        mNativeInterface.onExtAudioInGainPropsChanged(externalInputId, unit, min, max, address);
-
-        ArgumentCaptor<VolumeControlStackEvent> event =
-                ArgumentCaptor.forClass(VolumeControlStackEvent.class);
-        verify(mService).messageFromNative(event.capture());
-        assertThat(event.getValue().type)
-                .isEqualTo(VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_IN_GAIN_PROPS_CHANGED);
-    }
-}
diff --git a/android/pandora/OWNERS b/android/pandora/OWNERS
index ffae7f1..c199a82 100644
--- a/android/pandora/OWNERS
+++ b/android/pandora/OWNERS
@@ -1,3 +1,2 @@
 # Bug component: 1099313
 girardier@google.com
-charliebout@google.com
diff --git a/android/pandora/mmi2grpc/mmi2grpc/vcp.py b/android/pandora/mmi2grpc/mmi2grpc/vcp.py
index fa869ab..95a1a06 100644
--- a/android/pandora/mmi2grpc/mmi2grpc/vcp.py
+++ b/android/pandora/mmi2grpc/mmi2grpc/vcp.py
@@ -145,33 +145,32 @@
         # After discovery Android subscribes by itself, after profile connection
         return "OK"
 
-    def IUT_SEND_WRITE_REQUEST(self, description: str, **kwargs):
+    @match_description
+    def IUT_SEND_WRITE_REQUEST(self, description: str, chr_name: str, op_code: str, **kwargs):
         r"""
-        Please send write request to handle 0xXXXX with following value.
-        Characteristic name:
-            Op Code: [X (0xXX)] Op code name
-            Change Counter: <WildCard: Exists>
-            Value: <WildCard: Exists>
+        Please send write request to handle 0x([0-9A-Fa-f]{4}) with following value.
+        (?P<chr_name>(Volume Control Point|Volume Offset Control Point)):
+            Op Code: (?P<op_code>((<WildCard: Exists>)|(\[[0-9] \(0x0[0-9]\)\]\s([\w]*\s){1,3})))(.*)
         """
 
         # Wait a couple seconds so the VCP is ready (subscriptions and reads are completed)
         sleep(2)
 
-        if ("Set Absolute Volume" in description):
-            self.vcp.SetDeviceVolume(connection=self.connection, volume=42)
-        elif ("Unmute" in description):
-            # for now, there is no way to trigger this, and tests are skipped
+        if (chr_name == "Volume Control Point"):
+            if "Set Absolute Volume" in op_code:
+                self.vcp.SetDeviceVolume(connection=self.connection, volume=42)
+            elif ("Unmute" in op_code):
+                # for now, there is no way to trigger this, and tests are skipped
+                return "No"
+            elif ("<WildCard: Exists>" in op_code):
+                # Handles sending *any* OP Code on Volume Control Point
+                self.vcp.SetDeviceVolume(connection=self.connection, volume=42)
+        elif (chr_name == "Volume Offset Control Point"):
+            if ("Set Volume Offset" in op_code or
+                "<WildCard: Exists>" in op_code):
+                self.vcp.SetVolumeOffset(connection=self.connection, offset=42)
+        else:
             return "No"
-        elif ("Set Volume Offset" in description):
-            self.vcp.SetVolumeOffset(connection=self.connection, offset=42)
-        elif ("Volume Control Point" in description and
-              "Op Code: <WildCard: Exists>" in description):
-            # Handles sending *any* OP Code on Volume Control Point
-            self.vcp.SetDeviceVolume(connection=self.connection, volume=42)
-        elif ("Volume Offset Control Point" in description and
-              "Op Code: <WildCard: Exists>" in description):
-            self.vcp.SetVolumeOffset(connection=self.connection, offset=42)
-
 
         return "OK"
 
diff --git a/android/pandora/test/OWNERS b/android/pandora/test/OWNERS
deleted file mode 100644
index ffae7f1..0000000
--- a/android/pandora/test/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 1099313
-girardier@google.com
-charliebout@google.com
diff --git a/common/Android.bp b/common/Android.bp
new file mode 100644
index 0000000..900cc36
--- /dev/null
+++ b/common/Android.bp
@@ -0,0 +1,29 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_library {
+    name: "bluetooth_constants",
+    srcs: [
+        "bluetooth/constants/AudioInputType.aidl",
+        "bluetooth/constants/aics/AudioInputStatus.aidl",
+        "bluetooth/constants/aics/GainMode.aidl",
+        "bluetooth/constants/aics/Mute.aidl",
+    ],
+    visibility: ["//packages/modules/Bluetooth:__subpackages__"],
+}
+
+// other java component doesn't know how to depend on an aidl_library
+java_library {
+    name: "bluetooth_constants_java",
+    srcs: [
+        "bluetooth/constants/AudioInputType.aidl",
+        "bluetooth/constants/aics/AudioInputStatus.aidl",
+        "bluetooth/constants/aics/GainMode.aidl",
+        "bluetooth/constants/aics/Mute.aidl",
+    ],
+    apex_available: ["com.android.btservices"],
+    min_sdk_version: "Tiramisu",
+    sdk_version: "module_current",
+    visibility: ["//packages/modules/Bluetooth:__subpackages__"],
+}
diff --git a/common/OWNERS b/common/OWNERS
new file mode 100644
index 0000000..5b3f4ba
--- /dev/null
+++ b/common/OWNERS
@@ -0,0 +1 @@
+wescande@google.com
diff --git a/common/bluetooth/constants/AudioInputType.aidl b/common/bluetooth/constants/AudioInputType.aidl
new file mode 100644
index 0000000..2dfef1e
--- /dev/null
+++ b/common/bluetooth/constants/AudioInputType.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2024 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 bluetooth.constants;
+
+/**
+ * See Bluetooth SIG Assigned Numbers 6.12.2 Audio Input Type Definitions
+ * {@hide}
+ */
+@JavaDerive(toString = true)
+@Backing(type="int")
+enum AudioInputType {
+    UNSPECIFIED = 0x00,
+    BLUETOOTH = 0x01,
+    MICROPHONE = 0x02,
+    ANALOG = 0x03,
+    DIGITAL = 0x04,
+    RADIO = 0x05,
+    STREAMING = 0x06,
+    AMBIENT = 0x07,
+}
diff --git a/common/bluetooth/constants/aics/AudioInputStatus.aidl b/common/bluetooth/constants/aics/AudioInputStatus.aidl
new file mode 100644
index 0000000..b217e65
--- /dev/null
+++ b/common/bluetooth/constants/aics/AudioInputStatus.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 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 bluetooth.constants.aics;
+
+/**
+ * See Audio Input Control Service 1.0 - 3.4 Audio Input Status
+ * {@hide}
+ */
+@JavaDerive(toString = true)
+@Backing(type="byte")
+enum AudioInputStatus {
+    INACTIVE = 0x00,
+    ACTIVE = 0x01,
+    // RFU 0x02 - 0xFF
+}
diff --git a/common/bluetooth/constants/aics/GainMode.aidl b/common/bluetooth/constants/aics/GainMode.aidl
new file mode 100644
index 0000000..2f3516a
--- /dev/null
+++ b/common/bluetooth/constants/aics/GainMode.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2024 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 bluetooth.constants.aics;
+
+/**
+ * See Audio Input Control Service 1.0 - 2.2.1.3. Gain_Mode field
+ * The Gain_Mode field shall be set to a value that reflects whether gain modes are manual
+ * or automatic.
+ * - Manual Only, the server allows only manual gain.
+ * - Automatic Only, the server allows only automatic gain.
+ *
+ * For all other Gain_Mode field values, the server allows switchable automatic/manual gain.
+ * {@hide}
+ */
+@JavaDerive(toString = true)
+@Backing(type="byte")
+enum GainMode {
+    MANUAL_ONLY = 0x00,
+    AUTOMATIC_ONLY = 0x01,
+    MANUAL = 0x02,
+    AUTOMATIC = 0x03,
+}
diff --git a/common/bluetooth/constants/aics/Mute.aidl b/common/bluetooth/constants/aics/Mute.aidl
new file mode 100644
index 0000000..ce2af50
--- /dev/null
+++ b/common/bluetooth/constants/aics/Mute.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2024 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 bluetooth.constants.aics;
+
+/**
+ * See Audio Input Control Service 1.0 - 2.2.1.2. Mute field
+ * {@hide}
+ */
+@JavaDerive(toString = true)
+@Backing(type="byte")
+enum Mute {
+    NOT_MUTED = 0x00,
+    MUTED = 0x01,
+    DISABLED = 0x02,
+}
diff --git a/flags/Android.bp b/flags/Android.bp
index d5f0c1f..f9b8426 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -43,6 +43,7 @@
         "metric.aconfig",
         "opp.aconfig",
         "pairing.aconfig",
+        "pbapclient.aconfig",
         "ranging.aconfig",
         "rfcomm.aconfig",
         "rnr.aconfig",
diff --git a/flags/BUILD.gn b/flags/BUILD.gn
index ba23368..6f98c23 100644
--- a/flags/BUILD.gn
+++ b/flags/BUILD.gn
@@ -34,6 +34,7 @@
     "metric.aconfig",
     "opp.aconfig",
     "pairing.aconfig",
+    "pbapclient.aconfig",
     "ranging.aconfig",
     "rfcomm.aconfig",
     "rnr.aconfig",
diff --git a/flags/avrcp_controller.aconfig b/flags/avrcp_controller.aconfig
index de205eb..4374eef 100644
--- a/flags/avrcp_controller.aconfig
+++ b/flags/avrcp_controller.aconfig
@@ -2,16 +2,6 @@
 container: "com.android.btservices"
 
 flag {
-    name: "randomize_device_level_media_ids"
-    namespace: "bluetooth"
-    description: "Randomize the media id of device level nodes in our browse tree by attaching a randomized string after the UUID, each time a device connects"
-    bug: "332367017"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "uncache_player_when_browsed_player_changes"
     namespace: "bluetooth"
     description: "Uncache media player items when changing to a new player so updated player contents can be fetched if we change back to the previous player"
diff --git a/flags/framework.aconfig b/flags/framework.aconfig
index 32b88d4..255d3a7 100644
--- a/flags/framework.aconfig
+++ b/flags/framework.aconfig
@@ -80,4 +80,12 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
-}
\ No newline at end of file
+}
+
+flag {
+    name: "identity_address_type_api"
+    is_exported: true
+    namespace: "bluetooth"
+    description: "Add a new API to BluetoothDevice to retrieve Identity Address Type"
+    bug: "377171798"
+}
diff --git a/flags/pbapclient.aconfig b/flags/pbapclient.aconfig
new file mode 100644
index 0000000..dbbbb77
--- /dev/null
+++ b/flags/pbapclient.aconfig
@@ -0,0 +1,16 @@
+package: "com.android.bluetooth.flags"
+container: "com.android.btservices"
+
+flag {
+    name: "pbap_client_storage_refactor"
+    namespace: "bluetooth"
+    description: "Abstract away accounts/storage and change how contacts are passed and stored"
+    bug: "376461939"
+}
+
+flag {
+    name: "pbap_client_contacts_caching"
+    namespace: "bluetooth"
+    description: "Use primary and secondary versions to persist contacts across connections"
+    bug: "376461947"
+}
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index ebc0542..cd02d18 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -597,6 +597,7 @@
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
     method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[]);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getMaximumSourceCapacity(@NonNull android.bluetooth.BluetoothDevice);
+    method @FlaggedApi("com.android.bluetooth.flags.leaudio_broadcast_api_get_local_metadata") @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothLeBroadcastMetadata getSourceMetadata(@NonNull android.bluetooth.BluetoothDevice, @IntRange(from=0, to=255) int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isSearchInProgress();
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void modifySource(@NonNull android.bluetooth.BluetoothDevice, int, @NonNull android.bluetooth.BluetoothLeBroadcastMetadata);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothLeBroadcastAssistant.Callback);
diff --git a/framework/java/android/bluetooth/BluetoothA2dpSink.java b/framework/java/android/bluetooth/BluetoothA2dpSink.java
index 166b4a4..ecaa9d3 100644
--- a/framework/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/framework/java/android/bluetooth/BluetoothA2dpSink.java
@@ -138,11 +138,7 @@
      * @hide
      */
     @RequiresBluetoothConnectPermission
-    @RequiresPermission(
-            allOf = {
-                BLUETOOTH_CONNECT,
-                BLUETOOTH_PRIVILEGED,
-            })
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
     public boolean connect(BluetoothDevice device) {
         if (DBG) log("connect(" + device + ")");
         final IBluetoothA2dpSink service = getService();
@@ -311,11 +307,7 @@
      * @hide
      */
     @RequiresBluetoothConnectPermission
-    @RequiresPermission(
-            allOf = {
-                BLUETOOTH_CONNECT,
-                BLUETOOTH_PRIVILEGED,
-            })
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
     public boolean setPriority(BluetoothDevice device, int priority) {
         if (DBG) log("setPriority(" + device + ", " + priority + ")");
         return setConnectionPolicy(device, BluetoothAdapter.priorityToConnectionPolicy(priority));
@@ -367,11 +359,7 @@
      * @hide
      */
     @RequiresBluetoothConnectPermission
-    @RequiresPermission(
-            allOf = {
-                BLUETOOTH_CONNECT,
-                BLUETOOTH_PRIVILEGED,
-            })
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
     public int getPriority(BluetoothDevice device) {
         if (VDBG) log("getPriority(" + device + ")");
         return BluetoothAdapter.connectionPolicyToPriority(getConnectionPolicy(device));
@@ -389,11 +377,7 @@
      */
     @SystemApi
     @RequiresBluetoothConnectPermission
-    @RequiresPermission(
-            allOf = {
-                BLUETOOTH_CONNECT,
-                BLUETOOTH_PRIVILEGED,
-            })
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
     public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothA2dpSink service = getService();
@@ -419,11 +403,7 @@
      */
     @SystemApi
     @RequiresBluetoothConnectPermission
-    @RequiresPermission(
-            allOf = {
-                BLUETOOTH_CONNECT,
-                BLUETOOTH_PRIVILEGED,
-            })
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
     public boolean isAudioPlaying(@NonNull BluetoothDevice device) {
         if (VDBG) log("isAudioPlaying(" + device + ")");
         final IBluetoothA2dpSink service = getService();
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java
index 3477bed..ebbe975 100644
--- a/framework/java/android/bluetooth/BluetoothAdapter.java
+++ b/framework/java/android/bluetooth/BluetoothAdapter.java
@@ -1092,12 +1092,6 @@
     BluetoothAdapter(IBluetoothManager managerService, AttributionSource attributionSource) {
         mManagerService = requireNonNull(managerService);
         mAttributionSource = requireNonNull(attributionSource);
-        mServiceLock.writeLock().lock();
-        try {
-            mService = getBluetoothService(mManagerCallback);
-        } finally {
-            mServiceLock.writeLock().unlock();
-        }
 
         Consumer<IBluetooth> registerQualityReportCallbackConsumer =
                 (IBluetooth service) -> {
@@ -1165,6 +1159,13 @@
                 new CallbackWrapper(
                         registerBluetoothConnectionCallbackConsumer,
                         unregisterBluetoothConnectionCallbackConsumer);
+
+        mServiceLock.writeLock().lock();
+        try {
+            mService = registerBlueoothManagerCallback(mManagerCallback);
+        } finally {
+            mServiceLock.writeLock().unlock();
+        }
     }
 
     /**
@@ -4211,15 +4212,8 @@
         }
     }
 
-    /**
-     * Registers a IBluetoothManagerCallback and returns the cached Bluetooth service proxy object.
-     *
-     * <p>TODO: rename this API to registerBlueoothManagerCallback or something? the current name
-     * does not match what it does very well.
-     *
-     * <p>/ @UnsupportedAppUsage /*package
-     */
-    IBluetooth getBluetoothService(IBluetoothManagerCallback cb) {
+    /** Registers a IBluetoothManagerCallback and returns the cached service proxy object. */
+    IBluetooth registerBlueoothManagerCallback(IBluetoothManagerCallback cb) {
         requireNonNull(cb);
         if (Flags.getProfileUseLock()) {
             sServiceLock.writeLock().lock();
diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java
index 92d0582..4631953 100644
--- a/framework/java/android/bluetooth/BluetoothGatt.java
+++ b/framework/java/android/bluetooth/BluetoothGatt.java
@@ -299,7 +299,8 @@
                                 TAG,
                                 "onPhyUpdate() -"
                                         + (" status=" + status)
-                                        + (" address=" + address)
+                                        + (" address="
+                                                + BluetoothUtils.toAnonymizedAddress(address))
                                         + (" txPhy=" + txPhy)
                                         + (" rxPhy=" + rxPhy));
                     }
@@ -332,7 +333,8 @@
                                 TAG,
                                 "onPhyRead() -"
                                         + (" status=" + status)
-                                        + (" address=" + address)
+                                        + (" address="
+                                                + BluetoothUtils.toAnonymizedAddress(address))
                                         + (" txPhy=" + txPhy)
                                         + (" rxPhy=" + rxPhy));
                     }
@@ -414,7 +416,12 @@
                 public void onSearchComplete(
                         String address, List<BluetoothGattService> services, int status) {
                     if (DBG) {
-                        Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
+                        Log.d(
+                                TAG,
+                                "onSearchComplete() = address="
+                                        + BluetoothUtils.toAnonymizedAddress(address)
+                                        + " status="
+                                        + status);
                     }
                     if (!address.equals(mDevice.getAddress())) {
                         return;
@@ -613,8 +620,14 @@
                  */
                 @Override
                 public void onNotify(String address, int handle, byte[] value) {
-                    if (VDBG) Log.d(TAG, "onNotify() - Device=" + address + " handle=" + handle);
-
+                    if (VDBG) {
+                        Log.d(
+                                TAG,
+                                "onNotify() - address="
+                                        + BluetoothUtils.toAnonymizedAddress(address)
+                                        + " handle="
+                                        + handle);
+                    }
                     if (!address.equals(mDevice.getAddress())) {
                         return;
                     }
@@ -646,7 +659,12 @@
                 @SuppressLint("AndroidFrameworkRequiresPermission")
                 public void onDescriptorRead(String address, int status, int handle, byte[] value) {
                     if (VDBG) {
-                        Log.d(TAG, "onDescriptorRead() - Device=" + address + " handle=" + handle);
+                        Log.d(
+                                TAG,
+                                "onDescriptorRead() - address="
+                                        + BluetoothUtils.toAnonymizedAddress(address)
+                                        + " handle="
+                                        + handle);
                     }
 
                     if (!address.equals(mDevice.getAddress())) {
@@ -703,7 +721,12 @@
                 public void onDescriptorWrite(
                         String address, int status, int handle, byte[] value) {
                     if (VDBG) {
-                        Log.d(TAG, "onDescriptorWrite() - Device=" + address + " handle=" + handle);
+                        Log.d(
+                                TAG,
+                                "onDescriptorWrite() - address="
+                                        + BluetoothUtils.toAnonymizedAddress(address)
+                                        + " handle="
+                                        + handle);
                     }
 
                     if (!address.equals(mDevice.getAddress())) {
@@ -757,7 +780,12 @@
                 @Override
                 public void onExecuteWrite(String address, int status) {
                     if (VDBG) {
-                        Log.d(TAG, "onExecuteWrite() - Device=" + address + " status=" + status);
+                        Log.d(
+                                TAG,
+                                "onExecuteWrite() - address="
+                                        + BluetoothUtils.toAnonymizedAddress(address)
+                                        + " status="
+                                        + status);
                     }
                     if (!address.equals(mDevice.getAddress())) {
                         return;
diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java
index 289b587..53b491e 100644
--- a/framework/java/android/bluetooth/BluetoothGattServer.java
+++ b/framework/java/android/bluetooth/BluetoothGattServer.java
@@ -357,7 +357,13 @@
                 @Override
                 public void onMtuChanged(String address, int mtu) {
                     if (DBG) {
-                        Log.d(TAG, "onMtuChanged() - " + "device=" + address + ", mtu=" + mtu);
+                        Log.d(
+                                TAG,
+                                "onMtuChanged() - "
+                                        + "device="
+                                        + BluetoothUtils.toAnonymizedAddress(address)
+                                        + ", mtu="
+                                        + mtu);
                     }
 
                     BluetoothDevice device = mAdapter.getRemoteDevice(address);
diff --git a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
index 28b4e77..829ee2f 100644
--- a/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
+++ b/framework/java/android/bluetooth/BluetoothLeBroadcastAssistant.java
@@ -21,7 +21,9 @@
 import static android.Manifest.permission.BLUETOOTH_SCAN;
 
 import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresNoPermission;
@@ -40,6 +42,8 @@
 import android.util.CloseGuard;
 import android.util.Log;
 
+import com.android.bluetooth.flags.Flags;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -1205,6 +1209,52 @@
         return defaultValue;
     }
 
+    /**
+     * Gets the {@link BluetoothLeBroadcastMetadata} of a specified source added to this Broadcast
+     * Sink.
+     *
+     * <p>This method retrieves the {@link BluetoothLeBroadcastMetadata} associated with the
+     * specified source. The metadata is obtained from the {@link BluetoothLeBroadcastReceiveState}
+     * maintained by this Broadcast Sink. If no matching metadata is found, this method returns
+     * {@code null}.
+     *
+     * <p>The source's {@link BluetoothLeBroadcastMetadata} is initially set by {@link
+     * #addSource(BluetoothDevice, BluetoothLeBroadcastMetadata, boolean)} and can be updated with
+     * {@link #modifySource(BluetoothDevice, int, BluetoothLeBroadcastMetadata)}.
+     *
+     * @param sink Broadcast Sink device
+     * @param sourceId Broadcast source id. Valid range is [0, 0xFF] as defined in the Broadcast
+     *     Audio Scan Service 1.0 specification (section 3.2).
+     * @return metadata {@link BluetoothLeBroadcastMetadata} associated with the specified source.
+     * @throws IllegalArgumentException if sourceID is not [0, 0xFF].
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_LEAUDIO_BROADCAST_API_GET_LOCAL_METADATA)
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED})
+    public @Nullable BluetoothLeBroadcastMetadata getSourceMetadata(
+            @NonNull BluetoothDevice sink, @IntRange(from = 0x00, to = 0xFF) int sourceId) {
+        log("getSourceMetadata()");
+        Objects.requireNonNull(sink, "sink cannot be null");
+        if (sourceId < 0x00 || sourceId > 0xFF) {
+            throw new IllegalArgumentException(
+                    "sourceId " + sourceId + " does not fall between 0x00 and 0xFF");
+        }
+        final IBluetoothLeBroadcastAssistant service = getService();
+        if (service == null) {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        } else if (mBluetoothAdapter.isEnabled()) {
+            try {
+                return service.getSourceMetadata(sink, sourceId, mAttributionSource);
+            } catch (RemoteException e) {
+                Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+            }
+        }
+        return null;
+    }
+
     private static void log(@NonNull String msg) {
         if (DBG) {
             Log.d(TAG, msg);
diff --git a/framework/tests/bumble/OWNERS b/framework/tests/bumble/OWNERS
deleted file mode 100644
index 5bc200e..0000000
--- a/framework/tests/bumble/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 1099313
-# Project owners
-set noparent
-girardier@google.com
-charliebout@google.com
diff --git a/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt b/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt
index 9d42f3d..e658e8c 100644
--- a/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt
+++ b/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt
@@ -137,6 +137,7 @@
     }
 
     @Test
+    @VirtualOnly
     fun testSend() {
         Log.d(TAG, "testSend")
         val remoteDevice =
@@ -189,6 +190,7 @@
     }
 
     @Test
+    @VirtualOnly
     fun testReceive() {
         Log.d(TAG, "testReceive: Connect L2CAP")
         var bluetoothSocket: BluetoothSocket?
diff --git a/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java b/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java
index 3f728e4..506586b 100644
--- a/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java
+++ b/framework/tests/bumble/src/android/bluetooth/LeScanningTest.java
@@ -85,7 +85,8 @@
     @Rule(order = 2)
     public final EnableBluetoothRule mEnableBluetoothRule = new EnableBluetoothRule(false, true);
 
-    private final Context mContext = ApplicationProvider.getApplicationContext();
+    private final Context mContext =
+            ApplicationProvider.getApplicationContext().createAttributionContext(TAG);
     private final BluetoothManager mBluetoothManager =
             mContext.getSystemService(BluetoothManager.class);
     private final BluetoothAdapter mBluetoothAdapter = mBluetoothManager.getAdapter();
@@ -354,6 +355,7 @@
     }
 
     @Test
+    @VirtualOnly
     public void startBleScan_withServiceData() {
         advertiseWithBumbleWithServiceData();
 
diff --git a/framework/tests/bumble/src/android/bluetooth/VirtualOnly.java b/framework/tests/bumble/src/android/bluetooth/VirtualOnly.java
new file mode 100644
index 0000000..77d7555
--- /dev/null
+++ b/framework/tests/bumble/src/android/bluetooth/VirtualOnly.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+public @interface VirtualOnly {
+    String value() default "";
+}
diff --git a/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java b/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java
index 778fc23..cddee2a 100644
--- a/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java
+++ b/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java
@@ -49,6 +49,7 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.PandoraDevice;
+import android.bluetooth.VirtualOnly;
 import android.bluetooth.test_utils.EnableBluetoothRule;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -92,6 +93,7 @@
 /** Test cases for {@link BluetoothHidHost}. */
 @SuppressLint("MissingPermission")
 @RunWith(AndroidJUnit4.class)
+@VirtualOnly
 public class HidHostDualModeTest {
     private static final String TAG = HidHostDualModeTest.class.getSimpleName();
     private static final String BUMBLE_DEVICE_NAME = "Bumble";
diff --git a/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java b/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java
index fde1a29..76eda63 100644
--- a/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java
+++ b/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java
@@ -46,6 +46,7 @@
 import android.bluetooth.BluetoothManager;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.PandoraDevice;
+import android.bluetooth.VirtualOnly;
 import android.bluetooth.cts.EnableBluetoothRule;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -91,6 +92,7 @@
 
 /** Test cases for {@link BluetoothHidHost}. */
 @RunWith(AndroidJUnit4.class)
+@VirtualOnly
 public class HidHostTest {
     private static final String TAG = HidHostTest.class.getSimpleName();
     private static final Duration INTENT_TIMEOUT = Duration.ofSeconds(10);
diff --git a/framework/tests/bumble/src/android/bluetooth/service_discovery/LeAudioServiceDiscoveryTest.java b/framework/tests/bumble/src/android/bluetooth/service_discovery/LeAudioServiceDiscoveryTest.java
index 27b0f10..2f6a56c 100644
--- a/framework/tests/bumble/src/android/bluetooth/service_discovery/LeAudioServiceDiscoveryTest.java
+++ b/framework/tests/bumble/src/android/bluetooth/service_discovery/LeAudioServiceDiscoveryTest.java
@@ -32,6 +32,7 @@
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.Host;
 import android.bluetooth.PandoraDevice;
+import android.bluetooth.VirtualOnly;
 import android.bluetooth.test_utils.EnableBluetoothRule;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -204,6 +205,7 @@
      * Classic services
      */
     @Test
+    @VirtualOnly
     public void testServiceDiscoveryWithRandomAddr() {
 
         registerIntentActions(
diff --git a/pandora/OWNERS b/pandora/OWNERS
index f45a253..14634f3 100644
--- a/pandora/OWNERS
+++ b/pandora/OWNERS
@@ -1,3 +1,2 @@
 # Project owners
 girardier@google.com
-charliebout@google.com
diff --git a/system/audio_bluetooth_hw/stream_apis.cc b/system/audio_bluetooth_hw/stream_apis.cc
index 7b96f5a..6854a61 100644
--- a/system/audio_bluetooth_hw/stream_apis.cc
+++ b/system/audio_bluetooth_hw/stream_apis.cc
@@ -775,6 +775,7 @@
 
   out->frames_rendered_ = 0;
   out->frames_presented_ = 0;
+  out->last_write_time_us_ = 0;
 
   BluetoothStreamOut* out_ptr = out.release();
   {
diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 4ffce69..812c482 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -154,7 +154,11 @@
         "libcom.android.sysprop.bluetooth.wrapped",
         "liblc3",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
+        "libbinder",
         "libcrypto",
     ],
     generated_headers: [
@@ -651,12 +655,17 @@
         "vc/vc.cc",
         "vc/vc_test.cc",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libbase",
+        "libbinder",
         "libcrypto",
         "liblog",
     ],
     static_libs: [
+        "aics",
         "bluetooth_flags_c_lib_for_test",
         "libbluetooth-types",
         "libbluetooth_crypto_toolbox",
diff --git a/system/bta/BUILD.gn b/system/bta/BUILD.gn
index b328715..928359a 100644
--- a/system/bta/BUILD.gn
+++ b/system/bta/BUILD.gn
@@ -145,6 +145,7 @@
     "//bt/system/include",
     "//bt/system/linux_include",
     "//bt/system/bta",
+    "//bt/system/bta/aics/include",
     "//bt/system/gd",
     "//bt/system/stack/include",
     "//bt/system/stack/btm",
@@ -173,6 +174,7 @@
     "//bt/system/bta:install_audio_set_configurations_json",
     "//bt/system/bta:install_audio_set_scenarios_bfbs",
     "//bt/system/bta:install_audio_set_configurations_bfbs",
+    "//bt/system/bta/aics:aics",
     "//bt/system:libbt-platform-protos-lite",
   ]
 
diff --git a/system/bta/aics/Android.bp b/system/bta/aics/Android.bp
new file mode 100644
index 0000000..33bd649
--- /dev/null
+++ b/system/bta/aics/Android.bp
@@ -0,0 +1,32 @@
+cc_library_headers {
+    name: "aics_headers",
+    export_include_dirs: ["include"],
+    host_supported: true,
+    vendor_available: true, // remove when https://r.android.com/3302734 is merged
+    visibility: ["//packages/modules/Bluetooth:__subpackages__"],
+    apex_available: ["com.android.btservices"],
+    min_sdk_version: "33",
+}
+
+cc_library {
+    name: "aics",
+    defaults: ["fluoride_defaults"],
+    srcs: [
+        "aics.cc",
+    ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libbluetooth_log",
+        "liblog",
+    ],
+    header_libs: ["aics_headers"],
+    export_header_lib_headers: ["aics_headers"],
+    host_supported: true,
+    visibility: ["//packages/modules/Bluetooth:__subpackages__"],
+    apex_available: ["com.android.btservices"],
+    min_sdk_version: "33",
+}
diff --git a/system/bta/aics/BUILD.gn b/system/bta/aics/BUILD.gn
new file mode 100644
index 0000000..5663e36
--- /dev/null
+++ b/system/bta/aics/BUILD.gn
@@ -0,0 +1,40 @@
+# Copyright (C) 2024 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.
+
+config("aics_defaults") {
+  include_dirs = [
+    "include",
+  ]
+  configs = [
+    "//bt/system:target_defaults",
+    "//bt/system/log:log_defaults",
+  ]
+}
+
+static_library("aics") {
+  sources = [
+    "aics.cc",
+  ]
+  configs += [
+    ":aics_defaults",
+  ]
+  # include_dirs = [
+  #   "include",
+  # ]
+
+  # configs += [
+  #   "//bt/system:target_defaults",
+  #   "//bt/system/log:log_defaults",
+  # ]
+}
+
diff --git a/system/bta/aics/aics.cc b/system/bta/aics/aics.cc
new file mode 100644
index 0000000..5b5c088
--- /dev/null
+++ b/system/bta/aics/aics.cc
@@ -0,0 +1,32 @@
+// Copyright (C) 2024 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 "aics"
+
+#include <aics/api.h>
+#include <bluetooth/log.h>
+
+namespace bluetooth::aics {
+
+bool isValidAudioInputMuteValue(uint8_t data) {
+  return data >= static_cast<uint8_t>(Mute::NOT_MUTED) &&
+         data <= static_cast<uint8_t>(Mute::DISABLED);
+}
+
+Mute parseMuteField(uint8_t data) {
+  log::assert_that(isValidAudioInputMuteValue(data), "Not a valid Mute Value");
+
+  return static_cast<Mute>(data);
+}
+
+}  // namespace bluetooth::aics
diff --git a/system/bta/aics/include/aics/api.h b/system/bta/aics/include/aics/api.h
new file mode 100644
index 0000000..aba825d
--- /dev/null
+++ b/system/bta/aics/include/aics/api.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2024 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
+
+#ifndef TARGET_FLOSS
+#include <bluetooth/constants/aics/Mute.h>
+#endif
+
+#include <cstdint>
+
+namespace bluetooth::aics {
+
+#ifndef TARGET_FLOSS
+using Mute = bluetooth::constants::aics::Mute;
+#else
+// TODO: b/376941621 Support the aidl generation in FLOSS
+enum class Mute : int8_t { NOT_MUTED = 0, MUTED = 1, DISABLED = 2 };
+#endif
+
+/** Check if the data is a correct Mute value */
+bool isValidAudioInputMuteValue(uint8_t data);
+
+/** Convert valid data into a Mute value. Abort if data is not valid */
+Mute parseMuteField(uint8_t data);
+}  // namespace bluetooth::aics
diff --git a/system/bta/dm/bta_dm_device_search.cc b/system/bta/dm/bta_dm_device_search.cc
index a5428dc..76219c3 100644
--- a/system/bta/dm/bta_dm_device_search.cc
+++ b/system/bta/dm/bta_dm_device_search.cc
@@ -342,11 +342,12 @@
 }
 
 static void bta_dm_remote_name_cmpl(const tBTA_DM_REMOTE_NAME& remote_name_msg) {
-  BTM_LogHistory(kBtmLogTag, remote_name_msg.bd_addr, "Remote name completed",
-                 base::StringPrintf("status:%s state:%s name:\"%s\"",
-                                    hci_status_code_text(remote_name_msg.hci_status).c_str(),
-                                    bta_dm_state_text(bta_dm_search_get_state()).c_str(),
-                                    PRIVATE_NAME(remote_name_msg.bd_name)));
+  BTM_LogHistory(
+          kBtmLogTag, remote_name_msg.bd_addr, "Remote name completed",
+          base::StringPrintf("status:%s state:%s name:\"%s\"",
+                             hci_status_code_text(remote_name_msg.hci_status).c_str(),
+                             bta_dm_state_text(bta_dm_search_get_state()).c_str(),
+                             PRIVATE_NAME(reinterpret_cast<char const*>(remote_name_msg.bd_name))));
 
   tBTM_INQ_INFO* p_btm_inq_info =
           get_btm_client_interface().db.BTM_InqDbRead(remote_name_msg.bd_addr);
diff --git a/system/bta/include/bta_vc_api.h b/system/bta/include/bta_vc_api.h
index bba5b41..2eb33c2 100644
--- a/system/bta/include/bta_vc_api.h
+++ b/system/bta/include/bta_vc_api.h
@@ -65,8 +65,8 @@
   virtual void GetExtAudioInDescription(const RawAddress& address, uint8_t ext_input_id) = 0;
   virtual void SetExtAudioInDescription(const RawAddress& address, uint8_t ext_input_id,
                                         std::string descr) = 0;
-  virtual void SetExtAudioInGainValue(const RawAddress& address, uint8_t ext_input_id,
-                                      int8_t value) = 0;
+  virtual void SetExtAudioInGainSetting(const RawAddress& address, uint8_t ext_input_id,
+                                        int8_t gain_setting) = 0;
   /* Set Gain Mode as per AIS */
   virtual void SetExtAudioInGainMode(const RawAddress& address, uint8_t ext_input_id,
                                      bool auto_mode) = 0;
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 01c2856..6a3e3e8 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -867,7 +867,10 @@
                    BidirectionalPair<AudioContexts> remote_contexts) {
     LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
 
-    log::debug("configuration_context_type= {}", ToString(configuration_context_type));
+    log::debug(
+            "configuration_context_type= {}, remote sink contexts= {}, remote source contexts= {}",
+            ToString(configuration_context_type), ToString(remote_contexts.sink),
+            ToString(remote_contexts.source));
 
     log::debug("");
     if (configuration_context_type >= LeAudioContextType::RFU) {
@@ -4701,16 +4704,18 @@
         continue;
       }
 
+      // Use only available contexts
       contexts_pair.get(dir) &= group_available_contexts;
+
+      // Check we we should add UNSPECIFIED as well in case not available context is also not
+      // supported
       auto unavail_but_supported = (unavail_contexts & group->GetSupportedContexts(dir));
       if (unavail_but_supported.none() &&
           group_available_contexts.test(LeAudioContextType::UNSPECIFIED)) {
-        log::debug("Replaced the unsupported contexts: {} with UNSPECIFIED",
-                   ToString(unavail_contexts));
-        /* All unavailable are also unsupported - replace with UNSPECIFIED if
-         * available
-         */
         contexts_pair.get(dir).set(LeAudioContextType::UNSPECIFIED);
+
+        log::debug("Replaced the unsupported contexts: {} with UNSPECIFIED -> {}",
+                   ToString(unavail_contexts), ToString(contexts_pair.get(dir)));
       } else {
         log::debug("Some contexts are supported but currently unavailable: {}!",
                    ToString(unavail_but_supported));
@@ -4732,7 +4737,8 @@
       std::tie(dir, other_dir, local_hal_state) = entry;
 
       if (contexts_pair.get(dir).test(LeAudioContextType::UNSPECIFIED)) {
-        /* Try to use the other direction context if not UNSPECIFIED and active
+        /* If this directions is streaming just UNSPECIFIED and if other direction is streaming some
+         * meaningfull context type,  try to use the meaningful context type
          */
         if (contexts_pair.get(dir) == AudioContexts(LeAudioContextType::UNSPECIFIED)) {
           auto is_other_direction_streaming = (*local_hal_state == AudioState::STARTED) ||
@@ -4746,7 +4752,8 @@
             contexts_pair.get(dir) = contexts_pair.get(other_dir);
           }
         } else {
-          log::debug("Removing UNSPECIFIED from the remote sink context: {}",
+          log::debug("Removing UNSPECIFIED from the remote {} context: {}",
+                     dir == bluetooth::le_audio::types::kLeAudioDirectionSink ? " Sink" : " Source",
                      ToString(contexts_pair.get(other_dir)));
           contexts_pair.get(dir).unset(LeAudioContextType::UNSPECIFIED);
         }
@@ -4794,14 +4801,23 @@
 
   BidirectionalPair<AudioContexts> DirectionalRealignMetadataAudioContexts(
           LeAudioDeviceGroup* group, int remote_direction) {
-    auto remote_other_direction =
-            (remote_direction == bluetooth::le_audio::types::kLeAudioDirectionSink
-                     ? bluetooth::le_audio::types::kLeAudioDirectionSource
-                     : bluetooth::le_audio::types::kLeAudioDirectionSink);
-    auto other_direction_hal =
-            (remote_other_direction == bluetooth::le_audio::types::kLeAudioDirectionSource
-                     ? audio_receiver_state_
-                     : audio_sender_state_);
+    uint8_t remote_other_direction;
+    std::string remote_direction_str;
+    std::string remote_other_direction_str;
+    AudioState other_direction_hal;
+
+    if (remote_direction == bluetooth::le_audio::types::kLeAudioDirectionSink) {
+      remote_other_direction = bluetooth::le_audio::types::kLeAudioDirectionSource;
+      remote_direction_str = "Sink";
+      remote_other_direction_str = "Source";
+      other_direction_hal = audio_receiver_state_;
+    } else {
+      remote_other_direction = bluetooth::le_audio::types::kLeAudioDirectionSink;
+      remote_direction_str = "Source";
+      remote_other_direction_str = "Sink";
+      other_direction_hal = audio_sender_state_;
+    }
+
     auto is_streaming_other_direction = (other_direction_hal == AudioState::STARTED) ||
                                         (other_direction_hal == AudioState::READY_TO_START);
     auto is_releasing_for_reconfiguration =
@@ -4863,9 +4879,7 @@
                ToString(local_metadata_context_types_.sink));
     log::debug("remote_metadata.source= {}", ToString(remote_metadata.source));
     log::debug("remote_metadata.sink= {}", ToString(remote_metadata.sink));
-    log::debug("remote_direction= {}",
-               (remote_direction == bluetooth::le_audio::types::kLeAudioDirectionSource ? "Source"
-                                                                                        : "Sink"));
+    log::debug("remote_direction= {}", remote_direction_str);
     log::debug("is_streaming_other_direction= {}", is_streaming_other_direction ? "True" : "False");
     log::debug("is_releasing_for_reconfiguration= {}",
                is_releasing_for_reconfiguration ? "True" : "False");
diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc
index 05a2624..37367f0 100644
--- a/system/bta/le_audio/le_audio_client_test.cc
+++ b/system/bta/le_audio/le_audio_client_test.cc
@@ -10606,7 +10606,7 @@
   Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
   Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
 
-  log::info("TESTPOING 4: Disable call so we could go back to MEDIA");
+  log::info("TESTPOINT 4: Disable call so we could go back to MEDIA");
   // ---------------------------------------
   // Suspend should stop the stream
   EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1);
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 48d09a3..cc9f843 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -2946,6 +2946,11 @@
       return CIS_STILL_NEEDED;
     }
 
+    ase->cis_state = CisState::DISCONNECTING;
+    if (bidirection_ase) {
+      bidirection_ase->cis_state = CisState::DISCONNECTING;
+    }
+
     group->RemoveCisFromStreamIfNeeded(leAudioDevice, ase->cis_conn_hdl);
     IsoManager::GetInstance()->DisconnectCis(ase->cis_conn_hdl, HCI_ERR_PEER_USER);
     log_history_->AddLogHistory(kLogStateMachineTag, group->group_id_, leAudioDevice->address_,
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index 84b997b..dc601a5 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -4920,6 +4920,54 @@
   }
 }
 
+TEST_F(StateMachineTest, testInjectReleasingStateWhenEnabling) {
+  const auto context_type = kContextTypeConversational;
+  const int leaudio_group_id = 4;
+  channel_count_ = kLeAudioCodecChannelCountSingleChannel;
+
+  // Prepare fake connected device group
+  auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type);
+  PrepareConfigureCodecHandler(group, 2);
+  PrepareConfigureQosHandler(group, 2);
+  PrepareEnableHandler(group, 0, true, false);
+
+  InjectInitialConfiguredNotification(group);
+
+  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(0);
+  EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0);
+  EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0);
+  EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0);
+
+  // Stub Establish Cis and Remove CIG
+  ON_CALL(*mock_iso_manager_, EstablishCis).WillByDefault(Return());
+  ON_CALL(*mock_iso_manager_, RemoveCig).WillByDefault(Return());
+
+  group->PrintDebugState();
+
+  // 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(mock_iso_manager_);
+
+  group->PrintDebugState();
+
+  log::info("Inject Release of all ASEs");
+
+  EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(1);
+
+  // Stub DisconnectCis to trigger the issue.
+  ON_CALL(*mock_iso_manager_, DisconnectCis).WillByDefault(Return());
+
+  InjectReleaseAndIdleStateForAGroup(group, true, false);
+
+  testing::Mock::VerifyAndClearExpectations(mock_iso_manager_);
+}
+
 MATCHER_P(dataPathIsEq, expected, "") { return arg.data_path_id == expected; }
 
 TEST_F(StateMachineTest, testConfigureDataPathForHost) {
diff --git a/system/bta/vc/device.cc b/system/bta/vc/device.cc
index f4f016f..43f1e61 100644
--- a/system/bta/vc/device.cc
+++ b/system/bta/vc/device.cc
@@ -123,53 +123,61 @@
 }
 
 void VolumeControlDevice::set_audio_input_control_service_handles(const gatt::Service& service) {
-  VolumeAudioInput input = VolumeAudioInput(service.handle);
+  uint16_t state_handle{0};
+  uint16_t state_ccc_handle{0};
+  uint16_t gain_setting_handle{0};
+  uint16_t type_handle{0};
+  uint16_t status_handle{0};
+  uint16_t status_ccc_handle{0};
+  uint16_t control_point_handle{0};
+  uint16_t description_handle{0};
+  uint16_t description_ccc_handle{0};
+  uint16_t description_writable{0};
+
   for (const gatt::Characteristic& chrc : service.characteristics) {
     if (chrc.uuid == kVolumeAudioInputStateUuid) {
-      input.state_handle = chrc.value_handle;
-      input.state_ccc_handle = find_ccc_handle(chrc.value_handle);
-      log::debug("{}, input_state handle={:#x}, ccc {:#x}", address, input.state_handle,
-                 input.state_ccc_handle);
-
-    } else if (chrc.uuid == kVolumeAudioInputGainSettingUuid) {
-      input.gain_setting_handle = chrc.value_handle;
-
+      state_handle = chrc.value_handle;
+      state_ccc_handle = find_ccc_handle(chrc.value_handle);
+      log::debug("{} state_handle={:#x} ccc={:#x}", address, state_handle, state_ccc_handle);
+    } else if (chrc.uuid == kVolumeAudioInputGainSettingPropertiesUuid) {
+      gain_setting_handle = chrc.value_handle;
     } else if (chrc.uuid == kVolumeAudioInputTypeUuid) {
-      input.type_handle = chrc.value_handle;
-
+      type_handle = chrc.value_handle;
     } else if (chrc.uuid == kVolumeAudioInputStatusUuid) {
-      input.status_handle = chrc.value_handle;
-      input.status_ccc_handle = find_ccc_handle(chrc.value_handle);
-      log::debug("{}, input_status handle={:#x}, ccc {:#x}", address, input.status_handle,
-                 input.status_ccc_handle);
-
+      status_handle = chrc.value_handle;
+      status_ccc_handle = find_ccc_handle(chrc.value_handle);
+      log::debug("{} status_handle={:#x} ccc={:#x}", address, status_handle, status_ccc_handle);
     } else if (chrc.uuid == kVolumeAudioInputControlPointUuid) {
-      input.control_point_handle = chrc.value_handle;
-
+      control_point_handle = chrc.value_handle;
     } else if (chrc.uuid == kVolumeAudioInputDescriptionUuid) {
-      input.description_handle = chrc.value_handle;
-      input.description_ccc_handle = find_ccc_handle(chrc.value_handle);
-      input.description_writable = chrc.properties & GATT_CHAR_PROP_BIT_WRITE_NR;
-      log::debug("{}, input_desc handle={:#x}, ccc {:#x}", address, input.description_handle,
-                 input.description_ccc_handle);
-
+      description_handle = chrc.value_handle;
+      description_ccc_handle = find_ccc_handle(chrc.value_handle);
+      description_writable = chrc.properties & GATT_CHAR_PROP_BIT_WRITE_NR;
+      log::debug("{} description_handle={:#x} ccc={:#x}", address, description_handle,
+                 description_ccc_handle);
     } else {
-      log::warn("unknown characteristic={}", chrc.uuid);
+      log::info("found unexpected characteristic={}", chrc.uuid);
     }
   }
 
   // Check if all mandatory attributes are present
-  if (GATT_HANDLE_IS_VALID(input.state_handle) && GATT_HANDLE_IS_VALID(input.state_ccc_handle) &&
-      GATT_HANDLE_IS_VALID(input.gain_setting_handle) && GATT_HANDLE_IS_VALID(input.type_handle) &&
-      GATT_HANDLE_IS_VALID(input.status_handle) && GATT_HANDLE_IS_VALID(input.status_ccc_handle) &&
-      GATT_HANDLE_IS_VALID(input.control_point_handle) &&
-      GATT_HANDLE_IS_VALID(input.description_handle)
+  if (!GATT_HANDLE_IS_VALID(state_handle) || !GATT_HANDLE_IS_VALID(state_ccc_handle) ||
+      !GATT_HANDLE_IS_VALID(gain_setting_handle) || !GATT_HANDLE_IS_VALID(type_handle) ||
+      !GATT_HANDLE_IS_VALID(status_handle) || !GATT_HANDLE_IS_VALID(status_ccc_handle) ||
+      !GATT_HANDLE_IS_VALID(control_point_handle) || !GATT_HANDLE_IS_VALID(description_handle)
       /* description_ccc_handle is optional */) {
-    audio_inputs.Add(input);
-    log::info("{}, input added id={:#x}", address, input.id);
-  } else {
-    log::warn("{}, ignoring input handle={:#x}", address, service.handle);
+    log::error(
+            "The remote device {} does not comply with AICS 1-0, some handles are invalid. "
+            "The aics service with handle {:#x} will be ignored",
+            address, service.handle);
+    return;
   }
+  VolumeAudioInput input = VolumeAudioInput(
+          audio_inputs.Size(), service.handle, state_handle, state_ccc_handle, gain_setting_handle,
+          type_handle, status_handle, status_ccc_handle, control_point_handle, description_handle,
+          description_ccc_handle, description_writable);
+  audio_inputs.Add(input);
+  log::info("{}, input added id={:#x}", address, input.id);
 }
 
 void VolumeControlDevice::set_volume_offset_control_service_handles(const gatt::Service& service) {
diff --git a/system/bta/vc/devices.h b/system/bta/vc/devices.h
index 3e81072..8c0a573 100644
--- a/system/bta/vc/devices.h
+++ b/system/bta/vc/devices.h
@@ -156,7 +156,7 @@
   /*
    * This is used to track the pending GATT operation handles. Once the list is
    * empty the device is assumed ready and connected. We are doing it because we
-   * want to make sure all the required characteristics and descritors are
+   * want to make sure all the required characteristics and descriptors are
    * available on server side.
    */
   std::unordered_set<uint16_t> handles_pending;
diff --git a/system/bta/vc/devices_test.cc b/system/bta/vc/devices_test.cc
index d2f389a..fea281c 100644
--- a/system/bta/vc/devices_test.cc
+++ b/system/bta/vc/devices_test.cc
@@ -44,6 +44,7 @@
 using ::testing::DoAll;
 using ::testing::Invoke;
 using ::testing::Mock;
+using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::SaveArg;
 using ::testing::SetArgPointee;
@@ -74,8 +75,8 @@
   }
 
   VolumeControlDevices* devices_ = nullptr;
-  gatt::MockBtaGattInterface gatt_interface;
-  gatt::MockBtaGattQueue gatt_queue;
+  NiceMock<gatt::MockBtaGattInterface> gatt_interface;
+  NiceMock<gatt::MockBtaGattQueue> gatt_queue;
 };
 
 TEST_F(VolumeControlDevicesTest, test_add) {
@@ -288,7 +289,7 @@
     builder.AddService(0x0020, 0x002e, kVolumeAudioInputUuid, false);
     builder.AddCharacteristic(0x0021, 0x0022, kVolumeAudioInputStateUuid, GATT_CHAR_PROP_BIT_READ);
     builder.AddDescriptor(0x0023, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
-    builder.AddCharacteristic(0x0024, 0x0025, kVolumeAudioInputGainSettingUuid,
+    builder.AddCharacteristic(0x0024, 0x0025, kVolumeAudioInputGainSettingPropertiesUuid,
                               GATT_CHAR_PROP_BIT_READ);
     builder.AddCharacteristic(0x0026, 0x0027, kVolumeAudioInputTypeUuid, GATT_CHAR_PROP_BIT_READ);
     builder.AddCharacteristic(0x0028, 0x0029, kVolumeAudioInputStatusUuid,
@@ -304,7 +305,7 @@
     builder.AddCharacteristic(0x0041, 0x0042, kVolumeAudioInputStateUuid,
                               GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
     builder.AddDescriptor(0x0043, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
-    builder.AddCharacteristic(0x0044, 0x0045, kVolumeAudioInputGainSettingUuid,
+    builder.AddCharacteristic(0x0044, 0x0045, kVolumeAudioInputGainSettingPropertiesUuid,
                               GATT_CHAR_PROP_BIT_READ);
     builder.AddCharacteristic(0x0046, 0x0047, kVolumeAudioInputTypeUuid, GATT_CHAR_PROP_BIT_READ);
     builder.AddCharacteristic(0x0048, 0x0049, kVolumeAudioInputStatusUuid,
@@ -361,9 +362,9 @@
   }
 
   VolumeControlDevice* device = nullptr;
-  gatt::MockBtaGattInterface gatt_interface;
-  gatt::MockBtaGattQueue gatt_queue;
-  bluetooth::manager::MockBtmInterface btm_interface;
+  NiceMock<gatt::MockBtaGattInterface> gatt_interface;
+  NiceMock<gatt::MockBtaGattQueue> gatt_queue;
+  NiceMock<bluetooth::manager::MockBtmInterface> btm_interface;
   std::list<gatt::Service> services;
 };
 
@@ -387,7 +388,7 @@
   builder.AddCharacteristic(0x000c, 0x000d, kVolumeAudioInputStateUuid,
                             GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
   builder.AddDescriptor(0x000e, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
-  builder.AddCharacteristic(0x000f, 0x0010, kVolumeAudioInputGainSettingUuid,
+  builder.AddCharacteristic(0x000f, 0x0010, kVolumeAudioInputGainSettingPropertiesUuid,
                             GATT_CHAR_PROP_BIT_READ);
   builder.AddCharacteristic(0x0011, 0x0012, kVolumeAudioInputTypeUuid, GATT_CHAR_PROP_BIT_READ);
   builder.AddCharacteristic(0x0013, 0x0014, kVolumeAudioInputStatusUuid,
@@ -423,7 +424,7 @@
   builder.AddCharacteristic(0x000c, 0x000d, kVolumeAudioInputStateUuid,
                             GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
   builder.AddDescriptor(0x000e, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
-  builder.AddCharacteristic(0x000f, 0x0010, kVolumeAudioInputGainSettingUuid,
+  builder.AddCharacteristic(0x000f, 0x0010, kVolumeAudioInputGainSettingPropertiesUuid,
                             GATT_CHAR_PROP_BIT_READ);
   builder.AddCharacteristic(0x0011, 0x0012, kVolumeAudioInputTypeUuid, GATT_CHAR_PROP_BIT_READ);
   builder.AddCharacteristic(0x0013, 0x0014, kVolumeAudioInputStatusUuid,
@@ -542,8 +543,8 @@
   SetSampleDatabase1();
   ASSERT_EQ((size_t)2, device->audio_offsets.Size());
   ASSERT_EQ((size_t)2, device->audio_inputs.Size());
-  VolumeAudioInput* input_1 = device->audio_inputs.FindById(1);
-  VolumeAudioInput* input_2 = device->audio_inputs.FindById(2);
+  VolumeAudioInput* input_1 = device->audio_inputs.FindById(0);
+  VolumeAudioInput* input_2 = device->audio_inputs.FindById(1);
   ASSERT_NE(nullptr, input_1);
   ASSERT_NE(nullptr, input_2);
   ASSERT_NE(input_1->service_handle, input_2->service_handle);
@@ -578,7 +579,7 @@
   tGATT_IF gatt_if = 0x0001;
   std::vector<uint8_t> register_for_notification_data({0x01, 0x00});
 
-  std::map<uint16_t, uint16_t> expected_subscribtions{
+  std::map<uint16_t, uint16_t> expected_subscriptions{
           {0x0011, 0x0012} /* volume control state */,
           {0x0016, 0x0017} /* volume control flags */,
           {0x0022, 0x0023} /* audio input state 1 */,
@@ -595,7 +596,7 @@
   EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x0011, _, _));
   EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x0016, _, _));
 
-  for (auto const& handle_pair : expected_subscribtions) {
+  for (auto const& handle_pair : expected_subscriptions) {
     EXPECT_CALL(gatt_queue, WriteDescriptor(_, handle_pair.second, register_for_notification_data,
                                             GATT_WRITE, _, _));
     EXPECT_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, handle_pair.first));
@@ -856,7 +857,7 @@
                                uint16_t /*len*/, uint8_t* /*value*/, void* /*data*/) {};
   SetSampleDatabase1();
   EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x0022, read_cb, nullptr));
-  device->GetExtAudioInState(1, read_cb, nullptr);
+  device->GetExtAudioInState(0, read_cb, nullptr);
 }
 
 TEST_F(VolumeControlDeviceTest, test_get_ext_audio_in_status) {
@@ -864,7 +865,7 @@
                                uint16_t /*len*/, uint8_t* /*value*/, void* /*data*/) {};
   SetSampleDatabase1();
   EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x0049, read_cb, nullptr));
-  device->GetExtAudioInStatus(2, read_cb, nullptr);
+  device->GetExtAudioInStatus(1, read_cb, nullptr);
 }
 
 TEST_F(VolumeControlDeviceTest, test_get_ext_audio_in_gain_props) {
@@ -872,7 +873,7 @@
                                uint16_t /*len*/, uint8_t* /*value*/, void* /*data*/) {};
   SetSampleDatabase1();
   EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x0025, read_cb, nullptr));
-  device->GetExtAudioInGainProps(1, read_cb, nullptr);
+  device->GetExtAudioInGainProps(0, read_cb, nullptr);
 }
 
 TEST_F(VolumeControlDeviceTest, test_get_ext_audio_in_description) {
@@ -880,7 +881,7 @@
                                uint16_t /*len*/, uint8_t* /*value*/, void* /*data*/) {};
   SetSampleDatabase1();
   EXPECT_CALL(gatt_queue, ReadCharacteristic(_, 0x002e, read_cb, nullptr));
-  device->GetExtAudioInDescription(1, read_cb, nullptr);
+  device->GetExtAudioInDescription(0, read_cb, nullptr);
 }
 
 TEST_F(VolumeControlDeviceTest, test_set_ext_audio_in_description) {
@@ -889,7 +890,7 @@
   std::vector<uint8_t> expected_data(descr.begin(), descr.end());
   EXPECT_CALL(gatt_queue,
               WriteCharacteristic(_, 0x004e, expected_data, GATT_WRITE_NO_RSP, nullptr, nullptr));
-  device->SetExtAudioInDescription(2, descr);
+  device->SetExtAudioInDescription(1, descr);
 }
 
 TEST_F(VolumeControlDeviceTest, test_set_ext_audio_in_description_non_writable) {
@@ -897,34 +898,34 @@
   std::string descr = "AUX";
   std::vector<uint8_t> expected_data(descr.begin(), descr.end());
   EXPECT_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)).Times(0);
-  device->SetExtAudioInDescription(1, descr);
+  device->SetExtAudioInDescription(0, descr);
 }
 
 TEST_F(VolumeControlDeviceTest, test_ext_audio_in_control_point_operation) {
   GATT_WRITE_OP_CB write_cb = [](uint16_t /*conn_id*/, tGATT_STATUS /*status*/, uint16_t /*handle*/,
                                  uint16_t /*len*/, const uint8_t* /*value*/, void* /*data*/) {};
   SetSampleDatabase1();
-  VolumeAudioInput* input = device->audio_inputs.FindById(2);
+  VolumeAudioInput* input = device->audio_inputs.FindById(1);
   ASSERT_NE(nullptr, input);
   input->change_counter = 0x11;
   std::vector<uint8_t> expected_data({0x0c, 0x11});
   EXPECT_CALL(gatt_queue,
               WriteCharacteristic(_, 0x004c, expected_data, GATT_WRITE, write_cb, nullptr));
-  device->ExtAudioInControlPointOperation(2, 0x0c, nullptr, write_cb, nullptr);
+  device->ExtAudioInControlPointOperation(1, 0x0c, nullptr, write_cb, nullptr);
 }
 
 TEST_F(VolumeControlDeviceTest, test_ext_audio_in_control_point_operation_arg) {
   GATT_WRITE_OP_CB write_cb = [](uint16_t /*conn_id*/, tGATT_STATUS /*status*/, uint16_t /*handle*/,
                                  uint16_t /*len*/, const uint8_t* /*value*/, void* /*data*/) {};
   SetSampleDatabase1();
-  VolumeAudioInput* input = device->audio_inputs.FindById(2);
+  VolumeAudioInput* input = device->audio_inputs.FindById(1);
   ASSERT_NE(nullptr, input);
   input->change_counter = 0x12;
   std::vector<uint8_t> expected_data({0x0d, 0x12, 0x01, 0x02, 0x03, 0x04});
   std::vector<uint8_t> arg({0x01, 0x02, 0x03, 0x04});
   EXPECT_CALL(gatt_queue,
               WriteCharacteristic(_, 0x004c, expected_data, GATT_WRITE, write_cb, nullptr));
-  device->ExtAudioInControlPointOperation(2, 0x0d, &arg, write_cb, nullptr);
+  device->ExtAudioInControlPointOperation(1, 0x0d, &arg, write_cb, nullptr);
 }
 
 }  // namespace internal
diff --git a/system/bta/vc/types.h b/system/bta/vc/types.h
index f1f35a1..65d8355 100644
--- a/system/bta/vc/types.h
+++ b/system/bta/vc/types.h
@@ -17,6 +17,7 @@
 
 #pragma once
 
+#include <aics/api.h>
 #include <hardware/bt_vc.h>
 
 #include <algorithm>
@@ -53,24 +54,24 @@
 static constexpr uint8_t kVolumeInputControlPointOpcodeSetManualGainMode    = 0x04;
 static constexpr uint8_t kVolumeInputControlPointOpcodeSetAutoGainMode      = 0x05;
 
-static const Uuid kVolumeControlUuid                  = Uuid::From16Bit(0x1844);
-static const Uuid kVolumeControlStateUuid             = Uuid::From16Bit(0x2B7D);
-static const Uuid kVolumeControlPointUuid             = Uuid::From16Bit(0x2B7E);
-static const Uuid kVolumeFlagsUuid                    = Uuid::From16Bit(0x2B7F);
+static const Uuid kVolumeControlUuid                         = Uuid::From16Bit(0x1844);
+static const Uuid kVolumeControlStateUuid                    = Uuid::From16Bit(0x2B7D);
+static const Uuid kVolumeControlPointUuid                    = Uuid::From16Bit(0x2B7E);
+static const Uuid kVolumeFlagsUuid                           = Uuid::From16Bit(0x2B7F);
 
-static const Uuid kVolumeOffsetUuid                   = Uuid::From16Bit(0x1845);
-static const Uuid kVolumeOffsetStateUuid              = Uuid::From16Bit(0x2B80);
-static const Uuid kVolumeOffsetLocationUuid           = Uuid::From16Bit(0x2B81);
-static const Uuid kVolumeOffsetControlPointUuid       = Uuid::From16Bit(0x2B82);
-static const Uuid kVolumeOffsetOutputDescriptionUuid  = Uuid::From16Bit(0x2B83);
+static const Uuid kVolumeOffsetUuid                          = Uuid::From16Bit(0x1845);
+static const Uuid kVolumeOffsetStateUuid                     = Uuid::From16Bit(0x2B80);
+static const Uuid kVolumeOffsetLocationUuid                  = Uuid::From16Bit(0x2B81);
+static const Uuid kVolumeOffsetControlPointUuid              = Uuid::From16Bit(0x2B82);
+static const Uuid kVolumeOffsetOutputDescriptionUuid         = Uuid::From16Bit(0x2B83);
 
-static const Uuid kVolumeAudioInputUuid               = Uuid::From16Bit(0x1843);
-static const Uuid kVolumeAudioInputStateUuid          = Uuid::From16Bit(0x2B77);
-static const Uuid kVolumeAudioInputGainSettingUuid    = Uuid::From16Bit(0x2B78);
-static const Uuid kVolumeAudioInputTypeUuid           = Uuid::From16Bit(0x2B79);
-static const Uuid kVolumeAudioInputStatusUuid         = Uuid::From16Bit(0x2B7A);
-static const Uuid kVolumeAudioInputControlPointUuid   = Uuid::From16Bit(0x2B7B);
-static const Uuid kVolumeAudioInputDescriptionUuid    = Uuid::From16Bit(0x2B7C);
+static const Uuid kVolumeAudioInputUuid                      = Uuid::From16Bit(0x1843);
+static const Uuid kVolumeAudioInputStateUuid                 = Uuid::From16Bit(0x2B77);
+static const Uuid kVolumeAudioInputGainSettingPropertiesUuid = Uuid::From16Bit(0x2B78);
+static const Uuid kVolumeAudioInputTypeUuid                  = Uuid::From16Bit(0x2B79);
+static const Uuid kVolumeAudioInputStatusUuid                = Uuid::From16Bit(0x2B7A);
+static const Uuid kVolumeAudioInputControlPointUuid          = Uuid::From16Bit(0x2B7B);
+static const Uuid kVolumeAudioInputDescriptionUuid           = Uuid::From16Bit(0x2B7C);
 
 /* clang-format on */
 
@@ -127,64 +128,58 @@
 };
 
 struct GainSettings {
-  uint8_t unit;
-  int8_t min;
-  int8_t max;
+  uint8_t unit = 0;
+  int8_t min = 0;
+  int8_t max = 0;
 
-  GainSettings() : unit(0), min(0), max(0) {}
+  GainSettings() {}
 };
 
 struct VolumeAudioInput {
-  uint8_t id;
-  bool mute;
-  int8_t gain_value;
-  VolumeInputStatus status;
-  VolumeInputType type;
-  uint8_t change_counter;
-  uint8_t mode;
-  std::string description;
-  uint16_t service_handle;
-  uint16_t state_handle;
-  uint16_t state_ccc_handle;
-  uint16_t gain_setting_handle;
-  uint16_t type_handle;
-  uint16_t status_handle;
-  uint16_t status_ccc_handle;
-  uint16_t control_point_handle;
-  uint16_t description_handle;
-  uint16_t description_ccc_handle;
-  bool description_writable;
-  struct GainSettings gain_settings;
+  /* const */ uint8_t id;
+  Mute mute = bluetooth::aics::Mute::DISABLED;
+  int8_t gain_setting = 0;
+  VolumeInputStatus status = VolumeInputStatus::Inactive;
+  VolumeInputType type = VolumeInputType::Unspecified;
+  uint8_t change_counter = 0;
+  uint8_t mode = 0;
+  std::string description = "";
+  /* const */ uint16_t service_handle;
+  /* const */ uint16_t state_handle;
+  /* const */ uint16_t state_ccc_handle;
+  /* const */ uint16_t gain_setting_handle;
+  /* const */ uint16_t type_handle;
+  /* const */ uint16_t status_handle;
+  /* const */ uint16_t status_ccc_handle;
+  /* const */ uint16_t control_point_handle;
+  /* const */ uint16_t description_handle;
+  /* const */ uint16_t description_ccc_handle;
+  /* const */ bool description_writable;
+  struct GainSettings gain_settings = GainSettings();
 
-  explicit VolumeAudioInput(uint16_t service_handle)
-      : id(0),
-        mute(false),
-        gain_value(0),
-        status(VolumeInputStatus::Inactive),
-        type(VolumeInputType::Unspecified),
-        change_counter(0),
-        mode(0),
-        description(""),
+  explicit VolumeAudioInput(uint8_t id, uint16_t service_handle, uint16_t state_handle,
+                            uint16_t state_ccc_handle, uint16_t gain_setting_handle,
+                            uint16_t type_handle, uint16_t status_handle,
+                            uint16_t status_ccc_handle, uint16_t control_point_handle,
+                            uint16_t description_handle, uint16_t description_ccc_handle,
+                            bool description_writable)
+      : id(id),
         service_handle(service_handle),
-        state_handle(0),
-        state_ccc_handle(0),
-        gain_setting_handle(0),
-        type_handle(0),
-        status_handle(0),
-        status_ccc_handle(0),
-        control_point_handle(0),
-        description_handle(0),
-        description_ccc_handle(0),
-        description_writable(false),
-        gain_settings(GainSettings()) {}
+        state_handle(state_handle),
+        state_ccc_handle(state_ccc_handle),
+        gain_setting_handle(gain_setting_handle),
+        type_handle(type_handle),
+        status_handle(status_handle),
+        status_ccc_handle(status_ccc_handle),
+        control_point_handle(control_point_handle),
+        description_handle(description_handle),
+        description_ccc_handle(description_ccc_handle),
+        description_writable(description_writable) {}
 };
 
 class VolumeAudioInputs {
 public:
-  void Add(VolumeAudioInput input) {
-    input.id = (uint8_t)Size() + 1;
-    volume_audio_inputs.push_back(input);
-  }
+  void Add(VolumeAudioInput input) { volume_audio_inputs.push_back(input); }
 
   VolumeAudioInput* FindByType(VolumeInputType type) {
     auto iter = std::find_if(volume_audio_inputs.begin(), volume_audio_inputs.end(),
diff --git a/system/bta/vc/vc.cc b/system/bta/vc/vc.cc
index 2c415a5..9c89b98 100644
--- a/system/bta/vc/vc.cc
+++ b/system/bta/vc/vc.cc
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+#include <aics/api.h>
 #include <base/functional/bind.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
@@ -580,24 +581,31 @@
     }
 
     uint8_t* pp = value;
-    STREAM_TO_INT8(input->gain_value, pp);
-    STREAM_TO_UINT8(input->mute, pp);
+    STREAM_TO_INT8(input->gain_setting, pp);
+    uint8_t mute;
+    STREAM_TO_UINT8(mute, pp);
+    if (!bluetooth::aics::isValidAudioInputMuteValue(mute)) {
+      bluetooth::log::error("{} Invalid mute value: {:#x}", device->address, mute);
+      return;
+    }
+    input->mute = bluetooth::aics::parseMuteField(mute);
+
     STREAM_TO_UINT8(input->mode, pp);
     STREAM_TO_UINT8(input->change_counter, pp);
 
     bluetooth::log::verbose("{}, data:{}", device->address, base::HexEncode(value, len));
     bluetooth::log::info(
-            "{} id={:#x}gain_value {:#x}, mute: {:#x}, mode: {:#x}, "
+            "{} id={:#x}gain_setting {:#x}, mute: {:#x}, mode: {:#x}, "
             "change_counter: {}",
-            device->address, input->id, input->gain_value, input->mute, input->mode,
+            device->address, input->id, input->gain_setting, mute, input->mode,
             input->change_counter);
 
     if (!device->device_ready) {
       return;
     }
 
-    callbacks_->OnExtAudioInStateChanged(device->address, input->id, input->gain_value, input->mode,
-                                         input->mute);
+    callbacks_->OnExtAudioInStateChanged(device->address, input->id, input->gain_setting,
+                                         input->mute, input->mode);
   }
 
   void OnExtAudioInTypeChanged(VolumeControlDevice* device, VolumeAudioInput* input, uint16_t len,
@@ -1284,9 +1292,9 @@
     device->SetExtAudioInDescription(ext_input_id, descr);
   }
 
-  void SetExtAudioInGainValue(const RawAddress& address, uint8_t ext_input_id,
-                              int8_t value) override {
-    std::vector<uint8_t> arg({(uint8_t)value});
+  void SetExtAudioInGainSetting(const RawAddress& address, uint8_t ext_input_id,
+                                int8_t gain_setting) override {
+    std::vector<uint8_t> arg({(uint8_t)gain_setting});
     ext_audio_in_control_point_helper(address, ext_input_id, kVolumeInputControlPointOpcodeSetGain,
                                       &arg);
   }
@@ -1503,7 +1511,7 @@
 
       if (position + len >= total_len) {
         bluetooth::log::warn(
-                "Multi read was too long, value truncated conn_id: {:#x} handle: {:#x}, possition: "
+                "Multi read was too long, value truncated conn_id: {:#x} handle: {:#x}, position: "
                 "{:#x}, len: {:#x}, total_len: {:#x}, data: {}",
                 conn_id, hdl, position, len, total_len, base::HexEncode(value, total_len));
         break;
diff --git a/system/bta/vc/vc_test.cc b/system/bta/vc/vc_test.cc
index b0f203b..8b69fb3 100644
--- a/system/bta/vc/vc_test.cc
+++ b/system/bta/vc/vc_test.cc
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+#include <aics/api.h>
 #include <base/functional/bind.h>
 #include <com_android_bluetooth_flags.h>
 #include <gmock/gmock.h>
@@ -48,6 +49,8 @@
   bool on_main_loop = false;
 };
 
+using ::testing::NiceMock;
+
 namespace bluetooth {
 namespace vc {
 namespace internal {
@@ -56,6 +59,7 @@
 using base::Bind;
 using base::Unretained;
 
+using bluetooth::aics::Mute;
 using bluetooth::vc::ConnectionState;
 using bluetooth::vc::VolumeControlCallbacks;
 
@@ -101,8 +105,8 @@
   MOCK_METHOD((void), OnExtAudioOutDescriptionChanged,
               (const RawAddress& address, uint8_t ext_output_id, std::string descr), (override));
   MOCK_METHOD((void), OnExtAudioInStateChanged,
-              (const RawAddress& address, uint8_t ext_input_id, int8_t gain_val,
-               uint8_t gain_mode_auto, bool mute),
+              (const RawAddress& address, uint8_t ext_input_id, int8_t gain_setting, Mute mute,
+               uint8_t gain_mode_auto),
               (override));
   MOCK_METHOD((void), OnExtAudioInStatusChanged,
               (const RawAddress& address, uint8_t ext_input_id, VolumeInputStatus status),
@@ -152,7 +156,7 @@
         builder.AddCharacteristic(0x0031, 0x0032, kVolumeAudioInputStateUuid,
                                   GATT_CHAR_PROP_BIT_READ);
         builder.AddDescriptor(0x0033, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
-        builder.AddCharacteristic(0x0034, 0x0035, kVolumeAudioInputGainSettingUuid,
+        builder.AddCharacteristic(0x0034, 0x0035, kVolumeAudioInputGainSettingPropertiesUuid,
                                   GATT_CHAR_PROP_BIT_READ);
         builder.AddCharacteristic(0x0036, 0x0037, kVolumeAudioInputTypeUuid,
                                   GATT_CHAR_PROP_BIT_READ);
@@ -171,7 +175,7 @@
                                   GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY);
         builder.AddDescriptor(0x0053, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG));
         if (!aics_broken) {
-          builder.AddCharacteristic(0x0054, 0x0055, kVolumeAudioInputGainSettingUuid,
+          builder.AddCharacteristic(0x0054, 0x0055, kVolumeAudioInputGainSettingPropertiesUuid,
                                     GATT_CHAR_PROP_BIT_READ);
         }
         builder.AddCharacteristic(0x0056, 0x0057, kVolumeAudioInputTypeUuid,
@@ -315,7 +319,7 @@
               std::vector<uint8_t> value;
 
               auto add_element = [&](uint8_t data[], uint8_t len) -> void {
-                // LE order, 2 octects
+                // LE order, 2 octets
                 value.push_back(len);
                 value.push_back(0x00);
 
@@ -434,7 +438,6 @@
     MockCsisClient::SetMockInstanceForTesting(&mock_csis_client_module_);
     gatt::SetMockBtaGattInterface(&gatt_interface);
     gatt::SetMockBtaGattQueue(&gatt_queue);
-    callbacks.reset(new MockVolumeControlCallbacks());
     reset_mock_function_count_map();
 
     ON_CALL(btm_interface, IsLinkKeyKnown(_, _)).WillByDefault(DoAll(Return(true)));
@@ -535,7 +538,6 @@
   void TearDown(void) override {
     com::android::bluetooth::flags::provider_->reset_flags();
     services_map.clear();
-    callbacks.reset();
     gatt::SetMockBtaGattQueue(nullptr);
     gatt::SetMockBtaGattInterface(nullptr);
     bluetooth::manager::SetMockBtmInterface(nullptr);
@@ -546,7 +548,7 @@
     BtaAppRegisterCallback app_register_callback;
     EXPECT_CALL(gatt_interface, AppRegister(_, _, _))
             .WillOnce(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
-    VolumeControl::Initialize(callbacks.get(), base::DoNothing());
+    VolumeControl::Initialize(&callbacks, base::DoNothing());
     ASSERT_TRUE(gatt_callback);
     ASSERT_TRUE(app_register_callback);
     app_register_callback.Run(gatt_if, GATT_SUCCESS);
@@ -738,11 +740,12 @@
     set_sample_database(conn_id, true, false, true, false, true, false);
   }
 
-  std::unique_ptr<MockVolumeControlCallbacks> callbacks;
-  bluetooth::manager::MockBtmInterface btm_interface;
+  NiceMock<MockVolumeControlCallbacks> callbacks;
+  NiceMock<bluetooth::manager::MockBtmInterface> btm_interface;
   MockCsisClient mock_csis_client_module_;
-  gatt::MockBtaGattInterface gatt_interface;
-  gatt::MockBtaGattQueue gatt_queue;
+  NiceMock<gatt::MockBtaGattInterface> gatt_interface;
+  NiceMock<gatt::MockBtaGattQueue> gatt_queue;
+
   tBTA_GATTC_CBACK* gatt_callback;
   const uint8_t gatt_if = 0xff;
   std::map<uint16_t, std::list<gatt::Service>> services_map;
@@ -756,7 +759,7 @@
   EXPECT_CALL(gatt_interface, AppRegister(_, _, _))
           .WillOnce(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
   VolumeControl::Initialize(
-          callbacks.get(),
+          &callbacks,
           base::Bind([](bool* init_cb_called) { *init_cb_called = true; }, &init_cb_called));
   ASSERT_TRUE(gatt_callback);
   ASSERT_TRUE(app_register_callback);
@@ -768,15 +771,15 @@
 }
 
 TEST_F(VolumeControlTest, test_initialize_twice) {
-  VolumeControl::Initialize(callbacks.get(), base::DoNothing());
+  VolumeControl::Initialize(&callbacks, base::DoNothing());
   VolumeControl* volume_control_p = VolumeControl::Get();
-  VolumeControl::Initialize(callbacks.get(), base::DoNothing());
+  VolumeControl::Initialize(&callbacks, base::DoNothing());
   ASSERT_EQ(volume_control_p, VolumeControl::Get());
   VolumeControl::CleanUp();
 }
 
 TEST_F(VolumeControlTest, test_cleanup_initialized) {
-  VolumeControl::Initialize(callbacks.get(), base::DoNothing());
+  VolumeControl::Initialize(&callbacks, base::DoNothing());
   VolumeControl::CleanUp();
   ASSERT_FALSE(VolumeControl::IsVolumeControlRunning());
 }
@@ -805,18 +808,18 @@
 
   TestConnect(test_address);
   GetConnectedEvent(test_address, conn_id);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1);
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1);
 
   TestRemove(test_address, conn_id);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1);
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1);
   ON_CALL(btm_interface, IsLinkKeyKnown(_, _)).WillByDefault(DoAll(Return(false)));
 
   VolumeControl::Get()->Connect(test_address);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
   TestAppUnregister();
 }
 
@@ -828,15 +831,15 @@
   SetSampleDatabaseVOCS(1);
   TestAppRegister();
   TestConnect(test_address);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)).Times(0);
-  EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, 2, _)).Times(0);
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)).Times(0);
+  EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 2, _)).Times(0);
   GetConnectedEvent(test_address, 1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 
   // Remote disconnects in the middle of the service discovery
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
   GetDisconnectedEvent(test_address, 1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 
   // This time let the service discovery pass
   ON_CALL(gatt_interface, ServiceSearchRequest(_, _))
@@ -847,19 +850,19 @@
           }));
 
   // Remote is being connected by another GATT client
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
-  EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, 2, _));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 2, _));
   GetConnectedEvent(test_address, 1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 
   // Request connect when the remote was already connected by another service
-  EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, 2, _)).Times(0);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 2, _)).Times(0);
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
   VolumeControl::Get()->Connect(test_address);
   // The GetConnectedEvent(test_address, 1); should not be triggered here, since
   // GATT implementation will not send this event for the already connected
   // device
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 
   TestAppUnregister();
 }
@@ -871,14 +874,14 @@
   TestAddFromStorage(address);
   Mock::VerifyAndClearExpectations(&gatt_interface);
 
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, address)).Times(1);
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, address)).Times(1);
   TestConnect(address);
 
   EXPECT_CALL(gatt_interface, CancelOpen(gatt_if, address, _)).Times(0);
   EXPECT_CALL(gatt_interface, Open(gatt_if, address, BTM_BLE_DIRECT_CONNECTION, true)).Times(1);
 
   GetConnectedEvent(address, 1, GATT_ERROR);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
   Mock::VerifyAndClearExpectations(&gatt_interface);
   TestAppUnregister();
 }
@@ -890,12 +893,12 @@
   SetSampleDatabaseVOCS(1);
   TestAppRegister();
 
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, address)).Times(0);
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, address)).Times(0);
   TestConnect(address);
 
   // Disconnect not connected device - upper layer times out and needs a
   // disconnection event to leave the transient Connecting state
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, address));
   EXPECT_CALL(gatt_interface, CancelOpen(gatt_if, address, false)).Times(0);
   TestDisconnect(address, 0);
 
@@ -909,16 +912,16 @@
               GetSearchCompleteEvent(conn_id);
             }
           }));
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, address));
-  EXPECT_CALL(*callbacks, OnDeviceAvailable(address, 2, _));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, address));
+  EXPECT_CALL(callbacks, OnDeviceAvailable(address, 2, _));
   GetConnectedEvent(address, 1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 
   // Make sure that the upper layer gets the disconnection event even if not
   // connecting actively anymore due to the mentioned time-out mechanism.
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, address));
   GetDisconnectedEvent(address, 1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
   TestAppUnregister();
 }
 
@@ -932,7 +935,7 @@
   const RawAddress test_address = GetTestAddress(0);
   TestAppRegister();
   TestConnect(test_address);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
   TestRemove(test_address, 0);
   TestAppUnregister();
 }
@@ -942,7 +945,7 @@
   TestAppRegister();
   TestConnect(test_address);
   GetConnectedEvent(test_address, 1);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
   TestDisconnect(test_address, 1);
   TestAppUnregister();
 }
@@ -951,7 +954,7 @@
   const RawAddress test_address = GetTestAddress(0);
   TestAppRegister();
   TestConnect(test_address);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
   TestDisconnect(test_address, 0);
   TestAppUnregister();
 }
@@ -961,7 +964,7 @@
   TestAppRegister();
   TestConnect(test_address);
   GetConnectedEvent(test_address, 1);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
   TestDisconnect(test_address, 1);
   TestAppUnregister();
 }
@@ -971,7 +974,7 @@
   TestAppRegister();
   TestConnect(test_address);
   GetConnectedEvent(test_address, 1);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
   GetDisconnectedEvent(test_address, 1);
   TestAppUnregister();
 }
@@ -982,7 +985,7 @@
   TestAddFromStorage(test_address);
   GetConnectedEvent(test_address, 1);
   // autoconnect - don't indicate disconnection
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(0);
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(0);
   GetDisconnectedEvent(test_address, 1);
   TestAppUnregister();
 }
@@ -997,7 +1000,7 @@
           .WillByDefault(Return(tBTM_STATUS::BTM_ERR_KEY_MISSING));
 
   // autoconnect - don't indicate disconnection
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(0);
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(0);
   EXPECT_CALL(gatt_interface, Close(1));
   GetConnectedEvent(test_address, 1);
   Mock::VerifyAndClearExpectations(&btm_interface);
@@ -1010,7 +1013,7 @@
   TestAddFromStorage(test_address);
   SetEncryptionResult(test_address, false);
   // autoconnect - don't indicate disconnection
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(0);
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(0);
   GetConnectedEvent(test_address, 1);
   Mock::VerifyAndClearExpectations(&btm_interface);
   SetEncryptionResult(test_address, true);
@@ -1029,14 +1032,14 @@
   ON_CALL(btm_interface, SetEncryption(test_address, _, _, _, _))
           .WillByDefault(Return(tBTM_STATUS::BTM_SUCCESS));
 
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)).Times(0);
+  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());
+  Mock::VerifyAndClearExpectations(&callbacks);
 
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)).Times(1);
+  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(_, _));
@@ -1044,7 +1047,7 @@
   GetEncryptionCompleteEvt(test_address);
   GetSearchCompleteEvent(conn_id);
 
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
   Mock::VerifyAndClearExpectations(&gatt_interface);
 
   TestAppUnregister();
@@ -1055,11 +1058,11 @@
   SetSampleDatabaseVCS(1);
   TestAppRegister();
   TestConnect(test_address);
-  EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, _, _));
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, _, _));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
   GetConnectedEvent(test_address, 1);
   GetSearchCompleteEvent(1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
   TestAppUnregister();
 }
 
@@ -1068,11 +1071,11 @@
   SetSampleDatabaseNoVCS(1);
   TestAppRegister();
   TestConnect(test_address);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
   GetConnectedEvent(test_address, 1);
 
   GetSearchCompleteEvent(1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
   TestAppUnregister();
 }
 
@@ -1081,10 +1084,10 @@
   SetSampleDatabaseVCSBroken(1);
   TestAppRegister();
   TestConnect(test_address);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
   GetConnectedEvent(test_address, 1);
   GetSearchCompleteEvent(1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
   TestAppUnregister();
 }
 
@@ -1110,7 +1113,7 @@
 
 TEST_F(VolumeControlTest, test_read_vcs_volume_state) {
   const RawAddress test_address = GetTestAddress(0);
-  EXPECT_CALL(*callbacks, OnVolumeStateChanged(test_address, _, _, _, true)).Times(1);
+  EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address, _, _, _, true)).Times(1);
   std::vector<uint16_t> handles({0x0021});
   TestReadCharacteristic(test_address, 1, handles);
 }
@@ -1123,29 +1126,29 @@
 TEST_F(VolumeControlTest, test_read_vocs_volume_offset) {
   com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(false);
   const RawAddress test_address = GetTestAddress(0);
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
   std::vector<uint16_t> handles({0x0072, 0x0082});
   TestReadCharacteristic(test_address, 1, handles);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 }
 
 TEST_F(VolumeControlTest, test_read_vocs_volume_offset_multi) {
   com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(true);
   const RawAddress test_address = GetTestAddress(0);
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
   std::vector<uint16_t> handles({0x0072, 0x0082});
   TestReadCharacteristic(test_address, 1, handles);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 }
 
 TEST_F(VolumeControlTest, test_read_vocs_offset_location) {
@@ -1153,15 +1156,15 @@
   const RawAddress test_address = GetTestAddress(0);
   // It is called twice because after connect read is done once and second read is coming from the
   // test.
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
   std::vector<uint16_t> handles({0x0075, 0x0085});
   TestReadCharacteristic(test_address, 1, handles);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 }
 
 TEST_F(VolumeControlTest, test_read_vocs_offset_location_multi) {
@@ -1169,26 +1172,26 @@
   const RawAddress test_address = GetTestAddress(0);
   // It is called twice because after connect read is done once and second read is coming from the
   // test.
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
   std::vector<uint16_t> handles({0x0075, 0x0085});
   TestReadCharacteristic(test_address, 1, handles);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 }
 
 TEST_F(VolumeControlTest, test_read_vocs_output_description) {
   com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(false);
   const RawAddress test_address = GetTestAddress(0);
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
   std::vector<uint16_t> handles({0x0079, 0x008a});
   TestReadCharacteristic(test_address, 1, handles);
 }
@@ -1196,12 +1199,12 @@
 TEST_F(VolumeControlTest, test_read_vocs_output_description_multi) {
   com::android::bluetooth::flags::provider_->le_ase_read_multiple_variable(true);
   const RawAddress test_address = GetTestAddress(0);
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 1, _)).Times(1);
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, _)).Times(1);
   std::vector<uint16_t> handles({0x0079, 0x008a});
   TestReadCharacteristic(test_address, 1, handles);
 }
@@ -1211,11 +1214,11 @@
   SetSampleDatabaseVOCS(1);
   TestAppRegister();
   TestConnect(test_address);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
-  EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, 2, _));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 2, _));
   GetConnectedEvent(test_address, 1);
   GetSearchCompleteEvent(1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
   TestAppUnregister();
 }
 
@@ -1224,11 +1227,11 @@
   SetSampleDatabaseVCS(1);
   TestAppRegister();
   TestConnect(test_address);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
-  EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, 0, _));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 0, _));
   GetConnectedEvent(test_address, 1);
   GetSearchCompleteEvent(1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
   TestAppUnregister();
 }
 
@@ -1237,17 +1240,17 @@
   SetSampleDatabaseVOCSBroken(1);
   TestAppRegister();
   TestConnect(test_address);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
-  EXPECT_CALL(*callbacks, OnDeviceAvailable(test_address, 1, _));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnDeviceAvailable(test_address, 1, _));
   GetConnectedEvent(test_address, 1);
   GetSearchCompleteEvent(1);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
   TestAppUnregister();
 }
 
 TEST_F(VolumeControlTest, test_read_vcs_database_out_of_sync) {
   const RawAddress test_address = GetTestAddress(0);
-  EXPECT_CALL(*callbacks, OnVolumeStateChanged(test_address, _, _, _, true));
+  EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address, _, _, _, true));
   std::vector<uint16_t> handles({0x0021});
   uint16_t conn_id = 1;
 
@@ -1320,26 +1323,39 @@
 
 TEST_F(VolumeControlCallbackTest, test_volume_state_changed_stress) {
   std::vector<uint8_t> value({0x03, 0x01, 0x02});
-  EXPECT_CALL(*callbacks, OnVolumeStateChanged(test_address, 0x03, true, _, true));
+  EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address, 0x03, true, _, true));
   GetNotificationEvent(0x0021, value);
 }
 
 TEST_F(VolumeControlCallbackTest, test_volume_state_changed_malformed) {
-  EXPECT_CALL(*callbacks, OnVolumeStateChanged(test_address, _, _, _, _)).Times(0);
+  EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address, _, _, _, _)).Times(0);
   std::vector<uint8_t> too_short({0x03, 0x01});
   GetNotificationEvent(0x0021, too_short);
   std::vector<uint8_t> too_long({0x03, 0x01, 0x02, 0x03});
   GetNotificationEvent(0x0021, too_long);
 }
 
-TEST_F(VolumeControlCallbackTest, test_audio_input_state_changed) {
-  std::vector<uint8_t> value({0x03, 0x01, 0x02, 0x04});
-  EXPECT_CALL(*callbacks, OnExtAudioInStateChanged(test_address, 1, 0x03, 0x02, true));
+TEST_F(VolumeControlCallbackTest, audio_input_state_changed__invalid_mute__is_rejected) {
+  uint8_t invalid_mute = 0x03;
+  std::vector<uint8_t> value({0x03, invalid_mute, 0x02, 0x04});
+  EXPECT_CALL(callbacks, OnExtAudioInStateChanged(_, _, _, _, _)).Times(0);
+  GetNotificationEvent(0x0032, value);
+}
+
+TEST_F(VolumeControlCallbackTest, test_audio_input_state_changed__muted) {
+  std::vector<uint8_t> value({0x03, (uint8_t)Mute::MUTED, 0x02, 0x04});
+  EXPECT_CALL(callbacks, OnExtAudioInStateChanged(test_address, _, 0x03, Mute::MUTED, 0x02));
+  GetNotificationEvent(0x0032, value);
+}
+
+TEST_F(VolumeControlCallbackTest, test_audio_input_state_changed__disabled) {
+  std::vector<uint8_t> value({0x03, (uint8_t)Mute::DISABLED, 0x02, 0x04});
+  EXPECT_CALL(callbacks, OnExtAudioInStateChanged(test_address, _, 0x03, Mute::DISABLED, 0x02));
   GetNotificationEvent(0x0032, value);
 }
 
 TEST_F(VolumeControlCallbackTest, test_audio_input_state_changed_malformed) {
-  EXPECT_CALL(*callbacks, OnExtAudioInStateChanged(test_address, 1, _, _, _)).Times(0);
+  EXPECT_CALL(callbacks, OnExtAudioInStateChanged(test_address, _, _, _, _)).Times(0);
   std::vector<uint8_t> too_short({0x03, 0x01, 0x02});
   GetNotificationEvent(0x0032, too_short);
   std::vector<uint8_t> too_long({0x03, 0x01, 0x02, 0x04, 0x05});
@@ -1348,12 +1364,12 @@
 
 TEST_F(VolumeControlCallbackTest, test_audio_gain_props_changed) {
   std::vector<uint8_t> value({0x03, 0x01, 0x02});
-  EXPECT_CALL(*callbacks, OnExtAudioInGainPropsChanged(test_address, 2, 0x03, 0x01, 0x02));
+  EXPECT_CALL(callbacks, OnExtAudioInGainPropsChanged(test_address, _, 0x03, 0x01, 0x02));
   GetNotificationEvent(0x0055, value);
 }
 
 TEST_F(VolumeControlCallbackTest, test_audio_gain_props_changed_malformed) {
-  EXPECT_CALL(*callbacks, OnExtAudioInGainPropsChanged(test_address, 2, _, _, _)).Times(0);
+  EXPECT_CALL(callbacks, OnExtAudioInGainPropsChanged(test_address, _, _, _, _)).Times(0);
   std::vector<uint8_t> too_short({0x03, 0x01});
   GetNotificationEvent(0x0055, too_short);
   std::vector<uint8_t> too_long({0x03, 0x01, 0x02, 0x03});
@@ -1362,13 +1378,13 @@
 
 TEST_F(VolumeControlCallbackTest, test_audio_input_status_changed) {
   std::vector<uint8_t> value({static_cast<uint8_t>(bluetooth::vc::VolumeInputStatus::Inactive)});
-  EXPECT_CALL(*callbacks, OnExtAudioInStatusChanged(test_address, 1,
-                                                    bluetooth::vc::VolumeInputStatus::Inactive));
+  EXPECT_CALL(callbacks, OnExtAudioInStatusChanged(test_address, _,
+                                                   bluetooth::vc::VolumeInputStatus::Inactive));
   GetNotificationEvent(0x0039, value);
 }
 
 TEST_F(VolumeControlCallbackTest, test_audio_input_status_changed_malformed) {
-  EXPECT_CALL(*callbacks, OnExtAudioInStatusChanged(test_address, 1, _)).Times(0);
+  EXPECT_CALL(callbacks, OnExtAudioInStatusChanged(test_address, _, _)).Times(0);
   std::vector<uint8_t> too_short(0);
   GetNotificationEvent(0x0039, too_short);
   std::vector<uint8_t> too_long({0x03, 0x01});
@@ -1378,18 +1394,18 @@
 TEST_F(VolumeControlCallbackTest, test_audio_input_description_changed) {
   std::string descr = "SPDIF";
   std::vector<uint8_t> value(descr.begin(), descr.end());
-  EXPECT_CALL(*callbacks, OnExtAudioInDescriptionChanged(test_address, 2, descr));
+  EXPECT_CALL(callbacks, OnExtAudioInDescriptionChanged(test_address, _, descr));
   GetNotificationEvent(0x005e, value);
 }
 
 TEST_F(VolumeControlCallbackTest, test_volume_offset_changed) {
   std::vector<uint8_t> value({0x04, 0x05, 0x06});
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, 0x0504));
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, 0x0504));
   GetNotificationEvent(0x0082, value);
 }
 
 TEST_F(VolumeControlCallbackTest, test_volume_offset_changed_malformed) {
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(0);
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 2, _)).Times(0);
   std::vector<uint8_t> too_short({0x04});
   GetNotificationEvent(0x0082, too_short);
   std::vector<uint8_t> too_long({0x04, 0x05, 0x06, 0x07});
@@ -1398,12 +1414,12 @@
 
 TEST_F(VolumeControlCallbackTest, test_offset_location_changed) {
   std::vector<uint8_t> value({0x01, 0x02, 0x03, 0x04});
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 2, 0x04030201));
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, 0x04030201));
   GetNotificationEvent(0x0085, value);
 }
 
 TEST_F(VolumeControlCallbackTest, test_offset_location_changed_malformed) {
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(0);
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, _)).Times(0);
   std::vector<uint8_t> too_short({0x04});
   GetNotificationEvent(0x0085, too_short);
   std::vector<uint8_t> too_long({0x04, 0x05, 0x06});
@@ -1413,7 +1429,7 @@
 TEST_F(VolumeControlCallbackTest, test_audio_output_description_changed) {
   std::string descr = "left";
   std::vector<uint8_t> value(descr.begin(), descr.end());
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, descr));
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, descr));
   GetNotificationEvent(0x008a, value);
 }
 
@@ -1449,7 +1465,7 @@
   VolumeControl::Get()->GetExtAudioOutVolumeOffset(test_address, 1);
   EXPECT_TRUE(cb);
   std::vector<uint8_t> value({0x01, 0x02, 0x03});
-  EXPECT_CALL(*callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, 0x0201));
+  EXPECT_CALL(callbacks, OnExtAudioOutVolumeOffsetChanged(test_address, 1, 0x0201));
   cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data);
 }
 
@@ -1457,7 +1473,7 @@
   VolumeControl::Get()->GetExtAudioOutLocation(test_address, 2);
   EXPECT_TRUE(cb);
   std::vector<uint8_t> value({0x01, 0x02, 0x03, 0x04});
-  EXPECT_CALL(*callbacks, OnExtAudioOutLocationChanged(test_address, 2, 0x04030201));
+  EXPECT_CALL(callbacks, OnExtAudioOutLocationChanged(test_address, 2, 0x04030201));
   cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data);
 }
 
@@ -1466,32 +1482,32 @@
   EXPECT_TRUE(cb);
   std::string descr = "right";
   std::vector<uint8_t> value(descr.begin(), descr.end());
-  EXPECT_CALL(*callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, descr));
+  EXPECT_CALL(callbacks, OnExtAudioOutDescriptionChanged(test_address, 2, descr));
   cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data);
 }
 
 TEST_F(VolumeControlValueGetTest, test_get_ext_audio_in_state) {
   VolumeControl::Get()->GetExtAudioInState(test_address, 1);
   EXPECT_TRUE(cb);
-  std::vector<uint8_t> value({0x01, 0x00, 0x02, 0x03});
-  EXPECT_CALL(*callbacks, OnExtAudioInStateChanged(test_address, 1, 0x01, 0x02, false));
+  std::vector<uint8_t> value({0x01, (uint8_t)Mute::NOT_MUTED, 0x02, 0x03});
+  EXPECT_CALL(callbacks, OnExtAudioInStateChanged(test_address, 1, 0x01, Mute::NOT_MUTED, 0x02));
   cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data);
 }
 
 TEST_F(VolumeControlValueGetTest, test_get_ext_audio_in_status) {
-  VolumeControl::Get()->GetExtAudioInStatus(test_address, 2);
+  VolumeControl::Get()->GetExtAudioInStatus(test_address, 0);
   EXPECT_TRUE(cb);
   std::vector<uint8_t> value({static_cast<uint8_t>(bluetooth::vc::VolumeInputStatus::Active)});
-  EXPECT_CALL(*callbacks,
-              OnExtAudioInStatusChanged(test_address, 2, bluetooth::vc::VolumeInputStatus::Active));
+  EXPECT_CALL(callbacks,
+              OnExtAudioInStatusChanged(test_address, 0, bluetooth::vc::VolumeInputStatus::Active));
   cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data);
 }
 
 TEST_F(VolumeControlValueGetTest, test_get_ext_audio_in_gain_props) {
-  VolumeControl::Get()->GetExtAudioInGainProps(test_address, 2);
+  VolumeControl::Get()->GetExtAudioInGainProps(test_address, 0);
   EXPECT_TRUE(cb);
   std::vector<uint8_t> value({0x01, 0x02, 0x03});
-  EXPECT_CALL(*callbacks, OnExtAudioInGainPropsChanged(test_address, 2, 0x01, 0x02, 0x03));
+  EXPECT_CALL(callbacks, OnExtAudioInGainPropsChanged(test_address, 0, 0x01, 0x02, 0x03));
   cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data);
 }
 
@@ -1500,7 +1516,7 @@
   EXPECT_TRUE(cb);
   std::string descr = "AUX-IN";
   std::vector<uint8_t> value(descr.begin(), descr.end());
-  EXPECT_CALL(*callbacks, OnExtAudioInDescriptionChanged(test_address, 1, descr));
+  EXPECT_CALL(callbacks, OnExtAudioInDescriptionChanged(test_address, 1, descr));
   cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data);
 }
 
@@ -1508,7 +1524,7 @@
   VolumeControl::Get()->GetExtAudioInType(test_address, 1);
   EXPECT_TRUE(cb);
   std::vector<uint8_t> value({static_cast<uint8_t>(bluetooth::vc::VolumeInputType::Ambient)});
-  EXPECT_CALL(*callbacks,
+  EXPECT_CALL(callbacks,
               OnExtAudioInTypeChanged(test_address, 1, bluetooth::vc::VolumeInputType::Ambient));
   cb(conn_id, GATT_SUCCESS, handle, (uint16_t)value.size(), value.data(), cb_data);
 }
@@ -1641,11 +1657,11 @@
   ASSERT_NE(active_alarm_cb, nullptr);
 
   EXPECT_CALL(*AlarmMock::Get(), AlarmCancel(_)).Times(1);
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address));
   GetDisconnectedEvent(test_address, conn_id);
 
   ASSERT_EQ(active_alarm_cb, nullptr);
-  Mock::VerifyAndClearExpectations(callbacks.get());
+  Mock::VerifyAndClearExpectations(&callbacks);
 }
 
 TEST_F(VolumeControlValueSetTest, test_set_volume) {
@@ -1835,38 +1851,38 @@
   std::vector<uint8_t> expected_data(descr.begin(), descr.end());
   EXPECT_CALL(gatt_queue,
               WriteCharacteristic(conn_id, 0x005e, expected_data, GATT_WRITE_NO_RSP, _, _));
-  VolumeControl::Get()->SetExtAudioInDescription(test_address, 2, descr);
+  VolumeControl::Get()->SetExtAudioInDescription(test_address, 1, descr);
 }
 
 TEST_F(VolumeControlValueSetTest, test_set_ext_audio_in_description_non_writable) {
   std::string descr = "AUX";
   std::vector<uint8_t> expected_data(descr.begin(), descr.end());
   EXPECT_CALL(gatt_queue, WriteCharacteristic(_, _, _, _, _, _)).Times(0);
-  VolumeControl::Get()->SetExtAudioInDescription(test_address, 1, descr);
+  VolumeControl::Get()->SetExtAudioInDescription(test_address, 0, descr);
 }
 
-TEST_F(VolumeControlValueSetTest, test_set_ext_audio_in_gain_value) {
+TEST_F(VolumeControlValueSetTest, test_set_ext_audio_in_gain_setting) {
   std::vector<uint8_t> expected_data({0x01, 0x00, 0x34});
   EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005c, expected_data, GATT_WRITE, _, _));
-  VolumeControl::Get()->SetExtAudioInGainValue(test_address, 2, 0x34);
+  VolumeControl::Get()->SetExtAudioInGainSetting(test_address, 1, 0x34);
 }
 
 TEST_F(VolumeControlValueSetTest, test_set_ext_audio_in_gain_mode) {
   std::vector<uint8_t> mode_manual({0x04, 0x00});
   EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005c, mode_manual, GATT_WRITE, _, _));
-  VolumeControl::Get()->SetExtAudioInGainMode(test_address, 2, false);
+  VolumeControl::Get()->SetExtAudioInGainMode(test_address, 1, false);
   std::vector<uint8_t> mode_automatic({0x05, 0x00});
   EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005c, mode_automatic, GATT_WRITE, _, _));
-  VolumeControl::Get()->SetExtAudioInGainMode(test_address, 2, true);
+  VolumeControl::Get()->SetExtAudioInGainMode(test_address, 1, true);
 }
 
 TEST_F(VolumeControlValueSetTest, test_set_ext_audio_in_gain_mute) {
   std::vector<uint8_t> mute({0x03, 0x00});
   EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005c, mute, GATT_WRITE, _, _));
-  VolumeControl::Get()->SetExtAudioInGainMute(test_address, 2, true);
+  VolumeControl::Get()->SetExtAudioInGainMute(test_address, 1, true);
   std::vector<uint8_t> unmute({0x02, 0x00});
   EXPECT_CALL(gatt_queue, WriteCharacteristic(conn_id, 0x005c, unmute, GATT_WRITE, _, _));
-  VolumeControl::Get()->SetExtAudioInGainMute(test_address, 2, false);
+  VolumeControl::Get()->SetExtAudioInGainMute(test_address, 1, false);
 }
 
 class VolumeControlCsis : public VolumeControlTest {
@@ -1932,7 +1948,7 @@
   VolumeControl::Get()->SetVolume(group_id, 10);
 
   /* Now inject notification and make sure callback is sent up to Java layer */
-  EXPECT_CALL(*callbacks, OnGroupVolumeStateChanged(group_id, 0x03, true, false));
+  EXPECT_CALL(callbacks, OnGroupVolumeStateChanged(group_id, 0x03, true, false));
 
   std::vector<uint8_t> value({0x03, 0x01, 0x02});
   GetNotificationEvent(conn_id_1, test_address_1, 0x0021, value);
@@ -1947,8 +1963,8 @@
   VolumeControl::Get()->SetVolume(test_address_1, 20);
   VolumeControl::Get()->SetVolume(test_address_2, 20);
 
-  EXPECT_CALL(*callbacks, OnVolumeStateChanged(test_address_1, 20, false, _, false));
-  EXPECT_CALL(*callbacks, OnVolumeStateChanged(test_address_2, 20, false, _, false));
+  EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address_1, 20, false, _, false));
+  EXPECT_CALL(callbacks, OnVolumeStateChanged(test_address_2, 20, false, _, false));
   std::vector<uint8_t> value2({20, 0x00, 0x03});
   GetNotificationEvent(conn_id_1, test_address_1, 0x0021, value2);
   GetNotificationEvent(conn_id_2, test_address_2, 0x0021, value2);
@@ -1983,7 +1999,7 @@
   GetSearchCompleteEvent(conn_id_2);
 
   /* Now inject notification and make sure callback is sent up to Java layer */
-  EXPECT_CALL(*callbacks, OnGroupVolumeStateChanged(group_id, 0x03, false, true));
+  EXPECT_CALL(callbacks, OnGroupVolumeStateChanged(group_id, 0x03, false, true));
 
   std::vector<uint8_t> value({0x03, 0x00, 0x02});
   GetNotificationEvent(conn_id_1, test_address_1, 0x0021, value);
@@ -1999,11 +2015,11 @@
   GetSearchCompleteEvent(conn_id_2);
 
   /* Disconnect one device. */
-  EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address_1));
+  EXPECT_CALL(callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address_1));
   GetDisconnectedEvent(test_address_1, conn_id_1);
 
   /* Now inject notification and make sure callback is sent up to Java layer */
-  EXPECT_CALL(*callbacks, OnGroupVolumeStateChanged(group_id, 0x03, false, true));
+  EXPECT_CALL(callbacks, OnGroupVolumeStateChanged(group_id, 0x03, false, true));
 
   std::vector<uint8_t> value({0x03, 0x00, 0x02});
   GetNotificationEvent(conn_id_2, test_address_2, 0x0021, value);
diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 2402f95..e6bdf90 100644
--- a/system/btif/Android.bp
+++ b/system/btif/Android.bp
@@ -150,6 +150,7 @@
         },
     },
     static_libs: [
+        "aics",
         "avrcp-target-service",
         "bluetooth_flags_c_lib",
         "lib-bt-packets",
@@ -165,6 +166,12 @@
         "libbtif-core",
         "libflatbuffers-cpp",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
+    shared_libs: [
+        "libbinder",
+    ],
     apex_available: [
         "com.android.btservices",
     ],
@@ -243,7 +250,11 @@
         "libflatbuffers-cpp",
         "libstatslog_bt",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
+        "libbinder",
         "libcrypto",
         "libcutils",
     ],
@@ -279,9 +290,13 @@
         "test/btif_storage_test.cc",
     ],
     header_libs: ["libbluetooth_headers"],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
         "libbase",
+        "libbinder",
         "libcrypto",
         "libcutils",
         "libhidlbase",
@@ -290,6 +305,7 @@
         "server_configurable_flags",
     ],
     static_libs: [
+        "aics",
         "android.hardware.audio.common@5.0",
         "android.hardware.bluetooth.audio@2.0",
         "android.hardware.bluetooth.audio@2.1",
@@ -565,9 +581,13 @@
         "BluetoothGeneratedDumpsysDataSchema_h",
     ],
     header_libs: ["libbluetooth_headers"],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
         "libbase",
+        "libbinder",
         "libcrypto",
         "libcutils",
         "libfmq",
@@ -686,9 +706,13 @@
         "BluetoothGeneratedDumpsysDataSchema_h",
     ],
     header_libs: ["libbluetooth_headers"],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
         "libbase",
+        "libbinder",
         "libcrypto",
         "libcutils",
         "libfmq",
diff --git a/system/btif/BUILD.gn b/system/btif/BUILD.gn
index 884dc60..68ca336 100644
--- a/system/btif/BUILD.gn
+++ b/system/btif/BUILD.gn
@@ -93,6 +93,7 @@
     "//bt/system/bta/dm",
     "//bt/system/linux_include",
     "//bt/system/audio_hearing_aid_hw/include",
+    "//bt/system/bta/aics/include",
     "//bt/system/bta/include",
     "//bt/system/bta/sys",
     "//bt/system/device/include",
@@ -114,6 +115,7 @@
     "//bt/system:libbt-platform-protos-lite",
     "//bt/system/common",
     "//bt/system/profile/avrcp:profile_avrcp",
+    "//bt/system/bta/aics:aics",
   ]
 
   configs += [
diff --git a/system/btif/avrcp/avrcp_service.cc b/system/btif/avrcp/avrcp_service.cc
index 712f6e0..a1a4574 100644
--- a/system/btif/avrcp/avrcp_service.cc
+++ b/system/btif/avrcp/avrcp_service.cc
@@ -21,8 +21,13 @@
 #include <base/threading/thread.h>
 #include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
+#include <stdio.h>
 
+#include <cstddef>
+#include <cstdint>
+#include <memory>
 #include <mutex>
+#include <ostream>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -31,14 +36,26 @@
 #include "bta/sys/bta_sys.h"
 #include "btif/include/btif_av.h"
 #include "btif/include/btif_common.h"
+#include "hardware/avrcp/avrcp.h"
+#include "hardware/avrcp/avrcp_common.h"
+#include "internal_include/bt_target.h"
 #include "osi/include/osi.h"
 #include "profile/avrcp/avrcp_config.h"
+#include "profile/avrcp/avrcp_internal.h"
+#include "profile/avrcp/avrcp_sdp_records.h"
+#include "profile/avrcp/avrcp_sdp_service.h"
 #include "profile/avrcp/device.h"
 #include "stack/include/a2dp_api.h"
+#include "stack/include/avct_api.h"
+#include "stack/include/avrc_api.h"
+#include "stack/include/avrc_defs.h"
 #include "stack/include/bt_hdr.h"
 #include "stack/include/bt_uuid16.h"
 #include "stack/include/main_thread.h"
 #include "stack/include/sdp_api.h"
+#include "stack/include/sdp_callback.h"
+#include "stack/include/sdpdefs.h"
+#include "stack/sdp/sdp_discovery_db.h"
 #include "types/bluetooth/uuid.h"
 #include "types/raw_address.h"
 
diff --git a/system/btif/co/bta_av_co.cc b/system/btif/co/bta_av_co.cc
index de0f906..5cd91c8 100644
--- a/system/btif/co/bta_av_co.cc
+++ b/system/btif/co/bta_av_co.cc
@@ -29,7 +29,11 @@
 
 #include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
+#include <stdio.h>
 
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
 #include <mutex>
 #include <optional>
 #include <vector>
@@ -42,16 +46,16 @@
 #include "btif/include/btif_a2dp_source.h"
 #include "btif/include/btif_av.h"
 #include "btif/include/btif_av_co.h"
+#include "device/include/device_iot_conf_defs.h"
 #include "device/include/device_iot_config.h"
 #include "include/hardware/bt_av.h"
-#include "internal_include/bt_trace.h"
+#include "os/logging/log_adapter.h"
 #include "osi/include/allocator.h"
 #include "stack/include/a2dp_codec_api.h"
 #include "stack/include/a2dp_constants.h"
 #include "stack/include/a2dp_ext.h"
 #include "stack/include/avdt_api.h"
 #include "stack/include/bt_hdr.h"
-#include "stack/include/bt_types.h"
 #include "stack/include/bt_uuid16.h"
 #include "types/raw_address.h"
 
diff --git a/system/btif/co/bta_av_co_peer.cc b/system/btif/co/bta_av_co_peer.cc
index 5f9356c..4bc9544 100644
--- a/system/btif/co/bta_av_co_peer.cc
+++ b/system/btif/co/bta_av_co_peer.cc
@@ -20,7 +20,18 @@
 
 #include <bluetooth/log.h>
 
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <mutex>
+#include <vector>
+
 #include "bta/include/bta_av_api.h"
+#include "hardware/bt_av.h"
+#include "stack/include/a2dp_codec_api.h"
+#include "stack/include/avdt_api.h"
+#include "stack/include/bt_types.h"
+#include "types/raw_address.h"
 
 using namespace bluetooth;
 
diff --git a/system/btif/co/bta_hh_co.cc b/system/btif/co/bta_hh_co.cc
index f60a701..12735b6 100644
--- a/system/btif/co/bta_hh_co.cc
+++ b/system/btif/co/bta_hh_co.cc
@@ -18,29 +18,41 @@
 
 #include "bta_hh_co.h"
 
+#include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
 #include <fcntl.h>
+#include <linux/hid.h>
+#include <linux/input.h>
 #include <linux/uhid.h>
 #include <poll.h>
 #include <pthread.h>
-#include <stdint.h>
+#include <sched.h>
 #include <string.h>
 #include <sys/socket.h>
+#include <sys/types.h>
 #include <unistd.h>
 
+#include <array>
 #include <cerrno>
+#include <cstdint>
+#include <cstdio>
+#include <cstring>
 
 #include "bta_hh_api.h"
 #include "btif_config.h"
 #include "btif_hh.h"
+#include "hardware/bt_hh.h"
 #include "hci/controller_interface.h"
 #include "main/shim/entry.h"
+#include "osi/include/alarm.h"
 #include "osi/include/allocator.h"
 #include "osi/include/compat.h"
+#include "osi/include/fixed_queue.h"
 #include "osi/include/osi.h"
 #include "osi/include/properties.h"
 #include "storage/config_keys.h"
 #include "types/raw_address.h"
+
 #define BTA_HH_NV_LOAD_MAX 16
 static tBTA_HH_RPT_CACHE_ENTRY sReportCache[BTA_HH_NV_LOAD_MAX];
 #define BTA_HH_CACHE_REPORT_VERSION 1
diff --git a/system/btif/co/bta_pan_co.cc b/system/btif/co/bta_pan_co.cc
index db84d8b..0bdcae3 100644
--- a/system/btif/co/bta_pan_co.cc
+++ b/system/btif/co/bta_pan_co.cc
@@ -26,18 +26,15 @@
  ******************************************************************************/
 #include "bta_pan_co.h"
 
-#include <hardware/bluetooth.h>
-#include <hardware/bt_pan.h>
-#include <string.h>
+#include <bluetooth/log.h>
 
-#include "bta_api.h"
-#include "bta_pan_api.h"
+#include <cstdint>
+#include <cstring>
+
+#include "bta/include/bta_pan_api.h"
 #include "bta_pan_ci.h"
 #include "btif_pan_internal.h"
-#include "btif_sock_thread.h"
-#include "btif_util.h"
 #include "osi/include/allocator.h"
-#include "pan_api.h"
 #include "stack/include/bt_hdr.h"
 #include "types/raw_address.h"
 
diff --git a/system/btif/include/btif_bqr.h b/system/btif/include/btif_bqr.h
index 71105ef..909335b 100644
--- a/system/btif/include/btif_bqr.h
+++ b/system/btif/include/btif_bqr.h
@@ -88,6 +88,8 @@
 static constexpr uint32_t kQualityEventMaskConnectFail = 0x1 << 7;
 static constexpr uint32_t kQualityEventMaskAdvRFStatsEvent = 0x1 << 8;
 static constexpr uint32_t kQualityEventMaskAdvRFStatsMonitor = 0x1 << 9;
+static constexpr uint32_t kQualityEventMaskHealthMonitorStatsEvent = 0x1 << 10;
+static constexpr uint32_t kQualityEventMaskControllerHealthMonitor = 0x1 << 11;
 static constexpr uint32_t kQualityEventMaskVendorSpecificQuality = 0x1 << 15;
 static constexpr uint32_t kQualityEventMaskLmpMessageTrace = 0x1 << 16;
 static constexpr uint32_t kQualityEventMaskBtSchedulingTrace = 0x1 << 17;
@@ -99,6 +101,7 @@
         kQualityEventMaskRootInflammation | kQualityEventMaskEnergyMonitoring |
         kQualityEventMaskLeAudioChoppy | kQualityEventMaskConnectFail |
         kQualityEventMaskAdvRFStatsEvent | kQualityEventMaskAdvRFStatsMonitor |
+        kQualityEventMaskHealthMonitorStatsEvent | kQualityEventMaskControllerHealthMonitor |
         kQualityEventMaskVendorSpecificQuality | kQualityEventMaskLmpMessageTrace |
         kQualityEventMaskBtSchedulingTrace | kQualityEventMaskControllerDbgInfo |
         kQualityEventMaskVendorSpecificTrace;
@@ -132,6 +135,7 @@
 static constexpr uint8_t kVersion5_0ParamsTotalLen = 7;
 // Added in BQR V6.0
 static constexpr uint8_t kVersion6_0ParamsTotalLen = 6;
+
 // Warning criteria of the RSSI value.
 static constexpr int8_t kCriWarnRssi = -80;
 // Warning criteria of the unused AFH channel count.
@@ -176,7 +180,7 @@
 static constexpr uint16_t kBqrVersion5_0 = 0x103;
 // The REPORT_ACTION_QUERY and BQR_Report_interval starting v1.04(260)
 static constexpr uint16_t kBqrVersion6_0 = 0x104;
-
+static constexpr uint16_t kBqrVersion7_0 = 0x105;
 // Action definition
 //
 // Action to Add, Delete or Clear the reporting of quality event(s).
diff --git a/system/btif/include/stack_manager_t.h b/system/btif/include/stack_manager_t.h
index 0e7f00d..f319fff 100644
--- a/system/btif/include/stack_manager_t.h
+++ b/system/btif/include/stack_manager_t.h
@@ -20,6 +20,8 @@
 
 #include <stdbool.h>
 
+#include <future>
+
 #include "core_callbacks.h"
 #include "osi/include/future.h"
 
@@ -32,7 +34,7 @@
                                ProfileStopCallback);
   void (*shut_down_stack_async)(ProfileStopCallback);
   void (*clean_up_stack)(ProfileStopCallback);
-  void (*start_up_rust_module_async)();
+  void (*start_up_rust_module_async)(std::promise<void> promise);
   void (*shut_down_rust_module_async)();
 
   bool (*get_stack_is_running)(void);
diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc
index c4bb371..1e4b693 100644
--- a/system/btif/src/bluetooth.cc
+++ b/system/btif/src/bluetooth.cc
@@ -27,32 +27,23 @@
 
 #define LOG_TAG "bt_btif"
 
-#include <bluetooth/log.h>
-#include <com_android_bluetooth_flags.h>
-#include <hardware/bluetooth.h>
-#include <hardware/bluetooth_headset_interface.h>
-#include <hardware/bt_av.h>
-#include <hardware/bt_csis.h>
-#include <hardware/bt_gatt.h>
-#include <hardware/bt_has.h>
-#include <hardware/bt_hd.h>
-#include <hardware/bt_hearing_aid.h>
-#include <hardware/bt_hf_client.h>
-#include <hardware/bt_hh.h>
-#include <hardware/bt_le_audio.h>
-#include <hardware/bt_pan.h>
-#include <hardware/bt_rc.h>
-#include <hardware/bt_sdp.h>
-#include <hardware/bt_sock.h>
-#include <hardware/bt_vc.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include "hardware/bluetooth.h"
 
-#include "audio_hal_interface/a2dp_encoding.h"
-#include "bta/hh/bta_hh_int.h"  // for HID HACK profile methods
+#include <base/functional/bind.h>
+#include <base/functional/callback.h>
+#include <bluetooth/log.h>
+
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "bta/hh/bta_hh_int.h"
 #include "bta/include/bta_api.h"
 #include "bta/include/bta_ar_api.h"
+#include "bta/include/bta_av_api.h"
 #include "bta/include/bta_csis_api.h"
 #include "bta/include/bta_has_api.h"
 #include "bta/include/bta_hearing_aid_api.h"
@@ -66,6 +57,7 @@
 #include "btif/include/btif_api.h"
 #include "btif/include/btif_av.h"
 #include "btif/include/btif_bqr.h"
+#include "btif/include/btif_common.h"
 #include "btif/include/btif_config.h"
 #include "btif/include/btif_debug_conn.h"
 #include "btif/include/btif_dm.h"
@@ -91,11 +83,22 @@
 #include "device/include/esco_parameters.h"
 #include "device/include/interop.h"
 #include "device/include/interop_config.h"
+#include "hardware/avrcp/avrcp.h"
+#include "hardware/bt_csis.h"
+#include "hardware/bt_gatt.h"
+#include "hardware/bt_has.h"
+#include "hardware/bt_hearing_aid.h"
+#include "hardware/bt_le_audio.h"
+#include "hardware/bt_rc.h"
+#include "hardware/bt_sdp.h"
+#include "hardware/bt_sock.h"
+#include "hardware/bt_vc.h"
 #include "internal_include/bt_target.h"
 #include "main/shim/dumpsys.h"
 #include "os/parameter_provider.h"
 #include "osi/include/alarm.h"
 #include "osi/include/allocator.h"
+#include "osi/include/properties.h"
 #include "osi/include/stack_power_telemetry.h"
 #include "osi/include/wakelock.h"
 #include "stack/btm/btm_dev.h"
@@ -104,8 +107,14 @@
 #include "stack/include/a2dp_api.h"
 #include "stack/include/avct_api.h"
 #include "stack/include/avdt_api.h"
+#include "stack/include/avrc_api.h"
+#include "stack/include/bnep_api.h"
+#include "stack/include/bt_name.h"
+#include "stack/include/bt_octets.h"
 #include "stack/include/btm_client_interface.h"
 #include "stack/include/btm_status.h"
+#include "stack/include/gatt_api.h"
+#include "stack/include/hcidefs.h"
 #include "stack/include/hfp_lc3_decoder.h"
 #include "stack/include/hfp_lc3_encoder.h"
 #include "stack/include/hfp_msbc_decoder.h"
@@ -116,6 +125,7 @@
 #include "stack/include/pan_api.h"
 #include "stack/include/sdp_api.h"
 #include "storage/config_keys.h"
+#include "types/ble_address_with_type.h"
 #include "types/bt_transport.h"
 #include "types/raw_address.h"
 
@@ -124,7 +134,6 @@
 
 using bluetooth::csis::CsisClientInterface;
 using bluetooth::has::HasClientInterface;
-using bluetooth::hearing_aid::HearingAidInterface;
 using bluetooth::le_audio::LeAudioBroadcasterInterface;
 using bluetooth::le_audio::LeAudioClientInterface;
 using bluetooth::vc::VolumeControlInterface;
@@ -188,7 +197,6 @@
 
 bt_status_t btif_av_sink_execute_service(bool b_enable);
 
-extern void gatt_tcb_dump(int fd);
 extern void bta_gatt_client_dump(int fd);
 
 /*******************************************************************************
@@ -498,7 +506,15 @@
 
 static void cleanup(void) { stack_manager_get_interface()->clean_up_stack(&stop_profiles); }
 
-static void start_rust_module(void) { stack_manager_get_interface()->start_up_rust_module_async(); }
+static void start_rust_module(void) {
+  std::promise<void> rust_up_promise;
+  auto rust_up_future = rust_up_promise.get_future();
+  stack_manager_get_interface()->start_up_rust_module_async(std::move(rust_up_promise));
+  auto status = rust_up_future.wait_for(std::chrono::milliseconds(1000));
+  if (status != std::future_status::ready) {
+    log::error("Failed to wait for rust initialization in time. May lead to unpredictable crash");
+  }
+}
 
 static void stop_rust_module(void) { stack_manager_get_interface()->shut_down_rust_module_async(); }
 
diff --git a/system/btif/src/btif_a2dp.cc b/system/btif/src/btif_a2dp.cc
index 01127c3..f7632bf 100644
--- a/system/btif/src/btif_a2dp.cc
+++ b/system/btif/src/btif_a2dp.cc
@@ -23,17 +23,18 @@
 
 #include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
-#include <stdbool.h>
+
+#include <cstddef>
+#include <cstdint>
 
 #include "audio_hal_interface/a2dp_encoding.h"
+#include "avdt_api.h"
 #include "bta_av_api.h"
 #include "btif_a2dp_sink.h"
 #include "btif_a2dp_source.h"
 #include "btif_av.h"
 #include "btif_av_co.h"
 #include "btif_hf.h"
-#include "btif_util.h"
-#include "internal_include/bt_trace.h"
 #include "types/raw_address.h"
 
 using namespace bluetooth;
diff --git a/system/btif/src/btif_a2dp_sink.cc b/system/btif/src/btif_a2dp_sink.cc
index ec9365a..2d214b0 100644
--- a/system/btif/src/btif_a2dp_sink.cc
+++ b/system/btif/src/btif_a2dp_sink.cc
@@ -26,15 +26,23 @@
 #include <com_android_bluetooth_flags.h>
 
 #include <atomic>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <future>
 #include <mutex>
 #include <string>
+#include <utility>
 
+#include "a2dp_api.h"
+#include "a2dp_codec_api.h"
+#include "avdt_api.h"
+#include "bta_av_api.h"
 #include "btif/include/btif_av.h"
 #include "btif/include/btif_av_co.h"
 #include "btif/include/btif_avrcp_audio_track.h"
 #include "btif/include/btif_util.h"  // CASE_RETURN_STR
 #include "common/message_loop_thread.h"
-#include "hardware/bt_av.h"
 #include "osi/include/alarm.h"
 #include "osi/include/allocator.h"
 #include "osi/include/fixed_queue.h"
diff --git a/system/btif/src/btif_a2dp_source.cc b/system/btif/src/btif_a2dp_source.cc
index ae296a4..3f3bc5a 100644
--- a/system/btif/src/btif_a2dp_source.cc
+++ b/system/btif/src/btif_a2dp_source.cc
@@ -20,22 +20,29 @@
 #define LOG_TAG "bluetooth-a2dp"
 #define ATRACE_TAG ATRACE_TAG_AUDIO
 
-#include <base/run_loop.h>
+#include "btif_a2dp_source.h"
+
+#include <base/functional/bind.h>
 #include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
-#ifdef __ANDROID__
-#include <cutils/trace.h>
-#endif
-
-#include <limits.h>
-#include <string.h>
+#include <stdio.h>
 
 #include <algorithm>
+#include <chrono>
+#include <cstdint>
+#include <cstring>
 #include <future>
+#include <string>
+#include <utility>
+#include <vector>
 
+#include "a2dp_api.h"
+#include "a2dp_codec_api.h"
 #include "audio_hal_interface/a2dp_encoding.h"
+#include "avdt_api.h"
+#include "bt_transport.h"
+#include "bta_av_api.h"
 #include "bta_av_ci.h"
-#include "btif_a2dp_source.h"
 #include "btif_av.h"
 #include "btif_av_co.h"
 #include "btif_common.h"
@@ -46,6 +53,7 @@
 #include "common/metrics.h"
 #include "common/repeating_timer.h"
 #include "common/time_util.h"
+#include "hardware/bt_av.h"
 #include "osi/include/allocator.h"
 #include "osi/include/fixed_queue.h"
 #include "osi/include/wakelock.h"
@@ -58,6 +66,10 @@
 #include "stack/include/main_thread.h"
 #include "types/raw_address.h"
 
+#ifdef __ANDROID__
+#include <cutils/trace.h>
+#endif
+
 using bluetooth::audio::a2dp::BluetoothAudioStatus;
 using bluetooth::common::A2dpSessionMetrics;
 using bluetooth::common::BluetoothMetricsLogger;
diff --git a/system/btif/src/btif_av.cc b/system/btif/src/btif_av.cc
index 2132172..8fa008a 100644
--- a/system/btif/src/btif_av.cc
+++ b/system/btif/src/btif_av.cc
@@ -20,22 +20,33 @@
 
 #include "btif/include/btif_av.h"
 
-#include <android_bluetooth_sysprop.h>
 #include <base/functional/bind.h>
 #include <base/strings/stringprintf.h>
 #include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
 #include <frameworks/proto_logging/stats/enums/bluetooth/a2dp/enums.pb.h>
 #include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h>
+#include <stdio.h>
 
+#include <chrono>
 #include <cstdint>
+#include <cstdio>
+#include <cstring>
 #include <future>
+#include <ios>
+#include <map>
 #include <mutex>
 #include <optional>
+#include <set>
+#include <sstream>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "audio_hal_interface/a2dp_encoding.h"
+#include "bta/include/bta_api.h"
+#include "bta/include/bta_api_data_types.h"
+#include "bta/include/bta_av_api.h"
 #include "btif/avrcp/avrcp_service.h"
 #include "btif/include/btif_a2dp.h"
 #include "btif/include/btif_a2dp_sink.h"
@@ -49,17 +60,23 @@
 #include "btif/include/stack_manager_t.h"
 #include "btif_metrics_logging.h"
 #include "common/state_machine.h"
+#include "device/include/device_iot_conf_defs.h"
 #include "device/include/device_iot_config.h"
+#include "hardware/bluetooth.h"
 #include "hardware/bt_av.h"
 #include "include/hardware/bt_rc.h"
 #include "os/logging/log_adapter.h"
 #include "osi/include/alarm.h"
 #include "osi/include/allocator.h"
 #include "osi/include/properties.h"
+#include "stack/include/a2dp_codec_api.h"
+#include "stack/include/avdt_api.h"
 #include "stack/include/avrc_api.h"
+#include "stack/include/avrc_defs.h"
 #include "stack/include/bt_hdr.h"
 #include "stack/include/bt_uuid16.h"
 #include "stack/include/btm_ble_api.h"
+#include "stack/include/btm_ble_api_types.h"
 #include "stack/include/btm_log_history.h"
 #include "stack/include/main_thread.h"
 #include "types/raw_address.h"
@@ -3217,7 +3234,7 @@
         }
         break;
       } else {
-        FALLTHROUGH_INTENDED;
+        [[fallthrough]];
       }
     }
     case BTA_AV_OFFLOAD_START_RSP_EVT: {
diff --git a/system/btif/src/btif_avrcp_audio_track.cc b/system/btif/src/btif_avrcp_audio_track.cc
index 7a3d27f..fc24963 100644
--- a/system/btif/src/btif_avrcp_audio_track.cc
+++ b/system/btif/src/btif_avrcp_audio_track.cc
@@ -25,17 +25,15 @@
 
 #include <aaudio/AAudio.h>
 #include <bluetooth/log.h>
-#include <utils/StrongPointer.h>
 
 #include <algorithm>
+#include <cstddef>
+#include <cstdint>
 #include <thread>
 
-#include "internal_include/bt_target.h"
-
 // TODO(b/369381361) Enfore -Wmissing-prototypes
 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
 
-using namespace android;
 using namespace bluetooth;
 
 typedef struct {
@@ -80,7 +78,7 @@
 
   trackHolder->stream = stream;
 
-  if (trackHolder != NULL && trackHolder->stream != NULL) {
+  if (trackHolder != nullptr && trackHolder->stream != NULL) {
     log::debug("AAudio Error handle: restart A2dp Sink AudioTrack");
     AAudioStream_requestStart(trackHolder->stream);
   }
diff --git a/system/btif/src/btif_csis_client.cc b/system/btif/src/btif_csis_client.cc
index 2a745ca..65811c6 100644
--- a/system/btif/src/btif_csis_client.cc
+++ b/system/btif/src/btif_csis_client.cc
@@ -18,21 +18,23 @@
 #include <base/functional/bind.h>
 #include <base/location.h>
 #include <bluetooth/log.h>
-#include <hardware/bluetooth.h>
 #include <hardware/bt_csis.h>
 
+#include <atomic>
+#include <memory>
+
 #include "bind_helpers.h"
+#include "bluetooth/uuid.h"
 #include "bta_csis_api.h"
 #include "btif_common.h"
 #include "btif_profile_storage.h"
+#include "raw_address.h"
 #include "stack/include/main_thread.h"
 
 // TODO(b/369381361) Enfore -Wmissing-prototypes
 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
 
 using base::Bind;
-using base::Owned;
-using base::Passed;
 using base::Unretained;
 using bluetooth::csis::ConnectionState;
 using bluetooth::csis::CsisClientCallbacks;
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index 118aa5e..1022425 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -978,7 +978,8 @@
     }
   }
   BTM_LogHistory(kBtmLogTagCallback, bd_addr, "Pin request",
-                 base::StringPrintf("name:\"%s\" min16:%c", PRIVATE_NAME(bd_name.name),
+                 base::StringPrintf("name:\"%s\" min16:%c",
+                                    PRIVATE_NAME(reinterpret_cast<char const*>(bd_name.name)),
                                     (p_pin_req->min_16_digit) ? 'T' : 'F'));
   GetInterfaceToProfiles()->events->invoke_pin_request_cb(bd_addr, bd_name, cod,
                                                           p_pin_req->min_16_digit);
@@ -3550,7 +3551,8 @@
   cod = COD_UNCLASSIFIED;
 
   BTM_LogHistory(kBtmLogTagCallback, bd_addr, "PIN request",
-                 base::StringPrintf("name:'%s'", PRIVATE_NAME(bd_name.name)));
+                 base::StringPrintf("name:'%s'",
+                                    PRIVATE_NAME(reinterpret_cast<char const*>(bd_name.name))));
 
   GetInterfaceToProfiles()->events->invoke_pin_request_cb(bd_addr, bd_name, cod, false);
 }
diff --git a/system/btif/src/btif_gatt_client.cc b/system/btif/src/btif_gatt_client.cc
index a873664..075ed1b 100644
--- a/system/btif/src/btif_gatt_client.cc
+++ b/system/btif/src/btif_gatt_client.cc
@@ -332,24 +332,30 @@
 
   // Determine transport
   if (transport == BT_TRANSPORT_AUTO) {
-    switch (device_type) {
-      case BT_DEVICE_TYPE_BREDR:
-        transport = BT_TRANSPORT_BR_EDR;
-        break;
+    if (com::android::bluetooth::flags::default_gatt_transport()) {
+      // Prefer LE transport when LE is supported
+      transport = (device_type == BT_DEVICE_TYPE_BREDR) ? BT_TRANSPORT_BR_EDR : BT_TRANSPORT_LE;
+    } else {
+      switch (device_type) {
+        case BT_DEVICE_TYPE_BREDR:
+          transport = BT_TRANSPORT_BR_EDR;
+          break;
 
-      case BT_DEVICE_TYPE_BLE:
-        transport = BT_TRANSPORT_LE;
-        break;
+        case BT_DEVICE_TYPE_BLE:
+          transport = BT_TRANSPORT_LE;
+          break;
 
-      case BT_DEVICE_TYPE_DUMO:
-        transport = (addr_type == BLE_ADDR_RANDOM) ? BT_TRANSPORT_LE : BT_TRANSPORT_BR_EDR;
-        break;
+        case BT_DEVICE_TYPE_DUMO:
+          transport = (addr_type == BLE_ADDR_RANDOM) ? BT_TRANSPORT_LE : BT_TRANSPORT_BR_EDR;
+          break;
 
-      default:
-        log::error("Unknown device type {}", DeviceTypeText(device_type));
-        // transport must not be AUTO for finding control blocks. Use LE for backward compatibility.
-        transport = BT_TRANSPORT_LE;
-        break;
+        default:
+          log::error("Unknown device type {}", DeviceTypeText(device_type));
+          // transport must not be AUTO for finding control blocks. Use LE for backward
+          // compatibility.
+          transport = BT_TRANSPORT_LE;
+          break;
+      }
     }
   }
 
diff --git a/system/btif/src/btif_has_client.cc b/system/btif/src/btif_has_client.cc
index 18ae547..c44485d 100644
--- a/system/btif/src/btif_has_client.cc
+++ b/system/btif/src/btif_has_client.cc
@@ -17,23 +17,25 @@
 
 #include <base/functional/bind.h>
 #include <base/location.h>
-#include <hardware/bluetooth.h>
 #include <hardware/bt_has.h>
 
+#include <cstdint>
+#include <memory>
 #include <string>
+#include <utility>
+#include <variant>
 #include <vector>
 
 #include "bta_has_api.h"
 #include "btif_common.h"
 #include "btif_profile_storage.h"
+#include "raw_address.h"
 #include "stack/include/main_thread.h"
 
 // TODO(b/369381361) Enfore -Wmissing-prototypes
 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
 
 using base::Bind;
-using base::Owned;
-using base::Passed;
 using base::Unretained;
 using bluetooth::has::ConnectionState;
 using bluetooth::has::ErrorCode;
diff --git a/system/btif/src/btif_hd.cc b/system/btif/src/btif_hd.cc
index 149b9ae..8b2a673 100644
--- a/system/btif/src/btif_hd.cc
+++ b/system/btif/src/btif_hd.cc
@@ -25,23 +25,29 @@
  *
  *
  ***********************************************************************************/
+
 #define LOG_TAG "BTIF_HD"
 
 #include "btif/include/btif_hd.h"
 
 #include <bluetooth/log.h>
+#include <string.h>
 
+#include <cstddef>
 #include <cstdint>
+#include <cstring>
 
 #include "bta/include/bta_dm_api.h"
 #include "bta/include/bta_hd_api.h"
 #include "bta/sys/bta_sys.h"
+#include "bta_api.h"
 #include "bta_sec_api.h"
 #include "btif/include/btif_common.h"
 #include "btif/include/btif_dm.h"
 #include "btif/include/btif_hh.h"
 #include "btif/include/btif_profile_storage.h"
 #include "btif/include/btif_util.h"
+#include "hardware/bluetooth.h"
 #include "include/hardware/bt_hd.h"
 #include "internal_include/bt_target.h"
 #include "osi/include/allocator.h"
diff --git a/system/btif/src/btif_hearing_aid.cc b/system/btif/src/btif_hearing_aid.cc
index 21b134e..64dc35f 100644
--- a/system/btif/src/btif_hearing_aid.cc
+++ b/system/btif/src/btif_hearing_aid.cc
@@ -22,12 +22,16 @@
 
 #include <base/functional/bind.h>
 #include <base/location.h>
-#include <hardware/bluetooth.h>
 #include <hardware/bt_hearing_aid.h>
 
+#include <cstdint>
+#include <memory>
+#include <utility>
+
 #include "bta_hearing_aid_api.h"
 #include "btif_common.h"
 #include "btif_profile_storage.h"
+#include "hardware/avrcp/avrcp.h"
 #include "stack/include/main_thread.h"
 #include "types/raw_address.h"
 
diff --git a/system/btif/src/btif_hf.cc b/system/btif/src/btif_hf.cc
index 372bdf4..8edc70a 100644
--- a/system/btif/src/btif_hf.cc
+++ b/system/btif/src/btif_hf.cc
@@ -30,27 +30,40 @@
 #include "btif/include/btif_hf.h"
 
 #include <android_bluetooth_sysprop.h>
+#include <base/functional/bind.h>
 #include <base/functional/callback.h>
 #include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
 #include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h>
 
+#include <cstddef>
 #include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <sstream>
 #include <string>
+#include <vector>
 
+#include "bta/ag/bta_ag_int.h"
 #include "bta/include/bta_ag_api.h"
+#include "bta/include/bta_api.h"
 #include "bta/include/utl.h"
 #include "bta_ag_swb_aptx.h"
 #include "btif/include/btif_common.h"
 #include "btif/include/btif_metrics_logging.h"
 #include "btif/include/btif_profile_queue.h"
 #include "btif/include/btif_util.h"
+#include "btm_api_types.h"
 #include "common/metrics.h"
+#include "device/include/device_iot_conf_defs.h"
 #include "device/include/device_iot_config.h"
+#include "hardware/bluetooth.h"
 #include "include/hardware/bluetooth_headset_callbacks.h"
 #include "include/hardware/bluetooth_headset_interface.h"
 #include "include/hardware/bt_hf.h"
 #include "internal_include/bt_target.h"
+#include "os/logging/log_adapter.h"
 #include "stack/btm/btm_sco_hfp_hal.h"
 #include "stack/include/bt_uuid16.h"
 #include "stack/include/btm_client_interface.h"
diff --git a/system/btif/src/btif_hf_client.cc b/system/btif/src/btif_hf_client.cc
index 08e06e1..3049545 100644
--- a/system/btif/src/btif_hf_client.cc
+++ b/system/btif/src/btif_hf_client.cc
@@ -49,8 +49,12 @@
 #include <bluetooth/log.h>
 #include <hardware/bluetooth.h>
 #include <hardware/bt_hf_client.h>
-#include <string.h>
 
+#include <cstdint>
+#include <cstring>
+
+#include "bta/include/bta_api.h"
+#include "bta/include/bta_hfp_api.h"
 #include "bta_hf_client_api.h"
 #include "btif_common.h"
 #include "btif_profile_queue.h"
diff --git a/system/btif/src/btif_hh.cc b/system/btif/src/btif_hh.cc
index e01e2d8..e1a42df 100644
--- a/system/btif/src/btif_hh.cc
+++ b/system/btif/src/btif_hh.cc
@@ -29,11 +29,23 @@
 
 #include "btif/include/btif_hh.h"
 
+#include <base/functional/bind.h>
 #include <bluetooth/log.h>
 #include <com_android_bluetooth_flags.h>
+#include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h>
+#include <unistd.h>
 
+#include <algorithm>
+#include <cstddef>
 #include <cstdint>
+#include <cstring>
 
+#include "ble_address_with_type.h"
+#include "bluetooth/uuid.h"
+#include "bt_device_type.h"
+#include "bt_transport.h"
+#include "bta_api.h"
+#include "bta_hh_api.h"
 #include "bta_hh_co.h"
 #include "bta_sec_api.h"
 #include "btif/include/btif_common.h"
@@ -43,12 +55,14 @@
 #include "btif/include/btif_profile_storage.h"
 #include "btif/include/btif_storage.h"
 #include "btif/include/btif_util.h"
+#include "hardware/bluetooth.h"
 #include "include/hardware/bt_hh.h"
+#include "internal_include/bt_target.h"
 #include "main/shim/dumpsys.h"
+#include "osi/include/alarm.h"
 #include "osi/include/allocator.h"
 #include "stack/include/bt_hdr.h"
 #include "stack/include/bt_uuid16.h"
-#include "stack/include/btm_ble_api.h"
 #include "stack/include/btm_client_interface.h"
 #include "stack/include/hidh_api.h"
 #include "types/raw_address.h"
diff --git a/system/btif/src/btif_iot_config.cc b/system/btif/src/btif_iot_config.cc
index 1e70657..9db2159 100644
--- a/system/btif/src/btif_iot_config.cc
+++ b/system/btif/src/btif_iot_config.cc
@@ -18,9 +18,16 @@
 
 #include <bluetooth/log.h>
 
+#include <cstdint>
+#include <cstring>
+
+#include "bt_name.h"
 #include "bta_sec_api.h"
 #include "btif_storage.h"
+#include "device/include/device_iot_conf_defs.h"
 #include "device/include/device_iot_config.h"
+#include "hardware/bluetooth.h"
+#include "raw_address.h"
 #include "stack/include/btm_ble_api.h"
 #include "stack/include/btm_client_interface.h"
 
diff --git a/system/btif/src/btif_le_audio.cc b/system/btif/src/btif_le_audio.cc
index 0e4ab09..8498dab 100644
--- a/system/btif/src/btif_le_audio.cc
+++ b/system/btif/src/btif_le_audio.cc
@@ -16,14 +16,17 @@
  */
 
 #include <bluetooth/log.h>
-#include <hardware/bluetooth.h>
 #include <hardware/bt_le_audio.h>
 
+#include <atomic>
+#include <cstdint>
+#include <memory>
 #include <vector>
 
 #include "bta_le_audio_api.h"
 #include "btif_common.h"
 #include "btif_profile_storage.h"
+#include "raw_address.h"
 #include "stack/include/main_thread.h"
 
 // TODO(b/369381361) Enfore -Wmissing-prototypes
diff --git a/system/btif/src/btif_le_audio_broadcaster.cc b/system/btif/src/btif_le_audio_broadcaster.cc
index 45c4b5b..253c0e0 100644
--- a/system/btif/src/btif_le_audio_broadcaster.cc
+++ b/system/btif/src/btif_le_audio_broadcaster.cc
@@ -16,9 +16,15 @@
  */
 
 #include <base/functional/bind.h>
-#include <hardware/bluetooth.h>
 #include <hardware/bt_le_audio.h>
 
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
 #include "bta_le_audio_api.h"
 #include "bta_le_audio_broadcaster_api.h"
 #include "btif_common.h"
diff --git a/system/btif/src/btif_pan.cc b/system/btif/src/btif_pan.cc
index 53c7cdb..1f0bcb1 100644
--- a/system/btif/src/btif_pan.cc
+++ b/system/btif/src/btif_pan.cc
@@ -29,7 +29,6 @@
 
 #include "btif/include/btif_pan.h"
 
-#include <android_bluetooth_sysprop.h>
 #include <arpa/inet.h>
 #include <base/functional/bind.h>
 #include <base/location.h>
@@ -39,13 +38,24 @@
 #include <linux/if_tun.h>
 #include <net/if.h>
 #include <poll.h>
+#include <string.h>
 #include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
 #include <unistd.h>
 
+#include <cerrno>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+
 #include "bta/include/bta_pan_api.h"
 #include "btif/include/btif_common.h"
 #include "btif/include/btif_pan_internal.h"
 #include "btif/include/btif_sock_thread.h"
+#include "hardware/bluetooth.h"
 #include "hci/controller_interface.h"
 #include "include/hardware/bt_pan.h"
 #include "internal_include/bt_target.h"
@@ -53,6 +63,7 @@
 #include "main/shim/helpers.h"
 #include "osi/include/allocator.h"
 #include "osi/include/compat.h"
+#include "osi/include/osi.h"
 #include "stack/include/bt_hdr.h"
 #include "stack/include/main_thread.h"
 #include "stack/include/pan_api.h"
diff --git a/system/btif/src/btif_profile_queue.cc b/system/btif/src/btif_profile_queue.cc
index 15237ab..38d00f4 100644
--- a/system/btif/src/btif_profile_queue.cc
+++ b/system/btif/src/btif_profile_queue.cc
@@ -33,10 +33,13 @@
 #include <bluetooth/log.h>
 #include <string.h>
 
+#include <cstdint>
 #include <list>
+#include <string>
 
 #include "btif/include/stack_manager_t.h"
 #include "btif_common.h"
+#include "hardware/bluetooth.h"
 #include "types/raw_address.h"
 
 using namespace bluetooth;
diff --git a/system/btif/src/btif_profile_storage.cc b/system/btif/src/btif_profile_storage.cc
index d0949d6..2301019 100644
--- a/system/btif/src/btif_profile_storage.cc
+++ b/system/btif/src/btif_profile_storage.cc
@@ -15,6 +15,7 @@
  *  limitations under the License.
  *
  ******************************************************************************/
+
 #define LOG_TAG "bt_btif_profile_storage"
 
 #include "btif_profile_storage.h"
@@ -26,8 +27,13 @@
 #include <string.h>
 #include <time.h>
 
+#include <cstdint>
+#include <string>
+#include <utility>
 #include <vector>
 
+#include "ble_address_with_type.h"
+#include "bt_transport.h"
 #include "bta_csis_api.h"
 #include "bta_groups.h"
 #include "bta_has_api.h"
@@ -41,6 +47,7 @@
 #include "btif_config.h"
 #include "btif_hh.h"
 #include "btif_storage.h"
+#include "hardware/bluetooth.h"
 #include "stack/include/bt_uuid16.h"
 #include "stack/include/main_thread.h"
 #include "storage/config_keys.h"
diff --git a/system/btif/src/btif_rc.cc b/system/btif/src/btif_rc.cc
index cf24440..5b97f45 100644
--- a/system/btif/src/btif_rc.cc
+++ b/system/btif/src/btif_rc.cc
@@ -26,17 +26,19 @@
 
 #include "btif_rc.h"
 
+#include <base/functional/bind.h>
 #include <bluetooth/log.h>
-#include <fcntl.h>
 #include <hardware/bluetooth.h>
 #include <hardware/bt_rc.h>
-#include <pthread.h>
+#include <stdio.h>
 #include <string.h>
 #include <time.h>
-#include <unistd.h>
 
+#include <cstdint>
 #include <cstdio>
 #include <mutex>
+#include <sstream>
+#include <string>
 
 #include "bta/include/bta_av_api.h"
 #include "btif/avrcp/avrcp_service.h"
@@ -48,6 +50,7 @@
 #include "osi/include/alarm.h"
 #include "osi/include/allocator.h"
 #include "osi/include/list.h"
+#include "osi/include/osi.h"
 #include "osi/include/properties.h"
 #include "stack/include/avrc_api.h"
 #include "stack/include/avrc_defs.h"
@@ -1246,7 +1249,7 @@
 bool btif_rc_is_connected_peer(const RawAddress& peer_addr) {
   for (int idx = 0; idx < BTIF_RC_NUM_CONN; idx++) {
     btif_rc_device_cb_t* p_dev = get_connected_device(idx);
-    if (p_dev != NULL && (p_dev->rc_connected == TRUE) && peer_addr == p_dev->rc_addr) {
+    if (p_dev != NULL && p_dev->rc_connected && peer_addr == p_dev->rc_addr) {
       return true;
     }
   }
@@ -1666,7 +1669,7 @@
     case AVRC_PDU_REQUEST_CONTINUATION_RSP: {
       log::verbose("REQUEST CONTINUATION: target_pdu: 0x{:02d}", pavrc_cmd->continu.target_pdu);
       tAVRC_RESPONSE avrc_rsp;
-      if (p_dev->rc_connected == TRUE) {
+      if (p_dev->rc_connected) {
         memset(&(avrc_rsp.continu), 0, sizeof(tAVRC_NEXT_RSP));
         avrc_rsp.continu.opcode = opcode_from_pdu(AVRC_PDU_REQUEST_CONTINUATION_RSP);
         avrc_rsp.continu.pdu = AVRC_PDU_REQUEST_CONTINUATION_RSP;
@@ -1679,7 +1682,7 @@
     case AVRC_PDU_ABORT_CONTINUATION_RSP: {
       log::verbose("ABORT CONTINUATION: target_pdu: 0x{:02d}", pavrc_cmd->abort.target_pdu);
       tAVRC_RESPONSE avrc_rsp;
-      if (p_dev->rc_connected == TRUE) {
+      if (p_dev->rc_connected) {
         memset(&(avrc_rsp.abort), 0, sizeof(tAVRC_NEXT_RSP));
         avrc_rsp.abort.opcode = opcode_from_pdu(AVRC_PDU_ABORT_CONTINUATION_RSP);
         avrc_rsp.abort.pdu = AVRC_PDU_ABORT_CONTINUATION_RSP;
diff --git a/system/btif/src/btif_vc.cc b/system/btif/src/btif_vc.cc
index e106aed..22b5091 100644
--- a/system/btif/src/btif_vc.cc
+++ b/system/btif/src/btif_vc.cc
@@ -17,12 +17,19 @@
 
 /* Volume Control Interface */
 
+#include <aics/api.h>
 #include <base/functional/bind.h>
 #include <base/location.h>
 #include <bluetooth/log.h>
-#include <hardware/bluetooth.h>
 #include <hardware/bt_vc.h>
 
+#include <atomic>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <variant>
+
 #include "bta/include/bta_vc_api.h"
 #include "btif/include/btif_common.h"
 #include "btif/include/btif_profile_storage.h"
@@ -34,6 +41,7 @@
 
 using base::Bind;
 using base::Unretained;
+using bluetooth::aics::Mute;
 using bluetooth::vc::ConnectionState;
 using bluetooth::vc::VolumeControlCallbacks;
 using bluetooth::vc::VolumeControlInterface;
@@ -102,10 +110,10 @@
   }
 
   /* Callbacks for Audio Input Stream (AIS) - Extended Audio Inputs */
-  void OnExtAudioInStateChanged(const RawAddress& address, uint8_t ext_input_id, int8_t gain_val,
-                                uint8_t gain_mode, bool mute) override {
+  void OnExtAudioInStateChanged(const RawAddress& address, uint8_t ext_input_id,
+                                int8_t gain_setting, ::Mute mute, uint8_t gain_mode) override {
     do_in_jni_thread(Bind(&VolumeControlCallbacks::OnExtAudioInStateChanged, Unretained(callbacks_),
-                          address, ext_input_id, gain_val, gain_mode, mute));
+                          address, ext_input_id, gain_setting, mute, gain_mode));
   }
 
   void OnExtAudioInStatusChanged(const RawAddress& address, uint8_t ext_input_id,
@@ -351,8 +359,8 @@
                            Unretained(VolumeControl::Get()), address, ext_input_id, descr));
   }
 
-  void SetExtAudioInGainValue(const RawAddress& address, uint8_t ext_input_id,
-                              int8_t value) override {
+  void SetExtAudioInGainSetting(const RawAddress& address, uint8_t ext_input_id,
+                                int8_t gain_setting) override {
     if (!initialized || !VolumeControl::IsVolumeControlRunning()) {
       bluetooth::log::verbose(
               "call ignored, due to already started cleanup procedure or service "
@@ -360,8 +368,8 @@
       return;
     }
 
-    do_in_main_thread(Bind(&VolumeControl::SetExtAudioInGainValue, Unretained(VolumeControl::Get()),
-                           address, ext_input_id, value));
+    do_in_main_thread(Bind(&VolumeControl::SetExtAudioInGainSetting,
+                           Unretained(VolumeControl::Get()), address, ext_input_id, gain_setting));
   }
 
   void SetExtAudioInGainMode(const RawAddress& address, uint8_t ext_input_id,
diff --git a/system/btif/src/stack_manager.cc b/system/btif/src/stack_manager.cc
index 93407fc..ace7f8e 100644
--- a/system/btif/src/stack_manager.cc
+++ b/system/btif/src/stack_manager.cc
@@ -125,7 +125,7 @@
                                  ProfileStopCallback stopProfiles);
 static void event_shut_down_stack(ProfileStopCallback stopProfiles);
 static void event_clean_up_stack(std::promise<void> promise, ProfileStopCallback stopProfiles);
-static void event_start_up_rust_module();
+static void event_start_up_rust_module(std::promise<void> promise);
 static void event_shut_down_rust_module();
 
 static void event_signal_stack_up(void* context);
@@ -183,8 +183,9 @@
   }
 }
 
-static void start_up_rust_module_async() {
-  management_thread.DoInThread(FROM_HERE, base::BindOnce(event_start_up_rust_module));
+static void start_up_rust_module_async(std::promise<void> promise) {
+  management_thread.DoInThread(FROM_HERE,
+                               base::BindOnce(event_start_up_rust_module, std::move(promise)));
 }
 
 static void shut_down_rust_module_async() {
@@ -391,9 +392,10 @@
   info("finished");
 }
 
-static void event_start_up_rust_module() {
+static void event_start_up_rust_module(std::promise<void> promise) {
   info("is bringing up the Rust module");
   module_start_up(get_local_module(RUST_MODULE));
+  promise.set_value();
   info("finished");
 }
 
diff --git a/system/gd/Android.bp b/system/gd/Android.bp
index c74cff9..ff26e0b 100644
--- a/system/gd/Android.bp
+++ b/system/gd/Android.bp
@@ -58,13 +58,13 @@
     soong_config_variables: {
         vertical: {
             android_desktop: {
-                srcs: [":BluetoothHalSources_mgmt"],
+                srcs: [":BluetoothOsSources_mgmt_linux"],
             },
             android_default: {
-                srcs: [":BluetoothHalSources_mgmt_stub"],
+                srcs: [":BluetoothOsSources_mgmt"],
             },
             conditions_default: {
-                srcs: [":BluetoothHalSources_mgmt_stub"],
+                srcs: [":BluetoothOsSources_mgmt"],
             },
         },
     },
diff --git a/system/gd/hal/Android.bp b/system/gd/hal/Android.bp
index 1086c31..91e1573 100644
--- a/system/gd/hal/Android.bp
+++ b/system/gd/hal/Android.bp
@@ -52,20 +52,6 @@
 }
 
 filegroup {
-    name: "BluetoothHalSources_mgmt_stub",
-    srcs: [
-        "mgmt_stub.cc",
-    ],
-}
-
-filegroup {
-    name: "BluetoothHalSources_mgmt",
-    srcs: [
-        "mgmt.cc",
-    ],
-}
-
-filegroup {
     name: "BluetoothHalSources_ranging_android",
     srcs: [
         "ranging_hal_android.cc",
diff --git a/system/gd/hal/BUILD.gn b/system/gd/hal/BUILD.gn
index f0a2f9d..46be6e9 100644
--- a/system/gd/hal/BUILD.gn
+++ b/system/gd/hal/BUILD.gn
@@ -34,12 +34,10 @@
   if (use.floss_rootcanal) {
     sources = [
       "hci_hal_host_rootcanal.cc",
-      "mgmt.cc",
     ]
   } else {
     sources = [
       "hci_hal_host.cc",
-      "mgmt.cc",
     ]
   }
 
diff --git a/system/gd/hal/hci_hal_android.cc b/system/gd/hal/hci_hal_android.cc
index 3e66f98..51bc789 100644
--- a/system/gd/hal/hci_hal_android.cc
+++ b/system/gd/hal/hci_hal_android.cc
@@ -24,8 +24,8 @@
 #include "hal/hci_backend.h"
 #include "hal/hci_hal.h"
 #include "hal/link_clocker.h"
-#include "hal/mgmt.h"
 #include "hal/snoop_logger.h"
+#include "os/mgmt.h"
 
 namespace bluetooth::hal {
 
@@ -158,7 +158,9 @@
     backend_->sendIsoData(packet);
   }
 
-  uint16_t getMsftOpcode() override { return Mgmt().get_vs_opcode(MGMT_VS_OPCODE_MSFT); }
+  uint16_t getMsftOpcode() override {
+    return os::Management::getInstance().getVendorSpecificCode(MGMT_VS_OPCODE_MSFT);
+  }
 
 protected:
   void ListDependencies(ModuleList* list) const override {
diff --git a/system/gd/hal/hci_hal_host.cc b/system/gd/hal/hci_hal_host.cc
index 79e1cab..e00554c 100644
--- a/system/gd/hal/hci_hal_host.cc
+++ b/system/gd/hal/hci_hal_host.cc
@@ -34,9 +34,9 @@
 
 #include "hal/hci_hal.h"
 #include "hal/link_clocker.h"
-#include "hal/mgmt.h"
 #include "hal/snoop_logger.h"
 #include "metrics/counter_metrics.h"
+#include "os/mgmt.h"
 #include "os/reactor.h"
 #include "os/thread.h"
 
@@ -312,7 +312,9 @@
     write_to_fd(packet);
   }
 
-  uint16_t getMsftOpcode() override { return Mgmt().get_vs_opcode(MGMT_VS_OPCODE_MSFT); }
+  uint16_t getMsftOpcode() override {
+    return os::Management::getInstance().getVendorSpecificCode(MGMT_VS_OPCODE_MSFT);
+  }
 
   void markControllerBroken() override {
     std::lock_guard<std::mutex> lock(api_mutex_);
diff --git a/system/gd/hal/ranging_hal.h b/system/gd/hal/ranging_hal.h
index 981f16b..40afe0b 100644
--- a/system/gd/hal/ranging_hal.h
+++ b/system/gd/hal/ranging_hal.h
@@ -23,6 +23,12 @@
 namespace bluetooth {
 namespace hal {
 
+enum RangingHalVersion {
+  V_UNKNOWN = 0,
+  V_1 = 1,
+  V_2 = 2,
+};
+
 struct VendorSpecificCharacteristic {
   std::array<uint8_t, 16> characteristicUuid_;
   std::vector<uint8_t> value_;
diff --git a/system/gd/hal/ranging_hal_android.cc b/system/gd/hal/ranging_hal_android.cc
index c50ee18..c24a40c 100644
--- a/system/gd/hal/ranging_hal_android.cc
+++ b/system/gd/hal/ranging_hal_android.cc
@@ -109,6 +109,8 @@
 public:
   bool IsBound() override { return bluetooth_channel_sounding_ != nullptr; }
 
+  RangingHalVersion GetRangingHalVersion() { return hal_ver_; }
+
   void RegisterCallback(RangingHalCallback* callback) { ranging_hal_callback_ = callback; }
 
   std::vector<VendorSpecificCharacteristic> GetVendorSpecificCharacteristics() override {
@@ -248,6 +250,17 @@
 protected:
   void ListDependencies(ModuleList* /*list*/) const {}
 
+  RangingHalVersion get_ranging_hal_version() {
+    int ver = 0;
+    auto aidl_ret = bluetooth_channel_sounding_->getInterfaceVersion(&ver);
+    if (aidl_ret.isOk()) {
+      log::info("ranging HAL version - {}", ver);
+      return static_cast<RangingHalVersion>(ver);
+    }
+    log::warn("ranging HAL version is not available.");
+    return RangingHalVersion::V_UNKNOWN;
+  }
+
   void Start() override {
     std::string instance = std::string() + IBluetoothChannelSounding::descriptor + "/default";
     log::info("AServiceManager_isDeclared {}", AServiceManager_isDeclared(instance.c_str()));
@@ -255,6 +268,9 @@
       ::ndk::SpAIBinder binder(AServiceManager_waitForService(instance.c_str()));
       bluetooth_channel_sounding_ = IBluetoothChannelSounding::fromBinder(binder);
       log::info("Bind IBluetoothChannelSounding {}", IsBound() ? "Success" : "Fail");
+      if (bluetooth_channel_sounding_ != nullptr) {
+        hal_ver_ = get_ranging_hal_version();
+      }
     }
   }
 
@@ -267,6 +283,7 @@
   RangingHalCallback* ranging_hal_callback_;
   std::unordered_map<uint16_t, std::shared_ptr<BluetoothChannelSoundingSessionTracker>>
           session_trackers_;
+  RangingHalVersion hal_ver_;
 };
 
 const ModuleFactory RangingHal::Factory = ModuleFactory([]() { return new RangingHalAndroid(); });
diff --git a/system/gd/hal/ranging_hal_host.cc b/system/gd/hal/ranging_hal_host.cc
index 36c821f..b11d20d 100644
--- a/system/gd/hal/ranging_hal_host.cc
+++ b/system/gd/hal/ranging_hal_host.cc
@@ -27,6 +27,7 @@
 class RangingHalHost : public RangingHal {
 public:
   bool IsBound() override { return false; }
+  RangingHalVersion GetRangingHalVersion() { return V_UNKNOWN; }
   void RegisterCallback(RangingHalCallback* /* callback */) override {}
   std::vector<VendorSpecificCharacteristic> GetVendorSpecificCharacteristics() override {
     std::vector<VendorSpecificCharacteristic> vendor_specific_characteristics = {};
diff --git a/system/gd/hal/snoop_logger.cc b/system/gd/hal/snoop_logger.cc
index 6b9fc8a..0fc1939 100644
--- a/system/gd/hal/snoop_logger.cc
+++ b/system/gd/hal/snoop_logger.cc
@@ -23,7 +23,7 @@
 #include <com_android_bluetooth_flags.h>
 #ifdef __ANDROID__
 #include <cutils/trace.h>
-#endif // __ANDROID__
+#endif  // __ANDROID__
 #include <sys/stat.h>
 
 #include <algorithm>
@@ -469,8 +469,6 @@
 // PBAP, MAP and HFP packets filter mode - disabled
 const std::string SnoopLogger::kBtSnoopLogFilterProfileModeDisabled = "disabled";
 
-std::string SnoopLogger::btsnoop_mode_;
-
 // Consts accessible in unit tests
 const size_t SnoopLogger::PACKET_TYPE_LENGTH = 1;
 const size_t SnoopLogger::MAX_HCI_ACL_LEN = 14;
@@ -482,7 +480,8 @@
                          const std::chrono::milliseconds snooz_log_life_time,
                          const std::chrono::milliseconds snooz_log_delete_alarm_interval,
                          bool snoop_log_persists)
-    : snoop_log_path_(std::move(snoop_log_path)),
+    : btsnoop_mode_(btsnoop_mode),
+      snoop_log_path_(std::move(snoop_log_path)),
       snooz_log_path_(std::move(snooz_log_path)),
       max_packets_per_file_(max_packets_per_file),
       btsnooz_buffer_(max_packets_per_buffer),
@@ -490,8 +489,6 @@
       snooz_log_life_time_(snooz_log_life_time),
       snooz_log_delete_alarm_interval_(snooz_log_delete_alarm_interval),
       snoop_log_persists(snoop_log_persists) {
-  btsnoop_mode_ = btsnoop_mode;
-
   if (btsnoop_mode_ == kBtSnoopLogModeFiltered) {
     log::info("Snoop Logs filtered mode enabled");
     EnableFilters();
@@ -566,9 +563,6 @@
 }
 
 void SnoopLogger::EnableFilters() {
-  if (btsnoop_mode_ != kBtSnoopLogModeFiltered) {
-    return;
-  }
   std::lock_guard<std::mutex> lock(snoop_log_filters_mutex);
   for (auto itr = kBtSnoopLogFilterState.begin(); itr != kBtSnoopLogFilterState.end(); itr++) {
     auto filter_enabled_property = os::GetSystemProperty(itr->first);
@@ -1135,7 +1129,7 @@
   if (com::android::bluetooth::flags::snoop_logger_tracing()) {
     LogTracePoint(packet, direction, type);
   }
-  #endif // __ANDROID__
+#endif  // __ANDROID__
 
   uint64_t timestamp_us = std::chrono::duration_cast<std::chrono::microseconds>(
                                   std::chrono::system_clock::now().time_since_epoch())
@@ -1305,7 +1299,7 @@
     socket_ = nullptr;
   }
 
-  btsnoop_mode_.clear();
+  btsnoop_mode_ = kBtSnoopLogModeDisabled;
   // Disable all filters
   DisableFilters();
 
@@ -1350,36 +1344,26 @@
   return btsnooz_max_memory_usage_bytes / kDefaultBtSnoozMaxBytesPerPacket;
 }
 
-std::string SnoopLogger::GetBtSnoopMode() {
+std::string SnoopLogger::GetCurrentSnoopMode() { return btsnoop_mode_; }
+
+static std::string GetBtSnoopMode() {
   // Default mode is FILTERED on userdebug/eng build, DISABLED on user build.
   // In userdebug/eng build, it can also be overwritten by modifying the global setting
-  std::string default_mode = kBtSnoopLogModeDisabled;
-  {
-    auto is_debuggable = os::GetSystemPropertyBool(kIsDebuggableProperty, false);
-    if (is_debuggable) {
-      auto default_mode_property = os::GetSystemProperty(kBtSnoopDefaultLogModeProperty);
-      if (default_mode_property) {
-        default_mode = std::move(default_mode_property.value());
-      } else {
-        default_mode = kBtSnoopLogModeFiltered;
-      }
-    }
+  std::string btsnoop_mode = SnoopLogger::kBtSnoopLogModeDisabled;
+  if (os::GetSystemPropertyBool(SnoopLogger::kIsDebuggableProperty, false)) {
+    btsnoop_mode = os::GetSystemProperty(SnoopLogger::kBtSnoopDefaultLogModeProperty)
+                           .value_or(SnoopLogger::kBtSnoopLogModeFiltered);
   }
 
-  // Get the actual mode if exist
-  std::string btsnoop_mode = default_mode;
-  {
-    auto btsnoop_mode_prop = os::GetSystemProperty(kBtSnoopLogModeProperty);
-    if (btsnoop_mode_prop) {
-      btsnoop_mode = std::move(btsnoop_mode_prop.value());
-    }
-  }
+  btsnoop_mode = os::GetSystemProperty(SnoopLogger::kBtSnoopLogModeProperty).value_or(btsnoop_mode);
 
-  // If Snoop Logger already set up, return current mode
-  bool btsnoop_mode_empty = btsnoop_mode_.empty();
-  log::info("btsnoop_mode_empty: {}", btsnoop_mode_empty);
-  if (!btsnoop_mode_empty) {
-    return btsnoop_mode_;
+  // Only allow a subset of values:
+  if (!(btsnoop_mode == SnoopLogger::kBtSnoopLogModeDisabled ||
+        btsnoop_mode == SnoopLogger::kBtSnoopLogModeFull ||
+        btsnoop_mode == SnoopLogger::kBtSnoopLogModeFiltered ||
+        btsnoop_mode == SnoopLogger::kBtSnoopLogModeKernel)) {
+    log::warn("{}: Invalid btsnoop value, default back to disabled", btsnoop_mode);
+    return SnoopLogger::kBtSnoopLogModeDisabled;
   }
 
   return btsnoop_mode;
@@ -1463,7 +1447,7 @@
     } break;
   }
 }
-#endif // __ANDROID__
+#endif  // __ANDROID__
 
 }  // namespace hal
 }  // namespace bluetooth
diff --git a/system/gd/hal/snoop_logger.h b/system/gd/hal/snoop_logger.h
index 3566630..bcd406c 100644
--- a/system/gd/hal/snoop_logger.h
+++ b/system/gd/hal/snoop_logger.h
@@ -199,9 +199,8 @@
 
   static size_t GetMaxPacketsPerBuffer();
 
-  // Get snoop logger mode based on current system setup
-  // Changes to this values is only effective after restarting Bluetooth
-  static std::string GetBtSnoopMode();
+  // Get current snoop logger mode
+  std::string GetCurrentSnoopMode();
 
   // Returns whether the soc manufacturer is Qualcomm
   // Changes to this value is only effective after restarting Bluetooth
@@ -232,7 +231,7 @@
 
   // Set a RFCOMM dlci as acceptlisted, allowing packets with that RFCOMM CID
   // to show up in the snoop logs. The local_cid is used to associate it with
-  // its corrisponding ACL connection. The dlci is the channel with direction
+  // its corresponding ACL connection. The dlci is the channel with direction
   // so there is no chance of a collision if two services are using the same
   // channel but in different directions.
   void AcceptlistRfcommDlci(uint16_t conn_handle, uint16_t local_cid, uint8_t dlci);
@@ -305,7 +304,7 @@
   uint32_t FilterProfiles(bool is_received, uint8_t* packet);
   // Check if packet is A2DP media packet (a2dppktsfiltered mode)
   bool IsA2dpMediaPacket(bool is_received, uint8_t* packet);
-  // Chec if channel is cached in snoop logger for filtering (a2dppktsfiltered mode)
+  // Check if channel is cached in snoop logger for filtering (a2dppktsfiltered mode)
   bool IsA2dpMediaChannel(uint16_t conn_handle, uint16_t cid, bool is_local_cid);
   // Handle HFP filtering while profilesfiltered enabled
   uint32_t FilterProfilesHandleHfp(uint8_t* packet, uint32_t length, uint32_t totlen,
@@ -319,12 +318,12 @@
 
   std::unique_ptr<SnoopLoggerSocketThread> snoop_logger_socket_thread_;
 
-  #ifdef __ANDROID__
+#ifdef __ANDROID__
   void LogTracePoint(const HciPacket& packet, Direction direction, PacketType type);
-  #endif // __ANDROID__
+#endif  // __ANDROID__
 
 private:
-  static std::string btsnoop_mode_;
+  std::string btsnoop_mode_;
   std::string snoop_log_path_;
   std::string snooz_log_path_;
   std::ofstream btsnoop_ostream_;
diff --git a/system/gd/hci/acl_manager/classic_impl.h b/system/gd/hci/acl_manager/classic_impl.h
index ca0baf7..08cdcbd 100644
--- a/system/gd/hci/acl_manager/classic_impl.h
+++ b/system/gd/hci/acl_manager/classic_impl.h
@@ -624,6 +624,11 @@
     auto view = ReadRemoteSupportedFeaturesCompleteView::Create(packet);
     log::assert_that(view.IsValid(), "Read remote supported features packet invalid");
     uint16_t handle = view.GetConnectionHandle();
+    auto status = view.GetStatus();
+    if (status != ErrorCode::SUCCESS) {
+      log::error("handle:{} status:{}", handle, ErrorCodeText(status));
+      return;
+    }
     bluetooth::os::LogMetricBluetoothRemoteSupportedFeatures(connections.get_address(handle), 0,
                                                              view.GetLmpFeatures(), handle);
     connections.execute(handle, [=](ConnectionManagementCallbacks* callbacks) {
@@ -635,6 +640,11 @@
     auto view = ReadRemoteExtendedFeaturesCompleteView::Create(packet);
     log::assert_that(view.IsValid(), "Read remote extended features packet invalid");
     uint16_t handle = view.GetConnectionHandle();
+    auto status = view.GetStatus();
+    if (status != ErrorCode::SUCCESS) {
+      log::error("handle:{} status:{}", handle, ErrorCodeText(status));
+      return;
+    }
     bluetooth::os::LogMetricBluetoothRemoteSupportedFeatures(connections.get_address(handle),
                                                              view.GetPageNumber(),
                                                              view.GetExtendedLmpFeatures(), handle);
diff --git a/system/gd/hci/distance_measurement_manager.cc b/system/gd/hci/distance_measurement_manager.cc
index 5dcd379..08fe9fb 100644
--- a/system/gd/hci/distance_measurement_manager.cc
+++ b/system/gd/hci/distance_measurement_manager.cc
@@ -47,7 +47,8 @@
 static constexpr uint8_t kTxPowerNotAvailable = 0xfe;
 static constexpr int8_t kRSSIDropOffAt1M = 41;
 static constexpr uint8_t kCsMaxTxPower = 10;  // 10 dBm
-static constexpr CsSyncAntennaSelection kCsSyncAntennaSelection = CsSyncAntennaSelection::ANTENNA_2;
+static constexpr CsSyncAntennaSelection kCsSyncAntennaSelection =
+        CsSyncAntennaSelection::ANTENNAS_IN_ORDER;
 static constexpr uint8_t kConfigId = 0x01;  // Use 0x01 to create config and enable procedure
 static constexpr uint8_t kMinMainModeSteps = 0x02;
 static constexpr uint8_t kMaxMainModeSteps = 0x05;
@@ -129,8 +130,9 @@
     bool contains_sounding_sequence_remote_;
     CsProcedureDoneStatus local_status;
     CsProcedureDoneStatus remote_status;
-    // If the procedure is aborted by either the local or remote side.
-    bool aborted = false;
+    // If any subevent is received with a Subevent_Done_Status of 0x0 (All results complete for the
+    // CS subevent)
+    bool contains_complete_subevent_ = false;
     // RAS data
     SegmentationHeader segmentation_header_;
     RangingHeader ranging_header_;
@@ -1121,12 +1123,14 @@
       return;
     }
     procedure_data->ras_subevent_header_.num_steps_reported_ += result_data_structures.size();
+    if (subevent_done_status == CsSubeventDoneStatus::ALL_RESULTS_COMPLETE) {
+      procedure_data->contains_complete_subevent_ = true;
+    }
 
     if (procedure_abort_reason != ProcedureAbortReason::NO_ABORT ||
         subevent_abort_reason != SubeventAbortReason::NO_ABORT) {
       // Even the procedure is aborted, we should keep following process and
       // handle it when all corresponding remote data received.
-      procedure_data->aborted = true;
       procedure_data->ras_subevent_header_.ranging_abort_reason_ =
               static_cast<RangingAbortReason>(procedure_abort_reason);
       procedure_data->ras_subevent_header_.subevent_abort_reason_ =
@@ -1663,7 +1667,7 @@
     if (live_tracker->local_start &&
         procedure_data->local_status == CsProcedureDoneStatus::ALL_RESULTS_COMPLETE &&
         procedure_data->remote_status == CsProcedureDoneStatus::ALL_RESULTS_COMPLETE &&
-        !procedure_data->aborted) {
+        procedure_data->contains_complete_subevent_) {
       log::debug("Procedure complete counter:{} data size:{}, main_mode_type:{}, sub_mode_type:{}",
                  (uint16_t)procedure_data->counter, (uint16_t)procedure_data->step_channel.size(),
                  (uint16_t)live_tracker->main_mode_type, (uint16_t)live_tracker->sub_mode_type);
@@ -1684,7 +1688,6 @@
         raw_data.packet_quality_initiator = procedure_data->packet_quality_initiator;
         raw_data.packet_quality_reflector = procedure_data->packet_quality_reflector;
         ranging_hal_->WriteRawData(connection_handle, raw_data);
-        return;
       }
     }
 
diff --git a/system/gd/metrics/chromeos/metrics_event.cc b/system/gd/metrics/chromeos/metrics_event.cc
index a8c8c46..806db26 100644
--- a/system/gd/metrics/chromeos/metrics_event.cc
+++ b/system/gd/metrics/chromeos/metrics_event.cc
@@ -15,6 +15,7 @@
  */
 #include "metrics/chromeos/metrics_event.h"
 
+#include <android-base/parseint.h>
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/strings/pattern.h>
@@ -665,7 +666,7 @@
   int64_t id;
 
   if (base::ReadFileToString(base::FilePath(path).Append(file), &content)) {
-    if (base::HexStringToInt64(base::CollapseWhitespaceASCII(content, false), &id)) {
+    if (android::base::ParseInt(base::CollapseWhitespaceASCII(content, false), &id)) {
       return id;
     }
   }
diff --git a/system/gd/os/Android.bp b/system/gd/os/Android.bp
index a716973..1f4f4b4 100644
--- a/system/gd/os/Android.bp
+++ b/system/gd/os/Android.bp
@@ -126,3 +126,17 @@
         "system_properties_common.cc",
     ],
 }
+
+filegroup {
+    name: "BluetoothOsSources_mgmt",
+    srcs: [
+        "mgmt_stub.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothOsSources_mgmt_linux",
+    srcs: [
+        "linux_generic/mgmt.cc",
+    ],
+}
diff --git a/system/gd/os/BUILD.gn b/system/gd/os/BUILD.gn
index 6824184..7bd3c7c 100644
--- a/system/gd/os/BUILD.gn
+++ b/system/gd/os/BUILD.gn
@@ -53,6 +53,7 @@
     "handler.cc",
     "linux_generic/alarm.cc",
     "linux_generic/files.cc",
+    "linux_generic/mgmt.cc",
     "linux_generic/reactive_semaphore.cc",
     "linux_generic/reactor.cc",
     "linux_generic/repeating_alarm.cc",
diff --git a/system/gd/hal/mgmt.cc b/system/gd/os/linux_generic/mgmt.cc
similarity index 95%
rename from system/gd/hal/mgmt.cc
rename to system/gd/os/linux_generic/mgmt.cc
index c5de32c..8f2fbd0 100644
--- a/system/gd/hal/mgmt.cc
+++ b/system/gd/os/linux_generic/mgmt.cc
@@ -21,7 +21,7 @@
  * This file will be replaced such that it is not optimized for now.
  */
 
-#include "hal/mgmt.h"
+#include "os/mgmt.h"
 
 #include <bluetooth/log.h>
 #include <poll.h>
@@ -33,7 +33,7 @@
 extern int GetAdapterIndex();
 
 namespace bluetooth {
-namespace hal {
+namespace os {
 
 #define RETRY_ON_INTR(fn) \
   do {                    \
@@ -80,7 +80,7 @@
  * or failures in writing/reading the MGMT socket, the return opcode would
  * be HCI_OP_NOP (0x0000).
  */
-uint16_t Mgmt::get_vs_opcode(uint16_t vendor_specification) {
+uint16_t Management::getVendorSpecificCode(uint16_t vendor_specification) {
   int hci = GetAdapterIndex();
   int fd = btsocket_open_mgmt();
   uint16_t ret_opcode = HCI_OP_NOP;
@@ -140,7 +140,7 @@
           log::error("Failed to read mgmt socket: {}", -errno);
           close(fd);
           return ret_opcode;
-        } else if (ret == 0) { // unlikely to happen, just a safeguard.
+        } else if (ret == 0) {  // unlikely to happen, just a safeguard.
           log::error("Failed to read mgmt socket: EOF");
           close(fd);
           return ret_opcode;
@@ -173,5 +173,5 @@
   return ret_opcode;
 }
 
-}  // namespace hal
+}  // namespace os
 }  // namespace bluetooth
diff --git a/system/gd/hal/mgmt.h b/system/gd/os/mgmt.h
similarity index 76%
rename from system/gd/hal/mgmt.h
rename to system/gd/os/mgmt.h
index 35b22cc..3b039b6 100644
--- a/system/gd/hal/mgmt.h
+++ b/system/gd/os/mgmt.h
@@ -21,7 +21,7 @@
 #include <inttypes.h>
 
 namespace bluetooth {
-namespace hal {
+namespace os {
 
 #define HCI_OP_NOP 0x0000
 
@@ -56,10 +56,31 @@
 #define MGMT_POLL_TIMEOUT_MS 2000
 
 // This class provides an interface to interact with the kernel.
-class Mgmt {
+class Management {
 public:
-  uint16_t get_vs_opcode(uint16_t vendor_specification);
+  ~Management() = default;
+
+  /**
+   * Get the instance of singleton
+   *
+   * @return Management&
+   */
+  static Management& getInstance() {
+    static Management mgmt;
+    return mgmt;
+  }
+
+  uint16_t getVendorSpecificCode(uint16_t vendor_specification);
+
+protected:
+  // Singleton
+  Management() = default;
+
+private:
+  // delete copy constructor for singleton
+  Management(Management const&) = delete;
+  Management& operator=(Management const&) = delete;
 };
 
-}  // namespace hal
+}  // namespace os
 }  // namespace bluetooth
diff --git a/system/gd/hal/mgmt_stub.cc b/system/gd/os/mgmt_stub.cc
similarity index 87%
rename from system/gd/hal/mgmt_stub.cc
rename to system/gd/os/mgmt_stub.cc
index 19a3a96..3088388 100644
--- a/system/gd/hal/mgmt_stub.cc
+++ b/system/gd/os/mgmt_stub.cc
@@ -22,15 +22,15 @@
 
 #include <bluetooth/log.h>
 
-#include "hal/mgmt.h"
+#include "os/mgmt.h"
 
 namespace bluetooth {
-namespace hal {
+namespace os {
 
-uint16_t Mgmt::get_vs_opcode(uint16_t vendor_specification) {
+uint16_t Management::getVendorSpecificCode(uint16_t vendor_specification) {
   log::debug("Using stub for vendor opcode 0x{:04x}", vendor_specification);
   return 0;
 }
 
-}  // namespace hal
+}  // namespace os
 }  // namespace bluetooth
diff --git a/system/gd/rust/linux/mgmt/src/state_machine.rs b/system/gd/rust/linux/mgmt/src/state_machine.rs
index 2df9e7c..be4b796 100644
--- a/system/gd/rust/linux/mgmt/src/state_machine.rs
+++ b/system/gd/rust/linux/mgmt/src/state_machine.rs
@@ -946,6 +946,9 @@
 
     /// Stop the adapter process.
     ///
+    /// This should block the thread until the btadapterd process is completely stopped,
+    /// that is, another btadapterd process with the same index is ready to be |start|.
+    ///
     /// # Args
     /// * `virtual_hci` - Virtual index of adapter used for apis.
     /// * `real_hci` - Real index of the adapter on the system.
@@ -961,8 +964,7 @@
 
 #[derive(Default)]
 pub struct NativeInvoker {
-    process_container: Option<Child>,
-    bluetooth_pid: u32,
+    process_container: HashMap<VirtualHciIndex, Child>,
 }
 
 impl NativeInvoker {
@@ -973,23 +975,28 @@
 
 impl ProcessManager for NativeInvoker {
     fn start(&mut self, virtual_hci: VirtualHciIndex, real_hci: RealHciIndex) {
-        let new_process = Command::new("/usr/bin/btadapterd")
+        if self.process_container.contains_key(&virtual_hci) {
+            return;
+        }
+        match Command::new("/usr/bin/btadapterd")
             .arg(format!("INDEX={} HCI={}", virtual_hci.to_i32(), real_hci.to_i32()))
             .stdout(Stdio::piped())
             .spawn()
-            .expect("cannot open");
-        self.bluetooth_pid = new_process.id();
-        self.process_container = Some(new_process);
+        {
+            Ok(p) => {
+                self.process_container.insert(virtual_hci, p);
+            }
+            Err(e) => error!("Failed to start btadapterd: {}", e),
+        }
     }
-    fn stop(&mut self, _virtual_hci: VirtualHciIndex, _real_hci: RealHciIndex) {
-        match self.process_container {
-            Some(ref mut _p) => {
-                signal::kill(Pid::from_raw(self.bluetooth_pid as i32), Signal::SIGTERM).unwrap();
-                self.process_container = None;
+    fn stop(&mut self, virtual_hci: VirtualHciIndex, _real_hci: RealHciIndex) {
+        if let Some(mut p) = self.process_container.remove(&virtual_hci) {
+            if let Err(e) = signal::kill(Pid::from_raw(p.id() as i32), Signal::SIGTERM) {
+                warn!("Failed to send signal, process could have exited: {}", e);
             }
-            None => {
-                warn!("Process doesn't exist");
-            }
+            let _ = p.wait();
+        } else {
+            warn!("Process doesn't exist");
         }
     }
 }
@@ -1018,14 +1025,14 @@
         }
     }
 
-    fn stop(&mut self, virtual_hci: VirtualHciIndex, real_hci: RealHciIndex) {
+    fn stop(&mut self, virtual_hci: VirtualHciIndex, _real_hci: RealHciIndex) {
+        // Currently in the upstart script, only INDEX is used for identifying the instance. Thus,
+        // intentionally NOT passing HCI to upstart, to avoid the following confusing situation:
+        //   1. UpstartInvoker: start btadapterd INDEX=0 HCI=0
+        //   2. Kernel: The HCI0 crashed, and became HCI1
+        //   3. UpstartInvoker: stop btadapterd INDEX=0 HCI=1  <---- This is confusing
         if let Err(e) = Command::new("initctl")
-            .args([
-                "stop",
-                "btadapterd",
-                format!("INDEX={}", virtual_hci.to_i32()).as_str(),
-                format!("HCI={}", real_hci.to_i32()).as_str(),
-            ])
+            .args(["stop", "btadapterd", format!("INDEX={}", virtual_hci.to_i32()).as_str()])
             .output()
         {
             error!("Failed to stop btadapterd: {}", e);
@@ -1055,6 +1062,9 @@
     }
 
     fn stop(&mut self, virtual_hci: VirtualHciIndex, real_hci: RealHciIndex) {
+        // FIXME(b/307625503): If the real index changed (could be caused by FW crash or USB issues)
+        // then this function would be broken. Need a re-design of the virtual/real index management
+        // that is compatible other invokers.
         Command::new("systemctl")
             .args([
                 "stop",
@@ -1530,11 +1540,30 @@
                 let restart_count =
                     self.get_state(hci, |a: &AdapterState| Some(a.restart_count)).unwrap_or(0);
 
-                // If we've restarted a number of times, attempt to use the reset mechanism instead
-                // of retrying a start.
-                if restart_count >= RESET_ON_RESTART_COUNT {
+                // This is an unexpectedly stop, which means the ProcessManager might not yet be
+                // ready to restart the same instance. Explicitly call stop to make sure we can move
+                // on to the next step.
+                warn!(
+                    "{} stopped unexpectedly, first wait for the process to completely exit.",
+                    hci
+                );
+                self.process_manager.stop(hci, self.get_real_hci_by_virtual_id(hci));
+
+                if !present {
+                    // If the index doesn't present, we have nothing to do - We can't even trigger
+                    // the hardware reset because the sysfs reset entry would disappear as well.
+                    // After the index presents, we shall try restarting.
+                    warn!("{} exited. After {} restarts, index disappeared.", hci, restart_count);
+                    self.modify_state(hci, |s: &mut AdapterState| {
+                        s.state = ProcessState::Off;
+                        s.restart_count = 0;
+                    });
+                    (ProcessState::Off, CommandTimeoutAction::CancelTimer)
+                } else if restart_count >= RESET_ON_RESTART_COUNT {
+                    // If we've restarted a number of times, attempt to use the reset mechanism
+                    // instead of retrying a start.
                     warn!(
-                        "{} stopped unexpectedly. After {} restarts, trying a reset recovery.",
+                        "{} exited. After {} restarts, trying a reset recovery.",
                         hci, restart_count
                     );
                     // Reset the restart count since we're attempting a reset now.
@@ -1548,11 +1577,7 @@
                     self.reset_hci(real_hci);
                     (ProcessState::Off, CommandTimeoutAction::CancelTimer)
                 } else {
-                    warn!(
-                        "{} stopped unexpectedly, try restarting (attempt #{})",
-                        hci,
-                        restart_count + 1
-                    );
+                    warn!("{} exited. Try restarting (attempt #{})", hci, restart_count + 1);
                     self.modify_state(hci, |s: &mut AdapterState| {
                         s.state = ProcessState::TurningOn;
                         s.restart_count += 1;
@@ -1599,12 +1624,30 @@
                 let restart_count =
                     self.get_state(hci, |a: &AdapterState| Some(a.restart_count)).unwrap_or(0);
 
-                // If we've restarted a number of times, attempt to use the reset mechanism instead
-                // of retrying a start.
-                if restart_count >= RESET_ON_RESTART_COUNT {
+                // Explicitly call stop to make sure the ProcessManager is ready to restart the
+                // same instance.
+                warn!(
+                    "{} timed out while starting, first wait for the process to completely exit.",
+                    hci
+                );
+                self.process_manager.stop(hci, self.get_real_hci_by_virtual_id(hci));
+
+                if !present {
+                    // If the index doesn't present, we have nothing to do - We can't even trigger
+                    // the hardware reset because the sysfs reset entry would disappear as well.
+                    // After the index presents, we shall try restarting.
+                    warn!("{} exited. After {} restarts, index disappeared.", hci, restart_count);
+                    self.modify_state(hci, |s: &mut AdapterState| {
+                        s.state = ProcessState::Off;
+                        s.restart_count = 0;
+                    });
+                    StateMachineTimeoutActions::Noop
+                } else if restart_count >= RESET_ON_RESTART_COUNT {
+                    // If we've restarted a number of times, attempt to use the reset mechanism
+                    // instead of retrying a start.
                     warn!(
-                        "{} timed out while starting (present={}). After {} restarts, trying a reset recovery.",
-                        hci, present, restart_count
+                        "{} exited. After {} restarts, trying a reset recovery.",
+                        hci, restart_count
                     );
                     // Reset the restart count since we're attempting a reset now.
                     self.modify_state(hci, |s: &mut AdapterState| {
@@ -1617,17 +1660,11 @@
                     self.reset_hci(real_hci);
                     StateMachineTimeoutActions::Noop
                 } else {
-                    warn!(
-                        "{} timed out while starting (present={}), try restarting (attempt #{})",
-                        hci,
-                        present,
-                        restart_count + 1
-                    );
+                    warn!("{} exited. Try restarting (attempt #{})", hci, restart_count + 1);
                     self.modify_state(hci, |s: &mut AdapterState| {
                         s.state = ProcessState::TurningOn;
                         s.restart_count += 1;
                     });
-                    self.process_manager.stop(hci, self.get_real_hci_by_virtual_id(hci));
                     self.process_manager.start(hci, self.get_real_hci_by_virtual_id(hci));
                     StateMachineTimeoutActions::RetryStart
                 }
@@ -1920,7 +1957,8 @@
         tokio::runtime::Runtime::new().unwrap().block_on(async {
             let mut process_manager = MockProcessManager::new();
             process_manager.expect_start();
-            // Expect to start again
+            // Expect to wait for stopped and start again
+            process_manager.expect_stop();
             process_manager.expect_start();
             let mut state_machine = make_state_machine(process_manager);
             state_machine.action_on_hci_presence_changed(DEFAULT_ADAPTER, true);
@@ -1934,12 +1972,12 @@
             assert_eq!(state_machine.get_process_state(DEFAULT_ADAPTER), ProcessState::TurningOn);
         });
 
-        // Stopped with no presence should restart if config enabled.
+        // Stopped with no presence should not restart even if config enabled.
         tokio::runtime::Runtime::new().unwrap().block_on(async {
             let mut process_manager = MockProcessManager::new();
             process_manager.expect_start();
-            // Expect to start again.
-            process_manager.expect_start();
+            // Expect to wait for stopped
+            process_manager.expect_stop();
             let mut state_machine = make_state_machine(process_manager);
             state_machine.action_on_hci_presence_changed(DEFAULT_ADAPTER, true);
             state_machine.set_config_enabled(DEFAULT_ADAPTER, true);
@@ -1948,9 +1986,9 @@
             state_machine.action_on_hci_presence_changed(DEFAULT_ADAPTER, false);
             assert_eq!(
                 state_machine.action_on_bluetooth_stopped(DEFAULT_ADAPTER),
-                (ProcessState::TurningOn, CommandTimeoutAction::ResetTimer)
+                (ProcessState::Off, CommandTimeoutAction::CancelTimer)
             );
-            assert_eq!(state_machine.get_process_state(DEFAULT_ADAPTER), ProcessState::TurningOn);
+            assert_eq!(state_machine.get_process_state(DEFAULT_ADAPTER), ProcessState::Off);
         });
 
         // If floss was disabled and we see stopped, we shouldn't restart.
@@ -2084,7 +2122,6 @@
             let mut process_manager = MockProcessManager::new();
             process_manager.expect_start();
             process_manager.expect_stop();
-            process_manager.expect_start();
             let mut state_machine = make_state_machine(process_manager);
             state_machine.action_on_hci_presence_changed(DEFAULT_ADAPTER, true);
             state_machine.set_config_enabled(DEFAULT_ADAPTER, true);
@@ -2093,9 +2130,9 @@
             state_machine.action_on_hci_presence_changed(DEFAULT_ADAPTER, false);
             assert_eq!(
                 state_machine.action_on_command_timeout(DEFAULT_ADAPTER),
-                StateMachineTimeoutActions::RetryStart
+                StateMachineTimeoutActions::Noop
             );
-            assert_eq!(state_machine.get_process_state(DEFAULT_ADAPTER), ProcessState::TurningOn);
+            assert_eq!(state_machine.get_process_state(DEFAULT_ADAPTER), ProcessState::Off);
         });
     }
 
diff --git a/system/gd/rust/linux/stack/src/bluetooth_media.rs b/system/gd/rust/linux/stack/src/bluetooth_media.rs
index 4e6f6d2..1479df3 100644
--- a/system/gd/rust/linux/stack/src/bluetooth_media.rs
+++ b/system/gd/rust/linux/stack/src/bluetooth_media.rs
@@ -1,8 +1,8 @@
 //! Anything related to audio and media API.
 
 use bt_topshim::btif::{
-    BluetoothInterface, BtBondState, BtConnectionDirection, BtStatus, BtTransport, DisplayAddress,
-    RawAddress, ToggleableProfile,
+    BluetoothInterface, BtBondState, BtStatus, BtTransport, DisplayAddress, RawAddress,
+    ToggleableProfile,
 };
 use bt_topshim::profiles::a2dp::{
     A2dp, A2dpCallbacks, A2dpCallbacksDispatcher, A2dpCodecBitsPerSample, A2dpCodecChannelMode,
@@ -472,7 +472,8 @@
     adapter: Arc<Mutex<Box<Bluetooth>>>,
     a2dp: A2dp,
     avrcp: Avrcp,
-    avrcp_direction: BtConnectionDirection,
+    avrcp_address: Option<RawAddress>,
+    avrcp_states: HashMap<RawAddress, BtavConnectionState>,
     a2dp_states: HashMap<RawAddress, BtavConnectionState>,
     a2dp_audio_state: HashMap<RawAddress, BtavAudioState>,
     a2dp_has_interrupted_stream: bool, // Only used for qualification.
@@ -548,7 +549,8 @@
             adapter,
             a2dp,
             avrcp,
-            avrcp_direction: BtConnectionDirection::Unknown,
+            avrcp_address: None,
+            avrcp_states: HashMap::new(),
             a2dp_states: HashMap::new(),
             a2dp_audio_state: HashMap::new(),
             a2dp_has_interrupted_stream: false,
@@ -1307,6 +1309,31 @@
                     supported
                 );
 
+                // If is device initiated the AVRCP connection, emit a fake connecting state as
+                // stack don't receive one.
+                if self.avrcp_states.get(&addr) != Some(&BtavConnectionState::Connecting) {
+                    metrics::profile_connection_state_changed(
+                        addr,
+                        Profile::AvrcpController as u32,
+                        BtStatus::Success,
+                        BtavConnectionState::Connecting as u32,
+                    );
+                }
+                metrics::profile_connection_state_changed(
+                    addr,
+                    Profile::AvrcpController as u32,
+                    BtStatus::Success,
+                    BtavConnectionState::Connected as u32,
+                );
+                self.avrcp_states.insert(addr, BtavConnectionState::Connected);
+
+                if self.avrcp_address.is_some() {
+                    warn!("Another AVRCP connection exists. Disconnect {}", DisplayAddress(&addr));
+                    self.avrcp.disconnect(addr);
+                    return;
+                }
+                self.avrcp_address = Some(addr);
+
                 match self.uinput.create(self.adapter_get_remote_name(addr), addr.to_string()) {
                     Ok(()) => info!("uinput device created for: {}", DisplayAddress(&addr)),
                     Err(e) => warn!("{}", e),
@@ -1325,46 +1352,14 @@
                 }
 
                 self.absolute_volume = supported;
-
-                // If is device initiated the AVRCP connection, emit a fake connecting state as
-                // stack don't receive one.
-                if self.avrcp_direction != BtConnectionDirection::Outgoing {
-                    metrics::profile_connection_state_changed(
-                        addr,
-                        Profile::AvrcpController as u32,
-                        BtStatus::Success,
-                        BtavConnectionState::Connecting as u32,
-                    );
-                }
-                metrics::profile_connection_state_changed(
-                    addr,
-                    Profile::AvrcpController as u32,
-                    BtStatus::Success,
-                    BtavConnectionState::Connected as u32,
-                );
-                // Reset direction to unknown.
-                self.avrcp_direction = BtConnectionDirection::Unknown;
-
                 self.add_connected_profile(addr, Profile::AvrcpController);
             }
             AvrcpCallbacks::AvrcpDeviceDisconnected(addr) => {
                 info!("[{}]: avrcp disconnected.", DisplayAddress(&addr));
 
-                self.uinput.close(addr.to_string());
-
-                // TODO: better support for multi-device
-                self.absolute_volume = false;
-
-                // This may be considered a critical profile in the extreme case
-                // where only AVRCP was connected.
-                let is_profile_critical = match self.connected_profiles.get(&addr) {
-                    Some(profiles) => *profiles == HashSet::from([Profile::AvrcpController]),
-                    None => false,
-                };
-
                 // If the peer device initiated the AVRCP disconnection, emit a fake connecting
                 // state as stack don't receive one.
-                if self.avrcp_direction != BtConnectionDirection::Outgoing {
+                if self.avrcp_states.get(&addr) != Some(&BtavConnectionState::Disconnecting) {
                     metrics::profile_connection_state_changed(
                         addr,
                         Profile::AvrcpController as u32,
@@ -1378,8 +1373,25 @@
                     BtStatus::Success,
                     BtavConnectionState::Disconnected as u32,
                 );
-                // Reset direction to unknown.
-                self.avrcp_direction = BtConnectionDirection::Unknown;
+                self.avrcp_states.remove(&addr);
+
+                if self.avrcp_address != Some(addr) {
+                    // Ignore disconnection to address we don't care
+                    return;
+                }
+                self.avrcp_address = None;
+
+                self.uinput.close(addr.to_string());
+
+                // TODO: better support for multi-device
+                self.absolute_volume = false;
+
+                // This may be considered a critical profile in the extreme case
+                // where only AVRCP was connected.
+                let is_profile_critical = match self.connected_profiles.get(&addr) {
+                    Some(profiles) => *profiles == HashSet::from([Profile::AvrcpController]),
+                    None => false,
+                };
 
                 self.rm_connected_profile(addr, Profile::AvrcpController, is_profile_critical);
             }
@@ -3336,11 +3348,11 @@
                         BtStatus::Success,
                         BtavConnectionState::Connecting as u32,
                     );
-                    self.avrcp_direction = BtConnectionDirection::Outgoing;
+                    self.avrcp_states.insert(addr, BtavConnectionState::Connecting);
                     let status = self.avrcp.connect(addr);
                     if BtStatus::Success != status {
                         // Reset direction to unknown.
-                        self.avrcp_direction = BtConnectionDirection::Unknown;
+                        self.avrcp_states.remove(&addr);
                         metrics::profile_connection_state_changed(
                             addr,
                             Profile::AvrcpController as u32,
@@ -3463,11 +3475,11 @@
                         BtStatus::Success,
                         BtavConnectionState::Disconnecting as u32,
                     );
-                    self.avrcp_direction = BtConnectionDirection::Outgoing;
+                    self.avrcp_states.insert(addr, BtavConnectionState::Disconnecting);
                     let status = self.avrcp.disconnect(addr);
                     if BtStatus::Success != status {
                         // Reset direction to unknown.
-                        self.avrcp_direction = BtConnectionDirection::Unknown;
+                        self.avrcp_states.remove(&addr);
                         metrics::profile_connection_state_changed(
                             addr,
                             Profile::AvrcpController as u32,
@@ -3569,6 +3581,10 @@
     }
 
     fn set_volume(&mut self, volume: u8) {
+        if self.avrcp_address.is_none() {
+            return;
+        }
+
         // Guard the range 0-127 by the try_from cast from u8 to i8.
         let vol = match i8::try_from(volume) {
             Ok(val) => val,
@@ -3578,7 +3594,7 @@
             }
         };
 
-        self.avrcp.set_volume(vol);
+        self.avrcp.set_volume(self.avrcp_address.unwrap(), vol);
     }
 
     fn set_hfp_volume(&mut self, volume: u8, addr: RawAddress) {
diff --git a/system/gd/rust/topshim/Android.bp b/system/gd/rust/topshim/Android.bp
index af5ed8c..fe3a5cc 100644
--- a/system/gd/rust/topshim/Android.bp
+++ b/system/gd/rust/topshim/Android.bp
@@ -66,11 +66,18 @@
     ],
     host_supported: true,
     static_libs: [
+        "aics",
         "libbluetooth_hci_pdl",
         "libbluetooth_log",
         "libchrome",
         "libflatbuffers-cpp",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
+    shared_libs: [
+        "libbinder",
+    ],
     target: {
         darwin: {
             enabled: false,
diff --git a/system/gd/rust/topshim/BUILD.gn b/system/gd/rust/topshim/BUILD.gn
index a20dbee..b2c937d 100644
--- a/system/gd/rust/topshim/BUILD.gn
+++ b/system/gd/rust/topshim/BUILD.gn
@@ -50,9 +50,11 @@
   ]
   deps = [
     ":btif_bridge_header",
+    "//bt/system/bta/aics:aics",
     "//bt/system/pdl:BluetoothGeneratedPackets_h",
   ]
   configs = [
+    "//bt/system/bta/aics:aics_defaults",
     "//bt/system/gd:gd_defaults",
     "//bt/system/log:log_defaults",
   ]
@@ -82,6 +84,9 @@
     "//bt/system/gd:gd_defaults",
     "//bt/system/log:log_defaults",
   ]
+  include_dirs = [
+    "//bt/system/bta/aics/include",
+  ]
 }
 
 cxxbridge_libheader("cxxlibheader") {
diff --git a/system/gd/rust/topshim/btav/btav_shim.cc b/system/gd/rust/topshim/btav/btav_shim.cc
index 2723d53..3838d03 100644
--- a/system/gd/rust/topshim/btav/btav_shim.cc
+++ b/system/gd/rust/topshim/btav/btav_shim.cc
@@ -17,6 +17,7 @@
 #include "rust/topshim/btav/btav_shim.h"
 
 #include <cstdio>
+#include <map>
 #include <memory>
 
 #include "base/functional/callback.h"
@@ -156,12 +157,12 @@
   }
 
   void DeviceConnected(const RawAddress& addr, VolumeChangedCb cb) override {
-    volumeCb = std::move(cb);
+    volumeCbs[addr] = std::move(cb);
     rusty::avrcp_device_connected(addr, /*absolute_volume_enabled=*/true);
   }
 
   void DeviceDisconnected(const RawAddress& addr) override {
-    volumeCb.Reset();
+    volumeCbs.erase(addr);
     rusty::avrcp_device_disconnected(addr);
   }
 
@@ -175,16 +176,19 @@
   }
 
   // Set CT's (headsets, speakers) volume.
-  void SetDeviceVolume(int8_t volume) {
-    if (!volumeCb || volume < 0) {
+  void SetDeviceVolume(const RawAddress& addr, int8_t volume) {
+    if (volume < 0) {
       return;
     }
 
-    volumeCb.Run(volume);
+    const auto& cb_iter = this->volumeCbs.find(addr);
+    if (cb_iter != this->volumeCbs.end()) {
+      cb_iter->second.Run(volume);
+    }
   }
 
 private:
-  VolumeInterface::VolumeChangedCb volumeCb;
+  std::map<RawAddress, VolumeInterface::VolumeChangedCb> volumeCbs;
 };
 
 }  // namespace bluetooth::avrcp
@@ -288,10 +292,16 @@
 }
 
 int A2dpIntf::init() const {
-  std::vector<btav_a2dp_codec_config_t> a;
+  btav_a2dp_codec_config_t a2dp_config_sbc{
+          .codec_type = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC,
+          .codec_priority = BTAV_A2DP_CODEC_PRIORITY_HIGHEST,
+          // Using default settings for those untouched fields
+  };
+
+  std::vector<btav_a2dp_codec_config_t> codec_priorities(1, a2dp_config_sbc);
   std::vector<btav_a2dp_codec_config_t> b;
   std::vector<btav_a2dp_codec_info_t> c;
-  return btif_av_source_init(&internal::g_callbacks, 1, a, b, &c);
+  return btif_av_source_init(&internal::g_callbacks, 1, codec_priorities, b, &c);
 }
 
 uint32_t A2dpIntf::connect(RawAddress addr) const { return btif_av_source_connect(addr); }
@@ -362,7 +372,9 @@
 uint32_t AvrcpIntf::connect(RawAddress addr) { return intf_->ConnectDevice(addr); }
 uint32_t AvrcpIntf::disconnect(RawAddress addr) { return intf_->DisconnectDevice(addr); }
 
-void AvrcpIntf::set_volume(int8_t volume) { return mVolumeInterface.SetDeviceVolume(volume); }
+void AvrcpIntf::set_volume(RawAddress addr, int8_t volume) {
+  return mVolumeInterface.SetDeviceVolume(addr, volume);
+}
 
 void AvrcpIntf::set_playback_status(const ::rust::String& status) {
   avrcp::PlayState state = avrcp::PlayState::STOPPED;
diff --git a/system/gd/rust/topshim/btav/btav_shim.h b/system/gd/rust/topshim/btav/btav_shim.h
index 6482bca..f32fb24 100644
--- a/system/gd/rust/topshim/btav/btav_shim.h
+++ b/system/gd/rust/topshim/btav/btav_shim.h
@@ -67,7 +67,7 @@
   uint32_t disconnect(RawAddress addr);
 
   // interface for Audio server
-  void set_volume(int8_t volume);
+  void set_volume(RawAddress addr, int8_t volume);
 
   void set_playback_status(const ::rust::String& status);
   void set_position(int64_t position_us);
diff --git a/system/gd/rust/topshim/facade/Android.bp b/system/gd/rust/topshim/facade/Android.bp
index 725ddaa..505d5e8 100644
--- a/system/gd/rust/topshim/facade/Android.bp
+++ b/system/gd/rust/topshim/facade/Android.bp
@@ -32,6 +32,7 @@
         "libtokio",
     ],
     static_libs: [
+        "aics",
         "avrcp-target-service",
         "lib-bt-packets",
         "lib-bt-packets-avrcp",
@@ -72,6 +73,7 @@
     ],
     shared_libs: [
         "libaconfig_storage_read_api_cc",
+        "libbinder",
         "libcrypto",
         "libcutils",
         "libgrpc++",
diff --git a/system/gd/rust/topshim/src/profiles/avrcp.rs b/system/gd/rust/topshim/src/profiles/avrcp.rs
index 16039a1..1b63ef5 100644
--- a/system/gd/rust/topshim/src/profiles/avrcp.rs
+++ b/system/gd/rust/topshim/src/profiles/avrcp.rs
@@ -38,7 +38,7 @@
         fn cleanup(self: Pin<&mut AvrcpIntf>);
         fn connect(self: Pin<&mut AvrcpIntf>, bt_addr: RawAddress) -> u32;
         fn disconnect(self: Pin<&mut AvrcpIntf>, bt_addr: RawAddress) -> u32;
-        fn set_volume(self: Pin<&mut AvrcpIntf>, volume: i8);
+        fn set_volume(self: Pin<&mut AvrcpIntf>, bt_addr: RawAddress, volume: i8);
         fn set_playback_status(self: Pin<&mut AvrcpIntf>, status: &String);
         fn set_position(self: Pin<&mut AvrcpIntf>, position_us: i64);
         fn set_metadata(
@@ -173,8 +173,8 @@
     }
 
     #[profile_enabled_or]
-    pub fn set_volume(&mut self, volume: i8) {
-        self.internal.pin_mut().set_volume(volume);
+    pub fn set_volume(&mut self, addr: RawAddress, volume: i8) {
+        self.internal.pin_mut().set_volume(addr, volume);
     }
 
     #[profile_enabled_or(false)]
diff --git a/system/gd/rust/topshim/vc/vc_shim.cc b/system/gd/rust/topshim/vc/vc_shim.cc
index f7f68d5..da0e055 100644
--- a/system/gd/rust/topshim/vc/vc_shim.cc
+++ b/system/gd/rust/topshim/vc/vc_shim.cc
@@ -21,6 +21,7 @@
 
 #include <string>
 
+#include "aics/api.h"
 #include "src/profiles/vc.rs.h"
 #include "types/raw_address.h"
 
@@ -135,10 +136,11 @@
     topshim::rust::internal::ext_audio_out_description_cb(address, ext_output_id, descr);
   }
 
-  void OnExtAudioInStateChanged(const RawAddress& address, uint8_t ext_input_id, int8_t gain_val,
-                                uint8_t gain_mode_auto, bool mute) {
-    log::info("address={}, ext_input_id={}, gain_val={}, gain_mode_auto={}, mute={}", address,
-              ext_input_id, gain_val, gain_mode_auto, mute);
+  void OnExtAudioInStateChanged(const RawAddress& address, uint8_t ext_input_id,
+                                int8_t gain_setting, bluetooth::aics::Mute mute,
+                                uint8_t gain_mode_auto) {
+    log::info("address={}, ext_input_id={}, gain_setting={}, gain_mode_auto={}, mute={}", address,
+              ext_input_id, gain_setting, gain_mode_auto, static_cast<uint8_t>(mute));
     log::info("Not implemented");
   }
 
diff --git a/system/include/Android.bp b/system/include/Android.bp
index f76b6fc..5540d0dd 100644
--- a/system/include/Android.bp
+++ b/system/include/Android.bp
@@ -35,11 +35,13 @@
         "//vendor:__subpackages__",
     ],
     header_libs: [
+        "aics_headers",
         "avrcp_headers",
         "libbluetooth-types-header",
         "libbtcore_headers",
     ],
     export_header_lib_headers: [
+        "aics_headers",
         "avrcp_headers",
         "libbluetooth-types-header",
         "libbtcore_headers",
diff --git a/system/include/hardware/bt_has.h b/system/include/hardware/bt_has.h
index 631ca65..c5e1744 100644
--- a/system/include/hardware/bt_has.h
+++ b/system/include/hardware/bt_has.h
@@ -26,6 +26,7 @@
 namespace has {
 
 /** Connection State */
+// Must be kept in sync with BluetoothProfile.java
 enum class ConnectionState : uint8_t {
   DISCONNECTED = 0,
   CONNECTING,
diff --git a/system/include/hardware/bt_vc.h b/system/include/hardware/bt_vc.h
index 7d24a07..5e258b5 100644
--- a/system/include/hardware/bt_vc.h
+++ b/system/include/hardware/bt_vc.h
@@ -17,6 +17,7 @@
 
 #pragma once
 
+#include <aics/api.h>
 #include <hardware/bluetooth.h>
 #include <raw_address.h>
 
@@ -26,6 +27,8 @@
 namespace bluetooth {
 namespace vc {
 
+using bluetooth::aics::Mute;
+
 // Must be kept in sync with BluetoothProfile.java
 enum class ConnectionState { DISCONNECTED = 0, CONNECTING, CONNECTED, DISCONNECTING };
 
@@ -73,7 +76,7 @@
 
   /* Callbacks for Audio Input Stream (AIS) - Extended Audio Inputs */
   virtual void OnExtAudioInStateChanged(const RawAddress& address, uint8_t ext_input_id,
-                                        int8_t gain_val, uint8_t gain_mode_auto, bool mute) = 0;
+                                        int8_t gain_setting, Mute mute, uint8_t gain_mode_auto) = 0;
 
   virtual void OnExtAudioInStatusChanged(const RawAddress& address, uint8_t ext_input_id,
                                          VolumeInputStatus status) = 0;
@@ -131,8 +134,8 @@
   virtual void GetExtAudioInDescription(const RawAddress& address, uint8_t ext_input_id) = 0;
   virtual void SetExtAudioInDescription(const RawAddress& address, uint8_t ext_input_id,
                                         std::string descr) = 0;
-  virtual void SetExtAudioInGainValue(const RawAddress& address, uint8_t ext_input_id,
-                                      int8_t value) = 0;
+  virtual void SetExtAudioInGainSetting(const RawAddress& address, uint8_t ext_input_id,
+                                        int8_t gain_setting) = 0;
   virtual void SetExtAudioInGainMode(const RawAddress& address, uint8_t ext_input_id,
                                      bool automatic) = 0;
   virtual void SetExtAudioInGainMute(const RawAddress& address, uint8_t ext_input_id,
diff --git a/system/main/Android.bp b/system/main/Android.bp
index cfa85ee..5b27092 100644
--- a/system/main/Android.bp
+++ b/system/main/Android.bp
@@ -215,9 +215,13 @@
         "liblog",
         "libosi",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libPlatformProperties",
         "libaconfig_storage_read_api_cc",
+        "libbinder",
         "libbinder_ndk",
         "libcrypto",
         "server_configurable_flags",
diff --git a/system/profile/avrcp/device.cc b/system/profile/avrcp/device.cc
index 7c4b6b3..3167363 100644
--- a/system/profile/avrcp/device.cc
+++ b/system/profile/avrcp/device.cc
@@ -1365,6 +1365,7 @@
     auto no_items_rsp = GetFolderItemsResponseBuilder::MakePlayerListBuilder(
             Status::RANGE_OUT_OF_BOUNDS, 0x0000, browse_mtu_);
     send_message(label, true, std::move(no_items_rsp));
+    return;
   }
 
   auto builder = GetFolderItemsResponseBuilder::MakePlayerListBuilder(Status::NO_ERROR, 0x0000,
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index cb987de..aebe1e7 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -1214,7 +1214,11 @@
         "avct/avct_lcb_act.cc",
         "test/stack_avctp_test.cc",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
+        "libbinder",
         "libcrypto",
         "libcutils",
         "server_configurable_flags",
@@ -1284,7 +1288,11 @@
         "test/common/mock_stack_avdt_msg.cc",
         "test/stack_avdtp_test.cc",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
+        "libbinder",
         "libcrypto",
         "libcutils",
         "server_configurable_flags",
@@ -1392,7 +1400,11 @@
         "test/a2dp/wav_reader.cc",
         "test/a2dp/wav_reader_unittest.cc",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
+        "libbinder",
         "libcrypto",
         "libcutils",
         "libprotobuf-cpp-lite",
@@ -1449,7 +1461,11 @@
         "test/a2dp/a2dp_vendor_ldac_decoder_test.cc",
         "test/a2dp/misc_fake.cc",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
+        "libbinder",
         "libcrypto",
         "libcutils",
     ],
@@ -1506,8 +1522,12 @@
         "test/gatt/mock_gatt_utils_ref.cc",
         "test/stack_gatt_sr_hash_test.cc",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
+        "libbinder",
         "libcrypto",
         "libcutils",
         "server_configurable_flags",
@@ -1797,8 +1817,12 @@
         "libprotobuf-cpp-lite",
         "libudrv-uipc",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
+        "libbinder",
         "libcrypto",
         "libcutils",
         "server_configurable_flags",
@@ -1986,7 +2010,11 @@
         "libosi",
         "server_configurable_flags",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
+        "libbinder",
         "libbinder_ndk",
         "libcrypto",
     ],
@@ -2077,9 +2105,13 @@
         "libprotobuf-cpp-lite",
         "libstatslog_bt",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
         "libbase",
+        "libbinder",
         "libbinder_ndk",
         "libcrypto",
         "libcutils",
@@ -2169,9 +2201,13 @@
         "libprotobuf-cpp-lite",
         "libstatslog_bt",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
         "libbase",
+        "libbinder",
         "libbinder_ndk",
         "libcrypto",
         "libcutils",
@@ -2263,9 +2299,13 @@
         "libosi",
         "libstatslog_bt",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
         "libbase",
+        "libbinder",
         "libbinder_ndk",
         "libcrypto",
         "server_configurable_flags",
@@ -2367,8 +2407,12 @@
         "libprotobuf-cpp-lite",
         "libudrv-uipc",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
+        "libbinder",
         "libcrypto",
         "libcutils",
         "server_configurable_flags",
@@ -2432,8 +2476,12 @@
         "libgmock",
         "liblog",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
+        "libbinder",
         "libcrypto",
         "libcutils",
         "server_configurable_flags",
diff --git a/system/stack/acl/ble_acl.cc b/system/stack/acl/ble_acl.cc
index 0606716..683e480 100644
--- a/system/stack/acl/ble_acl.cc
+++ b/system/stack/acl/ble_acl.cc
@@ -27,14 +27,13 @@
 #include "stack/btm/btm_sec.h"
 #include "stack/connection_manager/connection_manager.h"
 #include "stack/include/acl_api.h"
+#include "stack/include/ble_acl_interface.h"
 #include "stack/include/btm_ble_addr.h"
 #include "stack/include/btm_ble_privacy.h"
+#include "stack/include/gatt_api.h"
 #include "stack/include/l2cap_hci_link_interface.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using namespace bluetooth;
 
 extern tBTM_CB btm_cb;
@@ -144,8 +143,6 @@
   btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, &address_with_type.bda, status);
 }
 
-void gatt_notify_conn_update(const RawAddress& remote, uint16_t interval, uint16_t latency,
-                             uint16_t timeout, tHCI_STATUS status);
 void acl_ble_update_event_received(tHCI_STATUS status, uint16_t handle, uint16_t interval,
                                    uint16_t latency, uint16_t timeout) {
   l2cble_process_conn_update_evt(handle, status, interval, latency, timeout);
diff --git a/system/stack/acl/btm_acl.cc b/system/stack/acl/btm_acl.cc
index 0c52e80..fecf7b9 100644
--- a/system/stack/acl/btm_acl.cc
+++ b/system/stack/acl/btm_acl.cc
@@ -73,6 +73,7 @@
 #include "stack/include/btm_status.h"
 #include "stack/include/hci_error_code.h"
 #include "stack/include/hcimsgs.h"
+#include "stack/include/inq_hci_link_interface.h"
 #include "stack/include/l2cap_acl_interface.h"
 #include "stack/include/l2cdefs.h"
 #include "stack/include/main_thread.h"
@@ -88,9 +89,6 @@
 #define PROPERTY_AUTO_FLUSH_TIMEOUT "bluetooth.core.classic.auto_flush_timeout"
 #endif
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using namespace bluetooth;
 using bluetooth::legacy::hci::GetInterface;
 
@@ -212,27 +210,6 @@
   p_acl.rs_disc_pending = BTM_SEC_RS_PENDING;
 }
 
-void hci_btm_set_link_supervision_timeout(tACL_CONN& link, uint16_t timeout) {
-  if (link.link_role != HCI_ROLE_CENTRAL) {
-    /* Only send if current role is Central; 2.0 spec requires this */
-    log::warn("Can only set link supervision timeout if central role:{}", RoleText(link.link_role));
-    return;
-  }
-
-  if (!bluetooth::shim::GetController()->IsSupported(
-              bluetooth::hci::OpCode::WRITE_LINK_SUPERVISION_TIMEOUT)) {
-    log::warn(
-            "UNSUPPORTED by controller write link supervision timeout:{:.2f}ms "
-            "bd_addr:{}",
-            supervision_timeout_to_seconds(timeout), link.RemoteAddress());
-    return;
-  }
-  log::debug("Setting link supervision timeout:{:.2f}s peer:{}", double(timeout) * 0.01,
-             link.RemoteAddress());
-  link.link_super_tout = timeout;
-  btsnd_hcic_write_link_super_tout(link.Handle(), timeout);
-}
-
 /* 3 seconds timeout waiting for responses */
 #define BTM_DEV_REPLY_TIMEOUT_MS (3 * 1000)
 
@@ -302,10 +279,6 @@
   return nullptr;
 }
 
-tACL_CONN* acl_get_connection_from_address(const RawAddress& bd_addr, tBT_TRANSPORT transport) {
-  return internal_.btm_bda_to_acl(bd_addr, transport);
-}
-
 void StackAclBtmAcl::btm_acl_consolidate(const RawAddress& identity_addr, const RawAddress& rpa) {
   tACL_CONN* p_acl = &btm_cb.acl_cb_.acl_db[0];
   for (uint8_t index = 0; index < MAX_L2CAP_LINKS; index++, p_acl++) {
@@ -356,7 +329,7 @@
   return &btm_cb.acl_cb_.acl_db[index];
 }
 
-tACL_CONN* acl_get_connection_from_handle(uint16_t handle) {
+static tACL_CONN* acl_get_connection_from_handle(uint16_t handle) {
   return internal_.acl_get_connection_from_handle(handle);
 }
 
@@ -803,8 +776,9 @@
   btm_iot_save_remote_versions(p_acl_cb);
 }
 
-void btm_process_remote_version_complete(uint8_t status, uint16_t handle, uint8_t lmp_version,
-                                         uint16_t manufacturer, uint16_t lmp_subversion) {
+static void btm_process_remote_version_complete(uint8_t status, uint16_t handle,
+                                                uint8_t lmp_version, uint16_t manufacturer,
+                                                uint16_t lmp_subversion) {
   tACL_CONN* p_acl_cb = internal_.acl_get_connection_from_handle(handle);
   if (p_acl_cb == nullptr) {
     log::warn("Received remote version complete for unknown acl");
@@ -2376,7 +2350,7 @@
 // 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) {
+static bool is_disconnect_reason_valid(const tHCI_REASON& reason) {
   switch (reason) {
     case HCI_ERR_AUTH_FAILURE:
     case HCI_ERR_PEER_USER:
diff --git a/system/stack/acl/btm_pm.cc b/system/stack/acl/btm_pm.cc
index c34e776..1914f30 100644
--- a/system/stack/acl/btm_pm.cc
+++ b/system/stack/acl/btm_pm.cc
@@ -44,19 +44,17 @@
 #include "main/shim/entry.h"
 #include "osi/include/stack_power_telemetry.h"
 #include "stack/btm/btm_int_types.h"
+#include "stack/include/acl_api.h"
+#include "stack/include/acl_hci_link_interface.h"
 #include "stack/include/bt_types.h"
 #include "stack/include/btm_log_history.h"
 #include "stack/include/btm_status.h"
+#include "stack/include/l2cap_hci_link_interface.h"
+#include "stack/include/sco_hci_link_interface.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using namespace bluetooth;
 
-void l2c_OnHciModeChangeSendPendingPackets(RawAddress remote);
-void btm_sco_chk_pend_unpark(tHCI_STATUS status, uint16_t handle);
-void btm_cont_rswitch_from_handle(uint16_t hci_handle);
 extern tBTM_CB btm_cb;
 
 namespace {
@@ -750,8 +748,8 @@
  * Returns          none.
  *
  ******************************************************************************/
-void process_ssr_event(tHCI_STATUS status, uint16_t handle, uint16_t /* max_tx_lat */,
-                       uint16_t max_rx_lat) {
+static void process_ssr_event(tHCI_STATUS status, uint16_t handle, uint16_t /* max_tx_lat */,
+                              uint16_t max_rx_lat) {
   if (pm_mode_db.count(handle) == 0) {
     log::warn("Received sniff subrating event with no active ACL");
     return;
diff --git a/system/stack/avct/avct_api.cc b/system/stack/avct/avct_api.cc
index 7e1e7a9..55c7cd0 100644
--- a/system/stack/avct/avct_api.cc
+++ b/system/stack/avct/avct_api.cc
@@ -113,8 +113,9 @@
 
   // Clean up AVCTP data structures
   for (int i = 0; i < AVCT_NUM_LINKS; i++) {
-    osi_free(avct_cb.lcb[i].p_rx_msg);
+    osi_free_and_reset((void**)&(avct_cb.lcb[i].p_rx_msg));
     fixed_queue_free(avct_cb.lcb[i].tx_q, nullptr);
+    avct_cb.lcb[i].tx_q = nullptr;
     osi_free_and_reset((void**)&(avct_cb.bcb[i].p_tx_msg));
   }
 }
diff --git a/system/stack/avct/avct_lcb.cc b/system/stack/avct/avct_lcb.cc
index ac4519c..43a2b94 100644
--- a/system/stack/avct/avct_lcb.cc
+++ b/system/stack/avct/avct_lcb.cc
@@ -327,7 +327,7 @@
   // If not, de-allocate now...
 
   log::verbose("Freeing LCB");
-  osi_free(p_lcb->p_rx_msg);
+  osi_free_and_reset((void**)&(p_lcb->p_rx_msg));
   fixed_queue_free(p_lcb->tx_q, NULL);
   memset(p_lcb, 0, sizeof(tAVCT_LCB));
 }
diff --git a/system/stack/avrc/avrc_api.cc b/system/stack/avrc/avrc_api.cc
index 28f6f85..ea82921 100644
--- a/system/stack/avrc/avrc_api.cc
+++ b/system/stack/avrc/avrc_api.cc
@@ -82,6 +82,8 @@
 #define AVRC_MSG_MASK_IS_VENDOR_CMD 0x01
 #define AVRC_MSG_MASK_IS_CONTINUATION_RSP 0x02
 
+static void avrc_start_cmd_timer(uint8_t handle, uint8_t label, uint8_t msg_mask);
+
 /******************************************************************************
  *
  * Function         avrcp_absolute_volume_is_enabled
@@ -216,14 +218,19 @@
  * Returns          Nothing.
  *
  *****************************************************************************/
-void avrc_start_cmd_timer(uint8_t handle, uint8_t label, uint8_t msg_mask) {
+static void avrc_start_cmd_timer(uint8_t handle, uint8_t label, uint8_t msg_mask) {
+  if (!avrc_cb.ccb_int[handle].tle) {
+    log::warn("Unable to start response timer handle=0x{:02x} label=0x{:02x} msg_mask:0x{:02x}",
+              handle, label, msg_mask);
+    return;
+  }
+
   tAVRC_PARAM* param = static_cast<tAVRC_PARAM*>(osi_malloc(sizeof(tAVRC_PARAM)));
   param->handle = handle;
   param->label = label;
   param->msg_mask = msg_mask;
 
-  log::verbose("AVRC: starting timer (handle=0x{:02x}, label=0x{:02x})", handle, label);
-
+  log::verbose("AVRC: starting timer (handle=0x{:02x} label=0x{:02x})", handle, label);
   alarm_set_on_mloop(avrc_cb.ccb_int[handle].tle, AVRC_CMD_TOUT_MS, avrc_process_timeout, param);
 }
 
diff --git a/system/stack/avrc/avrc_int.h b/system/stack/avrc/avrc_int.h
index 3394bb1..cf3a0de 100644
--- a/system/stack/avrc/avrc_int.h
+++ b/system/stack/avrc/avrc_int.h
@@ -159,7 +159,6 @@
 uint8_t avrc_opcode_from_pdu(uint8_t pdu);
 bool avrc_is_valid_opcode(uint8_t opcode);
 void avrc_flush_cmd_q(uint8_t handle);
-void avrc_start_cmd_timer(uint8_t handle, uint8_t label, uint8_t msg_mask);
 void avrc_send_next_vendor_cmd(uint8_t handle);
 
 #endif /* AVRC_INT_H */
diff --git a/system/stack/btm/btm_dev.cc b/system/stack/btm/btm_dev.cc
index cdc7e27..c791c03 100644
--- a/system/stack/btm/btm_dev.cc
+++ b/system/stack/btm/btm_dev.cc
@@ -45,6 +45,7 @@
 #include "stack/include/btm_ble_privacy.h"
 #include "stack/include/btm_client_interface.h"
 #include "stack/include/btm_log_history.h"
+#include "stack/include/gatt_api.h"
 #include "stack/include/l2cap_interface.h"
 #include "types/raw_address.h"
 
@@ -54,7 +55,6 @@
 using namespace bluetooth;
 
 extern tBTM_CB btm_cb;
-void gatt_consolidate(const RawAddress& identity_addr, const RawAddress& rpa);
 
 namespace {
 
diff --git a/system/stack/btm/btm_devctl.cc b/system/stack/btm/btm_devctl.cc
index d22fba7..953487b 100644
--- a/system/stack/btm/btm_devctl.cc
+++ b/system/stack/btm/btm_devctl.cc
@@ -41,6 +41,7 @@
 #include "stack/connection_manager/connection_manager.h"
 #include "stack/include/acl_api.h"
 #include "stack/include/acl_api_types.h"
+#include "stack/include/acl_hci_link_interface.h"
 #include "stack/include/bt_types.h"
 #include "stack/include/btm_ble_privacy.h"
 #include "stack/include/btm_inq.h"
@@ -57,7 +58,6 @@
 extern tBTM_CB btm_cb;
 
 void btm_inq_db_reset(void);
-void btm_pm_reset(void);
 /******************************************************************************/
 /*               L O C A L    D A T A    D E F I N I T I O N S                */
 /******************************************************************************/
diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc
index e647715..dc7ba92 100644
--- a/system/stack/btm/btm_sec.cc
+++ b/system/stack/btm/btm_sec.cc
@@ -1566,22 +1566,27 @@
             "enc: x{:x}",
             p_dev_rec->sec_rec.sec_flags & BTM_SEC_AUTHENTICATED,
             p_dev_rec->sec_rec.sec_flags & BTM_SEC_ENCRYPTED);
-    /* SM4, but we do not know for sure which level of security we need.
-     * as long as we have a link key, it's OK */
-    if ((0 == (p_dev_rec->sec_rec.sec_flags & BTM_SEC_AUTHENTICATED)) ||
-        (0 == (p_dev_rec->sec_rec.sec_flags & BTM_SEC_ENCRYPTED))) {
-      rc = tBTM_STATUS::BTM_DELAY_CHECK;
-      /*
-      2046 may report HCI_Encryption_Change and L2C Connection Request out of
-      sequence
-      because of data path issues. Delay this disconnect a little bit
-      */
-      log::info("peer should have initiated security process by now (SM4 to SM4)");
-      p_dev_rec->sec_rec.p_callback = p_callback;
-      p_dev_rec->sec_rec.classic_link = tSECURITY_STATE::DELAY_FOR_ENC;
-      (*p_callback)(bd_addr, transport, p_ref_data, rc);
 
-      return tBTM_STATUS::BTM_SUCCESS;
+    if (!com::android::bluetooth::flags::trigger_sec_proc_on_inc_access_req()) {
+      /* SM4, but we do not know for sure which level of security we need.
+       * as long as we have a link key, it's OK */
+      if ((0 == (p_dev_rec->sec_rec.sec_flags & BTM_SEC_AUTHENTICATED)) ||
+         (0 == (p_dev_rec->sec_rec.sec_flags & BTM_SEC_ENCRYPTED))) {
+        rc = tBTM_STATUS::BTM_DELAY_CHECK;
+        /*
+        2046 may report HCI_Encryption_Change and L2C Connection Request out of
+        sequence
+        because of data path issues. Delay this disconnect a little bit
+        */
+        log::info("peer should have initiated security process by now (SM4 to SM4)");
+        p_dev_rec->sec_rec.p_callback = p_callback;
+        p_dev_rec->sec_rec.classic_link = tSECURITY_STATE::DELAY_FOR_ENC;
+        (*p_callback)(bd_addr, transport, p_ref_data, rc);
+
+        return tBTM_STATUS::BTM_SUCCESS;
+      }
+    } else {
+       log::debug("force fallthrough to trigger sec proceudure");
     }
   }
 
@@ -2159,7 +2164,7 @@
   BTM_LogHistory(
           kBtmLogTag, (p_bd_addr) ? *p_bd_addr : RawAddress::kEmpty, "RNR complete",
           base::StringPrintf("hci_status:%s name:%s", hci_error_code_text(hci_status).c_str(),
-                             PRIVATE_NAME(p_bd_name)));
+                             PRIVATE_NAME(reinterpret_cast<char const*>(p_bd_name))));
 
   if (p_dev_rec == nullptr) {
     // We need to send the callbacks to complete the RNR cycle despite failure
@@ -3424,9 +3429,6 @@
   btm_sec_encrypt_change(handle, static_cast<tHCI_STATUS>(status), 1 /* enable */, key_size);
 }
 
-// TODO: Remove
-void smp_cancel_start_encryption_attempt();
-
 /*******************************************************************************
  *
  * Function         btm_encryption_change_evt
diff --git a/system/stack/btm/security_device_record.h b/system/stack/btm/security_device_record.h
index c1037a4..6dc0cfa 100644
--- a/system/stack/btm/security_device_record.h
+++ b/system/stack/btm/security_device_record.h
@@ -338,8 +338,8 @@
             "sec_prop:%s",
             ADDRESS_TO_LOGGABLE_CSTR(bd_addr), DeviceTypeText(device_type).c_str(),
             dev_class_text(dev_class).c_str(), remote_version_info.ToString().c_str(), sm4,
-            (remote_supports_secure_connections) ? 'T' : 'F', PRIVATE_NAME(sec_bd_name),
-            sec_rec.ToString().c_str());
+            (remote_supports_secure_connections) ? 'T' : 'F',
+            PRIVATE_NAME(reinterpret_cast<char const*>(sec_bd_name)), sec_rec.ToString().c_str());
   }
 
 public:
diff --git a/system/stack/btu/btu_hcif.cc b/system/stack/btu/btu_hcif.cc
index f184626..9dc7d98 100644
--- a/system/stack/btu/btu_hcif.cc
+++ b/system/stack/btu/btu_hcif.cc
@@ -55,6 +55,7 @@
 #include "stack/include/main_thread.h"
 #include "stack/include/sco_hci_link_interface.h"
 #include "stack/include/sec_hci_link_interface.h"
+#include "stack/include/smp_api.h"
 #include "stack/include/stack_metrics_logging.h"
 #include "types/hci_role.h"
 #include "types/raw_address.h"
@@ -68,7 +69,6 @@
 
 bool BTM_BLE_IS_RESOLVE_BDA(const RawAddress& x);  // TODO remove
 void BTA_sys_signal_hw_error();                    // TODO remove
-void smp_cancel_start_encryption_attempt();        // TODO remove
 void acl_disconnect_from_handle(uint16_t handle, tHCI_STATUS reason,
                                 std::string comment);  // TODO remove
 
diff --git a/system/stack/fuzzers/l2cap_fuzzer.cc b/system/stack/fuzzers/l2cap_fuzzer.cc
index 2c5c3d2..1963983 100644
--- a/system/stack/fuzzers/l2cap_fuzzer.cc
+++ b/system/stack/fuzzers/l2cap_fuzzer.cc
@@ -85,7 +85,7 @@
 
 const std::string SnoopLogger::kBtSnoopLogModeFiltered = "filtered";
 
-std::string SnoopLogger::GetBtSnoopMode() { return "filtered"; }
+std::string SnoopLogger::GetCurrentSnoopMode() { return "filtered"; }
 void SnoopLogger::AcceptlistL2capChannel(uint16_t, uint16_t, uint16_t) {}
 void SnoopLogger::AddA2dpMediaChannel(uint16_t, uint16_t, uint16_t) {}
 void SnoopLogger::AddRfcommL2capChannel(uint16_t, uint16_t, uint16_t) {}
diff --git a/system/stack/gap/gap_ble.cc b/system/stack/gap/gap_ble.cc
index 92c826a..a97a8ca 100644
--- a/system/stack/gap/gap_ble.cc
+++ b/system/stack/gap/gap_ble.cc
@@ -23,6 +23,7 @@
 #include <queue>
 
 #include "gap_api.h"
+#include "gap_int.h"
 #include "gatt_api.h"
 #include "hardware/bt_gatt_types.h"
 #include "stack/include/bt_types.h"
@@ -33,9 +34,6 @@
 #include "types/bt_transport.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using bluetooth::Uuid;
 using namespace bluetooth;
 
@@ -61,10 +59,10 @@
   tGAP_BLE_ATTR_VALUE attr_value;
 } tGAP_ATTR;
 
-void server_attr_request_cback(tCONN_ID, uint32_t, tGATTS_REQ_TYPE, tGATTS_DATA*);
-void client_connect_cback(tGATT_IF, const RawAddress&, tCONN_ID, bool, tGATT_DISCONN_REASON,
-                          tBT_TRANSPORT);
-void client_cmpl_cback(tCONN_ID, tGATTC_OPTYPE, tGATT_STATUS, tGATT_CL_COMPLETE*);
+static void server_attr_request_cback(tCONN_ID, uint32_t, tGATTS_REQ_TYPE, tGATTS_DATA*);
+static void client_connect_cback(tGATT_IF, const RawAddress&, tCONN_ID, bool, tGATT_DISCONN_REASON,
+                                 tBT_TRANSPORT);
+static void client_cmpl_cback(tCONN_ID, tGATTC_OPTYPE, tGATT_STATUS, tGATT_CL_COMPLETE*);
 
 tGATT_CBACK gap_cback = {
         .p_conn_cb = client_connect_cback,
@@ -88,7 +86,7 @@
 tGATT_IF gatt_if;
 
 /** returns LCB with matching bd address, or nullptr */
-tGAP_CLCB* find_clcb_by_bd_addr(const RawAddress& bda) {
+static tGAP_CLCB* find_clcb_by_bd_addr(const RawAddress& bda) {
   for (auto& cb : gap_clcbs) {
     if (cb.bda == bda) {
       return &cb;
@@ -99,7 +97,7 @@
 }
 
 /** returns LCB with matching connection ID, or nullptr if not found  */
-tGAP_CLCB* ble_find_clcb_by_conn_id(tCONN_ID conn_id) {
+static tGAP_CLCB* ble_find_clcb_by_conn_id(tCONN_ID conn_id) {
   for (auto& cb : gap_clcbs) {
     if (cb.connected && cb.conn_id == conn_id) {
       return &cb;
@@ -110,7 +108,7 @@
 }
 
 /** allocates a GAP connection link control block */
-tGAP_CLCB* clcb_alloc(const RawAddress& bda) {
+static tGAP_CLCB* clcb_alloc(const RawAddress& bda) {
   gap_clcbs.emplace_back();
   tGAP_CLCB& cb = gap_clcbs.back();
   cb.bda = bda;
@@ -118,7 +116,7 @@
 }
 
 /** The function clean up the pending request queue in GAP */
-void clcb_dealloc(tGAP_CLCB& clcb) {
+static void clcb_dealloc(tGAP_CLCB& clcb) {
   // put last element into place of current element, and remove last one - just
   // fast remove.
   for (auto it = gap_clcbs.begin(); it != gap_clcbs.end(); it++) {
@@ -132,7 +130,7 @@
 }
 
 /** GAP Attributes Database Request callback */
-tGATT_STATUS read_attr_value(uint16_t handle, tGATT_VALUE* p_value, bool is_long) {
+static tGATT_STATUS read_attr_value(uint16_t handle, tGATT_VALUE* p_value, bool is_long) {
   uint8_t* p = p_value->value;
   uint16_t offset = p_value->offset;
   uint8_t* p_dev_name = NULL;
@@ -191,7 +189,7 @@
 }
 
 /** GAP Attributes Database Read/Read Blob Request process */
-tGATT_STATUS proc_read(tGATTS_REQ_TYPE, tGATT_READ_REQ* p_data, tGATTS_RSP* p_rsp) {
+static tGATT_STATUS proc_read(tGATTS_REQ_TYPE, tGATT_READ_REQ* p_data, tGATTS_RSP* p_rsp) {
   if (p_data->is_long) {
     p_rsp->attr_value.offset = p_data->offset;
   }
@@ -202,7 +200,7 @@
 }
 
 /** GAP ATT server process a write request */
-tGATT_STATUS proc_write_req(tGATTS_REQ_TYPE, tGATT_WRITE_REQ* p_data) {
+static tGATT_STATUS proc_write_req(tGATTS_REQ_TYPE, tGATT_WRITE_REQ* p_data) {
   for (const auto& db_addr : gatt_attr) {
     if (p_data->handle == db_addr.handle) {
       return GATT_WRITE_NOT_PERMIT;
@@ -213,8 +211,8 @@
 }
 
 /** GAP ATT server attribute access request callback */
-void server_attr_request_cback(tCONN_ID conn_id, uint32_t trans_id, tGATTS_REQ_TYPE type,
-                               tGATTS_DATA* p_data) {
+static void server_attr_request_cback(tCONN_ID conn_id, uint32_t trans_id, tGATTS_REQ_TYPE type,
+                                      tGATTS_DATA* p_data) {
   tGATT_STATUS status = GATT_INVALID_PDU;
   bool ignore = false;
 
@@ -262,7 +260,7 @@
  * Utility function to send a read request for GAP characteristics.
  * Returns true if read started, else false if GAP is busy.
  */
-bool send_cl_read_request(tGAP_CLCB& clcb) {
+static bool send_cl_read_request(tGAP_CLCB& clcb) {
   if (!clcb.requests.size() || clcb.cl_op_uuid != 0) {
     return false;
   }
@@ -288,7 +286,7 @@
 }
 
 /** GAP client operation complete callback */
-void cl_op_cmpl(tGAP_CLCB& clcb, bool status, uint16_t len, uint8_t* p_name) {
+static void cl_op_cmpl(tGAP_CLCB& clcb, bool status, uint16_t len, uint8_t* p_name) {
   tGAP_BLE_CMPL_CBACK* p_cback = clcb.p_cback;
   uint16_t op = clcb.cl_op_uuid;
 
@@ -311,8 +309,8 @@
 }
 
 /** Client connection callback */
-void client_connect_cback(tGATT_IF, const RawAddress& bda, tCONN_ID conn_id, bool connected,
-                          tGATT_DISCONN_REASON /* reason */, tBT_TRANSPORT) {
+static void client_connect_cback(tGATT_IF, const RawAddress& bda, tCONN_ID conn_id, bool connected,
+                                 tGATT_DISCONN_REASON /* reason */, tBT_TRANSPORT) {
   tGAP_CLCB* p_clcb = find_clcb_by_bd_addr(bda);
   if (p_clcb == NULL) {
     log::info("No active GAP service found for peer:{} callback:{}", bda,
@@ -336,8 +334,8 @@
 }
 
 /** Client operation complete callback */
-void client_cmpl_cback(tCONN_ID conn_id, tGATTC_OPTYPE op, tGATT_STATUS status,
-                       tGATT_CL_COMPLETE* p_data) {
+static void client_cmpl_cback(tCONN_ID conn_id, tGATTC_OPTYPE op, tGATT_STATUS status,
+                              tGATT_CL_COMPLETE* p_data) {
   tGAP_CLCB* p_clcb = ble_find_clcb_by_conn_id(conn_id);
   uint16_t op_type;
   uint16_t min, max, latency, tout;
@@ -396,8 +394,8 @@
   }
 }
 
-bool accept_client_operation(const RawAddress& peer_bda, uint16_t uuid,
-                             tGAP_BLE_CMPL_CBACK* p_cback) {
+static bool accept_client_operation(const RawAddress& peer_bda, uint16_t uuid,
+                                    tGAP_BLE_CMPL_CBACK* p_cback) {
   if (p_cback == NULL && uuid != GATT_UUID_GAP_PREF_CONN_PARAM) {
     return false;
   }
diff --git a/system/stack/gap/gap_conn.cc b/system/stack/gap/gap_conn.cc
index 8208edf..fd988c8 100644
--- a/system/stack/gap/gap_conn.cc
+++ b/system/stack/gap/gap_conn.cc
@@ -21,6 +21,7 @@
 #include <string.h>
 
 #include "gap_api.h"
+#include "gap_int.h"
 #include "hci/controller_interface.h"
 #include "internal_include/bt_target.h"
 #include "main/shim/entry.h"
@@ -34,9 +35,6 @@
 #include "types/bt_transport.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using namespace bluetooth;
 
 /* Define the GAP Connection Control Block */
@@ -117,7 +115,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void gap_conn_init(void) {
+static void gap_conn_init(void) {
   memset(&conn, 0, sizeof(tGAP_CONN));
   conn.reg_info.pL2CA_ConnectInd_Cb = gap_connect_ind;
   conn.reg_info.pL2CA_ConnectCfm_Cb = gap_connect_cfm;
@@ -1050,8 +1048,6 @@
   }
 }
 
-void gap_attr_db_init(void);
-
 /*
  * This routine should not be called except once per stack invocation.
  */
diff --git a/system/stack/gap/gap_int.h b/system/stack/gap/gap_int.h
new file mode 100644
index 0000000..c63a4ce
--- /dev/null
+++ b/system/stack/gap/gap_int.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2024 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
+
+/// GAP ATT database initialization.
+void gap_attr_db_init(void);
diff --git a/system/stack/gatt/att_protocol.cc b/system/stack/gatt/att_protocol.cc
index 60bf695..8360b81 100644
--- a/system/stack/gatt/att_protocol.cc
+++ b/system/stack/gatt/att_protocol.cc
@@ -37,10 +37,6 @@
 #define GATT_OP_CODE_SIZE 1
 #define GATT_START_END_HANDLE_SIZE 4
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
-using base::StringPrintf;
 using bluetooth::Uuid;
 using namespace bluetooth;
 
diff --git a/system/stack/gatt/gatt_api.cc b/system/stack/gatt/gatt_api.cc
index 7ed0fe0..3f2c713 100644
--- a/system/stack/gatt/gatt_api.cc
+++ b/system/stack/gatt/gatt_api.cc
@@ -51,9 +51,6 @@
 #include "types/bt_transport.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using namespace bluetooth::legacy::stack::sdp;
 using namespace bluetooth;
 
@@ -63,7 +60,7 @@
  * Add a service handle range to the list in descending order of the start
  * handle. Return reference to the newly added element.
  **/
-tGATT_HDL_LIST_ELEM& gatt_add_an_item_to_list(uint16_t s_handle) {
+static tGATT_HDL_LIST_ELEM& gatt_add_an_item_to_list(uint16_t s_handle) {
   auto lst_ptr = gatt_cb.hdl_list_info;
   auto it = lst_ptr->begin();
   for (; it != lst_ptr->end(); it++) {
@@ -353,7 +350,7 @@
   return GATT_SERVICE_STARTED;
 }
 
-bool is_active_service(const Uuid& app_uuid128, Uuid* p_svc_uuid, uint16_t start_handle) {
+static bool is_active_service(const Uuid& app_uuid128, Uuid* p_svc_uuid, uint16_t start_handle) {
   for (auto& info : *gatt_cb.srv_list_info) {
     Uuid* p_this_uuid = gatts_get_service_uuid(info.p_db);
 
diff --git a/system/stack/gatt/gatt_attr.cc b/system/stack/gatt/gatt_attr.cc
index a4b2935..a9d9e12 100644
--- a/system/stack/gatt/gatt_attr.cc
+++ b/system/stack/gatt/gatt_attr.cc
@@ -40,10 +40,6 @@
 #include "types/bluetooth/uuid.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
-using base::StringPrintf;
 using bluetooth::Uuid;
 using namespace bluetooth;
 
@@ -192,8 +188,8 @@
  *                  block.
  *
  ******************************************************************************/
-tGATT_PROFILE_CLCB* gatt_profile_clcb_alloc(tCONN_ID conn_id, const RawAddress& bda,
-                                            tBT_TRANSPORT tranport) {
+static tGATT_PROFILE_CLCB* gatt_profile_clcb_alloc(tCONN_ID conn_id, const RawAddress& bda,
+                                                   tBT_TRANSPORT tranport) {
   uint8_t i_clcb = 0;
   tGATT_PROFILE_CLCB* p_clcb = NULL;
 
@@ -224,13 +220,13 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_profile_clcb_dealloc(tGATT_PROFILE_CLCB* p_clcb) {
+static void gatt_profile_clcb_dealloc(tGATT_PROFILE_CLCB* p_clcb) {
   memset(p_clcb, 0, sizeof(tGATT_PROFILE_CLCB));
 }
 
 /** GAP Attributes Database Request callback */
-tGATT_STATUS read_attr_value(tCONN_ID conn_id, uint16_t handle, tGATT_VALUE* p_value,
-                             bool is_long) {
+static tGATT_STATUS read_attr_value(tCONN_ID conn_id, uint16_t handle, tGATT_VALUE* p_value,
+                                    bool is_long) {
   uint8_t* p = p_value->value;
 
   if (handle == gatt_cb.handle_sr_supported_feat) {
@@ -271,8 +267,8 @@
 }
 
 /** GAP Attributes Database Read/Read Blob Request process */
-tGATT_STATUS proc_read_req(tCONN_ID conn_id, tGATTS_REQ_TYPE, tGATT_READ_REQ* p_data,
-                           tGATTS_RSP* p_rsp) {
+static tGATT_STATUS proc_read_req(tCONN_ID conn_id, tGATTS_REQ_TYPE, tGATT_READ_REQ* p_data,
+                                  tGATTS_RSP* p_rsp) {
   if (p_data->is_long) {
     p_rsp->attr_value.offset = p_data->offset;
   }
@@ -283,7 +279,7 @@
 }
 
 /** GAP ATT server process a write request */
-tGATT_STATUS proc_write_req(tCONN_ID conn_id, tGATTS_REQ_TYPE, tGATT_WRITE_REQ* p_data) {
+static tGATT_STATUS proc_write_req(tCONN_ID conn_id, tGATTS_REQ_TYPE, tGATT_WRITE_REQ* p_data) {
   uint16_t handle = p_data->handle;
 
   /* GATT_UUID_SERVER_SUP_FEAT*/
diff --git a/system/stack/gatt/gatt_auth.cc b/system/stack/gatt/gatt_auth.cc
index fc11e25..4323c8e 100644
--- a/system/stack/gatt/gatt_auth.cc
+++ b/system/stack/gatt/gatt_auth.cc
@@ -39,9 +39,6 @@
 #include "stack/include/btm_status.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using namespace bluetooth;
 
 /*******************************************************************************
@@ -281,7 +278,7 @@
  * This routine determine the security action based on auth_request and current
  * link status. Returns tGATT_SEC_ACTION (security action)
  */
-tGATT_SEC_ACTION gatt_determine_sec_act(tGATT_CLCB* p_clcb) {
+static tGATT_SEC_ACTION gatt_determine_sec_act(tGATT_CLCB* p_clcb) {
   tGATT_SEC_ACTION act = GATT_SEC_OK;
   tGATT_TCB* p_tcb = p_clcb->p_tcb;
   tGATT_AUTH_REQ auth_req = p_clcb->auth_req;
diff --git a/system/stack/gatt/gatt_cl.cc b/system/stack/gatt/gatt_cl.cc
index 61fb47f..5084496 100644
--- a/system/stack/gatt/gatt_cl.cc
+++ b/system/stack/gatt/gatt_cl.cc
@@ -52,9 +52,6 @@
 
 #define L2CAP_PKT_OVERHEAD 4
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using namespace bluetooth;
 using bluetooth::Uuid;
 using bluetooth::eatt::EattChannel;
@@ -277,6 +274,7 @@
       return;
   }
 }
+
 /*******************************************************************************
  *
  * Function         gatt_send_queue_write_cancel
@@ -299,6 +297,7 @@
     gatt_end_operation(p_clcb, rt, NULL);
   }
 }
+
 /*******************************************************************************
  *
  * Function         gatt_check_write_long_terminate
@@ -308,7 +307,8 @@
  * Returns          true: write long is terminated; false keep sending.
  *
  ******************************************************************************/
-bool gatt_check_write_long_terminate(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, tGATT_VALUE* p_rsp_value) {
+static bool gatt_check_write_long_terminate(tGATT_TCB& tcb, tGATT_CLCB* p_clcb,
+                                            tGATT_VALUE* p_rsp_value) {
   tGATT_VALUE* p_attr = (tGATT_VALUE*)p_clcb->p_attr_buf;
   bool terminate = false;
   tGATT_EXEC_FLAG flag = GATT_PREP_WRITE_EXEC;
@@ -389,8 +389,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_find_type_value_rsp(tGATT_TCB& /* tcb */, tGATT_CLCB* p_clcb, uint16_t len,
-                                      uint8_t* p_data) {
+static void gatt_process_find_type_value_rsp(tGATT_TCB& /* tcb */, tGATT_CLCB* p_clcb, uint16_t len,
+                                             uint8_t* p_data) {
   tGATT_DISC_RES result;
   uint8_t* p = p_data;
 
@@ -434,8 +434,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_read_info_rsp(tGATT_TCB& /* tcb */, tGATT_CLCB* p_clcb, uint8_t /* op_code */,
-                                uint16_t len, uint8_t* p_data) {
+static void gatt_process_read_info_rsp(tGATT_TCB& /* tcb */, tGATT_CLCB* p_clcb,
+                                       uint8_t /* op_code */, uint16_t len, uint8_t* p_data) {
   tGATT_DISC_RES result;
   uint8_t *p = p_data, uuid_len = 0, type;
 
@@ -481,6 +481,7 @@
   /* initiate another request */
   gatt_act_discovery(p_clcb);
 }
+
 /*******************************************************************************
  *
  * Function         gatt_proc_disc_error_rsp
@@ -491,8 +492,8 @@
  * Returns          void.
  *
  ******************************************************************************/
-void gatt_proc_disc_error_rsp(tGATT_TCB& /* tcb */, tGATT_CLCB* p_clcb, uint8_t opcode,
-                              uint16_t /* handle */, uint8_t reason) {
+static void gatt_proc_disc_error_rsp(tGATT_TCB& /* tcb */, tGATT_CLCB* p_clcb, uint8_t opcode,
+                                     uint16_t /* handle */, uint8_t reason) {
   tGATT_STATUS status = (tGATT_STATUS)reason;
 
   log::verbose("reason: {:02x} cmd_code {:04x}", reason, opcode);
@@ -525,8 +526,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_error_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint8_t /* op_code */, uint16_t len,
-                            uint8_t* p_data) {
+static void gatt_process_error_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint8_t /* op_code */,
+                                   uint16_t len, uint8_t* p_data) {
   uint8_t opcode, *p = p_data;
   uint8_t reason;
   uint16_t handle;
@@ -569,6 +570,7 @@
     }
   }
 }
+
 /*******************************************************************************
  *
  * Function         gatt_process_prep_write_rsp
@@ -579,8 +581,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_prep_write_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint8_t op_code, uint16_t len,
-                                 uint8_t* p_data) {
+static void gatt_process_prep_write_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint8_t op_code,
+                                        uint16_t len, uint8_t* p_data) {
   uint8_t* p = p_data;
 
   tGATT_VALUE value = {
@@ -630,8 +632,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_notification(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t len,
-                               uint8_t* p_data) {
+static void gatt_process_notification(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t len,
+                                      uint8_t* p_data) {
   tGATT_VALUE value = {};
   tGATT_REG* p_reg;
   tCONN_ID conn_id;
@@ -815,8 +817,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_read_by_type_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint8_t op_code,
-                                   uint16_t len, uint8_t* p_data) {
+static void gatt_process_read_by_type_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint8_t op_code,
+                                          uint16_t len, uint8_t* p_data) {
   tGATT_DISC_RES result;
   tGATT_DISC_VALUE record_value;
   uint8_t *p = p_data, value_len, handle_len = 2;
@@ -1027,8 +1029,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_read_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint8_t /* op_code */, uint16_t len,
-                           uint8_t* p_data) {
+static void gatt_process_read_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint8_t /* op_code */,
+                                  uint16_t len, uint8_t* p_data) {
   uint16_t offset = p_clcb->counter;
   uint8_t* p = p_data;
 
@@ -1110,7 +1112,10 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_handle_rsp(tGATT_CLCB* p_clcb) { gatt_end_operation(p_clcb, GATT_SUCCESS, NULL); }
+static void gatt_process_handle_rsp(tGATT_CLCB* p_clcb) {
+  gatt_end_operation(p_clcb, GATT_SUCCESS, NULL);
+}
+
 /*******************************************************************************
  *
  * Function         gatt_process_mtu_rsp
@@ -1121,7 +1126,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_mtu_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint16_t len, uint8_t* p_data) {
+static void gatt_process_mtu_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint16_t len,
+                                 uint8_t* p_data) {
   uint16_t mtu;
   tGATT_STATUS status = GATT_SUCCESS;
 
@@ -1173,7 +1179,7 @@
  * Returns          response code.
  *
  ******************************************************************************/
-uint8_t gatt_cmd_to_rsp_code(uint8_t cmd_code) {
+static uint8_t gatt_cmd_to_rsp_code(uint8_t cmd_code) {
   uint8_t rsp_code = 0;
 
   if (cmd_code > 1 && cmd_code != GATT_CMD_WRITE) {
diff --git a/system/stack/gatt/gatt_db.cc b/system/stack/gatt/gatt_db.cc
index 2141fd4..cd55ffd 100644
--- a/system/stack/gatt/gatt_db.cc
+++ b/system/stack/gatt/gatt_db.cc
@@ -31,9 +31,6 @@
 #include "stack/include/l2cap_types.h"
 #include "types/bluetooth/uuid.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using bluetooth::Uuid;
 using namespace bluetooth;
 
@@ -442,7 +439,7 @@
 /******************************************************************************/
 /* Service Attribute Database Query Utility Functions */
 /******************************************************************************/
-tGATT_ATTR* find_attr_by_handle(tGATT_SVC_DB* p_db, uint16_t handle) {
+static tGATT_ATTR* find_attr_by_handle(tGATT_SVC_DB* p_db, uint16_t handle) {
   if (!p_db) {
     return nullptr;
   }
diff --git a/system/stack/gatt/gatt_int.h b/system/stack/gatt/gatt_int.h
index 59dc338..88c8b88 100644
--- a/system/stack/gatt/gatt_int.h
+++ b/system/stack/gatt/gatt_int.h
@@ -499,10 +499,6 @@
                       int8_t initiating_phys);
 bool gatt_act_connect(tGATT_REG* p_reg, const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type,
                       tBT_TRANSPORT transport, int8_t initiating_phys);
-bool gatt_connect(const RawAddress& rem_bda, tGATT_TCB* p_tcb, tBT_TRANSPORT transport,
-                  uint8_t initiating_phys, tGATT_IF gatt_if);
-bool gatt_connect(const RawAddress& rem_bda, tGATT_TCB* p_tcb, tBLE_ADDR_TYPE addr_type,
-                  tBT_TRANSPORT transport, uint8_t initiating_phys, tGATT_IF gatt_if);
 void gatt_data_process(tGATT_TCB& p_tcb, uint16_t cid, BT_HDR* p_buf);
 void gatt_update_app_use_link_flag(tGATT_IF gatt_if, tGATT_TCB* p_tcb, bool is_add,
                                    bool check_acl_link);
@@ -692,10 +688,20 @@
 tGATT_STATUS gatts_read_attr_perm_check(tGATT_SVC_DB* p_db, bool is_long, uint16_t handle,
                                         tGATT_SEC_FLAG sec_flag, uint8_t key_size);
 bluetooth::Uuid* gatts_get_service_uuid(tGATT_SVC_DB* p_db);
+void gatts_proc_srv_chg_ind_ack(tGATT_TCB tcb);
 
 /* gatt_sr_hash.cc */
 Octet16 gatts_calculate_database_hash(std::list<tGATT_SRV_LIST_ELEM>* lst_ptr);
 
+namespace bluetooth {
+namespace legacy {
+namespace testing {
+BT_HDR* attp_build_value_cmd(uint16_t payload_size, uint8_t op_code, uint16_t handle,
+                             uint16_t offset, uint16_t len, uint8_t* p_data);
+}  // namespace testing
+}  // namespace legacy
+}  // namespace bluetooth
+
 namespace fmt {
 template <>
 struct formatter<tGATT_CH_STATE> : enum_formatter<tGATT_CH_STATE> {};
diff --git a/system/stack/gatt/gatt_main.cc b/system/stack/gatt/gatt_main.cc
index f5591d2..2cf2c42 100644
--- a/system/stack/gatt/gatt_main.cc
+++ b/system/stack/gatt/gatt_main.cc
@@ -45,15 +45,13 @@
 #include "stack/include/bt_psm_types.h"
 #include "stack/include/bt_types.h"
 #include "stack/include/btm_client_interface.h"
+#include "stack/include/gatt_api.h"
 #include "stack/include/l2cap_acl_interface.h"
 #include "stack/include/l2cap_interface.h"
 #include "stack/include/l2cdefs.h"
 #include "stack/include/srvc_api.h"  // tDIS_VALUE
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using bluetooth::eatt::EattExtension;
 using namespace bluetooth;
 
@@ -218,8 +216,8 @@
  * Returns          true if connection is started, otherwise return false.
  *
  ******************************************************************************/
-bool gatt_connect(const RawAddress& rem_bda, tBLE_ADDR_TYPE addr_type, tGATT_TCB* p_tcb,
-                  tBT_TRANSPORT transport, uint8_t /* initiating_phys */, tGATT_IF gatt_if) {
+static bool gatt_connect(const RawAddress& rem_bda, tBLE_ADDR_TYPE addr_type, tGATT_TCB* p_tcb,
+                         tBT_TRANSPORT transport, uint8_t /* initiating_phys */, tGATT_IF gatt_if) {
   if (gatt_get_ch_state(p_tcb) != GATT_CH_OPEN) {
     gatt_set_ch_state(p_tcb, GATT_CH_CONN);
   }
@@ -240,11 +238,6 @@
   return connection_manager::create_le_connection(gatt_if, rem_bda, addr_type);
 }
 
-bool gatt_connect(const RawAddress& rem_bda, tGATT_TCB* p_tcb, tBT_TRANSPORT transport,
-                  uint8_t initiating_phys, tGATT_IF gatt_if) {
-  return gatt_connect(rem_bda, BLE_ADDR_PUBLIC, p_tcb, transport, initiating_phys, gatt_if);
-}
-
 /*******************************************************************************
  *
  * Function         gatt_cancel_connect
diff --git a/system/stack/gatt/gatt_sr.cc b/system/stack/gatt/gatt_sr.cc
index 7e12f30..8b67f2e 100644
--- a/system/stack/gatt/gatt_sr.cc
+++ b/system/stack/gatt/gatt_sr.cc
@@ -44,9 +44,6 @@
 #define GATT_MTU_REQ_MIN_LEN 2
 #define L2CAP_PKT_OVERHEAD 4
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using bluetooth::Uuid;
 using bluetooth::eatt::EattChannel;
 using bluetooth::eatt::EattExtension;
@@ -107,7 +104,7 @@
  * Returns          true if empty, false if there is pending command.
  *
  ******************************************************************************/
-bool gatt_sr_cmd_empty(tGATT_TCB& tcb, uint16_t cid) {
+static bool gatt_sr_cmd_empty(tGATT_TCB& tcb, uint16_t cid) {
   if (cid == tcb.att_lcid) {
     return tcb.sr_cmd.op_code == 0;
   }
@@ -369,8 +366,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_exec_write_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t len,
-                                 uint8_t* p_data) {
+static void gatt_process_exec_write_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t len,
+                                        uint8_t* p_data) {
   uint8_t *p = p_data, flag;
   uint32_t trans_id = 0;
   tGATT_IF gatt_if;
@@ -441,8 +438,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatt_process_read_multi_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t len,
-                                 uint8_t* p_data) {
+static void gatt_process_read_multi_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t len,
+                                        uint8_t* p_data) {
   uint32_t trans_id;
   uint16_t handle = 0, ll = len;
   uint8_t* p = p_data;
@@ -730,8 +727,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatts_process_primary_service_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t len,
-                                       uint8_t* p_data) {
+static void gatts_process_primary_service_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code,
+                                              uint16_t len, uint8_t* p_data) {
   uint16_t s_hdl = 0, e_hdl = 0;
   Uuid uuid = Uuid::kEmpty;
 
@@ -1144,8 +1141,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatts_process_attribute_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t len,
-                                 uint8_t* p_data) {
+static void gatts_process_attribute_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code, uint16_t len,
+                                        uint8_t* p_data) {
   uint16_t handle = 0;
   uint8_t* p = p_data;
   tGATT_STATUS status = GATT_INVALID_HANDLE;
@@ -1291,7 +1288,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void gatts_process_value_conf(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code) {
+static void gatts_process_value_conf(tGATT_TCB& tcb, uint16_t cid, uint8_t op_code) {
   uint16_t handle;
 
   if (!gatt_tcb_find_indicate_handle(tcb, cid, &handle)) {
diff --git a/system/stack/gatt/gatt_utils.cc b/system/stack/gatt/gatt_utils.cc
index d159bbd..5be17c3 100644
--- a/system/stack/gatt/gatt_utils.cc
+++ b/system/stack/gatt/gatt_utils.cc
@@ -45,16 +45,12 @@
 #include "stack/include/bt_psm_types.h"
 #include "stack/include/bt_types.h"
 #include "stack/include/bt_uuid16.h"
+#include "stack/include/btm_sec_api.h"
 #include "stack/include/l2cdefs.h"
 #include "stack/include/sdp_api.h"
 #include "types/bluetooth/uuid.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
-uint8_t btm_ble_read_sec_key_size(const RawAddress& bd_addr);
-
 using namespace bluetooth::legacy::stack::sdp;
 using namespace bluetooth;
 
@@ -108,7 +104,7 @@
   return ATT_MTU_DEFAULT;
 }
 
-uint16_t gatt_get_max_phy_channel() {
+static uint16_t gatt_get_max_phy_channel() {
   static const uint16_t MAX_PHY_CHANNEL =
           std::min(std::max(osi_property_get_int32(
                                     "bluetooth.core.le.max_number_of_concurrent_connections", 0),
@@ -126,7 +122,7 @@
  * Returns       None
  *
  ******************************************************************************/
-void gatt_free_pending_ind(tGATT_TCB* p_tcb) {
+static void gatt_free_pending_ind(tGATT_TCB* p_tcb) {
   log::verbose("");
 
   if (p_tcb->pending_ind_q == NULL) {
@@ -377,28 +373,6 @@
 
 /*******************************************************************************
  *
- * Function         gatt_is_bda_connected
- *
- * Description
- *
- * Returns          GATT_INDEX_INVALID if not found. Otherwise index to the tcb.
- *
- ******************************************************************************/
-bool gatt_is_bda_connected(const RawAddress& bda) {
-  uint8_t i = 0;
-  bool connected = false;
-
-  for (i = 0; i < gatt_get_max_phy_channel(); i++) {
-    if (gatt_cb.tcb[i].in_use && gatt_cb.tcb[i].peer_bda == bda) {
-      connected = true;
-      break;
-    }
-  }
-  return connected;
-}
-
-/*******************************************************************************
- *
  * Function         gatt_find_i_tcb_by_addr
  *
  * Description      Search for an empty tcb entry, and return the index.
@@ -406,7 +380,7 @@
  * Returns          GATT_INDEX_INVALID if not found. Otherwise index to the tcb.
  *
  ******************************************************************************/
-uint8_t gatt_find_i_tcb_by_addr(const RawAddress& bda, tBT_TRANSPORT transport) {
+static uint8_t gatt_find_i_tcb_by_addr(const RawAddress& bda, tBT_TRANSPORT transport) {
   uint8_t i = 0;
 
   for (; i < gatt_get_max_phy_channel(); i++) {
@@ -794,8 +768,6 @@
   }
 }
 
-void gatts_proc_srv_chg_ind_ack(tGATT_TCB tcb);
-
 /*******************************************************************************
  *
  * Function         gatt_indication_confirmation_timeout
diff --git a/system/stack/include/acl_hci_link_interface.h b/system/stack/include/acl_hci_link_interface.h
index 561d56a..ac17f0d 100644
--- a/system/stack/include/acl_hci_link_interface.h
+++ b/system/stack/include/acl_hci_link_interface.h
@@ -58,6 +58,7 @@
 void acl_process_supported_features(uint16_t handle, uint64_t features);
 void acl_process_extended_features(uint16_t handle, uint8_t current_page_number,
                                    uint8_t max_page_number, uint64_t features);
+void btm_pm_reset();
 void btm_pm_on_mode_change(tHCI_STATUS status, uint16_t handle, tHCI_MODE current_mode,
                            uint16_t interval);
 void btm_pm_on_sniff_subrating(tHCI_STATUS status, uint16_t handle,
diff --git a/system/stack/include/gatt_api.h b/system/stack/include/gatt_api.h
index e51ddc0..54ecfdc 100644
--- a/system/stack/include/gatt_api.h
+++ b/system/stack/include/gatt_api.h
@@ -1272,6 +1272,10 @@
 // Frees resources used by the GATT profile.
 void gatt_free(void);
 
+void gatt_consolidate(const RawAddress& identity_addr, const RawAddress& rpa);
+void gatt_notify_conn_update(const RawAddress& remote, uint16_t interval, uint16_t latency,
+                             uint16_t timeout, tHCI_STATUS status);
+
 // Link encryption complete notification for all encryption process
 // initiated outside GATT.
 void gatt_notify_enc_cmpl(const RawAddress& bd_addr);
@@ -1283,6 +1287,8 @@
 // Initialize GATTS list of bonded device service change updates.
 void gatt_load_bonded(void);
 
+void gatt_tcb_dump(int fd);
+
 namespace fmt {
 template <>
 struct formatter<GattStatus> : enum_formatter<GattStatus> {};
diff --git a/system/stack/include/smp_api.h b/system/stack/include/smp_api.h
index b9eeee4..6957c71 100644
--- a/system/stack/include/smp_api.h
+++ b/system/stack/include/smp_api.h
@@ -207,4 +207,6 @@
 // Proceed to send LTK, DIV and ER to central if bonding the devices.
 void smp_link_encrypted(const RawAddress& bda, uint8_t encr_enable);
 
+void smp_cancel_start_encryption_attempt();
+
 #endif /* SMP_API_H */
diff --git a/system/stack/l2cap/internal/l2c_api.h b/system/stack/l2cap/internal/l2c_api.h
index 6f65f81..5ed3403 100644
--- a/system/stack/l2cap/internal/l2c_api.h
+++ b/system/stack/l2cap/internal/l2c_api.h
@@ -75,7 +75,7 @@
 #define L2CAP_FCR_CHAN_OPT_ALL_MASK (L2CAP_FCR_CHAN_OPT_BASIC | L2CAP_FCR_CHAN_OPT_ERTM)
 
 /* Validity check for PSM.  PSM values must be odd.  Also, all PSM values must
- * be assigned such that the least significant bit of the most sigificant
+ * be assigned such that the least significant bit of the most significant
  * octet equals zero.
  */
 #define L2C_INVALID_PSM(psm) (((psm) & 0x0101) != 0x0001)
diff --git a/system/stack/l2cap/l2c_api.cc b/system/stack/l2cap/l2c_api.cc
index a233046..b958745 100644
--- a/system/stack/l2cap/l2c_api.cc
+++ b/system/stack/l2cap/l2c_api.cc
@@ -24,7 +24,7 @@
 
 #define LOG_TAG "bt_l2cap"
 
-#include "stack/l2cap/internal/l2c_api.h"
+#include "stack/l2cap/l2c_api.h"
 
 #include <base/location.h>
 #include <base/strings/stringprintf.h>
@@ -47,6 +47,7 @@
 #include "stack/include/btm_client_interface.h"
 #include "stack/include/l2cap_module.h"
 #include "stack/include/main_thread.h"
+#include "stack/l2cap/internal/l2c_api.h"
 #include "stack/l2cap/l2c_int.h"
 #include "types/raw_address.h"
 
@@ -1639,7 +1640,7 @@
     return;
   }
 
-  if (snoop_logger->GetBtSnoopMode() != snoop_logger->kBtSnoopLogModeFiltered) {
+  if (snoop_logger->GetCurrentSnoopMode() != snoop_logger->kBtSnoopLogModeFiltered) {
     return;
   }
 
diff --git a/system/stack/smp/smp_act.cc b/system/stack/smp/smp_act.cc
index 07502a2..6de1db3 100644
--- a/system/stack/smp/smp_act.cc
+++ b/system/stack/smp/smp_act.cc
@@ -39,12 +39,10 @@
 #include "stack/include/btm_client_interface.h"
 #include "stack/include/btm_log_history.h"
 #include "stack/include/btm_status.h"
+#include "stack/include/smp_api.h"
 #include "stack/include/smp_api_types.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using namespace bluetooth;
 
 namespace {
diff --git a/system/stack/smp/smp_int.h b/system/stack/smp/smp_int.h
index 29782128..dd51c0d 100644
--- a/system/stack/smp/smp_int.h
+++ b/system/stack/smp/smp_int.h
@@ -489,6 +489,10 @@
 bool smp_calculate_link_key_from_long_term_key(tSMP_CB* p_cb);
 bool smp_calculate_long_term_key_from_link_key(tSMP_CB* p_cb);
 
+Octet16 smp_gen_p1_4_confirm(tSMP_CB* p_cb, tBLE_ADDR_TYPE remote_bd_addr_type);
+Octet16 smp_gen_p2_4_confirm(tSMP_CB* p_cb, const RawAddress& remote_bda);
+tSMP_STATUS smp_calculate_confirm(tSMP_CB* p_cb, const Octet16& rand, Octet16* output);
+
 void print128(const Octet16& x, const char* key_name);
 void smp_xor_128(Octet16* a, const Octet16& b);
 
diff --git a/system/stack/smp/smp_keys.cc b/system/stack/smp/smp_keys.cc
index 801a3d4..f0fb8b9 100644
--- a/system/stack/smp/smp_keys.cc
+++ b/system/stack/smp/smp_keys.cc
@@ -47,9 +47,6 @@
 #include "stack/include/main_thread.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using bluetooth::common::BindOnce;
 using bluetooth::common::OnceCallback;
 using crypto_toolbox::aes_128;
@@ -84,19 +81,16 @@
 
 bool smp_has_local_oob_data() { return !is_oob_data_empty(&saved_local_oob_data); }
 
-void smp_debug_print_nbyte_little_endian(uint8_t* /* p */, const char* /* key_name */,
-                                         uint8_t /* len */) {}
+static void smp_debug_print_nbyte_little_endian(uint8_t* /* p */, const char* /* key_name */,
+                                                uint8_t /* len */) {}
 
-inline void smp_debug_print_nbyte_little_endian(const Octet16& p, const char* key_name,
+static void smp_debug_print_nbyte_little_endian(const Octet16& p, const char* key_name,
                                                 uint8_t len) {
   smp_debug_print_nbyte_little_endian(const_cast<uint8_t*>(p.data()), key_name, len);
 }
 
-void smp_debug_print_nbyte_big_endian(uint8_t* /* p */, const char* /* key_name */,
-                                      uint8_t /* len */) {}
-
 /** This function is called to process a passkey. */
-void smp_proc_passkey(tSMP_CB* p_cb, uint64_t rand) {
+static void smp_proc_passkey(tSMP_CB* p_cb, uint64_t rand) {
   uint8_t* tt = p_cb->tk.data();
   uint32_t passkey = static_cast<uint32_t>(rand & SMP_PASSKEY_MASK);
 
@@ -176,7 +170,7 @@
 /**
  * This function is called to calculate CSRK
  */
-void smp_compute_csrk(uint16_t div, tSMP_CB* p_cb) {
+static void smp_compute_csrk(uint16_t div, tSMP_CB* p_cb) {
   Octet16 buffer{}; /* for (r || DIV)  r=1*/
   uint16_t r = 1;
   uint8_t* p = buffer.data();
@@ -216,10 +210,10 @@
 }
 
 /*******************************************************************************
- * Function         smp_concatenate_peer - LSB first
+ * Function         smp_concatenate_local - LSB first
  *                  add pairing command sent from local device into p1.
  ******************************************************************************/
-void smp_concatenate_local(tSMP_CB* p_cb, uint8_t** p_data, uint8_t op_code) {
+static void smp_concatenate_local(tSMP_CB* p_cb, uint8_t** p_data, uint8_t op_code) {
   uint8_t* p = *p_data;
 
   log::verbose("addr:{}", p_cb->pairing_bda);
@@ -238,7 +232,7 @@
  * Function         smp_concatenate_peer - LSB first
  *                  add pairing command received from peer device into p1.
  ******************************************************************************/
-void smp_concatenate_peer(tSMP_CB* p_cb, uint8_t** p_data, uint8_t op_code) {
+static void smp_concatenate_peer(tSMP_CB* p_cb, uint8_t** p_data, uint8_t op_code) {
   uint8_t* p = *p_data;
 
   log::verbose("addr:{}", p_cb->pairing_bda);
diff --git a/system/stack/smp/smp_l2c.cc b/system/stack/smp/smp_l2c.cc
index 04b6249..219cb72 100644
--- a/system/stack/smp/smp_l2c.cc
+++ b/system/stack/smp/smp_l2c.cc
@@ -263,7 +263,19 @@
   log::info("BDA:{} pairing_bda:{}, connected:{}", bd_addr, p_cb->pairing_bda, connected);
 
   if (bd_addr != p_cb->pairing_bda) {
-    return;
+    if (!com::android::bluetooth::flags::smp_state_machine_stuck_after_disconnection_fix()) {
+      log::info(
+              "If your pairing failed, get a build with "
+              "smp_state_machine_stuck_after_disconnection_fix and try again :)");
+      return;
+    }
+
+    tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
+    /* When pairing was initiated to RPA, and connection was on LE transport first using RPA, then
+     * we must check record pseudo address, it might be same device */
+    if (p_dev_rec == nullptr || p_dev_rec->RemoteAddress() != p_cb->pairing_bda) {
+      return;
+    }
   }
 
   /* Check if we already finished SMP pairing over LE, and are waiting to
diff --git a/system/stack/smp/smp_utils.cc b/system/stack/smp/smp_utils.cc
index 01a045b..bcde927 100644
--- a/system/stack/smp/smp_utils.cc
+++ b/system/stack/smp/smp_utils.cc
@@ -71,9 +71,6 @@
                                                                    Check*/)
 #define SMP_PAIR_KEYPR_NOTIF_SIZE (1 /* opcode */ + 1 /*Notif Type*/)
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using namespace bluetooth;
 
 namespace {
@@ -341,7 +338,7 @@
  * Description      Send message to L2CAP.
  *
  ******************************************************************************/
-bool smp_send_msg_to_L2CAP(const RawAddress& rem_bda, BT_HDR* p_toL2CAP) {
+static bool smp_send_msg_to_L2CAP(const RawAddress& rem_bda, BT_HDR* p_toL2CAP) {
   tL2CAP_DW_RESULT l2cap_ret;
   uint16_t fixed_cid = L2CAP_SMP_CID;
 
diff --git a/system/stack/test/btm/stack_btm_test.cc b/system/stack/test/btm/stack_btm_test.cc
index c1b90ee..c308911 100644
--- a/system/stack/test/btm/stack_btm_test.cc
+++ b/system/stack/test/btm/stack_btm_test.cc
@@ -277,25 +277,4 @@
                        .c_str());
 }
 
-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));
-    }
-  }
-}
-
 TEST_F(StackBtmWithInitFreeTest, Init) { ASSERT_FALSE(btm_cb.rnr.remname_active); }
diff --git a/system/stack/test/gatt/gatt_sr_test.cc b/system/stack/test/gatt/gatt_sr_test.cc
index 7992f87..285aa23 100644
--- a/system/stack/test/gatt/gatt_sr_test.cc
+++ b/system/stack/test/gatt/gatt_sr_test.cc
@@ -20,6 +20,7 @@
 #include <cstdint>
 #include <memory>
 
+#include "stack/connection_manager/connection_manager.h"
 #include "stack/gatt/gatt_int.h"
 #include "stack/include/bt_hdr.h"
 #include "stack/include/main_thread.h"
@@ -120,12 +121,8 @@
 void gatt_update_app_use_link_flag(tGATT_IF /*gatt_if*/, tGATT_TCB* /*p_tcb*/, bool /*is_add*/,
                                    bool /*check_acl_link*/) {}
 bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; }
-void l2cble_set_fixed_channel_tx_data_length(const RawAddress& /*remote_bda*/, uint16_t /*fix_cid*/,
-                                             uint16_t /*tx_mtu*/) {}
-void L2CA_SetLeFixedChannelTxDataLength(const RawAddress& /*remote_bda*/, uint16_t /*fix_cid*/,
-                                        uint16_t /*tx_mtu*/) {}
-void ApplicationRequestCallback(uint16_t conn_id, uint32_t trans_id, tGATTS_REQ_TYPE type,
-                                tGATTS_DATA* p_data) {
+static void ApplicationRequestCallback(uint16_t conn_id, uint32_t trans_id, tGATTS_REQ_TYPE type,
+                                       tGATTS_DATA* p_data) {
   test_state_.application_request_callback.conn_id_ = conn_id;
   test_state_.application_request_callback.trans_id_ = trans_id;
   test_state_.application_request_callback.type_ = type;
diff --git a/system/stack/test/gatt/mock_gatt_utils_ref.cc b/system/stack/test/gatt/mock_gatt_utils_ref.cc
index 2f392b3..fcd7586 100644
--- a/system/stack/test/gatt/mock_gatt_utils_ref.cc
+++ b/system/stack/test/gatt/mock_gatt_utils_ref.cc
@@ -63,9 +63,3 @@
   return 0x0000;
 }
 void gatt_dequeue_sr_cmd(tGATT_TCB& tcb, uint16_t cid) {}
-
-/** stack/l2cap/l2c_ble.cc */
-void l2cble_set_fixed_channel_tx_data_length(const RawAddress& remote_bda, uint16_t fix_cid,
-                                             uint16_t tx_mtu) {}
-void L2CA_SetLeFixedChannelTxDataLength(const RawAddress& remote_bda, uint16_t fix_cid,
-                                        uint16_t tx_mtu) {}
diff --git a/system/stack/test/stack_avctp_test.cc b/system/stack/test/stack_avctp_test.cc
index 17abac1..e617b64 100644
--- a/system/stack/test/stack_avctp_test.cc
+++ b/system/stack/test/stack_avctp_test.cc
@@ -30,14 +30,27 @@
 
 namespace {
 constexpr uint16_t kRemoteCid = 0x0123;
+constexpr uint16_t kRemoteBrowseCid = 0x0456;
 const RawAddress kRawAddress = RawAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x66});
 }  // namespace
 
-class StackAvctpTest : public ::testing::Test {
+class StackAvctpWithMocksTest : public ::testing::Test {
 protected:
   void SetUp() override {
     fake_osi_ = std::make_unique<::test::fake::FakeOsi>();
     bluetooth::testing::stack::l2cap::set_interface(&mock_stack_l2cap_interface_);
+  }
+
+  void TearDown() override {}
+
+  bluetooth::testing::stack::l2cap::Mock mock_stack_l2cap_interface_;
+  std::unique_ptr<test::fake::FakeOsi> fake_osi_;
+};
+
+class StackAvctpTest : public StackAvctpWithMocksTest {
+protected:
+  void SetUp() override {
+    StackAvctpWithMocksTest::SetUp();
     EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_RegisterWithSecurity(_, _, _, _, _, _, _))
             .WillRepeatedly([this](unsigned short psm, const tL2CAP_APPL_INFO& cb, bool /* c */,
                                    tL2CAP_ERTM_INFO* /*d*/, unsigned short /* e */,
@@ -57,11 +70,10 @@
     EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_Deregister(BT_PSM_AVCTP));
     EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_Deregister(BT_PSM_AVCTP_BROWSE));
     AVCT_Deregister();
+    StackAvctpWithMocksTest::TearDown();
   }
 
   std::map<uint16_t, tL2CAP_APPL_INFO> callback_map_;
-  bluetooth::testing::stack::l2cap::Mock mock_stack_l2cap_interface_;
-  std::unique_ptr<test::fake::FakeOsi> fake_osi_;
   int fd_{STDOUT_FILENO};
 };
 
@@ -126,3 +138,57 @@
 
   AVCT_Dumpsys(fd_);
 }
+
+TEST_F(StackAvctpWithMocksTest, AVCT_Lifecycle) {
+  // Register the AVCT profile and capture the l2cap callbacks
+  std::map<uint16_t, tL2CAP_APPL_INFO> callback_map;
+  EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_RegisterWithSecurity(_, _, _, _, _, _, _))
+          .WillRepeatedly([&callback_map](unsigned short psm, const tL2CAP_APPL_INFO& cb,
+                                          bool /* c */, tL2CAP_ERTM_INFO* /*d*/,
+                                          unsigned short /* e */, unsigned short /* f */,
+                                          unsigned short /* g */) {
+            callback_map.insert(std::make_tuple(psm, cb));
+            return psm;
+          });
+  AVCT_Register();
+
+  // Return well known l2cap channel IDs for each of the two PSMs
+  EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectReqWithSecurity(_, _, _))
+          .WillRepeatedly(
+                  [](unsigned short psm, const RawAddress /* bd_addr */, uint16_t /* sec_level */) {
+                    return (psm == BT_PSM_AVCTP) ? kRemoteCid : kRemoteBrowseCid;
+                  });
+
+  uint8_t handle;
+  tAVCT_CC cc = {
+          .p_ctrl_cback = [](uint8_t /* handle */, uint8_t /* event */, uint16_t /* result */,
+                             const RawAddress* /* peer_addr */) {},
+          .p_msg_cback = [](uint8_t /* handle */, uint8_t /* label */, uint8_t /* cr */,
+                            BT_HDR* /* p_pkt */) {},
+          .pid = 0x1234,
+          .role = AVCT_ROLE_INITIATOR,
+          .control = 1,
+  };
+
+  // Initiate the creation of both control and browse AVCT connections
+  ASSERT_EQ(AVCT_SUCCESS, AVCT_CreateConn(&handle, &cc, kRawAddress));
+  ASSERT_EQ(AVCT_SUCCESS, AVCT_CreateBrowse(handle, AVCT_ROLE_INITIATOR));
+
+  // Provide appropriate l2cap remote responses to open both channels
+  tL2CAP_CFG_INFO l2cap_cfg{};
+  callback_map[BT_PSM_AVCTP].pL2CA_ConnectCfm_Cb(kRemoteCid, tL2CAP_CONN::L2CAP_CONN_OK);
+  callback_map[BT_PSM_AVCTP].pL2CA_ConfigCfm_Cb(kRemoteCid, 0, &l2cap_cfg);
+  callback_map[BT_PSM_AVCTP_BROWSE].pL2CA_ConnectCfm_Cb(kRemoteBrowseCid,
+                                                        tL2CAP_CONN::L2CAP_CONN_OK);
+  callback_map[BT_PSM_AVCTP_BROWSE].pL2CA_ConfigCfm_Cb(kRemoteBrowseCid, 0, &l2cap_cfg);
+
+  // Close the profile by deregistering from l2cap
+  EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_Deregister(BT_PSM_AVCTP));
+  EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_Deregister(BT_PSM_AVCTP_BROWSE));
+  AVCT_Deregister();
+
+  // Ensure that API calls with a cached handle value or any run from a reactor
+  // thread or message loop do not fail
+  ASSERT_EQ(AVCT_SUCCESS, AVCT_RemoveBrowse(handle));
+  ASSERT_EQ(AVCT_SUCCESS, AVCT_RemoveConn(handle));
+}
diff --git a/system/test/headless/Android.bp b/system/test/headless/Android.bp
index 846f9ec..af86f9f 100644
--- a/system/test/headless/Android.bp
+++ b/system/test/headless/Android.bp
@@ -71,6 +71,7 @@
         "packages/modules/Bluetooth/system/stack/include",
     ],
     static_libs: [
+        "aics",
         "android.hardware.audio.common@5.0",
         "android.hardware.common-V2-ndk",
         "android.hardware.common.fmq-V1-ndk",
@@ -119,9 +120,13 @@
         "libudrv-uipc",
         "libz",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libPlatformProperties",
         "libaconfig_storage_read_api_cc",
+        "libbinder",
         "libcrypto",
         "libcutils", // property_get_bool
         "libhidlbase",
diff --git a/system/test/mock/mock_bluetooth_interface.cc b/system/test/mock/mock_bluetooth_interface.cc
index 8fb6163..5937d42 100644
--- a/system/test/mock/mock_bluetooth_interface.cc
+++ b/system/test/mock/mock_bluetooth_interface.cc
@@ -15,6 +15,7 @@
  */
 
 #include <cstdint>
+#include <future>
 
 #include "btif/include/stack_manager_t.h"
 #include "hardware/bluetooth.h"
@@ -72,7 +73,7 @@
 
 static void clean_up_stack(ProfileStopCallback /* stopProfiles */) {}
 
-static void start_up_rust_module_async() {}
+static void start_up_rust_module_async(std::promise<void> /* promise */) {}
 
 static void shut_down_rust_module_async() {}
 
diff --git a/system/test/mock/mock_stack_acl.cc b/system/test/mock/mock_stack_acl.cc
index 1e85c50..d4504ba 100644
--- a/system/test/mock/mock_stack_acl.cc
+++ b/system/test/mock/mock_stack_acl.cc
@@ -27,17 +27,19 @@
 #include <string>
 
 #include "hci/class_of_device.h"
+#include "stack/include/acl_api.h"
 #include "stack/include/acl_client_callbacks.h"
+#include "stack/include/acl_hci_link_interface.h"
 #include "stack/include/bt_hdr.h"
+#include "stack/include/btm_ble_api.h"
+#include "stack/include/inq_hci_link_interface.h"
+#include "stack/include/l2cap_acl_interface.h"
 #include "test/common/mock_functions.h"
 #include "types/raw_address.h"
 
 // Mocked compile conditionals, if any
 // Mocked internal structures, if any
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 namespace test {
 namespace mock {
 namespace stack_acl {
@@ -63,9 +65,7 @@
         acl_peer_supports_ble_connection_subrating_host;
 struct acl_refresh_remote_address acl_refresh_remote_address;
 struct acl_set_peer_le_features_from_handle acl_set_peer_le_features_from_handle;
-struct acl_get_connection_from_address acl_get_connection_from_address;
 struct btm_acl_for_bda btm_acl_for_bda;
-struct acl_get_connection_from_handle acl_get_connection_from_handle;
 struct BTM_ReadFailedContactCounter BTM_ReadFailedContactCounter;
 struct BTM_ReadTxPower BTM_ReadTxPower;
 struct BTM_SetLinkSuperTout BTM_SetLinkSuperTout;
@@ -98,28 +98,18 @@
 struct btm_acl_process_sca_cmpl_pkt btm_acl_process_sca_cmpl_pkt;
 struct btm_acl_removed btm_acl_removed;
 struct btm_acl_role_changed btm_acl_role_changed;
-struct btm_acl_update_conn_addr btm_acl_update_conn_addr;
-struct btm_ble_refresh_local_resolvable_private_addr btm_ble_refresh_local_resolvable_private_addr;
 struct btm_cont_rswitch_from_handle btm_cont_rswitch_from_handle;
 struct btm_establish_continue_from_address btm_establish_continue_from_address;
-struct btm_process_remote_ext_features btm_process_remote_ext_features;
-struct btm_process_remote_version_complete btm_process_remote_version_complete;
 struct btm_read_automatic_flush_timeout_complete btm_read_automatic_flush_timeout_complete;
 struct btm_read_failed_contact_counter_complete btm_read_failed_contact_counter_complete;
-struct btm_read_failed_contact_counter_timeout btm_read_failed_contact_counter_timeout;
-struct btm_read_remote_ext_features btm_read_remote_ext_features;
 struct btm_read_remote_ext_features_complete btm_read_remote_ext_features_complete;
 struct btm_read_remote_ext_features_complete_raw btm_read_remote_ext_features_complete_raw;
 struct btm_read_remote_ext_features_failed btm_read_remote_ext_features_failed;
 struct btm_read_remote_version_complete btm_read_remote_version_complete;
 struct btm_read_rssi_complete btm_read_rssi_complete;
-struct btm_read_rssi_timeout btm_read_rssi_timeout;
 struct btm_read_tx_power_complete btm_read_tx_power_complete;
-struct btm_read_tx_power_timeout btm_read_tx_power_timeout;
 struct btm_rejectlist_role_change_device btm_rejectlist_role_change_device;
-struct btm_set_link_policy btm_set_link_policy;
 struct btm_set_packet_types_from_address btm_set_packet_types_from_address;
-struct hci_btm_set_link_supervision_timeout hci_btm_set_link_supervision_timeout;
 struct on_acl_br_edr_connected on_acl_br_edr_connected;
 struct on_acl_br_edr_failed on_acl_br_edr_failed;
 struct BTM_unblock_role_switch_and_sniff_mode_for BTM_unblock_role_switch_and_sniff_mode_for;
@@ -210,18 +200,10 @@
   inc_func_call_count(__func__);
   test::mock::stack_acl::acl_send_data_packet_br_edr(bd_addr, p_buf);
 }
-tACL_CONN* acl_get_connection_from_address(const RawAddress& bd_addr, tBT_TRANSPORT transport) {
-  inc_func_call_count(__func__);
-  return test::mock::stack_acl::acl_get_connection_from_address(bd_addr, transport);
-}
 tACL_CONN* btm_acl_for_bda(const RawAddress& bd_addr, tBT_TRANSPORT transport) {
   inc_func_call_count(__func__);
   return test::mock::stack_acl::btm_acl_for_bda(bd_addr, transport);
 }
-tACL_CONN* acl_get_connection_from_handle(uint16_t handle) {
-  inc_func_call_count(__func__);
-  return test::mock::stack_acl::acl_get_connection_from_handle(handle);
-}
 tBTM_STATUS BTM_ReadFailedContactCounter(const RawAddress& remote_bda, tBTM_CMPL_CB* p_cb) {
   inc_func_call_count(__func__);
   return test::mock::stack_acl::BTM_ReadFailedContactCounter(remote_bda, p_cb);
@@ -354,15 +336,6 @@
   inc_func_call_count(__func__);
   test::mock::stack_acl::btm_acl_role_changed(hci_status, bd_addr, new_role);
 }
-void btm_acl_update_conn_addr(uint16_t handle, const RawAddress& address) {
-  inc_func_call_count(__func__);
-  test::mock::stack_acl::btm_acl_update_conn_addr(handle, address);
-}
-void btm_ble_refresh_local_resolvable_private_addr(const RawAddress& pseudo_addr,
-                                                   const RawAddress& local_rpa) {
-  inc_func_call_count(__func__);
-  test::mock::stack_acl::btm_ble_refresh_local_resolvable_private_addr(pseudo_addr, local_rpa);
-}
 void btm_cont_rswitch_from_handle(uint16_t hci_handle) {
   inc_func_call_count(__func__);
   test::mock::stack_acl::btm_cont_rswitch_from_handle(hci_handle);
@@ -371,16 +344,6 @@
   inc_func_call_count(__func__);
   test::mock::stack_acl::btm_establish_continue_from_address(bda, transport);
 }
-void btm_process_remote_ext_features(tACL_CONN* p_acl_cb, uint8_t max_page_number) {
-  inc_func_call_count(__func__);
-  test::mock::stack_acl::btm_process_remote_ext_features(p_acl_cb, max_page_number);
-}
-void btm_process_remote_version_complete(uint8_t status, uint16_t handle, uint8_t lmp_version,
-                                         uint16_t manufacturer, uint16_t lmp_subversion) {
-  inc_func_call_count(__func__);
-  test::mock::stack_acl::btm_process_remote_version_complete(status, handle, lmp_version,
-                                                             manufacturer, lmp_subversion);
-}
 void btm_read_automatic_flush_timeout_complete(uint8_t* p) {
   inc_func_call_count(__func__);
   test::mock::stack_acl::btm_read_automatic_flush_timeout_complete(p);
@@ -389,14 +352,6 @@
   inc_func_call_count(__func__);
   test::mock::stack_acl::btm_read_failed_contact_counter_complete(p);
 }
-void btm_read_failed_contact_counter_timeout(void* data) {
-  inc_func_call_count(__func__);
-  test::mock::stack_acl::btm_read_failed_contact_counter_timeout(data);
-}
-void btm_read_remote_ext_features(uint16_t handle, uint8_t page_number) {
-  inc_func_call_count(__func__);
-  test::mock::stack_acl::btm_read_remote_ext_features(handle, page_number);
-}
 void btm_read_remote_ext_features_complete(uint16_t handle, uint8_t page_num, uint8_t max_page,
                                            uint8_t* features) {
   inc_func_call_count(__func__);
@@ -421,34 +376,18 @@
   inc_func_call_count(__func__);
   test::mock::stack_acl::btm_read_rssi_complete(p, evt_len);
 }
-void btm_read_rssi_timeout(void* data) {
-  inc_func_call_count(__func__);
-  test::mock::stack_acl::btm_read_rssi_timeout(data);
-}
 void btm_read_tx_power_complete(uint8_t* p, uint16_t evt_len, bool is_ble) {
   inc_func_call_count(__func__);
   test::mock::stack_acl::btm_read_tx_power_complete(p, evt_len, is_ble);
 }
-void btm_read_tx_power_timeout(void* data) {
-  inc_func_call_count(__func__);
-  test::mock::stack_acl::btm_read_tx_power_timeout(data);
-}
 void btm_rejectlist_role_change_device(const RawAddress& bd_addr, uint8_t hci_status) {
   inc_func_call_count(__func__);
   test::mock::stack_acl::btm_rejectlist_role_change_device(bd_addr, hci_status);
 }
-void btm_set_link_policy(tACL_CONN* conn, tLINK_POLICY policy) {
-  inc_func_call_count(__func__);
-  test::mock::stack_acl::btm_set_link_policy(conn, policy);
-}
 void btm_set_packet_types_from_address(const RawAddress& bd_addr, uint16_t pkt_types) {
   inc_func_call_count(__func__);
   test::mock::stack_acl::btm_set_packet_types_from_address(bd_addr, pkt_types);
 }
-void hci_btm_set_link_supervision_timeout(tACL_CONN& link, uint16_t timeout) {
-  inc_func_call_count(__func__);
-  test::mock::stack_acl::hci_btm_set_link_supervision_timeout(link, timeout);
-}
 void btm_connection_request(const RawAddress& bda, const bluetooth::hci::ClassOfDevice& cod) {
   test::mock::stack_acl::btm_connection_request(bda, cod);
 }
diff --git a/system/test/mock/mock_stack_acl.h b/system/test/mock/mock_stack_acl.h
index a454f7f..8e123f3 100644
--- a/system/test/mock/mock_stack_acl.h
+++ b/system/test/mock/mock_stack_acl.h
@@ -221,17 +221,6 @@
   bool operator()(uint16_t hci_handle, const uint8_t* p) { return body(hci_handle, p); }
 };
 extern struct acl_set_peer_le_features_from_handle acl_set_peer_le_features_from_handle;
-// Name: acl_get_connection_from_address
-// Params: const RawAddress& bd_addr, tBT_TRANSPORT transport
-// Returns: tACL_CONN*
-struct acl_get_connection_from_address {
-  std::function<tACL_CONN*(const RawAddress& bd_addr, tBT_TRANSPORT transport)> body{
-          [](const RawAddress& /* bd_addr */, tBT_TRANSPORT /* transport */) { return nullptr; }};
-  tACL_CONN* operator()(const RawAddress& bd_addr, tBT_TRANSPORT transport) {
-    return body(bd_addr, transport);
-  }
-};
-extern struct acl_get_connection_from_address acl_get_connection_from_address;
 // Name: btm_acl_for_bda
 // Params: const RawAddress& bd_addr, tBT_TRANSPORT transport
 // Returns: tACL_CONN*
@@ -243,14 +232,6 @@
   }
 };
 extern struct btm_acl_for_bda btm_acl_for_bda;
-// Name: acl_get_connection_from_handle
-// Params: uint16_t handle
-// Returns: tACL_CONN*
-struct acl_get_connection_from_handle {
-  std::function<tACL_CONN*(uint16_t handle)> body{[](uint16_t /* handle */) { return nullptr; }};
-  tACL_CONN* operator()(uint16_t handle) { return body(handle); }
-};
-extern struct acl_get_connection_from_handle acl_get_connection_from_handle;
 // Name: BTM_ReadFailedContactCounter
 // Params: const RawAddress& remote_bda, tBTM_CMPL_CB* p_cb
 // Returns: tBTM_STATUS
@@ -578,27 +559,6 @@
   }
 };
 extern struct btm_acl_role_changed btm_acl_role_changed;
-// Name: btm_acl_update_conn_addr
-// Params: uint16_t handle, const RawAddress& address
-// Returns: void
-struct btm_acl_update_conn_addr {
-  std::function<void(uint16_t handle, const RawAddress& address)> body{
-          [](uint16_t /* handle */, const RawAddress& /* address */) { ; }};
-  void operator()(uint16_t handle, const RawAddress& address) { body(handle, address); }
-};
-extern struct btm_acl_update_conn_addr btm_acl_update_conn_addr;
-// Name: btm_ble_refresh_local_resolvable_private_addr
-// Params:  const RawAddress& pseudo_addr, const RawAddress& local_rpa
-// Returns: void
-struct btm_ble_refresh_local_resolvable_private_addr {
-  std::function<void(const RawAddress& pseudo_addr, const RawAddress& local_rpa)> body{
-          [](const RawAddress& /* pseudo_addr */, const RawAddress& /* local_rpa */) { ; }};
-  void operator()(const RawAddress& pseudo_addr, const RawAddress& local_rpa) {
-    body(pseudo_addr, local_rpa);
-  }
-};
-extern struct btm_ble_refresh_local_resolvable_private_addr
-        btm_ble_refresh_local_resolvable_private_addr;
 // Name: btm_cont_rswitch_from_handle
 // Params: uint16_t hci_handle
 // Returns: void
@@ -616,29 +576,6 @@
   void operator()(const RawAddress& bda, tBT_TRANSPORT transport) { body(bda, transport); }
 };
 extern struct btm_establish_continue_from_address btm_establish_continue_from_address;
-// Name: btm_process_remote_ext_features
-// Params: tACL_CONN* p_acl_cb, uint8_t max_page_number
-// Returns: void
-struct btm_process_remote_ext_features {
-  std::function<void(tACL_CONN* p_acl_cb, uint8_t max_page_number)> body{
-          [](tACL_CONN* /* p_acl_cb */, uint8_t /* max_page_number */) { ; }};
-  void operator()(tACL_CONN* p_acl_cb, uint8_t max_page_number) { body(p_acl_cb, max_page_number); }
-};
-extern struct btm_process_remote_ext_features btm_process_remote_ext_features;
-// Name: btm_process_remote_version_complete
-// Params: uint8_t status, uint16_t handle, uint8_t lmp_version, uint16_t
-// manufacturer, uint16_t lmp_subversion Returns: void
-struct btm_process_remote_version_complete {
-  std::function<void(uint8_t status, uint16_t handle, uint8_t lmp_version, uint16_t manufacturer,
-                     uint16_t lmp_subversion)>
-          body{[](uint8_t /* status */, uint16_t /* handle */, uint8_t /* lmp_version */,
-                  uint16_t /* manufacturer */, uint16_t /* lmp_subversion */) { ; }};
-  void operator()(uint8_t status, uint16_t handle, uint8_t lmp_version, uint16_t manufacturer,
-                  uint16_t lmp_subversion) {
-    body(status, handle, lmp_version, manufacturer, lmp_subversion);
-  }
-};
-extern struct btm_process_remote_version_complete btm_process_remote_version_complete;
 // Name: btm_read_automatic_flush_timeout_complete
 // Params: uint8_t* p
 // Returns: void
@@ -655,23 +592,6 @@
   void operator()(uint8_t* p) { body(p); }
 };
 extern struct btm_read_failed_contact_counter_complete btm_read_failed_contact_counter_complete;
-// Name: btm_read_failed_contact_counter_timeout
-// Params: void* data
-// Returns: void
-struct btm_read_failed_contact_counter_timeout {
-  std::function<void(void* data)> body{[](void* /* data */) { ; }};
-  void operator()(void* data) { body(data); }
-};
-extern struct btm_read_failed_contact_counter_timeout btm_read_failed_contact_counter_timeout;
-// Name: btm_read_remote_ext_features
-// Params: uint16_t handle, uint8_t page_number
-// Returns: void
-struct btm_read_remote_ext_features {
-  std::function<void(uint16_t handle, uint8_t page_number)> body{
-          [](uint16_t /* handle */, uint8_t /* page_number */) { ; }};
-  void operator()(uint16_t handle, uint8_t page_number) { body(handle, page_number); }
-};
-extern struct btm_read_remote_ext_features btm_read_remote_ext_features;
 // Name: btm_read_remote_ext_features_complete
 // Params: uint16_t handle, uint8_t page_num, uint8_t max_page, uint8_t*
 // features Returns: void
@@ -725,14 +645,6 @@
   void operator()(uint8_t* p, uint16_t evt_len) { body(p, evt_len); }
 };
 extern struct btm_read_rssi_complete btm_read_rssi_complete;
-// Name: btm_read_rssi_timeout
-// Params: void* data
-// Returns: void
-struct btm_read_rssi_timeout {
-  std::function<void(void* data)> body{[](void* /* data */) { ; }};
-  void operator()(void* data) { body(data); }
-};
-extern struct btm_read_rssi_timeout btm_read_rssi_timeout;
 // Name: btm_read_tx_power_complete
 // Params: uint8_t* p, bool is_ble
 // Returns: void
@@ -742,14 +654,6 @@
   void operator()(uint8_t* p, uint16_t evt_len, bool is_ble) { body(p, evt_len, is_ble); }
 };
 extern struct btm_read_tx_power_complete btm_read_tx_power_complete;
-// Name: btm_read_tx_power_timeout
-// Params: void* data
-// Returns: void
-struct btm_read_tx_power_timeout {
-  std::function<void(void* data)> body{[](void* /* data */) { ; }};
-  void operator()(void* data) { body(data); }
-};
-extern struct btm_read_tx_power_timeout btm_read_tx_power_timeout;
 // Name: btm_rejectlist_role_change_device
 // Params: const RawAddress& bd_addr, uint8_t hci_status
 // Returns: void
@@ -759,15 +663,6 @@
   void operator()(const RawAddress& bd_addr, uint8_t hci_status) { body(bd_addr, hci_status); }
 };
 extern struct btm_rejectlist_role_change_device btm_rejectlist_role_change_device;
-// Name: btm_set_link_policy
-// Params: tACL_CONN* conn, tLINK_POLICY policy
-// Returns: void
-struct btm_set_link_policy {
-  std::function<void(tACL_CONN* conn, tLINK_POLICY policy)> body{
-          [](tACL_CONN* /* conn */, tLINK_POLICY /* policy */) { ; }};
-  void operator()(tACL_CONN* conn, tLINK_POLICY policy) { body(conn, policy); }
-};
-extern struct btm_set_link_policy btm_set_link_policy;
 // Name: btm_set_packet_types_from_address
 // Params: const RawAddress& bd_addr, uint16_t pkt_types
 // Returns: void
@@ -777,15 +672,6 @@
   void operator()(const RawAddress& bd_addr, uint16_t pkt_types) { body(bd_addr, pkt_types); }
 };
 extern struct btm_set_packet_types_from_address btm_set_packet_types_from_address;
-// Name: hci_btm_set_link_supervision_timeout
-// Params: tACL_CONN& link, uint16_t timeout
-// Returns: void
-struct hci_btm_set_link_supervision_timeout {
-  std::function<void(tACL_CONN& link, uint16_t timeout)> body{
-          [](tACL_CONN& /* link */, uint16_t /* timeout */) { ; }};
-  void operator()(tACL_CONN& link, uint16_t timeout) { body(link, timeout); }
-};
-extern struct hci_btm_set_link_supervision_timeout hci_btm_set_link_supervision_timeout;
 // Name: on_acl_br_edr_connected
 // Params: const RawAddress& bda, uint16_t handle, uint8_t enc_mode, bool
 // locally_initiated Returns: void
diff --git a/system/test/mock/mock_stack_acl_ble.cc b/system/test/mock/mock_stack_acl_ble.cc
index e403a92..8f888dc 100644
--- a/system/test/mock/mock_stack_acl_ble.cc
+++ b/system/test/mock/mock_stack_acl_ble.cc
@@ -21,15 +21,13 @@
 
 #include <cstdint>
 
+#include "stack/include/ble_acl_interface.h"
 #include "stack/include/hci_error_code.h"
 #include "test/common/mock_functions.h"
 #include "types/ble_address_with_type.h"
 #include "types/hci_role.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 void acl_ble_connection_fail(const tBLE_BD_ADDR& /* address_with_type */, uint16_t /* handle */,
                              bool /* enhanced */, tHCI_STATUS /* status */) {
   inc_func_call_count(__func__);
diff --git a/system/test/mock/mock_stack_acl_btm_pm.cc b/system/test/mock/mock_stack_acl_btm_pm.cc
index 8ae9f01..bc1d3de 100644
--- a/system/test/mock/mock_stack_acl_btm_pm.cc
+++ b/system/test/mock/mock_stack_acl_btm_pm.cc
@@ -22,13 +22,12 @@
 #include <cstdint>
 
 #include "stack/btm/power_mode.h"
+#include "stack/include/acl_api.h"
+#include "stack/include/acl_hci_link_interface.h"
 #include "stack/include/btm_status.h"
 #include "test/common/mock_functions.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 bool BTM_ReadPowerMode(const RawAddress& /* remote_bda */, tBTM_PM_MODE* /* p_mode */) {
   inc_func_call_count(__func__);
   return false;
@@ -81,7 +80,3 @@
   inc_func_call_count(__func__);
 }
 void btm_pm_reset(void) { inc_func_call_count(__func__); }
-void process_ssr_event(tHCI_STATUS /* status */, uint16_t /* handle */, uint16_t /* max_tx_lat */,
-                       uint16_t /* max_rx_lat */) {
-  inc_func_call_count(__func__);
-}
diff --git a/system/test/mock/mock_stack_avrc_api.cc b/system/test/mock/mock_stack_avrc_api.cc
index 655690c..eedf433 100644
--- a/system/test/mock/mock_stack_avrc_api.cc
+++ b/system/test/mock/mock_stack_avrc_api.cc
@@ -77,6 +77,3 @@
 }
 void avrc_flush_cmd_q(uint8_t /* handle */) { inc_func_call_count(__func__); }
 void avrc_send_next_vendor_cmd(uint8_t /* handle */) { inc_func_call_count(__func__); }
-void avrc_start_cmd_timer(uint8_t /* handle */, uint8_t /* label */, uint8_t /* msg_mask */) {
-  inc_func_call_count(__func__);
-}
diff --git a/system/test/mock/mock_stack_gap_ble.cc b/system/test/mock/mock_stack_gap_ble.cc
index 941ca7d..3f852ff 100644
--- a/system/test/mock/mock_stack_gap_ble.cc
+++ b/system/test/mock/mock_stack_gap_ble.cc
@@ -21,13 +21,11 @@
 
 #include <cstdint>
 
+#include "stack/gap/gap_int.h"
 #include "stack/include/gap_api.h"
 #include "test/common/mock_functions.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 bool GAP_BleCancelReadPeerDevName(const RawAddress& /* peer_bda */) {
   inc_func_call_count(__func__);
   return false;
diff --git a/system/test/mock/mock_stack_gap_conn.cc b/system/test/mock/mock_stack_gap_conn.cc
index 6fb9826..19034a9 100644
--- a/system/test/mock/mock_stack_gap_conn.cc
+++ b/system/test/mock/mock_stack_gap_conn.cc
@@ -24,9 +24,6 @@
 #include "test/common/mock_functions.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 const RawAddress* GAP_ConnGetRemoteAddr(uint16_t /* gap_handle */) {
   inc_func_call_count(__func__);
   return nullptr;
@@ -65,6 +62,3 @@
   return 0;
 }
 void GAP_Init(void) { inc_func_call_count(__func__); }
-void gap_tx_complete_ind(uint16_t /* l2cap_cid */, uint16_t /* sdu_sent */) {
-  inc_func_call_count(__func__);
-}
diff --git a/system/test/mock/mock_stack_gatt.cc b/system/test/mock/mock_stack_gatt.cc
index 297ec57..a6dfb1d 100644
--- a/system/test/mock/mock_stack_gatt.cc
+++ b/system/test/mock/mock_stack_gatt.cc
@@ -26,17 +26,8 @@
 #include "test/common/mock_functions.h"
 #include "types/bluetooth/uuid.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 using namespace bluetooth;
 
-tGATT_HDL_LIST_ELEM elem;  // gatt_add_an_item_to_list
-
-tGATT_HDL_LIST_ELEM& gatt_add_an_item_to_list(uint16_t /* s_handle */) {
-  inc_func_call_count(__func__);
-  return elem;
-}
 tGATT_STATUS GATTC_Discover(uint16_t /* conn_id */, tGATT_DISC_TYPE /* disc_type */,
                             uint16_t /* start_handle */, uint16_t /* end_handle */,
                             const Uuid& /* uuid */) {
diff --git a/system/test/mock/mock_stack_gatt_api.cc b/system/test/mock/mock_stack_gatt_api.cc
index cde6c3f..2822d79 100644
--- a/system/test/mock/mock_stack_gatt_api.cc
+++ b/system/test/mock/mock_stack_gatt_api.cc
@@ -27,9 +27,6 @@
 
 #include "test/common/mock_functions.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 // Original usings
 using bluetooth::Uuid;
 
@@ -66,8 +63,6 @@
 struct GATT_Register GATT_Register;
 struct GATT_SetIdleTimeout GATT_SetIdleTimeout;
 struct GATT_StartIf GATT_StartIf;
-// struct gatt_add_an_item_to_list gatt_add_an_item_to_list;
-struct is_active_service is_active_service;
 
 }  // namespace stack_gatt_api
 }  // namespace mock
@@ -99,9 +94,6 @@
 bool GATT_GetConnIdIfConnected::return_value = false;
 bool GATT_GetConnectionInfor::return_value = false;
 tGATT_IF GATT_Register::return_value = 0;
-// tGATT_HDL_LIST_ELEM gatt_add_an_item_to_list::return_value = { .svc_db = {},
-// .asgn_range = {}};
-bool is_active_service::return_value = false;
 
 }  // namespace stack_gatt_api
 }  // namespace mock
@@ -230,14 +222,6 @@
   inc_func_call_count(__func__);
   test::mock::stack_gatt_api::GATT_StartIf(gatt_if);
 }
-// tGATT_HDL_LIST_ELEM& gatt_add_an_item_to_list(uint16_t s_handle) {
-//   inc_func_call_count(__func__);
-//   return test::mock::stack_gatt_api::gatt_add_an_item_to_list(s_handle);
-// }
-bool is_active_service(const Uuid& app_uuid128, Uuid* p_svc_uuid, uint16_t start_handle) {
-  inc_func_call_count(__func__);
-  return test::mock::stack_gatt_api::is_active_service(app_uuid128, p_svc_uuid, start_handle);
-}
 // Mocked functions complete
 //
 bool GATT_Connect(tGATT_IF gatt_if, const RawAddress& bd_addr, tBTM_BLE_CONN_TYPE connection_type,
diff --git a/system/test/mock/mock_stack_gatt_api.h b/system/test/mock/mock_stack_gatt_api.h
index 74bf566..63d4e0f 100644
--- a/system/test/mock/mock_stack_gatt_api.h
+++ b/system/test/mock/mock_stack_gatt_api.h
@@ -398,33 +398,6 @@
 };
 extern struct GATT_StartIf GATT_StartIf;
 
-// // Name: gatt_add_an_item_to_list
-// // Params: uint16_t s_handle
-// // Return: tGATT_HDL_LIST_ELEM&
-// struct gatt_add_an_item_to_list {
-//   static tGATT_HDL_LIST_ELEM return_value;
-//   std::function<tGATT_HDL_LIST_ELEM&(uint16_t s_handle)> body{
-//       [](uint16_t s_handle) { return return_value; }};
-//   tGATT_HDL_LIST_ELEM& operator()(uint16_t s_handle) { return body(s_handle);
-//   };
-// };
-// extern struct gatt_add_an_item_to_list gatt_add_an_item_to_list;
-
-// Name: is_active_service
-// Params: const Uuid& app_uuid128, Uuid* p_svc_uuid, uint16_t start_handle
-// Return: bool
-struct is_active_service {
-  static bool return_value;
-  std::function<bool(const Uuid& app_uuid128, Uuid* p_svc_uuid, uint16_t start_handle)> body{
-          [](const Uuid& /* app_uuid128 */, Uuid* /* p_svc_uuid */, uint16_t /* start_handle */) {
-            return return_value;
-          }};
-  bool operator()(const Uuid& app_uuid128, Uuid* p_svc_uuid, uint16_t start_handle) {
-    return body(app_uuid128, p_svc_uuid, start_handle);
-  }
-};
-extern struct is_active_service is_active_service;
-
 }  // namespace stack_gatt_api
 }  // namespace mock
 }  // namespace test
diff --git a/system/test/mock/mock_stack_gatt_attr.cc b/system/test/mock/mock_stack_gatt_attr.cc
index 6de9539..4e46e87 100644
--- a/system/test/mock/mock_stack_gatt_attr.cc
+++ b/system/test/mock/mock_stack_gatt_attr.cc
@@ -28,15 +28,11 @@
 #include "types/bt_transport.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 uint16_t gatt_profile_find_conn_id_by_bd_addr(const RawAddress& /* remote_bda */) {
   inc_func_call_count(__func__);
   return 0;
 }
-bool gatt_profile_get_eatt_support(const RawAddress& /* remote_bda */,
-                                   base::OnceCallback<void(const RawAddress&, bool)> /* cb */) {
+bool gatt_profile_get_eatt_support(const RawAddress& /* remote_bda */) {
   inc_func_call_count(__func__);
   return false;
 }
@@ -48,31 +44,10 @@
   inc_func_call_count(__func__);
   return false;
 }
-tGATT_PROFILE_CLCB* gatt_profile_clcb_alloc(uint16_t /* conn_id */, const RawAddress& /* bda */,
-                                            tBT_TRANSPORT /* tranport */) {
-  inc_func_call_count(__func__);
-  return nullptr;
-}
-tGATT_STATUS proc_read_req(uint16_t /* conn_id */, tGATTS_REQ_TYPE, tGATT_READ_REQ* /* p_data */,
-                           tGATTS_RSP* /* p_rsp */) {
-  inc_func_call_count(__func__);
-  return GATT_SUCCESS;
-}
-tGATT_STATUS proc_write_req(uint16_t /* conn_id */, tGATTS_REQ_TYPE,
-                            tGATT_WRITE_REQ* /* p_data */) {
-  inc_func_call_count(__func__);
-  return GATT_SUCCESS;
-}
-tGATT_STATUS read_attr_value(uint16_t /* conn_id */, uint16_t /* handle */,
-                             tGATT_VALUE* /* p_value */, bool /* is_long */) {
-  inc_func_call_count(__func__);
-  return GATT_SUCCESS;
-}
 void GATT_ConfigServiceChangeCCC(const RawAddress& /* remote_bda */, bool /* enable */,
                                  tBT_TRANSPORT /* transport */) {
   inc_func_call_count(__func__);
 }
-void gatt_profile_clcb_dealloc(tGATT_PROFILE_CLCB* /* p_clcb */) { inc_func_call_count(__func__); }
 void gatt_profile_db_init(void) { inc_func_call_count(__func__); }
 void gatt_sr_init_cl_status(tGATT_TCB& /* tcb */) { inc_func_call_count(__func__); }
 void gatt_sr_update_cl_status(tGATT_TCB& /* tcb */, bool /* chg_aware */) {
diff --git a/system/test/mock/mock_stack_gatt_auth.cc b/system/test/mock/mock_stack_gatt_auth.cc
index 1e59e81..0351bf3 100644
--- a/system/test/mock/mock_stack_gatt_auth.cc
+++ b/system/test/mock/mock_stack_gatt_auth.cc
@@ -26,17 +26,10 @@
 #include "test/common/mock_functions.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 bool gatt_security_check_start(tGATT_CLCB* /* p_clcb */) {
   inc_func_call_count(__func__);
   return false;
 }
-tGATT_SEC_ACTION gatt_determine_sec_act(tGATT_CLCB* /* p_clcb */) {
-  inc_func_call_count(__func__);
-  return GATT_SEC_NONE;
-}
 tGATT_SEC_ACTION gatt_get_sec_act(tGATT_TCB* /* p_tcb */) {
   inc_func_call_count(__func__);
   return GATT_SEC_NONE;
diff --git a/system/test/mock/mock_stack_gatt_main.cc b/system/test/mock/mock_stack_gatt_main.cc
index 8ef0677..2c09b4a 100644
--- a/system/test/mock/mock_stack_gatt_main.cc
+++ b/system/test/mock/mock_stack_gatt_main.cc
@@ -21,13 +21,11 @@
 
 #include "stack/gatt/gatt_int.h"
 #include "stack/include/bt_hdr.h"
+#include "stack/include/gatt_api.h"
 #include "stack/include/l2cap_interface.h"
 #include "test/common/mock_functions.h"
 #include "types/raw_address.h"
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 void gatt_init(void) { inc_func_call_count(__func__); }
 bool gatt_act_connect(tGATT_REG* /* p_reg */, const RawAddress& /* bd_addr */,
                       tBLE_ADDR_TYPE /* addr_type */, tBT_TRANSPORT /* transport */,
@@ -35,12 +33,6 @@
   inc_func_call_count(__func__);
   return false;
 }
-bool gatt_connect(const RawAddress& /* rem_bda */, tGATT_TCB* /* p_tcb */,
-                  tBLE_ADDR_TYPE /* addr_type */, tBT_TRANSPORT /* transport */,
-                  uint8_t /* initiating_phys */, tGATT_IF /* gatt_if */) {
-  inc_func_call_count(__func__);
-  return false;
-}
 void gatt_cancel_connect(const RawAddress& /* bd_addr */, tBT_TRANSPORT /* transport*/) {
   inc_func_call_count(__func__);
 }
@@ -64,16 +56,6 @@
 }
 void gatt_free(void) { inc_func_call_count(__func__); }
 void gatt_init_srv_chg(void) { inc_func_call_count(__func__); }
-void gatt_l2cif_config_cfm_cback(uint16_t /* lcid */, uint16_t /* initiator */,
-                                 tL2CAP_CFG_INFO* /* p_cfg */) {
-  inc_func_call_count(__func__);
-}
-void gatt_l2cif_config_ind_cback(uint16_t /* lcid */, tL2CAP_CFG_INFO* /* p_cfg */) {
-  inc_func_call_count(__func__);
-}
-void gatt_l2cif_disconnect_ind_cback(uint16_t /* lcid */, bool /* ack_needed */) {
-  inc_func_call_count(__func__);
-}
 void gatt_notify_conn_update(const RawAddress& /* remote */, uint16_t /* interval */,
                              uint16_t /* latency */, uint16_t /* timeout */,
                              tHCI_STATUS /* status */) {
diff --git a/system/test/mock/mock_stack_smp_act.cc b/system/test/mock/mock_stack_smp_act.cc
index 486cf5d..2802fb6 100644
--- a/system/test/mock/mock_stack_smp_act.cc
+++ b/system/test/mock/mock_stack_smp_act.cc
@@ -26,15 +26,13 @@
 #include <cstdint>
 
 // Original included files, if any
+#include "stack/include/smp_api.h"
 #include "test/common/mock_functions.h"
 #include "types/raw_address.h"
 
 // Mocked compile conditionals, if any
 // Mocked internal structures, if any
 
-// TODO(b/369381361) Enfore -Wmissing-prototypes
-#pragma GCC diagnostic ignored "-Wmissing-prototypes"
-
 namespace test {
 namespace mock {
 namespace stack_smp_act {
diff --git a/system/test/suite/Android.bp b/system/test/suite/Android.bp
index 09e8fda..d495cc0 100644
--- a/system/test/suite/Android.bp
+++ b/system/test/suite/Android.bp
@@ -120,6 +120,7 @@
         "gatt/gatt_unittest.cc",
     ],
     static_libs: [
+        "aics",
         "bluetooth_flags_c_lib_for_test",
         "libbluetooth_crypto_toolbox",
         "libbluetooth_gd",
@@ -133,9 +134,13 @@
         "libflags_rust_cpp_bridge",
         "libprotobuf-cpp-lite",
     ],
+    aidl: {
+        libs: ["bluetooth_constants"],
+    },
     shared_libs: [
         "libaconfig_storage_read_api_cc",
         "libbase",
+        "libbinder",
         "server_configurable_flags",
     ],
     generated_headers: [